8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libnisdb / db_dictionary.cc
blob2b44a6a1264f8496d43aef3a2eb0bf89f01ffe83
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * db_dictionary.cc
25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
29 #include "db_headers.h"
30 #include "db_entry.h"
31 #include "db_dictionary.h"
32 #include "db_dictlog.h"
33 #include "db_vers.h"
34 #include "nisdb_mt.h"
35 #include "nisdb_rw.h"
36 #include "ldap_parse.h"
37 #include "ldap_map.h"
38 #include "nis_hashitem.h"
39 #include "ldap_util.h"
40 #include "nis_db.h"
41 #include <rpcsvc/nis.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <malloc.h>
46 #ifdef TDRPC
47 #include <sysent.h>
48 #endif
49 #include <unistd.h>
50 #include <syslog.h>
51 #include <rpc/rpc.h>
53 typedef bool_t (*db_func)(XDR *, db_table_desc *);
55 extern db_dictionary *InUseDictionary;
56 extern db_dictionary *FreeDictionary;
58 /* *************** dictionary version ****************** */
60 #define DB_MAGIC 0x12340000
61 #define DB_MAJOR 0
62 #define DB_MINOR 10
63 #define DB_VERSION_0_9 (DB_MAGIC|(DB_MAJOR<<8)|9)
64 #define DB_ORIG_VERSION DB_VERSION_0_9
65 #define DB_CURRENT_VERSION (DB_MAGIC|DB_MAJOR<<8|DB_MINOR)
67 vers db_update_version; /* Note 'global' for all dbs. */
69 #define INMEMORY_ONLY 1
72 * Checks for valid version. For now, there are two:
73 * DB_VERSION_ORIG was the ORIGINAL one with major = 0, minor = 9
74 * DB_CURRENT_VERSION is the latest one with changes in the database format
75 * for entry objects and the change in the dictionary format.
77 * Our current implementation can support both versions.
79 static inline bool_t
80 db_valid_version(u_int vers)
82 return ((vers == DB_CURRENT_VERSION) || (vers == DB_ORIG_VERSION));
85 static char *
86 db_version_str(u_int vers)
88 static char vstr[128];
89 u_int d_major = (vers&0x0000ff00)>>8;
90 u_int d_minor = (vers&0x000000ff);
92 sprintf(vstr, "SunSoft, SSM, Version %d.%d", d_major, d_minor);
93 return (vstr);
97 * Special XDR version that checks for a valid version number.
98 * If we don't find an acceptable version, exit immediately instead
99 * of continuing to xdr rest of dictionary, which might lead to
100 * a core dump if the formats between versions are incompatible.
101 * In the future, there might be a switch to determine versions
102 * and their corresponding XDR routines for the rest of the dictionary.
104 extern "C" {
105 bool_t
106 xdr_db_dict_version(XDR *xdrs, db_dict_version *objp)
108 if (xdrs->x_op == XDR_DECODE) {
109 if (!xdr_u_int(xdrs, (u_int*) objp) ||
110 !db_valid_version(((u_int) *objp))) {
111 syslog(LOG_ERR,
112 "db_dictionary: invalid dictionary format! Expecting %s",
113 db_version_str(DB_CURRENT_VERSION));
114 fprintf(stderr,
115 "db_dictionary: invalid dictionary format! Expecting %s\n",
116 db_version_str(DB_CURRENT_VERSION));
117 exit(1);
119 } else if (!xdr_u_int(xdrs, (u_int*) objp))
120 return (FALSE);
121 return (TRUE);
124 void
125 make_zero(vers* v)
127 v->zero();
134 /* ******************* dictionary data structures *************** */
136 /* Delete contents of single db_table_desc pointed to by 'current.' */
137 static void
138 delete_table_desc(db_table_desc *current)
140 if (current->table_name != NULL) delete current->table_name;
141 if (current->scheme != NULL) delete current->scheme;
142 if (current->database != NULL) delete current->database;
143 delete current;
146 /* Create new table descriptor using given table name and table_object. */
147 db_status
148 db_dictionary::create_table_desc(char *tab, table_obj* zdesc,
149 db_table_desc** answer)
151 db_table_desc *newtab;
152 if ((newtab = new db_table_desc) == NULL) {
153 FATAL3(
154 "db_dictionary::add_table: could not allocate space for new table",
155 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
158 newtab->database = NULL;
159 newtab->table_name = NULL;
160 newtab->next = NULL;
162 if ((newtab->scheme = new db_scheme(zdesc)) == NULL) {
163 delete_table_desc(newtab);
164 FATAL3(
165 "db_dictionary::add_table: could not allocate space for scheme",
166 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
169 if (newtab->scheme->numkeys() == 0) {
170 WARNING(
171 "db_dictionary::add_table: could not translate table_obj to scheme");
172 delete_table_desc(newtab);
173 return (DB_BADOBJECT);
176 if ((newtab->table_name = strdup(tab)) == NULL) {
177 delete_table_desc(newtab);
178 FATAL3(
179 "db_dictionary::add_table: could not allocate space for table name",
180 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
183 if (answer)
184 *answer = newtab;
185 return (DB_SUCCESS);
189 /* Delete list of db_table_desc pointed to by 'head.' */
190 static void
191 delete_bucket(db_table_desc *head)
193 db_table_desc * nextone, *current;
195 for (current = head; current != NULL; current = nextone) {
196 nextone = current->next; // remember next
197 delete_table_desc(current);
201 static void
202 delete_dictionary(db_dict_desc *dict)
204 db_table_desc* bucket;
205 int i;
206 if (dict) {
207 if (dict->tables.tables_val) {
208 /* delete each bucket */
209 for (i = 0; i < dict->tables.tables_len; i++)
210 bucket = dict->tables.tables_val[i];
211 if (bucket)
212 delete_bucket(bucket);
213 /* delete table */
214 delete dict->tables.tables_val;
216 /* delete dictionary */
217 delete dict;
221 /* Relocate bucket starting with this entry to new hashtable 'new_tab'. */
222 static void
223 relocate_bucket(db_table_desc* bucket,
224 db_table_desc_p *new_tab, unsigned long hashsize)
226 db_table_desc_p np, next_np, *hp;
228 for (np = bucket; np != NULL; np = next_np) {
229 next_np = np->next;
230 hp = &new_tab[np->hashval % hashsize];
231 np->next = *hp;
232 *hp = np;
237 * Return pointer to entry with same hash value and table_name
238 * as those supplied. Returns NULL if not found.
240 static db_status
241 enumerate_bucket(db_table_desc* bucket, db_status(*func)(db_table_desc *))
243 db_table_desc_p np;
244 db_status status;
246 for (np = bucket; np != NULL; np = np->next) {
247 status = (func)(np);
248 if (status != DB_SUCCESS)
249 return (status);
251 return (DB_SUCCESS);
256 * Return pointer to entry with same hash value and table_name
257 * as those supplied. Returns NULL if not found.
259 static db_table_desc_p
260 search_bucket(db_table_desc* bucket, unsigned long hval, char *target)
262 db_table_desc_p np;
264 for (np = bucket; np != NULL; np = np->next) {
265 if (np->hashval == hval &&
266 strcmp(np->table_name, target) == 0) {
267 break;
270 return (np);
275 * Remove entry with the specified hashvalue and target table name.
276 * Returns 'TRUE' if successful, FALSE otherwise.
277 * If the entry being removed is at the head of the list, then
278 * the head is updated to reflect the removal. The storage for the
279 * entry is freed if desired.
281 static bool_t
282 remove_from_bucket(db_table_desc_p bucket,
283 db_table_desc_p *head, unsigned long hval, char *target,
284 bool_t free_storage)
286 db_table_desc_p np, dp;
288 /* Search for it in the bucket */
289 for (dp = np = bucket; np != NULL; np = np->next) {
290 if (np->hashval == hval &&
291 strcmp(np->table_name, target) == 0) {
292 break;
293 } else {
294 dp = np;
298 if (np == NULL)
299 return (FALSE); // cannot delete if it is not there
301 if (dp == np) {
302 *head = np->next; // deleting head of bucket
303 } else {
304 dp->next = np->next; // deleting interior link
306 if (free_storage)
307 delete_table_desc(np);
309 return (TRUE);
314 * Add given entry to the bucket pointed to by 'bucket'.
315 * If an entry with the same table_name is found, no addition
316 * is done. The entry is added to the head of the bucket.
318 static bool_t
319 add_to_bucket(db_table_desc_p bucket, db_table_desc **head, db_table_desc_p td)
321 db_table_desc_p curr, prev;
322 register char *target_name;
323 unsigned long target_hval;
324 target_name = td->table_name;
325 target_hval = td->hashval;
327 /* Search for it in the bucket */
328 for (prev = curr = bucket; curr != NULL; curr = curr->next) {
329 if (curr->hashval == target_hval &&
330 strcmp(curr->table_name, target_name) == 0) {
331 break;
332 } else {
333 prev = curr;
337 if (curr != NULL)
338 return (FALSE); /* duplicates not allowed */
340 curr = *head;
341 *head = td;
342 td->next = curr;
343 return (TRUE);
347 /* Print bucket starting with this entry. */
348 static void
349 print_bucket(db_table_desc *head)
351 db_table_desc *np;
352 for (np = head; np != NULL; np = np->next) {
353 printf("%s: %d\n", np->table_name, np->hashval);
357 static db_status
358 print_table(db_table_desc *tbl)
360 if (tbl == NULL)
361 return (DB_BADTABLE);
362 printf("%s: %d\n", tbl->table_name, tbl->hashval);
363 return (DB_SUCCESS);
367 static int hashsizes[] = { /* hashtable sizes */
370 113,
371 337,
372 977,
373 2053,
374 4073,
375 8011,
376 16001,
380 // prevents wrap around numbers from being passed
381 #define CALLOC_LIMIT 536870911
383 /* Returns the next size to use for the hash table */
384 static unsigned int
385 get_next_hashsize(long unsigned oldsize)
387 long unsigned newsize, n;
388 if (oldsize == 0)
389 newsize = hashsizes[0];
390 else {
391 for (n = 0; newsize = hashsizes[n++]; )
392 if (oldsize == newsize) {
393 newsize = hashsizes[n]; /* get next size */
394 break;
396 if (newsize == 0)
397 newsize = oldsize * 2 + 1; /* just double */
399 return (newsize);
403 * Grow the current hashtable upto the next size.
404 * The contents of the existing hashtable is copied to the new one and
405 * relocated according to its hashvalue relative to the new size.
406 * Old table is deleted after the relocation.
408 static void
409 grow_dictionary(db_dict_desc_p dd)
411 unsigned int oldsize, i, new_size;
412 db_table_desc_p * oldtab, *newtab;
414 oldsize = dd->tables.tables_len;
415 oldtab = dd->tables.tables_val;
417 new_size = get_next_hashsize(oldsize);
419 if (new_size > CALLOC_LIMIT) {
420 FATAL("db_dictionary::grow: table size exceeds calloc limit",
421 DB_MEMORY_LIMIT);
424 if ((newtab = (db_table_desc_p*)
425 calloc((unsigned int) new_size,
426 sizeof (db_table_desc_p))) == NULL) {
427 FATAL("db_dictionary::grow: cannot allocate space",
428 DB_MEMORY_LIMIT);
431 if (oldtab != NULL) { // must transfer contents of old to new
432 for (i = 0; i < oldsize; i++) {
433 relocate_bucket(oldtab[i], newtab, new_size);
435 delete oldtab; // delete old hashtable
438 dd->tables.tables_val = newtab;
439 dd->tables.tables_len = new_size;
442 #define HASHSHIFT 3
443 #define HASHMASK 0x1f
445 static u_int
446 get_hashval(char *value)
448 int i, len;
449 u_int hval = 0;
451 len = strlen(value);
452 for (i = 0; i < len; i++) {
453 hval = ((hval<<HASHSHIFT)^hval);
454 hval += (value[i] & HASHMASK);
457 return (hval);
460 static db_status
461 enumerate_dictionary(db_dict_desc *dd, db_status (*func) (db_table_desc*))
463 int i;
464 db_table_desc *bucket;
465 db_status status;
467 if (dd == NULL)
468 return (DB_SUCCESS);
470 for (i = 0; i < dd->tables.tables_len; i++) {
471 bucket = dd->tables.tables_val[i];
472 if (bucket) {
473 status = enumerate_bucket(bucket, func);
474 if (status != DB_SUCCESS)
475 return (status);
479 return (DB_SUCCESS);
484 * Look up target table_name in hashtable and return its db_table_desc.
485 * Return NULL if not found.
487 static db_table_desc *
488 search_dictionary(db_dict_desc *dd, char *target)
490 register unsigned long hval;
491 unsigned long bucket;
493 if (target == NULL || dd == NULL || dd->tables.tables_len == 0)
494 return (NULL);
496 hval = get_hashval(target);
497 bucket = hval % dd->tables.tables_len;
499 db_table_desc_p fst = dd->tables.tables_val[bucket];
501 if (fst != NULL)
502 return (search_bucket(fst, hval, target));
503 else
504 return (NULL);
508 * Remove the entry with the target table_name from the dictionary.
509 * If successful, return DB_SUCCESS; otherwise DB_NOTUNIQUE if target
510 * is null; DB_NOTFOUND if entry is not found.
511 * If successful, decrement count of number of entries in hash table.
513 static db_status
514 remove_from_dictionary(db_dict_desc *dd, char *target, bool_t remove_storage)
516 register unsigned long hval;
517 unsigned long bucket;
518 register db_table_desc *fst;
520 if (target == NULL)
521 return (DB_NOTUNIQUE);
522 if (dd == NULL || dd->tables.tables_len == 0)
523 return (DB_NOTFOUND);
524 hval = get_hashval(target);
525 bucket = hval % dd->tables.tables_len;
526 fst = dd->tables.tables_val[bucket];
527 if (fst == NULL)
528 return (DB_NOTFOUND);
529 if (remove_from_bucket(fst, &dd->tables.tables_val[bucket],
530 hval, target, remove_storage)) {
531 --(dd->count);
532 return (DB_SUCCESS);
533 } else
534 return (DB_NOTFOUND);
538 * Add a new db_table_desc to the dictionary.
539 * Return DB_NOTUNIQUE, if entry with identical table_name
540 * already exists. If entry is added, return DB_SUCCESS.
541 * Increment count of number of entries in index table and grow table
542 * if number of entries equals size of table.
544 * Inputs: db_dict_desc_p dd pointer to dictionary to add to.
545 * db_table_desc *td pointer to table entry to be added. The
546 * db_table_desc.next field will be altered
547 * without regard to it's current setting.
548 * This means that if next points to a list of
549 * table entries, they may be either linked into
550 * the dictionary unexpectly or cut off (leaked).
552 static db_status
553 add_to_dictionary(db_dict_desc_p dd, db_table_desc *td)
555 register unsigned long hval;
556 char *target;
558 if (dd == NULL)
559 return (DB_NOTFOUND);
561 if (td == NULL)
562 return (DB_NOTFOUND);
563 target = td->table_name;
564 if (target == NULL)
565 return (DB_NOTUNIQUE);
567 hval = get_hashval(target);
569 if (dd->tables.tables_val == NULL)
570 grow_dictionary(dd);
572 db_table_desc_p fst;
573 unsigned long bucket;
574 bucket = hval % dd->tables.tables_len;
575 fst = dd->tables.tables_val[bucket];
576 td->hashval = hval;
577 if (fst == NULL) { /* Empty bucket */
578 dd->tables.tables_val[bucket] = td;
579 } else if (!add_to_bucket(fst, &dd->tables.tables_val[bucket], td)) {
580 return (DB_NOTUNIQUE);
583 /* increase hash table size if number of entries equals table size */
584 if (++(dd->count) > dd->tables.tables_len)
585 grow_dictionary(dd);
587 return (DB_SUCCESS);
590 /* ******************* pickling routines for dictionary ******************* */
593 /* Does the actual writing to/from file specific for db_dict_desc structure. */
594 static bool_t
595 transfer_aux(XDR* x, pptr tbl)
597 return (xdr_db_dict_desc_p(x, (db_dict_desc_p *) tbl));
600 class pickle_dict_desc: public pickle_file {
601 public:
602 pickle_dict_desc(char *f, pickle_mode m) : pickle_file(f, m) {}
604 /* Transfers db_dict_desc structure pointed to by dp to/from file. */
605 int transfer(db_dict_desc_p * dp)
606 { return (pickle_file::transfer((pptr) dp, &transfer_aux)); }
609 /* ************************ dictionary methods *************************** */
611 db_dictionary::db_dictionary()
613 dictionary = NULL;
614 initialized = FALSE;
615 filename = NULL;
616 tmpfilename = NULL;
617 logfilename = NULL;
618 logfile = NULL;
619 logfile_opened = FALSE;
620 changed = FALSE;
621 INITRW(dict);
622 READLOCKOK(dict);
626 * This routine clones an entire hash bucket chain. If you clone a
627 * data dictionary entry with the ->next pointer set, you will get a
628 * clone of that entry, as well as the entire linked list. This can cause
629 * pain if you then pass the cloned bucket to routines such as
630 * add_to_dictionary(), which do not expect nor handle dictionary hash
631 * entries with the ->next pointer set. You might either get duplicate
632 * entires or lose entries. If you wish to clone the entire bucket chain
633 * and add it to a new dictionary, loop through the db_table_desc->next list
634 * and call add_to_dictionary() for each item.
637 db_dictionary::db_clone_bucket(db_table_desc *bucket, db_table_desc **clone)
639 u_long size;
640 XDR xdrs;
641 char *bufin = NULL;
643 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_clone_bucket");
644 db_func use_this = xdr_db_table_desc;
645 size = xdr_sizeof((xdrproc_t) use_this, (void *) bucket);
646 bufin = (char *) calloc(1, (size_t) size * sizeof (char));
647 if (!bufin) {
648 READUNLOCK(this, DB_MEMORY_LIMIT,
649 "db_dictionary::insert_modified_table: out of memory");
650 FATAL3("db_dictionary::insert_modified_table: out of memory",
651 DB_MEMORY_LIMIT, 0);
653 xdrmem_create(&xdrs, bufin, (size_t) size, XDR_ENCODE);
654 if (!xdr_db_table_desc(&xdrs, bucket)) {
655 free(bufin);
656 xdr_destroy(&xdrs);
657 READUNLOCK(this, DB_MEMORY_LIMIT,
658 "db_dictionary::insert_modified_table: xdr encode failed");
659 FATAL3(
660 "db_dictionary::insert_modified_table: xdr encode failed.",
661 DB_MEMORY_LIMIT, 0);
663 *clone = (db_table_desc *) calloc(1, (size_t) size * sizeof (char));
664 if (!*clone) {
665 xdr_destroy(&xdrs);
666 free(bufin);
667 READUNLOCK(this, DB_MEMORY_LIMIT,
668 "db_dictionary::insert_modified_table: out of memory");
669 FATAL3("db_dictionary::insert_modified_table: out of memory",
670 DB_MEMORY_LIMIT, 0);
673 xdrmem_create(&xdrs, bufin, (size_t) size, XDR_DECODE);
674 if (!xdr_db_table_desc(&xdrs, *clone)) {
675 free(bufin);
676 free(*clone);
677 xdr_destroy(&xdrs);
678 READUNLOCK(this, DB_MEMORY_LIMIT,
679 "db_dictionary::insert_modified_table: xdr encode failed");
680 FATAL3(
681 "db_dictionary::insert_modified_table: xdr encode failed.",
682 DB_MEMORY_LIMIT, 0);
684 free(bufin);
685 xdr_destroy(&xdrs);
686 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_clone_bucket");
687 return (1);
692 db_dictionary::change_table_name(db_table_desc *clone, char *tok, char *repl)
694 char *newname;
695 char *loc_end, *loc_beg;
697 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::change_table_name");
698 while (clone) {
700 * Special case for a tok="". This is used for the
701 * nisrestore(1M), when restoring a replica in another
702 * domain. This routine is used to change the datafile
703 * names in the data.dict (see bugid #4031273). This will not
704 * effect massage_dict(), since it never generates an empty
705 * string for tok.
707 if (strlen(tok) == 0) {
708 strcat(clone->table_name, repl);
709 clone = clone->next;
710 continue;
712 newname = (char *) calloc(1, sizeof (char) *
713 strlen(clone->table_name) +
714 strlen(repl) - strlen(tok) + 1);
715 if (!newname) {
716 WRITEUNLOCK(this, DB_MEMORY_LIMIT,
717 "db_dictionary::change_table_name: out of memory");
718 FATAL3("db_dictionary::change_table_name: out of memory.",
719 DB_MEMORY_LIMIT, 0);
721 if (loc_beg = strstr(clone->table_name, tok)) {
722 loc_end = loc_beg + strlen(tok);
723 int s = loc_beg - clone->table_name;
724 memcpy(newname, clone->table_name, s);
725 strcat(newname + s, repl);
726 strcat(newname, loc_end);
727 free(clone->table_name);
728 clone->table_name = newname;
729 } else {
730 free(newname);
732 clone = clone->next;
734 WRITEUNLOCK(this, DB_LOCK_ERROR,
735 "wu db_dictionary::change_table_name");
736 return (1);
740 #ifdef curdict
741 #undef curdict
742 #endif
744 * A function to initialize the temporary dictionary from the real
745 * dictionary.
747 bool_t
748 db_dictionary::inittemp(char *dictname, db_dictionary& curdict)
750 int status;
751 db_table_desc_p *newtab;
753 db_shutdown();
755 WRITELOCK(this, FALSE, "w db_dictionary::inittemp");
756 if (initialized) {
757 /* Someone else got in between db_shutdown() and lock() */
758 WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp");
759 return (TRUE);
762 pickle_dict_desc f(dictname, PICKLE_READ);
763 filename = strdup(dictname);
764 if (filename == NULL) {
765 WRITEUNLOCK(this, FALSE,
766 "db_dictionary::inittemp: could not allocate space");
767 FATAL3("db_dictionary::inittemp: could not allocate space",
768 DB_MEMORY_LIMIT, FALSE);
770 int len = strlen(filename);
771 tmpfilename = new char[len+5];
772 if (tmpfilename == NULL) {
773 delete filename;
774 WRITEUNLOCK(this, FALSE,
775 "db_dictionary::inittemp: could not allocate space");
776 FATAL3("db_dictionary::inittemp: could not allocate space",
777 DB_MEMORY_LIMIT, FALSE);
779 logfilename = new char[len+5];
780 if (logfilename == NULL) {
781 delete filename;
782 delete tmpfilename;
783 WRITEUNLOCK(this, FALSE,
784 "db_dictionary::inittemp: cannot allocate space");
785 FATAL3("db_dictionary::inittemp: cannot allocate space",
786 DB_MEMORY_LIMIT, FALSE);
789 sprintf(tmpfilename, "%s.tmp", filename);
790 sprintf(logfilename, "%s.log", filename);
791 unlink(tmpfilename); /* get rid of partial checkpoints */
792 dictionary = NULL;
794 if ((status = f.transfer(&dictionary)) < 0) {
795 initialized = FALSE;
796 } else if (status == 1) { /* no dictionary exists, create one */
797 dictionary = new db_dict_desc;
798 if (dictionary == NULL) {
799 WRITEUNLOCK(this, FALSE,
800 "db_dictionary::inittemp: could not allocate space");
801 FATAL3(
802 "db_dictionary::inittemp: could not allocate space",
803 DB_MEMORY_LIMIT, FALSE);
805 dictionary->tables.tables_len =
806 curdict.dictionary->tables.tables_len;
807 if ((newtab = (db_table_desc_p *) calloc(
808 (unsigned int) dictionary->tables.tables_len,
809 sizeof (db_table_desc_p))) == NULL) {
810 WRITEUNLOCK(this, FALSE,
811 "db_dictionary::inittemp: cannot allocate space");
812 FATAL3(
813 "db_dictionary::inittemp: cannot allocate space",
814 DB_MEMORY_LIMIT, 0);
816 dictionary->tables.tables_val = newtab;
817 dictionary->count = 0;
818 dictionary->impl_vers = curdict.dictionary->impl_vers;
819 initialized = TRUE;
820 } else /* dictionary loaded successfully */
821 initialized = TRUE;
823 if (initialized == TRUE) {
824 changed = FALSE;
825 reset_log();
828 WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp");
829 return (initialized);
834 * This method replaces the token string specified with the replacment
835 * string specified. It assumes that at least one and only one instance of
836 * the token exists. It is the responsibility of the caller to ensure that
837 * the above assumption stays valid.
839 db_status
840 db_dictionary::massage_dict(char *newdictname, char *tok, char *repl)
842 int retval;
843 u_int i, tbl_count;
844 db_status status;
845 db_table_desc *bucket, *np, *clone, *next_np;
846 char tail[NIS_MAXNAMELEN];
847 db_dictionary *tmpptr;
849 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::massage_dict");
850 if (dictionary == NULL) {
851 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
852 "db_dictionary::massage_dict: uninitialized dictionary file");
853 FATAL3(
854 "db_dictionary::massage_dict: uninitialized dictionary file.",
855 DB_INTERNAL_ERROR, DB_INTERNAL_ERROR);
858 if ((tbl_count = dictionary->count) == 0) {
859 WRITEUNLOCK(this, DB_SUCCESS,
860 "wu db_dictionary::massage_dict");
861 return (DB_SUCCESS);
864 /* First checkpoint */
865 if ((status = checkpoint()) != DB_SUCCESS) {
866 WRITEUNLOCK(this, status, "wu db_dictionary::massage_dict");
867 return (status);
870 #ifdef DEBUG
871 enumerate_dictionary(dictionary, &print_table);
872 #endif
874 /* Initialize the free dictionary so that we can start populating it */
875 FreeDictionary->inittemp(newdictname, *this);
877 for (i = 0; i < dictionary->tables.tables_len; i++) {
878 bucket = dictionary->tables.tables_val[i];
879 if (bucket) {
880 np = bucket;
881 while (np != NULL) {
882 next_np = np->next;
883 retval = db_clone_bucket(np, &clone);
884 if (retval != 1) {
885 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
886 "wu db_dictionary::massage_dict");
887 return (DB_INTERNAL_ERROR);
889 if (change_table_name(clone, tok, repl) == -1) {
890 delete_table_desc(clone);
891 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
892 "wu db_dictionary::massage_dict");
893 return (DB_INTERNAL_ERROR);
896 * We know we don't have a log file, so we will
897 * just add to the in-memory database and dump
898 * all of it once we are done.
900 status = add_to_dictionary
901 (FreeDictionary->dictionary,
902 clone);
903 if (status != DB_SUCCESS) {
904 delete_table_desc(clone);
905 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
906 "wu db_dictionary::massage_dict");
907 return (DB_INTERNAL_ERROR);
909 status = remove_from_dictionary(dictionary,
910 np->table_name, TRUE);
911 if (status != DB_SUCCESS) {
912 delete_table_desc(clone);
913 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
914 "wu db_dictionary::massage_dict");
915 return (DB_INTERNAL_ERROR);
917 np = next_np;
922 if (FreeDictionary->dump() != DB_SUCCESS) {
923 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
924 "wu db_dictionary::massage_dict");
925 FATAL3(
926 "db_dictionary::massage_dict: Unable to dump new dictionary.",
927 DB_INTERNAL_ERROR, DB_INTERNAL_ERROR);
931 * Now, shutdown the inuse dictionary and update the FreeDictionary
932 * and InUseDictionary pointers as well. Also, delete the old dictionary
933 * file.
935 unlink(filename); /* There shouldn't be a tmpfile or logfile */
936 db_shutdown();
937 tmpptr = InUseDictionary;
938 InUseDictionary = FreeDictionary;
939 FreeDictionary = tmpptr;
940 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::massage_dict");
941 return (DB_SUCCESS);
945 db_status
946 db_dictionary::merge_dict(db_dictionary& tempdict, char *tok, char *repl)
949 db_status dbstat = DB_SUCCESS;
951 db_table_desc *tbl = NULL, *clone = NULL, *next_td = NULL;
952 int retval, i;
954 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::merge_dict");
956 for (i = 0; i < tempdict.dictionary->tables.tables_len; ++i) {
957 tbl = tempdict.dictionary->tables.tables_val[i];
958 if (!tbl)
959 continue;
960 retval = db_clone_bucket(tbl, &clone);
961 if (retval != 1) {
962 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
963 "wu db_dictionary::merge_dict");
964 return (DB_INTERNAL_ERROR);
966 while (clone) {
967 next_td = clone->next;
968 clone->next = NULL;
969 if ((tok) &&
970 (change_table_name(clone, tok, repl) == -1)) {
971 delete_table_desc(clone);
972 if (next_td)
973 delete_table_desc(next_td);
974 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
975 "wu db_dictionary::merge_dict");
976 return (DB_INTERNAL_ERROR);
979 dbstat = add_to_dictionary(dictionary, clone);
980 if (dbstat == DB_NOTUNIQUE) {
981 /* Overide */
982 dbstat = remove_from_dictionary(dictionary,
983 clone->table_name, TRUE);
984 if (dbstat != DB_SUCCESS) {
985 WRITEUNLOCK(this, dbstat,
986 "wu db_dictionary::merge_dict");
987 return (dbstat);
989 dbstat = add_to_dictionary(dictionary,
990 clone);
991 } else {
992 if (dbstat != DB_SUCCESS) {
993 WRITEUNLOCK(this, dbstat,
994 "wu db_dictionary::merge_dict");
995 return (dbstat);
998 clone = next_td;
1002 * If we were successful in merging the dictionaries, then mark the
1003 * dictionary changed, so that it will be properly checkpointed and
1004 * dumped to disk.
1006 if (dbstat == DB_SUCCESS)
1007 changed = TRUE;
1008 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::merge_dict");
1009 return (dbstat);
1013 db_dictionary::copyfile(char *infile, char *outfile)
1015 db_table_desc *tbl = NULL;
1016 db *dbase;
1017 int ret;
1019 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile");
1021 * We need to hold the read-lock until the dump() is done.
1022 * However, we must avoid the lock migration (read -> write)
1023 * that would happen in find_table() if the db must be loaded.
1024 * Hence, look first look for an already loaded db.
1026 dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE);
1027 if (dbase == NULL) {
1028 /* Release the read-lock, and try again, allowing load */
1029 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::copyfile");
1030 dbase = find_table(infile, &tbl, TRUE, TRUE, TRUE);
1031 if (dbase == NULL)
1032 return (DB_NOTFOUND);
1034 * Read-lock again, and get a 'tbl' we can use since we're
1035 * still holding the lock.
1037 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile");
1038 dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE);
1039 if (dbase == NULL) {
1040 READUNLOCK(this, DB_NOTFOUND,
1041 "ru db_dictionary::copyfile");
1042 return (DB_NOTFOUND);
1045 ret = tbl->database->dump(outfile) ? DB_SUCCESS : DB_INTERNAL_ERROR;
1046 READUNLOCK(this, ret, "ru db_dictionary::copyfile");
1047 return (ret);
1051 bool_t
1052 db_dictionary::extract_entries(db_dictionary& tempdict, char **fs, int fscnt)
1054 int i, retval;
1055 db_table_desc *tbl, *clone;
1056 db_table_desc tbl_ent;
1057 db_status dbstat;
1059 READLOCK(this, FALSE, "r db_dictionary::extract_entries");
1060 for (i = 0; i < fscnt; ++i) {
1061 tbl = find_table_desc(fs[i]);
1062 if (!tbl) {
1063 syslog(LOG_DEBUG,
1064 "extract_entries: no dictionary entry for %s",
1065 fs[i]);
1066 READUNLOCK(this, FALSE,
1067 "ru db_dictionary::extract_entries");
1068 return (FALSE);
1069 } else {
1070 tbl_ent.table_name = tbl->table_name;
1071 tbl_ent.hashval = tbl->hashval;
1072 tbl_ent.scheme = tbl->scheme;
1073 tbl_ent.database = tbl->database;
1074 tbl_ent.next = NULL;
1076 retval = db_clone_bucket(&tbl_ent, &clone);
1077 if (retval != 1) {
1078 syslog(LOG_DEBUG,
1079 "extract_entries: unable to clone entry for %s",
1080 fs[i]);
1081 READUNLOCK(this, FALSE,
1082 "ru db_dictionary::extract_entries");
1083 return (FALSE);
1085 dbstat = add_to_dictionary(tempdict.dictionary, clone);
1086 if (dbstat != DB_SUCCESS) {
1087 delete_table_desc(clone);
1088 READUNLOCK(this, FALSE,
1089 "ru db_dictionary::extract_entries");
1090 return (FALSE);
1093 if (tempdict.dump() != DB_SUCCESS) {
1094 READUNLOCK(this, FALSE,
1095 "ru db_dictionary::extract_entries");
1096 return (FALSE);
1098 READUNLOCK(this, FALSE,
1099 "ru db_dictionary::extract_entries");
1100 return (TRUE);
1105 * Initialize dictionary from contents in 'file'.
1106 * If there is already information in this dictionary, it is removed.
1107 * Therefore, regardless of whether the load from the file succeeds,
1108 * the contents of this dictionary will be altered. Returns
1109 * whether table has been initialized successfully.
1111 bool_t
1112 db_dictionary::init(char *file)
1114 int status;
1116 WRITELOCK(this, FALSE, "w db_dictionary::init");
1117 db_shutdown();
1119 pickle_dict_desc f(file, PICKLE_READ);
1120 filename = strdup(file);
1121 if (filename == NULL) {
1122 WRITEUNLOCK(this, FALSE,
1123 "db_dictionary::init: could not allocate space");
1124 FATAL3("db_dictionary::init: could not allocate space",
1125 DB_MEMORY_LIMIT, FALSE);
1127 int len = strlen(filename);
1128 tmpfilename = new char[len+5];
1129 if (tmpfilename == NULL) {
1130 delete filename;
1131 WRITEUNLOCK(this, FALSE,
1132 "db_dictionary::init: could not allocate space");
1133 FATAL3("db_dictionary::init: could not allocate space",
1134 DB_MEMORY_LIMIT, FALSE);
1136 logfilename = new char[len+5];
1137 if (logfilename == NULL) {
1138 delete filename;
1139 delete tmpfilename;
1140 WRITEUNLOCK(this, FALSE,
1141 "db_dictionary::init: cannot allocate space");
1142 FATAL3("db_dictionary::init: cannot allocate space",
1143 DB_MEMORY_LIMIT, FALSE);
1146 sprintf(tmpfilename, "%s.tmp", filename);
1147 sprintf(logfilename, "%s.log", filename);
1148 unlink(tmpfilename); /* get rid of partial checkpoints */
1149 dictionary = NULL;
1151 /* load dictionary */
1152 if ((status = f.transfer(&dictionary)) < 0) {
1153 initialized = FALSE;
1154 } else if (status == 1) { /* no dictionary exists, create one */
1155 dictionary = new db_dict_desc;
1156 if (dictionary == NULL) {
1157 WRITEUNLOCK(this, FALSE,
1158 "db_dictionary::init: could not allocate space");
1159 FATAL3("db_dictionary::init: could not allocate space",
1160 DB_MEMORY_LIMIT, FALSE);
1162 dictionary->tables.tables_len = 0;
1163 dictionary->tables.tables_val = NULL;
1164 dictionary->count = 0;
1165 dictionary->impl_vers = DB_CURRENT_VERSION;
1166 initialized = TRUE;
1167 } else /* dictionary loaded successfully */
1168 initialized = TRUE;
1170 if (initialized == TRUE) {
1171 int num_changes = 0;
1172 changed = FALSE;
1173 reset_log();
1174 if ((num_changes = incorporate_log(logfilename)) < 0)
1175 syslog(LOG_ERR,
1176 "incorporation of dictionary logfile '%s' failed",
1177 logfilename);
1178 changed = (num_changes > 0);
1181 WRITEUNLOCK(this, initialized, "wu db_dictionary::init");
1182 return (initialized);
1186 * Execute log entry 'j' on the dictionary identified by 'dict' if the
1187 * version of j is later than that of the dictionary. If 'j' is executed,
1188 * 'count' is incremented and the dictionary's verison is updated to
1189 * that of 'j'.
1190 * Returns TRUE always for valid log entries; FALSE otherwise.
1192 static bool_t
1193 apply_log_entry(db_dictlog_entry *j, char *dictchar, int *count)
1195 db_dictionary *dict = (db_dictionary*) dictchar;
1197 WRITELOCK(dict, FALSE, "w apply_log_entry");
1198 if (db_update_version.earlier_than(j->get_version())) {
1199 ++ *count;
1200 #ifdef DEBUG
1201 j->print();
1202 #endif /* DEBUG */
1203 switch (j->get_action()) {
1204 case DB_ADD_TABLE:
1205 dict->add_table_aux(j->get_table_name(),
1206 j->get_table_object(), INMEMORY_ONLY);
1207 // ignore status
1208 break;
1210 case DB_REMOVE_TABLE:
1211 dict->delete_table_aux(j->get_table_name(),
1212 INMEMORY_ONLY);
1213 // ignore status
1214 break;
1216 default:
1217 WARNING("db::apply_log_entry: unknown action_type");
1218 WRITEUNLOCK(dict, FALSE, "wu apply_log_entry");
1219 return (FALSE);
1221 db_update_version.assign(j->get_version());
1224 WRITEUNLOCK(dict, TRUE, "wu apply_log_entry");
1225 return (TRUE);
1229 db_dictionary::incorporate_log(char *file_name)
1231 db_dictlog f(file_name, PICKLE_READ);
1232 int ret;
1234 WRITELOCK(this, -1, "w db_dictionary::incorporate_log");
1235 setNoWriteThrough();
1236 ret = f.execute_on_log(&(apply_log_entry), (char *) this);
1237 clearNoWriteThrough();
1238 WRITEUNLOCK(this, -1, "wu db_dictionary::incorporate_log");
1239 return (ret);
1243 /* Frees memory of filename and tables. Has no effect on disk storage. */
1244 db_status
1245 db_dictionary::db_shutdown()
1247 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::db_shutdown");
1248 if (!initialized) {
1249 WRITEUNLOCK(this, DB_LOCK_ERROR,
1250 "wu db_dictionary::db_shutdown");
1251 return (DB_SUCCESS); /* DB_NOTFOUND? */
1254 if (filename) {
1255 delete filename;
1256 filename = NULL;
1258 if (tmpfilename) {
1259 delete tmpfilename;
1260 tmpfilename = NULL;
1262 if (logfilename) {
1263 delete logfilename;
1264 logfilename = NULL;
1266 if (dictionary) {
1267 delete_dictionary(dictionary);
1268 dictionary = NULL;
1270 initialized = FALSE;
1271 changed = FALSE;
1272 reset_log();
1274 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_shutdown");
1275 return (DB_SUCCESS);
1279 * Dump contents of this dictionary (minus the database representations)
1280 * to its file. Returns 0 if operation succeeds, -1 otherwise.
1283 db_dictionary::dump()
1285 int status;
1287 READLOCK(this, -1, "r db_dictionary::dump");
1288 if (!initialized) {
1289 READUNLOCK(this, -1, "ru db_dictionary::dump");
1290 return (-1);
1293 unlink(tmpfilename); /* get rid of partial dumps */
1294 pickle_dict_desc f(tmpfilename, PICKLE_WRITE);
1296 status = f.transfer(&dictionary); /* dump table descs */
1297 if (status != 0) {
1298 WARNING("db_dictionary::dump: could not write out dictionary");
1299 } else if (rename(tmpfilename, filename) < 0) {
1300 WARNING_M("db_dictionary::dump: could not rename temp file: ");
1301 status = -1;
1304 READUNLOCK(this, -1, "ru db_dictionary::dump");
1305 return (status);
1309 * Write out in-memory copy of dictionary to file.
1310 * 1. Update major version.
1311 * 2. Dump contents to temporary file.
1312 * 3. Rename temporary file to real dictionary file.
1313 * 4. Remove log file.
1314 * A checkpoint is done only if it has changed since the previous checkpoint.
1315 * Returns DB_SUCCESS if checkpoint was successful; error code otherwise
1317 db_status
1318 db_dictionary::checkpoint()
1320 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::checkpoint");
1322 if (changed == FALSE) {
1323 WRITEUNLOCK(this, DB_LOCK_ERROR,
1324 "wu db_dictionary::checkpoint");
1325 return (DB_SUCCESS);
1328 vers *oldv = new vers(db_update_version); // copy
1329 vers * newv = db_update_version.nextmajor(); // get next version
1330 db_update_version.assign(newv); // update version
1331 delete newv;
1333 if (dump() != 0) {
1334 WARNING_M(
1335 "db_dictionary::checkpoint: could not dump dictionary: ");
1336 db_update_version.assign(oldv); // rollback
1337 delete oldv;
1338 WRITEUNLOCK(this, DB_INTERNAL_ERROR,
1339 "wu db_dictionary::checkpoint");
1340 return (DB_INTERNAL_ERROR);
1342 unlink(logfilename); /* should do atomic rename and log delete */
1343 reset_log(); /* should check for what? */
1344 delete oldv;
1345 changed = FALSE;
1346 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::checkpoint");
1347 return (DB_SUCCESS);
1350 /* close existing logfile and delete its structure */
1352 db_dictionary::reset_log()
1354 WRITELOCK(this, -1, "w db_dictionary::reset_log");
1355 /* try to close old log file */
1356 /* doesnot matter since we do synchronous writes only */
1357 if (logfile != NULL) {
1358 if (logfile_opened == TRUE) {
1359 if (logfile->close() < 0) {
1360 WARNING_M(
1361 "db_dictionary::reset_log: could not close log file: ");
1364 delete logfile;
1365 logfile = NULL;
1367 logfile_opened = FALSE;
1368 WRITEUNLOCK(this, -1, "wu db_dictionary::reset_log");
1369 return (0);
1372 /* close existing logfile, but leave its structure if exists */
1374 db_dictionary::close_log()
1376 WRITELOCK(this, -1, "w db_dictionary::close_log");
1377 if (logfile != NULL && logfile_opened == TRUE) {
1378 logfile->close();
1380 logfile_opened = FALSE;
1381 WRITEUNLOCK(this, -1, "wu db_dictionary::close_log");
1382 return (0);
1385 /* open logfile, creating its structure if it does not exist */
1387 db_dictionary::open_log()
1389 WRITELOCK(this, -1, "w db_dictionary::open_log");
1390 if (logfile == NULL) {
1391 if ((logfile = new db_dictlog(logfilename, PICKLE_APPEND)) ==
1392 NULL) {
1393 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1394 FATAL3(
1395 "db_dictionary::reset_log: cannot allocate space",
1396 DB_MEMORY_LIMIT, -1);
1400 if (logfile_opened == TRUE) {
1401 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1402 return (0);
1405 if ((logfile->open()) == FALSE) {
1406 WARNING_M("db_dictionary::open_log: could not open log file: ");
1407 delete logfile;
1408 logfile = NULL;
1409 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1410 return (-1);
1413 logfile_opened = TRUE;
1414 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1415 return (0);
1419 * closes any open log files for all tables in dictionary or 'tab'.
1420 * "tab" is an optional argument.
1422 static int close_standby_list();
1424 db_status
1425 db_dictionary::db_standby(char *tab)
1427 db_table_desc *tbl;
1429 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::db_standby");
1430 if (!initialized) {
1431 WRITEUNLOCK(this, DB_BADDICTIONARY,
1432 "wu db_dictionary::db_standby");
1433 return (DB_BADDICTIONARY);
1436 if (tab == NULL) {
1437 close_log(); // close dictionary log
1438 close_standby_list();
1439 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby");
1440 return (DB_SUCCESS);
1443 if ((tbl = find_table_desc(tab)) == NULL) {
1444 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby");
1445 return (DB_BADTABLE);
1448 if (tbl->database != NULL)
1449 tbl->database->close_log();
1450 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby");
1451 return (DB_SUCCESS);
1455 * Returns db_table_desc of table name 'tab'. 'prev', if supplied,
1456 * is set to the entry located ahead of 'tab's entry in the dictionary.
1458 db_table_desc*
1459 db_dictionary::find_table_desc(char *tab)
1461 db_table_desc *ret;
1463 READLOCK(this, NULL, "r db_dictionary::find_table_desc");
1464 if (initialized)
1465 ret = search_dictionary(dictionary, tab);
1466 else
1467 ret = NULL;
1469 READUNLOCK(this, ret, "r db_dictionary::find_table_desc");
1470 return (ret);
1473 db_table_desc *
1474 db_dictionary::find_table_desc(char *tab, bool_t searchDeferred) {
1475 db_table_desc *ret = NULL;
1477 READLOCK(this, NULL, "r db_dictionary::find_table_desc_d");
1479 /* If desired, look in the deferred dictionary first */
1480 if (initialized && searchDeferred && deferred.dictionary != NULL)
1481 ret = search_dictionary(deferred.dictionary, tab);
1483 /* No result yet => search the "normal" dictionary */
1484 if (ret == NULL)
1485 ret = find_table_desc(tab);
1487 READUNLOCK(this, ret, "r db_dictionary::find_table_desc_d");
1488 return (ret);
1491 db *
1492 db_dictionary::find_table(char *tab, db_table_desc **where) {
1493 /* Most operations should use the deferred dictionary if it exists */
1494 return (find_table(tab, where, TRUE, TRUE, TRUE));
1497 db *
1498 db_dictionary::find_table(char *tab, db_table_desc **where,
1499 bool_t searchDeferred) {
1500 return (find_table(tab, where, searchDeferred, TRUE, TRUE));
1503 db *
1504 db_dictionary::find_table(char *tab, db_table_desc **where,
1505 bool_t searchDeferred, bool_t doLDAP,
1506 bool_t doLoad) {
1507 db *res;
1508 int lstat;
1509 db_status dstat;
1510 const char *myself = "db_dictionary::find_table";
1512 res = find_table_noLDAP(tab, where, searchDeferred, doLoad);
1513 /* If found, or shouldn't try LDAP, we're done */
1514 if (res != 0 || !doLDAP)
1515 return (res);
1517 /* See if we can retrieve the object from LDAP */
1518 dstat = dbCreateFromLDAP(tab, &lstat);
1519 if (dstat != DB_SUCCESS) {
1520 if (dstat == DB_NOTFOUND) {
1521 if (lstat != LDAP_SUCCESS) {
1522 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1523 "%s: LDAP error for \"%s\": %s",
1524 myself, NIL(tab),
1525 ldap_err2string(lstat));
1527 } else {
1528 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1529 "%s: DB error %d for \"%s\"",
1530 myself, dstat, NIL(tab));
1532 return (0);
1535 /* Try the dictionary again */
1536 res = find_table_noLDAP(tab, where, searchDeferred, doLoad);
1538 return (res);
1542 * Return database structure of table named by 'tab'.
1543 * If 'where' is set, set it to the table_desc of 'tab.'
1544 * If the database is loaded in from stable store if it has not been loaded.
1545 * If it cannot be loaded, it is initialized using the scheme stored in
1546 * the table_desc. NULL is returned if the initialization fails.
1548 db *
1549 db_dictionary::find_table_noLDAP(char *tab, db_table_desc **where,
1550 bool_t searchDeferred, bool_t doLoad)
1552 if (!initialized)
1553 return (NULL);
1555 db_table_desc* tbl;
1556 db *dbase = NULL;
1557 int lret;
1559 READLOCK(this, NULL, "r db_dictionary::find_table");
1560 tbl = find_table_desc(tab, searchDeferred);
1561 if (tbl == NULL) {
1562 READUNLOCK(this, NULL, "ru db_dictionary::find_table");
1563 return (NULL); // not found
1566 if (tbl->database != NULL || !doLoad) {
1567 if (tbl->database && where) *where = tbl;
1568 READUNLOCK(this, NULL, "ru db_dictionary::find_table");
1569 return (tbl->database); // return handle
1572 READUNLOCK(this, NULL, "ru db_dictionary::find_table");
1573 WRITELOCK(this, NULL, "w db_dictionary::find_table");
1574 /* Re-check; some other thread might have loaded the db */
1575 if (tbl->database != NULL) {
1576 if (where) *where = tbl;
1577 WRITEUNLOCK(this, NULL, "wu db_dictionary::find_table");
1578 return (tbl->database); // return handle
1581 // need to load in/init database
1582 dbase = new db(tab);
1584 if (dbase == NULL) {
1585 WRITEUNLOCK(this, NULL,
1586 "db_dictionary::find_table: could not allocate space");
1587 FATAL3("db_dictionary::find_table: could not allocate space",
1588 DB_MEMORY_LIMIT, NULL);
1592 * Lock the newly created 'dbase', so we can release the general
1593 * db_dictionary lock.
1595 WRITELOCKNR(dbase, lret, "w dbase db_dictionary::find_table");
1596 if (lret != 0) {
1597 WRITEUNLOCK(this, NULL,
1598 "db_dictionary::find_table: could not lock dbase");
1599 FATAL3("db_dictionary::find_table: could not lock dbase",
1600 DB_LOCK_ERROR, NULL);
1602 /* Assign tbl->database, and then release the 'this' lock */
1603 tbl->database = dbase;
1604 WRITEUNLOCK(this, NULL, "wu db_dictionary::find_table");
1606 if (dbase->load()) { // try to load in database
1607 if (where) *where = tbl;
1608 WRITEUNLOCK(dbase, dbase, "wu dbase db_dictionary::find_table");
1609 return (dbase);
1612 delete dbase;
1613 tbl->database = NULL;
1614 WARNING("db_dictionary::find_table: could not load database");
1615 return (NULL);
1618 /* Log action to be taken on the dictionary and update db_update_version. */
1620 db_status
1621 db_dictionary::log_action(int action, char *tab, table_obj *tobj)
1623 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::log_action");
1625 vers *newv = db_update_version.nextminor();
1626 db_dictlog_entry le(action, newv, tab, tobj);
1628 if (open_log() < 0) {
1629 delete newv;
1630 WRITEUNLOCK(this, DB_STORAGE_LIMIT,
1631 "wu db_dictionary::log_action");
1632 return (DB_STORAGE_LIMIT);
1635 if (logfile->append(&le) < 0) {
1636 WARNING_M("db::log_action: could not add log entry: ");
1637 close_log();
1638 delete newv;
1639 WRITEUNLOCK(this, DB_STORAGE_LIMIT,
1640 "wu db_dictionary::log_action");
1641 return (DB_STORAGE_LIMIT);
1644 db_update_version.assign(newv);
1645 delete newv;
1646 changed = TRUE;
1648 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::log_action");
1649 return (DB_SUCCESS);
1652 // For a complete 'delete' operation, we want the following behaviour:
1653 // 1. If there is an entry in the log, the physical table exists and is
1654 // stable.
1655 // 2. If there is no entry in the log, the physical table may or may not
1656 // exist.
1658 db_status
1659 db_dictionary::delete_table_aux(char *tab, int mode)
1661 db_status ret;
1663 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::delete_table_aux");
1664 if (!initialized) {
1665 WRITEUNLOCK(this, DB_LOCK_ERROR,
1666 "wu db_dictionary::delete_table_aux");
1667 return (DB_BADDICTIONARY);
1670 db_table_desc *tbl;
1671 if ((tbl = find_table_desc(tab)) == NULL) { // table not found
1672 WRITEUNLOCK(this, DB_LOCK_ERROR,
1673 "wu db_dictionary::delete_table_aux");
1674 return (DB_NOTFOUND);
1677 if (mode != INMEMORY_ONLY) {
1678 int need_free = 0;
1680 // Update log.
1681 db_status status = log_action(DB_REMOVE_TABLE, tab);
1682 if (status != DB_SUCCESS) {
1683 WRITEUNLOCK(this, status,
1684 "wu db_dictionary::delete_table_aux");
1685 return (status);
1688 // Remove physical structures
1689 db *dbase = tbl->database;
1690 if (dbase == NULL) { // need to get desc to access files
1691 dbase = new db(tab);
1692 need_free = 1;
1694 if (dbase == NULL) {
1695 WARNING(
1696 "db_dictionary::delete_table: could not create db structure");
1697 WRITEUNLOCK(this, DB_MEMORY_LIMIT,
1698 "wu db_dictionary::delete_table_aux");
1699 return (DB_MEMORY_LIMIT);
1701 dbase->remove_files(); // remove physical files
1702 if (need_free)
1703 delete dbase;
1706 // Remove in-memory structures
1707 ret = remove_from_dictionary(dictionary, tab, TRUE);
1708 WRITEUNLOCK(this, ret, "wu db_dictionary::delete_table_aux");
1709 return (ret);
1713 * Delete table with given name 'tab' from dictionary.
1714 * Returns error code if table does not exist or if dictionary has not been
1715 * initialized. Dictionary is updated to stable store if deletion is
1716 * successful. Fatal error occurs if dictionary cannot be saved.
1717 * Returns DB_SUCCESS if dictionary has been updated successfully.
1718 * Note that the files associated with the table are also removed.
1720 db_status
1721 db_dictionary::delete_table(char *tab)
1723 return (delete_table_aux(tab, !INMEMORY_ONLY));
1726 // For a complete 'add' operation, we want the following behaviour:
1727 // 1. If there is an entry in the log, then the physical table exists and
1728 // has been initialized properly.
1729 // 2. If there is no entry in the log, the physical table may or may not
1730 // exist. In this case, we don't really care because we cannot get at
1731 // it. The next time we add a table with the same name to the dictionary,
1732 // it will be initialized properly.
1733 // This mode is used when the table is first created.
1735 // For an INMEMORY_ONLY operation, only the internal structure is created and
1736 // updated. This mode is used when the database gets loaded and the internal
1737 // dictionary gets updated from the log entries.
1739 db_status
1740 db_dictionary::add_table_aux(char *tab, table_obj* tobj, int mode)
1742 db_status ret;
1744 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::add_table_aux");
1745 if (!initialized) {
1746 WRITEUNLOCK(this, DB_LOCK_ERROR,
1747 "wu db_dictionary::add_table_aux");
1748 return (DB_BADDICTIONARY);
1751 if (find_table_desc(tab) != NULL) {
1752 WRITEUNLOCK(this, DB_LOCK_ERROR,
1753 "wu db_dictionary::add_table_aux");
1754 return (DB_NOTUNIQUE); // table already exists
1757 // create data structures for table
1758 db_table_desc *new_table = 0;
1759 db_status status = create_table_desc(tab, tobj, &new_table);
1761 if (status != DB_SUCCESS) {
1762 WRITEUNLOCK(this, DB_LOCK_ERROR,
1763 "wu db_dictionary::add_table_aux");
1764 return (status);
1767 if (mode != INMEMORY_ONLY) {
1768 // create physical structures for table
1769 new_table->database = new db(tab);
1770 if (new_table->database == NULL) {
1771 delete_table_desc(new_table);
1772 WRITEUNLOCK(this, DB_MEMORY_LIMIT,
1773 "db_dictionary::add_table: could not allocate space for db");
1774 FATAL3(
1775 "db_dictionary::add_table: could not allocate space for db",
1776 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
1778 if (new_table->database->init(new_table->scheme) == 0) {
1779 WARNING(
1780 "db_dictionary::add_table: could not initialize database from scheme");
1781 new_table->database->remove_files();
1782 delete_table_desc(new_table);
1783 WRITEUNLOCK(this, DB_STORAGE_LIMIT,
1784 "wu db_dictionary::add_table_aux");
1785 return (DB_STORAGE_LIMIT);
1788 // update 'external' copy of dictionary
1789 status = log_action(DB_ADD_TABLE, tab, tobj);
1791 if (status != DB_SUCCESS) {
1792 new_table->database->remove_files();
1793 delete_table_desc(new_table);
1794 WRITEUNLOCK(this, status,
1795 "wu db_dictionary::add_table_aux");
1796 return (status);
1800 // finally, update in-memory copy of dictionary
1801 ret = add_to_dictionary(dictionary, new_table);
1802 WRITEUNLOCK(this, ret, "wu db_dictionary::add_table_aux");
1803 return (ret);
1807 * Add table with given name 'tab' and description 'zdesc' to dictionary.
1808 * Returns errror code if table already exists, or if no memory can be found
1809 * to store the descriptor, or if dictionary has not been intialized.
1810 * Dictionary is updated to stable store if addition is successful.
1811 * Fatal error occurs if dictionary cannot be saved.
1812 * Returns DB_SUCCESS if dictionary has been updated successfully.
1814 db_status
1815 db_dictionary::add_table(char *tab, table_obj* tobj)
1817 return (add_table_aux(tab, tobj, !INMEMORY_ONLY));
1821 * Translate given NIS attribute list to a db_query structure.
1822 * Return FALSE if dictionary has not been initialized, or
1823 * table does not have a scheme (which should be a fatal error?).
1825 db_query*
1826 db_dictionary::translate_to_query(db_table_desc* tbl, int numattrs,
1827 nis_attr* attrlist)
1829 READLOCK(this, NULL, "r db_dictionary::translate_to_query");
1830 if (!initialized ||
1831 tbl->scheme == NULL || numattrs == 0 || attrlist == NULL) {
1832 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query");
1833 return (NULL);
1836 db_query *q = new db_query(tbl->scheme, numattrs, attrlist);
1837 if (q == NULL) {
1838 READUNLOCK(this, NULL,
1839 "db_dictionary::translate: could not allocate space");
1840 FATAL3("db_dictionary::translate: could not allocate space",
1841 DB_MEMORY_LIMIT, NULL);
1844 if (q->size() == 0) {
1845 delete q;
1846 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query");
1847 return (NULL);
1849 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query");
1850 return (q);
1853 static db_table_names gt_answer;
1854 static int gt_posn;
1856 static db_status
1857 get_table_name(db_table_desc* tbl)
1859 if (tbl)
1860 return (DB_BADTABLE);
1862 if (gt_posn < gt_answer.db_table_names_len)
1863 gt_answer.db_table_names_val[gt_posn++] =
1864 strdup(tbl->table_name);
1865 else
1866 return (DB_BADTABLE);
1868 return (DB_SUCCESS);
1873 * Return the names of tables in this dictionary.
1874 * XXX This routine is used only for testing only;
1875 * if to be used for real, need to free memory sensibly, or
1876 * caller of get_table_names should have freed them.
1878 db_table_names*
1879 db_dictionary::get_table_names()
1881 READLOCK(this, NULL, "r db_dictionary::get_table_names");
1882 gt_answer.db_table_names_len = dictionary->count;
1883 gt_answer.db_table_names_val = new db_table_namep[dictionary->count];
1884 gt_posn = 0;
1885 if ((gt_answer.db_table_names_val) == NULL) {
1886 READUNLOCK(this, NULL,
1887 "db_dictionary::get_table_names: could not allocate space for names");
1888 FATAL3(
1889 "db_dictionary::get_table_names: could not allocate space for names",
1890 DB_MEMORY_LIMIT, NULL);
1893 enumerate_dictionary(dictionary, &get_table_name);
1894 READUNLOCK(this, NULL, "ru db_dictionary::get_table_names");
1895 return (&gt_answer);
1898 static db_status
1899 db_checkpoint_aux(db_table_desc *current)
1901 db *dbase;
1902 int status;
1904 if (current == NULL)
1905 return (DB_BADTABLE);
1907 if (current->database == NULL) { /* need to load it in */
1908 dbase = new db(current->table_name);
1909 if (dbase == NULL) {
1910 FATAL3(
1911 "db_dictionary::db_checkpoint: could not allocate space",
1912 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT);
1914 if (dbase->load() == 0) {
1915 syslog(LOG_ERR,
1916 "db_dictionary::db_checkpoint: could not load table %s",
1917 current->table_name);
1918 delete dbase;
1919 return (DB_BADTABLE);
1921 status = dbase->checkpoint();
1922 delete dbase; // unload
1923 } else
1924 status = current->database->checkpoint();
1926 if (status == 0)
1927 return (DB_STORAGE_LIMIT);
1928 return (DB_SUCCESS);
1931 /* Like db_checkpoint_aux except only stops on LIMIT errors */
1932 static db_status
1933 db_checkpoint_aux_cont(db_table_desc *current)
1935 db_status status = db_checkpoint_aux(current);
1937 if (status == DB_STORAGE_LIMIT || status == DB_MEMORY_LIMIT)
1938 return (status);
1939 else
1940 return (DB_SUCCESS);
1943 db_status
1944 db_dictionary::db_checkpoint(char *tab)
1946 db_table_desc *tbl;
1947 db_status ret;
1948 bool_t init;
1950 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_checkpoint");
1951 init = initialized;
1952 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_checkpoint");
1953 if (!init)
1954 return (DB_BADDICTIONARY);
1956 checkpoint(); // checkpoint dictionary first
1958 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_checkpoint");
1960 if (tab == NULL) {
1961 ret = enumerate_dictionary(dictionary, &db_checkpoint_aux_cont);
1962 READUNLOCK(this, ret, "ru db_dictionary::db_checkpoint");
1963 return (ret);
1966 if ((tbl = find_table_desc(tab)) == NULL) {
1967 READUNLOCK(this, DB_LOCK_ERROR,
1968 "ru db_dictionary::db_checkpoint");
1969 return (DB_BADTABLE);
1972 ret = db_checkpoint_aux(tbl);
1973 READUNLOCK(this, ret, "ru db_dictionary::db_checkpoint");
1974 return (ret);
1977 /* *********************** db_standby **************************** */
1978 /* Deal with list of tables that need to be 'closed' */
1980 #define OPENED_DBS_CHUNK 12
1981 static db **db_standby_list;
1982 static uint_t db_standby_size = 0;
1983 static uint_t db_standby_count = 0;
1984 DECLMUTEXLOCK(db_standby_list);
1987 * Returns 1 if all databases on the list could be closed, 0
1988 * otherwise.
1990 static int
1991 close_standby_list()
1993 db *database;
1994 int i, ret;
1995 const char *myself = "close_standby_list";
1997 MUTEXLOCK(db_standby_list, "close_standby_list");
1999 if (db_standby_count == 0) {
2000 MUTEXUNLOCK(db_standby_list, "close_standby_list");
2001 return (1);
2004 for (i = 0, ret = 0; i < db_standby_size; i++) {
2005 if ((database = db_standby_list[i])) {
2007 * In order to avoid a potential dead-lock, we
2008 * check to see if close_log() would be able to
2009 * lock the db; if not, just skip the db.
2011 int lockok;
2013 TRYWRITELOCK(database, lockok,
2014 "try w db_dictionary::close_standby_list");
2016 if (lockok == 0) {
2017 database->close_log(1);
2018 db_standby_list[i] = (db*)NULL;
2019 --db_standby_count;
2020 WRITEUNLOCK(database, db_standby_count == 0,
2021 "db_dictionary::close_standby_list");
2022 if (db_standby_count == 0) {
2023 ret = 1;
2024 break;
2026 } else if (lockok != EBUSY) {
2027 logmsg(MSG_NOTIMECHECK, LOG_INFO,
2028 "%s: try-lock error %d",
2029 myself, lockok);
2030 } /* else it's EBUSY; skip to the next one */
2034 MUTEXUNLOCK(db_standby_list, "close_standby_list");
2036 return (ret);
2040 * Add given database to list of databases that have been opened for updates.
2041 * If size of list exceeds maximum, close opened databases first.
2045 add_to_standby_list(db* database)
2047 int i;
2048 const char *myself = "add_to_standby_list";
2050 MUTEXLOCK(db_standby_list, "add_to_standby_list");
2052 if (database == 0) {
2053 MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
2054 return (1);
2057 /* Try to keep the list below OPENED_DBS_CHUNK */
2058 if (db_standby_count >= OPENED_DBS_CHUNK) {
2059 MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
2060 close_standby_list();
2061 MUTEXLOCK(db_standby_list, "add_to_standby_list");
2064 if (db_standby_count >= db_standby_size) {
2065 db **ndsl = (db **)realloc(db_standby_list,
2066 (db_standby_size+OPENED_DBS_CHUNK) *
2067 sizeof (ndsl[0]));
2069 if (ndsl == 0) {
2070 MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
2071 logmsg(MSG_NOMEM, LOG_ERR,
2072 "%s: realloc(%d) => NULL",
2073 myself, (db_standby_size+OPENED_DBS_CHUNK) *
2074 sizeof (ndsl[0]));
2075 return (0);
2078 db_standby_list = ndsl;
2080 for (i = db_standby_size; i < db_standby_size+OPENED_DBS_CHUNK;
2081 i++)
2082 db_standby_list[i] = 0;
2084 db_standby_size += OPENED_DBS_CHUNK;
2087 for (i = 0; i < db_standby_size; i++) {
2088 if (db_standby_list[i] == (db*)NULL) {
2089 db_standby_list[i] = database;
2090 ++db_standby_count;
2091 MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
2092 return (1);
2096 MUTEXUNLOCK(db_standby_list, "add_to_standby_list");
2098 return (0);
2102 remove_from_standby_list(db* database)
2104 int i;
2106 MUTEXLOCK(db_standby_list, "remove_from_standby_list");
2108 if (database == 0) {
2109 MUTEXUNLOCK(db_standby_list, "remove_from_standby_list");
2110 return (1);
2113 for (i = 0; i < db_standby_size; i++) {
2114 if ((database == db_standby_list[i])) {
2115 db_standby_list[i] = (db*)NULL;
2116 --db_standby_count;
2117 MUTEXUNLOCK(db_standby_list,
2118 "remove_from_standby_list");
2119 return (1);
2123 MUTEXUNLOCK(db_standby_list, "remove_from_standby_list");
2125 return (0);
2128 /* Release space for copied dictionary */
2129 static void
2130 db_release_dictionary(db_dict_desc_p d) {
2132 int i;
2134 if (d != NULL) {
2135 for (i = 0; i < d->tables.tables_len; i++) {
2136 db_table_desc_p n, t = d->tables.tables_val[i];
2137 while (t != NULL) {
2138 n = t->next;
2139 delete_table_desc(t);
2140 t = n;
2143 delete d;
2146 return;
2150 * Make a copy of the dictionary
2152 db_dict_desc_p
2153 db_dictionary::db_copy_dictionary(void) {
2155 db_dict_desc_p tmp;
2156 int i, ok = 1, count = 0;
2158 WRITELOCK(this, NULL, "db_dictionary::db_copy_dictionary w");
2160 if (dictionary == NULL) {
2161 WRITEUNLOCK(this, NULL,
2162 "db_dictionary::db_copy_dictionary wu");
2163 return (NULL);
2166 tmp = new db_dict_desc;
2167 if (tmp == NULL) {
2168 WRITEUNLOCK(this, NULL,
2169 "db_dictionary::db_copy_dictionary wu: no memory");
2170 return (NULL);
2173 tmp->tables.tables_val = (db_table_desc_p *)calloc(
2174 tmp->tables.tables_len,
2175 sizeof (tmp->tables.tables_val[0]));
2176 if (tmp->tables.tables_val == NULL) {
2177 delete tmp;
2178 WRITEUNLOCK(this, NULL,
2179 "db_dictionary::db_copy_dictionary wu: no memory");
2180 return (NULL);
2183 tmp->impl_vers = dictionary->impl_vers;
2184 tmp->tables.tables_len = 0;
2185 tmp->count = 0;
2187 /* For each table ... */
2188 for (i = 0; ok && i < dictionary->tables.tables_len; i++) {
2189 db_table_desc_p tbl = NULL,
2190 t = dictionary->tables.tables_val[i];
2191 /* ... and each bucket in the chain ... */
2192 while (ok && t != NULL) {
2193 db_table_desc_p n, savenext = t->next;
2194 t->next = NULL;
2195 if (db_clone_bucket(t, &n)) {
2196 if (tbl != NULL) {
2197 tbl->next = n;
2198 } else {
2199 tmp->tables.tables_val[i] = n;
2201 tbl = n;
2202 tmp->count++;
2203 } else {
2204 ok = 0;
2206 t->next = savenext;
2208 tmp->tables.tables_len++;
2211 if (ok) {
2212 #ifdef NISDB_LDAP_DEBUG
2213 if ((tmp->tables.tables_len !=
2214 dictionary->tables.tables_len) ||
2215 (tmp->count != dictionary->count))
2216 abort();
2217 #endif /* NISDB_LDAP_DEBUG */
2218 } else {
2219 db_release_dictionary(tmp);
2220 tmp = NULL;
2223 return (tmp);
2227 * Set deferred commit mode. To do this, we make a copy of the table
2228 * (structures and data), and put that on the deferred dictionary list.
2229 * This list is used for lookups during a resync, so clients continue
2230 * to see the pre-resync data. Meanwhile, any changes (including table
2231 * deletes) are done to the (temporarily hidden to clients) table in
2232 * the normal dictionary.
2234 db_status
2235 db_dictionary::defer(char *table) {
2236 db_status ret = DB_SUCCESS;
2237 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::defer");
2238 db_table_desc *tbl = find_table_desc(table);
2239 int res;
2240 const char *myself = "db_dictionary::defer";
2242 if (tbl != NULL) {
2243 db_table_desc *clone, *savenext = tbl->next;
2245 * Only want to clone one db_table_desc, so temporarily
2246 * unlink the tail.
2248 tbl->next = NULL;
2249 res = db_clone_bucket(tbl, &clone);
2250 /* Restore link to tail */
2251 tbl->next = savenext;
2252 if (res == 1) {
2253 db_status stat;
2254 if (deferred.dictionary == NULL) {
2255 deferred.dictionary = new db_dict_desc;
2256 if (deferred.dictionary == NULL) {
2257 WRITEUNLOCK(this, DB_MEMORY_LIMIT,
2258 "wu db_dictionary::defer");
2259 return (DB_MEMORY_LIMIT);
2261 deferred.dictionary->tables.tables_len = 0;
2262 deferred.dictionary->tables.tables_val = NULL;
2263 deferred.dictionary->count = 0;
2264 deferred.dictionary->impl_vers =
2265 DB_CURRENT_VERSION;
2267 ret = DB_SUCCESS;
2268 /* Initialize and load the database for the clone */
2269 if (clone->database == 0) {
2270 clone->database = new db(table);
2271 if (clone->database != 0) {
2272 if (clone->database->load()) {
2273 logmsg(MSG_NOTIMECHECK,
2274 #ifdef NISDB_LDAP_DEBUG
2275 LOG_WARNING,
2276 #else
2277 LOG_INFO,
2278 #endif /* NISDB_LDAP_DEBUG */
2279 "%s: Clone DB for \"%s\" loaded",
2280 myself, NIL(table));
2281 } else {
2282 logmsg(MSG_NOTIMECHECK, LOG_ERR,
2283 "%s: Error loading clone DB for \"%s\"",
2284 myself, NIL(table));
2285 delete clone->database;
2286 clone->database = 0;
2287 ret = DB_INTERNAL_ERROR;
2289 } else {
2290 logmsg(MSG_NOTIMECHECK, LOG_ERR,
2291 "%s: Unable to clone DB for \"%s\"",
2292 myself, NIL(table));
2293 ret = DB_MEMORY_LIMIT;
2296 if (clone->database != 0) {
2297 clone->database->markDeferred();
2298 stat = add_to_dictionary(deferred.dictionary,
2299 clone);
2300 ret = stat;
2301 if (stat != DB_SUCCESS) {
2302 delete clone->database;
2303 clone->database = 0;
2304 delete clone;
2305 if (stat == DB_NOTUNIQUE) {
2306 /* Already deferred */
2307 ret = DB_SUCCESS;
2310 } else {
2311 delete clone;
2312 /* Return value already set above */
2314 } else {
2315 ret = DB_INTERNAL_ERROR;
2317 } else {
2318 ret = DB_NOTFOUND;
2320 WRITEUNLOCK(this, ret, "wu db_dictionary::defer");
2321 return (ret);
2325 * Unset deferred commit mode and roll back changes; doesn't recover the
2326 * disk data, but that's OK, since we only want to be able to continue
2327 * serving the table until we can try a full dump again.
2329 * The rollback is done by removing (and deleting) the updated table from
2330 * the dictionary, and then moving the saved table from the deferred
2331 * dictionary list to the actual one.
2333 db_status
2334 db_dictionary::rollback(char *table) {
2335 db_status ret = DB_SUCCESS;
2336 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::rollback");
2337 db_table_desc *old = search_dictionary(deferred.dictionary, table);
2338 db_table_desc *upd = search_dictionary(dictionary, table);
2340 if (old == NULL) {
2341 WRITEUNLOCK(this, DB_NOTFOUND, "wu db_dictionary::rollback");
2342 return (DB_NOTFOUND);
2346 * Remove old incarnation from deferred dictionary. We already hold
2347 * a pointer ('old') to it, so don't delete.
2349 ret = remove_from_dictionary(deferred.dictionary, table, FALSE);
2350 if (ret != DB_SUCCESS) {
2351 #ifdef NISDB_LDAP_DEBUG
2352 abort();
2353 #endif /* NISDB_LDAP_DEBUG */
2354 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback");
2355 return (ret);
2358 if (old->database != 0)
2359 old->database->unmarkDeferred();
2362 * Remove updated incarnation from dictionary. If 'upd' is NULL,
2363 * the table has been removed while we were in deferred mode, and
2364 * that's OK; we just need to retain the old incarnation.
2366 if (upd != NULL) {
2367 ret = remove_from_dictionary(dictionary, table, FALSE);
2368 if (ret != DB_SUCCESS) {
2369 #ifdef NISDB_LDAP_DEBUG
2370 abort();
2371 #endif /* NISDB_LDAP_DEBUG */
2373 * Cut our losses; delete old incarnation, and leave
2374 * updated one in place.
2376 delete_table_desc(old);
2377 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback");
2378 return (ret);
2380 /* Throw away updates */
2381 delete_table_desc(upd);
2384 /* (Re-)insert old incarnation in the dictionary */
2385 ret = add_to_dictionary(dictionary, old);
2386 if (ret != DB_SUCCESS) {
2387 #ifdef NISDB_LDAP_DEBUG
2388 abort();
2389 #endif /* NISDB_LDAP_DEBUG */
2390 /* At least avoid memory leak */
2391 delete_table_desc(old);
2392 syslog(LOG_ERR,
2393 "db_dictionary::rollback: rollback error %d for \"%s\"", ret, table);
2396 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback");
2397 return (ret);
2401 * Commit changes. Done by simply removing and deleting the pre-resync
2402 * data from the deferred dictionary.
2404 db_status
2405 db_dictionary::commit(char *table) {
2406 db_status ret = DB_SUCCESS;
2407 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::commit");
2408 db_table_desc *old = search_dictionary(deferred.dictionary, table);
2410 if (old == NULL) {
2411 /* Fine (we hope); nothing to do */
2412 WRITEUNLOCK(this, ret, "wu db_dictionary::commit");
2413 return (DB_SUCCESS);
2416 ret = remove_from_dictionary(deferred.dictionary, table, FALSE);
2417 if (ret == DB_SUCCESS)
2418 delete_table_desc(old);
2419 #ifdef NISDB_LDAP_DEBUG
2420 else
2421 abort();
2422 #endif /* NISDB_LDAP_DEBUG */
2424 WRITEUNLOCK(this, ret, "wu db_dictionary::commit");
2425 return (ret);
2429 * The noWriteThrough flag is used to prevent modifies/updates to LDAP
2430 * while we're incorporating log data into the in-memory tables.
2432 void
2433 db_dictionary::setNoWriteThrough(void) {
2434 ASSERTWHELD(this->dict);
2435 noWriteThrough.flag++;
2438 void
2439 db_dictionary::clearNoWriteThrough(void) {
2440 ASSERTWHELD(this->dict);
2441 if (noWriteThrough.flag > 0)
2442 noWriteThrough.flag--;
2443 #ifdef NISDB_LDAP_DEBUG
2444 else
2445 abort();
2446 #endif /* NISDB_LDAP_DEBUG */