#include "db_headers.h"
#include "db_entry.h"
#include "db_dictionary.h"
#include "db_dictlog.h"
#include "db_vers.h"
#include "nisdb_mt.h"
#include "nisdb_rw.h"
#include "ldap_parse.h"
#include "ldap_map.h"
#include "nis_hashitem.h"
#include "ldap_util.h"
#include "nis_db.h"
#include <rpcsvc/nis.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#ifdef TDRPC
#include <sysent.h>
#endif
#include <unistd.h>
#include <syslog.h>
#include <rpc/rpc.h>
typedef bool_t (*db_func)(XDR *, db_table_desc *);
extern db_dictionary *InUseDictionary;
extern db_dictionary *FreeDictionary;
#define DB_MAGIC 0x12340000
#define DB_MAJOR 0
#define DB_MINOR 10
#define DB_VERSION_0_9 (DB_MAGIC|(DB_MAJOR<<8)|9)
#define DB_ORIG_VERSION DB_VERSION_0_9
#define DB_CURRENT_VERSION (DB_MAGIC|DB_MAJOR<<8|DB_MINOR)
vers db_update_version;
#define INMEMORY_ONLY 1
static inline bool_t
db_valid_version(u_int vers)
{
return ((vers == DB_CURRENT_VERSION) || (vers == DB_ORIG_VERSION));
}
static char *
db_version_str(u_int vers)
{
static char vstr[128];
u_int d_major = (vers&0x0000ff00)>>8;
u_int d_minor = (vers&0x000000ff);
sprintf(vstr, "SunSoft, SSM, Version %d.%d", d_major, d_minor);
return (vstr);
}
extern "C" {
bool_t
xdr_db_dict_version(XDR *xdrs, db_dict_version *objp)
{
if (xdrs->x_op == XDR_DECODE) {
if (!xdr_u_int(xdrs, (u_int*) objp) ||
!db_valid_version(((u_int) *objp))) {
syslog(LOG_ERR,
"db_dictionary: invalid dictionary format! Expecting %s",
db_version_str(DB_CURRENT_VERSION));
fprintf(stderr,
"db_dictionary: invalid dictionary format! Expecting %s\n",
db_version_str(DB_CURRENT_VERSION));
exit(1);
}
} else if (!xdr_u_int(xdrs, (u_int*) objp))
return (FALSE);
return (TRUE);
}
void
make_zero(vers* v)
{
v->zero();
}
};
static void
delete_table_desc(db_table_desc *current)
{
if (current->table_name != NULL) delete current->table_name;
if (current->scheme != NULL) delete current->scheme;
if (current->database != NULL) delete current->database;
delete current;
}
db_status
db_dictionary::create_table_desc(char *tab, table_obj* zdesc,
db_table_desc** answer)
{
db_table_desc *newtab;
if ((newtab = new db_table_desc) == NULL) {
FATAL3(
"db_dictionary::add_table: could not allocate space for new table",
DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
}
newtab->database = NULL;
newtab->table_name = NULL;
newtab->next = NULL;
if ((newtab->scheme = new db_scheme(zdesc)) == NULL) {
delete_table_desc(newtab);
FATAL3(
"db_dictionary::add_table: could not allocate space for scheme",
DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
}
if (newtab->scheme->numkeys() == 0) {
WARNING(
"db_dictionary::add_table: could not translate table_obj to scheme");
delete_table_desc(newtab);
return (DB_BADOBJECT);
}
if ((newtab->table_name = strdup(tab)) == NULL) {
delete_table_desc(newtab);
FATAL3(
"db_dictionary::add_table: could not allocate space for table name",
DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
}
if (answer)
*answer = newtab;
return (DB_SUCCESS);
}
static void
delete_bucket(db_table_desc *head)
{
db_table_desc * nextone, *current;
for (current = head; current != NULL; current = nextone) {
nextone = current->next;
delete_table_desc(current);
}
}
static void
delete_dictionary(db_dict_desc *dict)
{
db_table_desc* bucket;
int i;
if (dict) {
if (dict->tables.tables_val) {
for (i = 0; i < dict->tables.tables_len; i++) {
bucket = dict->tables.tables_val[i];
if (bucket)
delete_bucket(bucket);
}
delete dict->tables.tables_val;
}
delete dict;
}
}
static void
relocate_bucket(db_table_desc* bucket,
db_table_desc_p *new_tab, unsigned long hashsize)
{
db_table_desc_p np, next_np, *hp;
for (np = bucket; np != NULL; np = next_np) {
next_np = np->next;
hp = &new_tab[np->hashval % hashsize];
np->next = *hp;
*hp = np;
}
}
static db_status
enumerate_bucket(db_table_desc* bucket, db_status(*func)(db_table_desc *))
{
db_table_desc_p np;
db_status status;
for (np = bucket; np != NULL; np = np->next) {
status = (func)(np);
if (status != DB_SUCCESS)
return (status);
}
return (DB_SUCCESS);
}
static db_table_desc_p
search_bucket(db_table_desc* bucket, unsigned long hval, char *target)
{
db_table_desc_p np;
for (np = bucket; np != NULL; np = np->next) {
if (np->hashval == hval &&
strcmp(np->table_name, target) == 0) {
break;
}
}
return (np);
}
static bool_t
remove_from_bucket(db_table_desc_p bucket,
db_table_desc_p *head, unsigned long hval, char *target,
bool_t free_storage)
{
db_table_desc_p np, dp;
for (dp = np = bucket; np != NULL; np = np->next) {
if (np->hashval == hval &&
strcmp(np->table_name, target) == 0) {
break;
} else {
dp = np;
}
}
if (np == NULL)
return (FALSE);
if (dp == np) {
*head = np->next;
} else {
dp->next = np->next;
}
if (free_storage)
delete_table_desc(np);
return (TRUE);
}
static bool_t
add_to_bucket(db_table_desc_p bucket, db_table_desc **head, db_table_desc_p td)
{
db_table_desc_p curr;
char *target_name;
unsigned long target_hval;
target_name = td->table_name;
target_hval = td->hashval;
for (curr = bucket; curr != NULL; curr = curr->next) {
if (curr->hashval == target_hval &&
strcmp(curr->table_name, target_name) == 0) {
break;
}
}
if (curr != NULL)
return (FALSE);
curr = *head;
*head = td;
td->next = curr;
return (TRUE);
}
static void
print_bucket(db_table_desc *head)
{
db_table_desc *np;
for (np = head; np != NULL; np = np->next) {
printf("%s: %d\n", np->table_name, np->hashval);
}
}
static db_status
print_table(db_table_desc *tbl)
{
if (tbl == NULL)
return (DB_BADTABLE);
printf("%s: %d\n", tbl->table_name, tbl->hashval);
return (DB_SUCCESS);
}
static int hashsizes[] = {
11,
53,
113,
337,
977,
2053,
4073,
8011,
16001,
0
};
#define CALLOC_LIMIT 536870911
static unsigned int
get_next_hashsize(long unsigned oldsize)
{
long unsigned newsize, n;
if (oldsize == 0)
newsize = hashsizes[0];
else {
for (n = 0; newsize = hashsizes[n++]; )
if (oldsize == newsize) {
newsize = hashsizes[n];
break;
}
if (newsize == 0)
newsize = oldsize * 2 + 1;
}
return (newsize);
}
static void
grow_dictionary(db_dict_desc_p dd)
{
unsigned int oldsize, i, new_size;
db_table_desc_p * oldtab, *newtab;
oldsize = dd->tables.tables_len;
oldtab = dd->tables.tables_val;
new_size = get_next_hashsize(oldsize);
if (new_size > CALLOC_LIMIT) {
FATAL("db_dictionary::grow: table size exceeds calloc limit",
DB_MEMORY_LIMIT);
}
if ((newtab = (db_table_desc_p*)
calloc((unsigned int) new_size,
sizeof (db_table_desc_p))) == NULL) {
FATAL("db_dictionary::grow: cannot allocate space",
DB_MEMORY_LIMIT);
}
if (oldtab != NULL) {
for (i = 0; i < oldsize; i++) {
relocate_bucket(oldtab[i], newtab, new_size);
}
delete oldtab;
}
dd->tables.tables_val = newtab;
dd->tables.tables_len = new_size;
}
#define HASHSHIFT 3
#define HASHMASK 0x1f
static u_int
get_hashval(char *value)
{
int i, len;
u_int hval = 0;
len = strlen(value);
for (i = 0; i < len; i++) {
hval = ((hval<<HASHSHIFT)^hval);
hval += (value[i] & HASHMASK);
}
return (hval);
}
static db_status
enumerate_dictionary(db_dict_desc *dd, db_status (*func) (db_table_desc*))
{
int i;
db_table_desc *bucket;
db_status status;
if (dd == NULL)
return (DB_SUCCESS);
for (i = 0; i < dd->tables.tables_len; i++) {
bucket = dd->tables.tables_val[i];
if (bucket) {
status = enumerate_bucket(bucket, func);
if (status != DB_SUCCESS)
return (status);
}
}
return (DB_SUCCESS);
}
static db_table_desc *
search_dictionary(db_dict_desc *dd, char *target)
{
unsigned long hval;
unsigned long bucket;
if (target == NULL || dd == NULL || dd->tables.tables_len == 0)
return (NULL);
hval = get_hashval(target);
bucket = hval % dd->tables.tables_len;
db_table_desc_p fst = dd->tables.tables_val[bucket];
if (fst != NULL)
return (search_bucket(fst, hval, target));
else
return (NULL);
}
static db_status
remove_from_dictionary(db_dict_desc *dd, char *target, bool_t remove_storage)
{
unsigned long hval;
unsigned long bucket;
db_table_desc *fst;
if (target == NULL)
return (DB_NOTUNIQUE);
if (dd == NULL || dd->tables.tables_len == 0)
return (DB_NOTFOUND);
hval = get_hashval(target);
bucket = hval % dd->tables.tables_len;
fst = dd->tables.tables_val[bucket];
if (fst == NULL)
return (DB_NOTFOUND);
if (remove_from_bucket(fst, &dd->tables.tables_val[bucket],
hval, target, remove_storage)) {
--(dd->count);
return (DB_SUCCESS);
} else
return (DB_NOTFOUND);
}
static db_status
add_to_dictionary(db_dict_desc_p dd, db_table_desc *td)
{
unsigned long hval;
char *target;
if (dd == NULL)
return (DB_NOTFOUND);
if (td == NULL)
return (DB_NOTFOUND);
target = td->table_name;
if (target == NULL)
return (DB_NOTUNIQUE);
hval = get_hashval(target);
if (dd->tables.tables_val == NULL)
grow_dictionary(dd);
db_table_desc_p fst;
unsigned long bucket;
bucket = hval % dd->tables.tables_len;
fst = dd->tables.tables_val[bucket];
td->hashval = hval;
if (fst == NULL) {
dd->tables.tables_val[bucket] = td;
} else if (!add_to_bucket(fst, &dd->tables.tables_val[bucket], td)) {
return (DB_NOTUNIQUE);
}
if (++(dd->count) > dd->tables.tables_len)
grow_dictionary(dd);
return (DB_SUCCESS);
}
static bool_t
transfer_aux(XDR* x, pptr tbl)
{
return (xdr_db_dict_desc_p(x, (db_dict_desc_p *) tbl));
}
class pickle_dict_desc: public pickle_file {
public:
pickle_dict_desc(char *f, pickle_mode m) : pickle_file(f, m) {}
int transfer(db_dict_desc_p * dp)
{ return (pickle_file::transfer((pptr) dp, &transfer_aux)); }
};
db_dictionary::db_dictionary()
{
dictionary = NULL;
initialized = FALSE;
filename = NULL;
tmpfilename = NULL;
logfilename = NULL;
logfile = NULL;
logfile_opened = FALSE;
changed = FALSE;
INITRW(dict);
READLOCKOK(dict);
}
int
db_dictionary::db_clone_bucket(db_table_desc *bucket, db_table_desc **clone)
{
u_long size;
XDR xdrs;
char *bufin = NULL;
READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_clone_bucket");
db_func use_this = xdr_db_table_desc;
size = xdr_sizeof((xdrproc_t) use_this, (void *) bucket);
bufin = (char *) calloc(1, (size_t) size * sizeof (char));
if (!bufin) {
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: out of memory");
FATAL3("db_dictionary::insert_modified_table: out of memory",
DB_MEMORY_LIMIT, 0);
}
xdrmem_create(&xdrs, bufin, (size_t) size, XDR_ENCODE);
if (!xdr_db_table_desc(&xdrs, bucket)) {
free(bufin);
xdr_destroy(&xdrs);
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: xdr encode failed");
FATAL3(
"db_dictionary::insert_modified_table: xdr encode failed.",
DB_MEMORY_LIMIT, 0);
}
*clone = (db_table_desc *) calloc(1, (size_t) size * sizeof (char));
if (!*clone) {
xdr_destroy(&xdrs);
free(bufin);
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: out of memory");
FATAL3("db_dictionary::insert_modified_table: out of memory",
DB_MEMORY_LIMIT, 0);
}
xdrmem_create(&xdrs, bufin, (size_t) size, XDR_DECODE);
if (!xdr_db_table_desc(&xdrs, *clone)) {
free(bufin);
free(*clone);
xdr_destroy(&xdrs);
READUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::insert_modified_table: xdr encode failed");
FATAL3(
"db_dictionary::insert_modified_table: xdr encode failed.",
DB_MEMORY_LIMIT, 0);
}
free(bufin);
xdr_destroy(&xdrs);
READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_clone_bucket");
return (1);
}
int
db_dictionary::change_table_name(db_table_desc *clone, char *tok, char *repl)
{
char *newname;
char *loc_end, *loc_beg;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::change_table_name");
while (clone) {
if (strlen(tok) == 0) {
strcat(clone->table_name, repl);
clone = clone->next;
continue;
}
newname = (char *) calloc(1, sizeof (char) *
strlen(clone->table_name) +
strlen(repl) - strlen(tok) + 1);
if (!newname) {
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::change_table_name: out of memory");
FATAL3("db_dictionary::change_table_name: out of memory.",
DB_MEMORY_LIMIT, 0);
}
if (loc_beg = strstr(clone->table_name, tok)) {
loc_end = loc_beg + strlen(tok);
int s = loc_beg - clone->table_name;
memcpy(newname, clone->table_name, s);
strcat(newname + s, repl);
strcat(newname, loc_end);
free(clone->table_name);
clone->table_name = newname;
} else {
free(newname);
}
clone = clone->next;
}
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::change_table_name");
return (1);
}
#ifdef curdict
#undef curdict
#endif
bool_t
db_dictionary::inittemp(char *dictname, db_dictionary& curdict)
{
int status;
db_table_desc_p *newtab;
db_shutdown();
WRITELOCK(this, FALSE, "w db_dictionary::inittemp");
if (initialized) {
WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp");
return (TRUE);
}
pickle_dict_desc f(dictname, PICKLE_READ);
filename = strdup(dictname);
if (filename == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: could not allocate space");
FATAL3("db_dictionary::inittemp: could not allocate space",
DB_MEMORY_LIMIT, FALSE);
}
int len = strlen(filename);
tmpfilename = new char[len+5];
if (tmpfilename == NULL) {
delete filename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: could not allocate space");
FATAL3("db_dictionary::inittemp: could not allocate space",
DB_MEMORY_LIMIT, FALSE);
}
logfilename = new char[len+5];
if (logfilename == NULL) {
delete filename;
delete tmpfilename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: cannot allocate space");
FATAL3("db_dictionary::inittemp: cannot allocate space",
DB_MEMORY_LIMIT, FALSE);
}
sprintf(tmpfilename, "%s.tmp", filename);
sprintf(logfilename, "%s.log", filename);
unlink(tmpfilename);
dictionary = NULL;
if ((status = f.transfer(&dictionary)) < 0) {
initialized = FALSE;
} else if (status == 1) {
dictionary = new db_dict_desc;
if (dictionary == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: could not allocate space");
FATAL3(
"db_dictionary::inittemp: could not allocate space",
DB_MEMORY_LIMIT, FALSE);
}
dictionary->tables.tables_len =
curdict.dictionary->tables.tables_len;
if ((newtab = (db_table_desc_p *) calloc(
(unsigned int) dictionary->tables.tables_len,
sizeof (db_table_desc_p))) == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::inittemp: cannot allocate space");
FATAL3(
"db_dictionary::inittemp: cannot allocate space",
DB_MEMORY_LIMIT, 0);
}
dictionary->tables.tables_val = newtab;
dictionary->count = 0;
dictionary->impl_vers = curdict.dictionary->impl_vers;
initialized = TRUE;
} else
initialized = TRUE;
if (initialized == TRUE) {
changed = FALSE;
reset_log();
}
WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp");
return (initialized);
}
db_status
db_dictionary::massage_dict(char *newdictname, char *tok, char *repl)
{
int retval;
u_int i, tbl_count;
db_status status;
db_table_desc *bucket, *np, *clone, *next_np;
char tail[NIS_MAXNAMELEN];
db_dictionary *tmpptr;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::massage_dict");
if (dictionary == NULL) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"db_dictionary::massage_dict: uninitialized dictionary file");
FATAL3(
"db_dictionary::massage_dict: uninitialized dictionary file.",
DB_INTERNAL_ERROR, DB_INTERNAL_ERROR);
}
if ((tbl_count = dictionary->count) == 0) {
WRITEUNLOCK(this, DB_SUCCESS,
"wu db_dictionary::massage_dict");
return (DB_SUCCESS);
}
if ((status = checkpoint()) != DB_SUCCESS) {
WRITEUNLOCK(this, status, "wu db_dictionary::massage_dict");
return (status);
}
#ifdef DEBUG
enumerate_dictionary(dictionary, &print_table);
#endif
FreeDictionary->inittemp(newdictname, *this);
for (i = 0; i < dictionary->tables.tables_len; i++) {
bucket = dictionary->tables.tables_val[i];
if (bucket) {
np = bucket;
while (np != NULL) {
next_np = np->next;
retval = db_clone_bucket(np, &clone);
if (retval != 1) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
if (change_table_name(clone, tok, repl) == -1) {
delete_table_desc(clone);
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
status = add_to_dictionary
(FreeDictionary->dictionary,
clone);
if (status != DB_SUCCESS) {
delete_table_desc(clone);
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
status = remove_from_dictionary(dictionary,
np->table_name, TRUE);
if (status != DB_SUCCESS) {
delete_table_desc(clone);
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
return (DB_INTERNAL_ERROR);
}
np = next_np;
}
}
}
if (FreeDictionary->dump() != DB_SUCCESS) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::massage_dict");
FATAL3(
"db_dictionary::massage_dict: Unable to dump new dictionary.",
DB_INTERNAL_ERROR, DB_INTERNAL_ERROR);
}
unlink(filename);
db_shutdown();
tmpptr = InUseDictionary;
InUseDictionary = FreeDictionary;
FreeDictionary = tmpptr;
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::massage_dict");
return (DB_SUCCESS);
}
db_status
db_dictionary::merge_dict(db_dictionary& tempdict, char *tok, char *repl)
{
db_status dbstat = DB_SUCCESS;
db_table_desc *tbl = NULL, *clone = NULL, *next_td = NULL;
int retval, i;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::merge_dict");
for (i = 0; i < tempdict.dictionary->tables.tables_len; ++i) {
tbl = tempdict.dictionary->tables.tables_val[i];
if (!tbl)
continue;
retval = db_clone_bucket(tbl, &clone);
if (retval != 1) {
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::merge_dict");
return (DB_INTERNAL_ERROR);
}
while (clone) {
next_td = clone->next;
clone->next = NULL;
if ((tok) &&
(change_table_name(clone, tok, repl) == -1)) {
delete_table_desc(clone);
if (next_td)
delete_table_desc(next_td);
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::merge_dict");
return (DB_INTERNAL_ERROR);
}
dbstat = add_to_dictionary(dictionary, clone);
if (dbstat == DB_NOTUNIQUE) {
dbstat = remove_from_dictionary(dictionary,
clone->table_name, TRUE);
if (dbstat != DB_SUCCESS) {
WRITEUNLOCK(this, dbstat,
"wu db_dictionary::merge_dict");
return (dbstat);
}
dbstat = add_to_dictionary(dictionary,
clone);
} else {
if (dbstat != DB_SUCCESS) {
WRITEUNLOCK(this, dbstat,
"wu db_dictionary::merge_dict");
return (dbstat);
}
}
clone = next_td;
}
}
if (dbstat == DB_SUCCESS)
changed = TRUE;
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::merge_dict");
return (dbstat);
}
int
db_dictionary::copyfile(char *infile, char *outfile)
{
db_table_desc *tbl = NULL;
db *dbase;
int ret;
READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile");
dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE);
if (dbase == NULL) {
READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::copyfile");
dbase = find_table(infile, &tbl, TRUE, TRUE, TRUE);
if (dbase == NULL)
return (DB_NOTFOUND);
READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile");
dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE);
if (dbase == NULL) {
READUNLOCK(this, DB_NOTFOUND,
"ru db_dictionary::copyfile");
return (DB_NOTFOUND);
}
}
ret = tbl->database->dump(outfile) ? DB_SUCCESS : DB_INTERNAL_ERROR;
READUNLOCK(this, ret, "ru db_dictionary::copyfile");
return (ret);
}
bool_t
db_dictionary::extract_entries(db_dictionary& tempdict, char **fs, int fscnt)
{
int i, retval;
db_table_desc *tbl, *clone;
db_table_desc tbl_ent;
db_status dbstat;
READLOCK(this, FALSE, "r db_dictionary::extract_entries");
for (i = 0; i < fscnt; ++i) {
tbl = find_table_desc(fs[i]);
if (!tbl) {
syslog(LOG_DEBUG,
"extract_entries: no dictionary entry for %s",
fs[i]);
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
} else {
tbl_ent.table_name = tbl->table_name;
tbl_ent.hashval = tbl->hashval;
tbl_ent.scheme = tbl->scheme;
tbl_ent.database = tbl->database;
tbl_ent.next = NULL;
}
retval = db_clone_bucket(&tbl_ent, &clone);
if (retval != 1) {
syslog(LOG_DEBUG,
"extract_entries: unable to clone entry for %s",
fs[i]);
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
}
dbstat = add_to_dictionary(tempdict.dictionary, clone);
if (dbstat != DB_SUCCESS) {
delete_table_desc(clone);
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
}
}
if (tempdict.dump() != DB_SUCCESS) {
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (FALSE);
}
READUNLOCK(this, FALSE,
"ru db_dictionary::extract_entries");
return (TRUE);
}
bool_t
db_dictionary::init(char *file)
{
int status;
WRITELOCK(this, FALSE, "w db_dictionary::init");
db_shutdown();
pickle_dict_desc f(file, PICKLE_READ);
filename = strdup(file);
if (filename == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: could not allocate space");
FATAL3("db_dictionary::init: could not allocate space",
DB_MEMORY_LIMIT, FALSE);
}
int len = strlen(filename);
tmpfilename = new char[len+5];
if (tmpfilename == NULL) {
delete filename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: could not allocate space");
FATAL3("db_dictionary::init: could not allocate space",
DB_MEMORY_LIMIT, FALSE);
}
logfilename = new char[len+5];
if (logfilename == NULL) {
delete filename;
delete tmpfilename;
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: cannot allocate space");
FATAL3("db_dictionary::init: cannot allocate space",
DB_MEMORY_LIMIT, FALSE);
}
sprintf(tmpfilename, "%s.tmp", filename);
sprintf(logfilename, "%s.log", filename);
unlink(tmpfilename);
dictionary = NULL;
if ((status = f.transfer(&dictionary)) < 0) {
initialized = FALSE;
} else if (status == 1) {
dictionary = new db_dict_desc;
if (dictionary == NULL) {
WRITEUNLOCK(this, FALSE,
"db_dictionary::init: could not allocate space");
FATAL3("db_dictionary::init: could not allocate space",
DB_MEMORY_LIMIT, FALSE);
}
dictionary->tables.tables_len = 0;
dictionary->tables.tables_val = NULL;
dictionary->count = 0;
dictionary->impl_vers = DB_CURRENT_VERSION;
initialized = TRUE;
} else
initialized = TRUE;
if (initialized == TRUE) {
int num_changes = 0;
changed = FALSE;
reset_log();
if ((num_changes = incorporate_log(logfilename)) < 0)
syslog(LOG_ERR,
"incorporation of dictionary logfile '%s' failed",
logfilename);
changed = (num_changes > 0);
}
WRITEUNLOCK(this, initialized, "wu db_dictionary::init");
return (initialized);
}
static bool_t
apply_log_entry(db_dictlog_entry *j, char *dictchar, int *count)
{
db_dictionary *dict = (db_dictionary*) dictchar;
WRITELOCK(dict, FALSE, "w apply_log_entry");
if (db_update_version.earlier_than(j->get_version())) {
++ *count;
#ifdef DEBUG
j->print();
#endif
switch (j->get_action()) {
case DB_ADD_TABLE:
dict->add_table_aux(j->get_table_name(),
j->get_table_object(), INMEMORY_ONLY);
break;
case DB_REMOVE_TABLE:
dict->delete_table_aux(j->get_table_name(),
INMEMORY_ONLY);
break;
default:
WARNING("db::apply_log_entry: unknown action_type");
WRITEUNLOCK(dict, FALSE, "wu apply_log_entry");
return (FALSE);
}
db_update_version.assign(j->get_version());
}
WRITEUNLOCK(dict, TRUE, "wu apply_log_entry");
return (TRUE);
}
int
db_dictionary::incorporate_log(char *file_name)
{
db_dictlog f(file_name, PICKLE_READ);
int ret;
WRITELOCK(this, -1, "w db_dictionary::incorporate_log");
setNoWriteThrough();
ret = f.execute_on_log(&(apply_log_entry), (char *) this);
clearNoWriteThrough();
WRITEUNLOCK(this, -1, "wu db_dictionary::incorporate_log");
return (ret);
}
db_status
db_dictionary::db_shutdown()
{
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::db_shutdown");
if (!initialized) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::db_shutdown");
return (DB_SUCCESS);
}
if (filename) {
delete filename;
filename = NULL;
}
if (tmpfilename) {
delete tmpfilename;
tmpfilename = NULL;
}
if (logfilename) {
delete logfilename;
logfilename = NULL;
}
if (dictionary) {
delete_dictionary(dictionary);
dictionary = NULL;
}
initialized = FALSE;
changed = FALSE;
reset_log();
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_shutdown");
return (DB_SUCCESS);
}
int
db_dictionary::dump()
{
int status;
READLOCK(this, -1, "r db_dictionary::dump");
if (!initialized) {
READUNLOCK(this, -1, "ru db_dictionary::dump");
return (-1);
}
unlink(tmpfilename);
pickle_dict_desc f(tmpfilename, PICKLE_WRITE);
status = f.transfer(&dictionary);
if (status != 0) {
WARNING("db_dictionary::dump: could not write out dictionary");
} else if (rename(tmpfilename, filename) < 0) {
WARNING_M("db_dictionary::dump: could not rename temp file: ");
status = -1;
}
READUNLOCK(this, -1, "ru db_dictionary::dump");
return (status);
}
db_status
db_dictionary::checkpoint()
{
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::checkpoint");
if (changed == FALSE) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::checkpoint");
return (DB_SUCCESS);
}
vers *oldv = new vers(db_update_version);
vers * newv = db_update_version.nextmajor();
db_update_version.assign(newv);
delete newv;
if (dump() != 0) {
WARNING_M(
"db_dictionary::checkpoint: could not dump dictionary: ");
db_update_version.assign(oldv);
delete oldv;
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_dictionary::checkpoint");
return (DB_INTERNAL_ERROR);
}
unlink(logfilename);
reset_log();
delete oldv;
changed = FALSE;
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::checkpoint");
return (DB_SUCCESS);
}
int
db_dictionary::reset_log()
{
WRITELOCK(this, -1, "w db_dictionary::reset_log");
if (logfile != NULL) {
if (logfile_opened == TRUE) {
if (logfile->close() < 0) {
WARNING_M(
"db_dictionary::reset_log: could not close log file: ");
}
}
delete logfile;
logfile = NULL;
}
logfile_opened = FALSE;
WRITEUNLOCK(this, -1, "wu db_dictionary::reset_log");
return (0);
}
int
db_dictionary::close_log()
{
WRITELOCK(this, -1, "w db_dictionary::close_log");
if (logfile != NULL && logfile_opened == TRUE) {
logfile->close();
}
logfile_opened = FALSE;
WRITEUNLOCK(this, -1, "wu db_dictionary::close_log");
return (0);
}
int
db_dictionary::open_log()
{
WRITELOCK(this, -1, "w db_dictionary::open_log");
if (logfile == NULL) {
if ((logfile = new db_dictlog(logfilename, PICKLE_APPEND)) ==
NULL) {
WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
FATAL3(
"db_dictionary::reset_log: cannot allocate space",
DB_MEMORY_LIMIT, -1);
}
}
if (logfile_opened == TRUE) {
WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
return (0);
}
if ((logfile->open()) == FALSE) {
WARNING_M("db_dictionary::open_log: could not open log file: ");
delete logfile;
logfile = NULL;
WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
return (-1);
}
logfile_opened = TRUE;
WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
return (0);
}
static int close_standby_list();
db_status
db_dictionary::db_standby(char *tab)
{
db_table_desc *tbl;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::db_standby");
if (!initialized) {
WRITEUNLOCK(this, DB_BADDICTIONARY,
"wu db_dictionary::db_standby");
return (DB_BADDICTIONARY);
}
if (tab == NULL) {
close_log();
close_standby_list();
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby");
return (DB_SUCCESS);
}
if ((tbl = find_table_desc(tab)) == NULL) {
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby");
return (DB_BADTABLE);
}
if (tbl->database != NULL)
tbl->database->close_log();
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby");
return (DB_SUCCESS);
}
db_table_desc*
db_dictionary::find_table_desc(char *tab)
{
db_table_desc *ret;
READLOCK(this, NULL, "r db_dictionary::find_table_desc");
if (initialized)
ret = search_dictionary(dictionary, tab);
else
ret = NULL;
READUNLOCK(this, ret, "r db_dictionary::find_table_desc");
return (ret);
}
db_table_desc *
db_dictionary::find_table_desc(char *tab, bool_t searchDeferred) {
db_table_desc *ret = NULL;
READLOCK(this, NULL, "r db_dictionary::find_table_desc_d");
if (initialized && searchDeferred && deferred.dictionary != NULL)
ret = search_dictionary(deferred.dictionary, tab);
if (ret == NULL)
ret = find_table_desc(tab);
READUNLOCK(this, ret, "r db_dictionary::find_table_desc_d");
return (ret);
}
db *
db_dictionary::find_table(char *tab, db_table_desc **where) {
return (find_table(tab, where, TRUE, TRUE, TRUE));
}
db *
db_dictionary::find_table(char *tab, db_table_desc **where,
bool_t searchDeferred) {
return (find_table(tab, where, searchDeferred, TRUE, TRUE));
}
db *
db_dictionary::find_table(char *tab, db_table_desc **where,
bool_t searchDeferred, bool_t doLDAP,
bool_t doLoad) {
db *res;
int lstat;
db_status dstat;
const char *myself = "db_dictionary::find_table";
res = find_table_noLDAP(tab, where, searchDeferred, doLoad);
if (res != 0 || !doLDAP)
return (res);
dstat = dbCreateFromLDAP(tab, &lstat);
if (dstat != DB_SUCCESS) {
if (dstat == DB_NOTFOUND) {
if (lstat != LDAP_SUCCESS) {
logmsg(MSG_NOTIMECHECK, LOG_INFO,
"%s: LDAP error for \"%s\": %s",
myself, NIL(tab),
ldap_err2string(lstat));
}
} else {
logmsg(MSG_NOTIMECHECK, LOG_INFO,
"%s: DB error %d for \"%s\"",
myself, dstat, NIL(tab));
}
return (0);
}
res = find_table_noLDAP(tab, where, searchDeferred, doLoad);
return (res);
}
db *
db_dictionary::find_table_noLDAP(char *tab, db_table_desc **where,
bool_t searchDeferred, bool_t doLoad)
{
if (!initialized)
return (NULL);
db_table_desc* tbl;
db *dbase = NULL;
int lret;
READLOCK(this, NULL, "r db_dictionary::find_table");
tbl = find_table_desc(tab, searchDeferred);
if (tbl == NULL) {
READUNLOCK(this, NULL, "ru db_dictionary::find_table");
return (NULL);
}
if (tbl->database != NULL || !doLoad) {
if (tbl->database && where) *where = tbl;
READUNLOCK(this, NULL, "ru db_dictionary::find_table");
return (tbl->database);
}
READUNLOCK(this, NULL, "ru db_dictionary::find_table");
WRITELOCK(this, NULL, "w db_dictionary::find_table");
if (tbl->database != NULL) {
if (where) *where = tbl;
WRITEUNLOCK(this, NULL, "wu db_dictionary::find_table");
return (tbl->database);
}
dbase = new db(tab);
if (dbase == NULL) {
WRITEUNLOCK(this, NULL,
"db_dictionary::find_table: could not allocate space");
FATAL3("db_dictionary::find_table: could not allocate space",
DB_MEMORY_LIMIT, NULL);
}
WRITELOCKNR(dbase, lret, "w dbase db_dictionary::find_table");
if (lret != 0) {
WRITEUNLOCK(this, NULL,
"db_dictionary::find_table: could not lock dbase");
FATAL3("db_dictionary::find_table: could not lock dbase",
DB_LOCK_ERROR, NULL);
}
tbl->database = dbase;
WRITEUNLOCK(this, NULL, "wu db_dictionary::find_table");
if (dbase->load()) {
if (where) *where = tbl;
WRITEUNLOCK(dbase, dbase, "wu dbase db_dictionary::find_table");
return (dbase);
}
delete dbase;
tbl->database = NULL;
WARNING("db_dictionary::find_table: could not load database");
return (NULL);
}
db_status
db_dictionary::log_action(int action, char *tab, table_obj *tobj)
{
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::log_action");
vers *newv = db_update_version.nextminor();
db_dictlog_entry le(action, newv, tab, tobj);
if (open_log() < 0) {
delete newv;
WRITEUNLOCK(this, DB_STORAGE_LIMIT,
"wu db_dictionary::log_action");
return (DB_STORAGE_LIMIT);
}
if (logfile->append(&le) < 0) {
WARNING_M("db::log_action: could not add log entry: ");
close_log();
delete newv;
WRITEUNLOCK(this, DB_STORAGE_LIMIT,
"wu db_dictionary::log_action");
return (DB_STORAGE_LIMIT);
}
db_update_version.assign(newv);
delete newv;
changed = TRUE;
WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::log_action");
return (DB_SUCCESS);
}
db_status
db_dictionary::delete_table_aux(char *tab, int mode)
{
db_status ret;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::delete_table_aux");
if (!initialized) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::delete_table_aux");
return (DB_BADDICTIONARY);
}
db_table_desc *tbl;
if ((tbl = find_table_desc(tab)) == NULL) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::delete_table_aux");
return (DB_NOTFOUND);
}
if (mode != INMEMORY_ONLY) {
int need_free = 0;
db_status status = log_action(DB_REMOVE_TABLE, tab);
if (status != DB_SUCCESS) {
WRITEUNLOCK(this, status,
"wu db_dictionary::delete_table_aux");
return (status);
}
db *dbase = tbl->database;
if (dbase == NULL) {
dbase = new db(tab);
need_free = 1;
}
if (dbase == NULL) {
WARNING(
"db_dictionary::delete_table: could not create db structure");
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"wu db_dictionary::delete_table_aux");
return (DB_MEMORY_LIMIT);
}
dbase->remove_files();
if (need_free)
delete dbase;
}
ret = remove_from_dictionary(dictionary, tab, TRUE);
WRITEUNLOCK(this, ret, "wu db_dictionary::delete_table_aux");
return (ret);
}
db_status
db_dictionary::delete_table(char *tab)
{
return (delete_table_aux(tab, !INMEMORY_ONLY));
}
db_status
db_dictionary::add_table_aux(char *tab, table_obj* tobj, int mode)
{
db_status ret;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::add_table_aux");
if (!initialized) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::add_table_aux");
return (DB_BADDICTIONARY);
}
if (find_table_desc(tab) != NULL) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::add_table_aux");
return (DB_NOTUNIQUE);
}
db_table_desc *new_table = 0;
db_status status = create_table_desc(tab, tobj, &new_table);
if (status != DB_SUCCESS) {
WRITEUNLOCK(this, DB_LOCK_ERROR,
"wu db_dictionary::add_table_aux");
return (status);
}
if (mode != INMEMORY_ONLY) {
new_table->database = new db(tab);
if (new_table->database == NULL) {
delete_table_desc(new_table);
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"db_dictionary::add_table: could not allocate space for db");
FATAL3(
"db_dictionary::add_table: could not allocate space for db",
DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
}
if (new_table->database->init(new_table->scheme) == 0) {
WARNING(
"db_dictionary::add_table: could not initialize database from scheme");
new_table->database->remove_files();
delete_table_desc(new_table);
WRITEUNLOCK(this, DB_STORAGE_LIMIT,
"wu db_dictionary::add_table_aux");
return (DB_STORAGE_LIMIT);
}
status = log_action(DB_ADD_TABLE, tab, tobj);
if (status != DB_SUCCESS) {
new_table->database->remove_files();
delete_table_desc(new_table);
WRITEUNLOCK(this, status,
"wu db_dictionary::add_table_aux");
return (status);
}
}
ret = add_to_dictionary(dictionary, new_table);
WRITEUNLOCK(this, ret, "wu db_dictionary::add_table_aux");
return (ret);
}
db_status
db_dictionary::add_table(char *tab, table_obj* tobj)
{
return (add_table_aux(tab, tobj, !INMEMORY_ONLY));
}
db_query*
db_dictionary::translate_to_query(db_table_desc* tbl, int numattrs,
nis_attr* attrlist)
{
READLOCK(this, NULL, "r db_dictionary::translate_to_query");
if (!initialized ||
tbl->scheme == NULL || numattrs == 0 || attrlist == NULL) {
READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query");
return (NULL);
}
db_query *q = new db_query(tbl->scheme, numattrs, attrlist);
if (q == NULL) {
READUNLOCK(this, NULL,
"db_dictionary::translate: could not allocate space");
FATAL3("db_dictionary::translate: could not allocate space",
DB_MEMORY_LIMIT, NULL);
}
if (q->size() == 0) {
delete q;
READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query");
return (NULL);
}
READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query");
return (q);
}
static db_table_names gt_answer;
static int gt_posn;
static db_status
get_table_name(db_table_desc* tbl)
{
if (tbl)
return (DB_BADTABLE);
if (gt_posn < gt_answer.db_table_names_len)
gt_answer.db_table_names_val[gt_posn++] =
strdup(tbl->table_name);
else
return (DB_BADTABLE);
return (DB_SUCCESS);
}
db_table_names*
db_dictionary::get_table_names()
{
READLOCK(this, NULL, "r db_dictionary::get_table_names");
gt_answer.db_table_names_len = dictionary->count;
gt_answer.db_table_names_val = new db_table_namep[dictionary->count];
gt_posn = 0;
if ((gt_answer.db_table_names_val) == NULL) {
READUNLOCK(this, NULL,
"db_dictionary::get_table_names: could not allocate space for names");
FATAL3(
"db_dictionary::get_table_names: could not allocate space for names",
DB_MEMORY_LIMIT, NULL);
}
enumerate_dictionary(dictionary, &get_table_name);
READUNLOCK(this, NULL, "ru db_dictionary::get_table_names");
return (>_answer);
}
static db_status
db_checkpoint_aux(db_table_desc *current)
{
db *dbase;
int status;
if (current == NULL)
return (DB_BADTABLE);
if (current->database == NULL) {
dbase = new db(current->table_name);
if (dbase == NULL) {
FATAL3(
"db_dictionary::db_checkpoint: could not allocate space",
DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
}
if (dbase->load() == 0) {
syslog(LOG_ERR,
"db_dictionary::db_checkpoint: could not load table %s",
current->table_name);
delete dbase;
return (DB_BADTABLE);
}
status = dbase->checkpoint();
delete dbase;
} else
status = current->database->checkpoint();
if (status == 0)
return (DB_STORAGE_LIMIT);
return (DB_SUCCESS);
}
static db_status
db_checkpoint_aux_cont(db_table_desc *current)
{
db_status status = db_checkpoint_aux(current);
if (status == DB_STORAGE_LIMIT || status == DB_MEMORY_LIMIT)
return (status);
else
return (DB_SUCCESS);
}
db_status
db_dictionary::db_checkpoint(char *tab)
{
db_table_desc *tbl;
db_status ret;
bool_t init;
READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_checkpoint");
init = initialized;
READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_checkpoint");
if (!init)
return (DB_BADDICTIONARY);
checkpoint();
READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_checkpoint");
if (tab == NULL) {
ret = enumerate_dictionary(dictionary, &db_checkpoint_aux_cont);
READUNLOCK(this, ret, "ru db_dictionary::db_checkpoint");
return (ret);
}
if ((tbl = find_table_desc(tab)) == NULL) {
READUNLOCK(this, DB_LOCK_ERROR,
"ru db_dictionary::db_checkpoint");
return (DB_BADTABLE);
}
ret = db_checkpoint_aux(tbl);
READUNLOCK(this, ret, "ru db_dictionary::db_checkpoint");
return (ret);
}
#define OPENED_DBS_CHUNK 12
static db **db_standby_list;
static uint_t db_standby_size = 0;
static uint_t db_standby_count = 0;
DECLMUTEXLOCK(db_standby_list);
static int
close_standby_list()
{
db *database;
int i, ret;
const char *myself = "close_standby_list";
MUTEXLOCK(db_standby_list, "close_standby_list");
if (db_standby_count == 0) {
MUTEXUNLOCK(db_standby_list, "close_standby_list");
return (1);
}
for (i = 0, ret = 0; i < db_standby_size; i++) {
if ((database = db_standby_list[i])) {
int lockok;
TRYWRITELOCK(database, lockok,
"try w db_dictionary::close_standby_list");
if (lockok == 0) {
database->close_log(1);
db_standby_list[i] = (db*)NULL;
--db_standby_count;
WRITEUNLOCK(database, db_standby_count == 0,
"db_dictionary::close_standby_list");
if (db_standby_count == 0) {
ret = 1;
break;
}
} else if (lockok != EBUSY) {
logmsg(MSG_NOTIMECHECK, LOG_INFO,
"%s: try-lock error %d",
myself, lockok);
}
}
}
MUTEXUNLOCK(db_standby_list, "close_standby_list");
return (ret);
}
int
add_to_standby_list(db* database)
{
int i;
const char *myself = "add_to_standby_list";
MUTEXLOCK(db_standby_list, "add_to_standby_list");
if (database == 0) {
MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
return (1);
}
if (db_standby_count >= OPENED_DBS_CHUNK) {
MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
close_standby_list();
MUTEXLOCK(db_standby_list, "add_to_standby_list");
}
if (db_standby_count >= db_standby_size) {
db **ndsl = (db **)realloc(db_standby_list,
(db_standby_size+OPENED_DBS_CHUNK) *
sizeof (ndsl[0]));
if (ndsl == 0) {
MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
logmsg(MSG_NOMEM, LOG_ERR,
"%s: realloc(%d) => NULL",
myself, (db_standby_size+OPENED_DBS_CHUNK) *
sizeof (ndsl[0]));
return (0);
}
db_standby_list = ndsl;
for (i = db_standby_size; i < db_standby_size+OPENED_DBS_CHUNK;
i++)
db_standby_list[i] = 0;
db_standby_size += OPENED_DBS_CHUNK;
}
for (i = 0; i < db_standby_size; i++) {
if (db_standby_list[i] == (db*)NULL) {
db_standby_list[i] = database;
++db_standby_count;
MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
return (1);
}
}
MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
return (0);
}
int
remove_from_standby_list(db* database)
{
int i;
MUTEXLOCK(db_standby_list, "remove_from_standby_list");
if (database == 0) {
MUTEXUNLOCK(db_standby_list, "remove_from_standby_list");
return (1);
}
for (i = 0; i < db_standby_size; i++) {
if ((database == db_standby_list[i])) {
db_standby_list[i] = (db*)NULL;
--db_standby_count;
MUTEXUNLOCK(db_standby_list,
"remove_from_standby_list");
return (1);
}
}
MUTEXUNLOCK(db_standby_list, "remove_from_standby_list");
return (0);
}
static void
db_release_dictionary(db_dict_desc_p d) {
int i;
if (d != NULL) {
for (i = 0; i < d->tables.tables_len; i++) {
db_table_desc_p n, t = d->tables.tables_val[i];
while (t != NULL) {
n = t->next;
delete_table_desc(t);
t = n;
}
}
delete d;
}
return;
}
db_dict_desc_p
db_dictionary::db_copy_dictionary(void) {
db_dict_desc_p tmp;
int i, ok = 1, count = 0;
WRITELOCK(this, NULL, "db_dictionary::db_copy_dictionary w");
if (dictionary == NULL) {
WRITEUNLOCK(this, NULL,
"db_dictionary::db_copy_dictionary wu");
return (NULL);
}
tmp = new db_dict_desc;
if (tmp == NULL) {
WRITEUNLOCK(this, NULL,
"db_dictionary::db_copy_dictionary wu: no memory");
return (NULL);
}
tmp->tables.tables_val = (db_table_desc_p *)calloc(
tmp->tables.tables_len,
sizeof (tmp->tables.tables_val[0]));
if (tmp->tables.tables_val == NULL) {
delete tmp;
WRITEUNLOCK(this, NULL,
"db_dictionary::db_copy_dictionary wu: no memory");
return (NULL);
}
tmp->impl_vers = dictionary->impl_vers;
tmp->tables.tables_len = 0;
tmp->count = 0;
for (i = 0; ok && i < dictionary->tables.tables_len; i++) {
db_table_desc_p tbl = NULL,
t = dictionary->tables.tables_val[i];
while (ok && t != NULL) {
db_table_desc_p n, savenext = t->next;
t->next = NULL;
if (db_clone_bucket(t, &n)) {
if (tbl != NULL) {
tbl->next = n;
} else {
tmp->tables.tables_val[i] = n;
}
tbl = n;
tmp->count++;
} else {
ok = 0;
}
t->next = savenext;
}
tmp->tables.tables_len++;
}
if (ok) {
#ifdef NISDB_LDAP_DEBUG
if ((tmp->tables.tables_len !=
dictionary->tables.tables_len) ||
(tmp->count != dictionary->count))
abort();
#endif
} else {
db_release_dictionary(tmp);
tmp = NULL;
}
return (tmp);
}
db_status
db_dictionary::defer(char *table) {
db_status ret = DB_SUCCESS;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::defer");
db_table_desc *tbl = find_table_desc(table);
int res;
const char *myself = "db_dictionary::defer";
if (tbl != NULL) {
db_table_desc *clone, *savenext = tbl->next;
tbl->next = NULL;
res = db_clone_bucket(tbl, &clone);
tbl->next = savenext;
if (res == 1) {
db_status stat;
if (deferred.dictionary == NULL) {
deferred.dictionary = new db_dict_desc;
if (deferred.dictionary == NULL) {
WRITEUNLOCK(this, DB_MEMORY_LIMIT,
"wu db_dictionary::defer");
return (DB_MEMORY_LIMIT);
}
deferred.dictionary->tables.tables_len = 0;
deferred.dictionary->tables.tables_val = NULL;
deferred.dictionary->count = 0;
deferred.dictionary->impl_vers =
DB_CURRENT_VERSION;
}
ret = DB_SUCCESS;
if (clone->database == 0) {
clone->database = new db(table);
if (clone->database != 0) {
if (clone->database->load()) {
logmsg(MSG_NOTIMECHECK,
#ifdef NISDB_LDAP_DEBUG
LOG_WARNING,
#else
LOG_INFO,
#endif
"%s: Clone DB for \"%s\" loaded",
myself, NIL(table));
} else {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Error loading clone DB for \"%s\"",
myself, NIL(table));
delete clone->database;
clone->database = 0;
ret = DB_INTERNAL_ERROR;
}
} else {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Unable to clone DB for \"%s\"",
myself, NIL(table));
ret = DB_MEMORY_LIMIT;
}
}
if (clone->database != 0) {
clone->database->markDeferred();
stat = add_to_dictionary(deferred.dictionary,
clone);
ret = stat;
if (stat != DB_SUCCESS) {
delete clone->database;
clone->database = 0;
delete clone;
if (stat == DB_NOTUNIQUE) {
ret = DB_SUCCESS;
}
}
} else {
delete clone;
}
} else {
ret = DB_INTERNAL_ERROR;
}
} else {
ret = DB_NOTFOUND;
}
WRITEUNLOCK(this, ret, "wu db_dictionary::defer");
return (ret);
}
db_status
db_dictionary::rollback(char *table) {
db_status ret = DB_SUCCESS;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::rollback");
db_table_desc *old = search_dictionary(deferred.dictionary, table);
db_table_desc *upd = search_dictionary(dictionary, table);
if (old == NULL) {
WRITEUNLOCK(this, DB_NOTFOUND, "wu db_dictionary::rollback");
return (DB_NOTFOUND);
}
ret = remove_from_dictionary(deferred.dictionary, table, FALSE);
if (ret != DB_SUCCESS) {
#ifdef NISDB_LDAP_DEBUG
abort();
#endif
WRITEUNLOCK(this, ret, "wu db_dictionary::rollback");
return (ret);
}
if (old->database != 0)
old->database->unmarkDeferred();
if (upd != NULL) {
ret = remove_from_dictionary(dictionary, table, FALSE);
if (ret != DB_SUCCESS) {
#ifdef NISDB_LDAP_DEBUG
abort();
#endif
delete_table_desc(old);
WRITEUNLOCK(this, ret, "wu db_dictionary::rollback");
return (ret);
}
delete_table_desc(upd);
}
ret = add_to_dictionary(dictionary, old);
if (ret != DB_SUCCESS) {
#ifdef NISDB_LDAP_DEBUG
abort();
#endif
delete_table_desc(old);
syslog(LOG_ERR,
"db_dictionary::rollback: rollback error %d for \"%s\"", ret, table);
}
WRITEUNLOCK(this, ret, "wu db_dictionary::rollback");
return (ret);
}
db_status
db_dictionary::commit(char *table) {
db_status ret = DB_SUCCESS;
WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::commit");
db_table_desc *old = search_dictionary(deferred.dictionary, table);
if (old == NULL) {
WRITEUNLOCK(this, ret, "wu db_dictionary::commit");
return (DB_SUCCESS);
}
ret = remove_from_dictionary(deferred.dictionary, table, FALSE);
if (ret == DB_SUCCESS)
delete_table_desc(old);
#ifdef NISDB_LDAP_DEBUG
else
abort();
#endif
WRITEUNLOCK(this, ret, "wu db_dictionary::commit");
return (ret);
}
void
db_dictionary::setNoWriteThrough(void) {
ASSERTWHELD(this->dict);
noWriteThrough.flag++;
}
void
db_dictionary::clearNoWriteThrough(void) {
ASSERTWHELD(this->dict);
if (noWriteThrough.flag > 0)
noWriteThrough.flag--;
#ifdef NISDB_LDAP_DEBUG
else
abort();
#endif
}