1 /* 2 * CDDL HEADER START 3 * 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. 8 * 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. 13 * 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] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * db_dictionary.cc 24 * 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 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> 42 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> 52 53 typedef bool_t (*db_func)(XDR *, db_table_desc *); 54 55 extern db_dictionary *InUseDictionary; 56 extern db_dictionary *FreeDictionary; 57 58 /* *************** dictionary version ****************** */ 59 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) 66 67 vers db_update_version; /* Note 'global' for all dbs. */ 68 69 #define INMEMORY_ONLY 1 70 71 /* 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. 76 * 77 * Our current implementation can support both versions. 78 */ 79 static inline bool_t 80 db_valid_version(u_int vers) 81 { 82 return ((vers == DB_CURRENT_VERSION) || (vers == DB_ORIG_VERSION)); 83 } 84 85 static char * 86 db_version_str(u_int vers) 87 { 88 static char vstr[128]; 89 u_int d_major = (vers&0x0000ff00)>>8; 90 u_int d_minor = (vers&0x000000ff); 91 92 sprintf(vstr, "SunSoft, SSM, Version %d.%d", d_major, d_minor); 93 return (vstr); 94 } 95 96 /* 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. 103 */ 104 extern "C" { 105 bool_t 106 xdr_db_dict_version(XDR *xdrs, db_dict_version *objp) 107 { 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); 118 } 119 } else if (!xdr_u_int(xdrs, (u_int*) objp)) 120 return (FALSE); 121 return (TRUE); 122 } 123 124 void 125 make_zero(vers* v) 126 { 127 v->zero(); 128 } 129 130 131 }; 132 133 134 /* ******************* dictionary data structures *************** */ 135 136 /* Delete contents of single db_table_desc pointed to by 'current.' */ 137 static void 138 delete_table_desc(db_table_desc *current) 139 { 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; 144 } 145 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) 150 { 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); 156 } 157 158 newtab->database = NULL; 159 newtab->table_name = NULL; 160 newtab->next = NULL; 161 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); 167 } 168 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); 174 } 175 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); 181 } 182 183 if (answer) 184 *answer = newtab; 185 return (DB_SUCCESS); 186 } 187 188 189 /* Delete list of db_table_desc pointed to by 'head.' */ 190 static void 191 delete_bucket(db_table_desc *head) 192 { 193 db_table_desc * nextone, *current; 194 195 for (current = head; current != NULL; current = nextone) { 196 nextone = current->next; // remember next 197 delete_table_desc(current); 198 } 199 } 200 201 static void 202 delete_dictionary(db_dict_desc *dict) 203 { 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; 215 } 216 /* delete dictionary */ 217 delete dict; 218 } 219 } 220 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) 225 { 226 db_table_desc_p np, next_np, *hp; 227 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; 233 } 234 } 235 236 /* 237 * Return pointer to entry with same hash value and table_name 238 * as those supplied. Returns NULL if not found. 239 */ 240 static db_status 241 enumerate_bucket(db_table_desc* bucket, db_status(*func)(db_table_desc *)) 242 { 243 db_table_desc_p np; 244 db_status status; 245 246 for (np = bucket; np != NULL; np = np->next) { 247 status = (func)(np); 248 if (status != DB_SUCCESS) 249 return (status); 250 } 251 return (DB_SUCCESS); 252 } 253 254 255 /* 256 * Return pointer to entry with same hash value and table_name 257 * as those supplied. Returns NULL if not found. 258 */ 259 static db_table_desc_p 260 search_bucket(db_table_desc* bucket, unsigned long hval, char *target) 261 { 262 db_table_desc_p np; 263 264 for (np = bucket; np != NULL; np = np->next) { 265 if (np->hashval == hval && 266 strcmp(np->table_name, target) == 0) { 267 break; 268 } 269 } 270 return (np); 271 } 272 273 274 /* 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. 280 */ 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) 285 { 286 db_table_desc_p np, dp; 287 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; 295 } 296 } 297 298 if (np == NULL) 299 return (FALSE); // cannot delete if it is not there 300 301 if (dp == np) { 302 *head = np->next; // deleting head of bucket 303 } else { 304 dp->next = np->next; // deleting interior link 305 } 306 if (free_storage) 307 delete_table_desc(np); 308 309 return (TRUE); 310 } 311 312 313 /* 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. 317 */ 318 static bool_t 319 add_to_bucket(db_table_desc_p bucket, db_table_desc **head, db_table_desc_p td) 320 { 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; 326 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; 334 } 335 } 336 337 if (curr != NULL) 338 return (FALSE); /* duplicates not allowed */ 339 340 curr = *head; 341 *head = td; 342 td->next = curr; 343 return (TRUE); 344 } 345 346 347 /* Print bucket starting with this entry. */ 348 static void 349 print_bucket(db_table_desc *head) 350 { 351 db_table_desc *np; 352 for (np = head; np != NULL; np = np->next) { 353 printf("%s: %d\n", np->table_name, np->hashval); 354 } 355 } 356 357 static db_status 358 print_table(db_table_desc *tbl) 359 { 360 if (tbl == NULL) 361 return (DB_BADTABLE); 362 printf("%s: %d\n", tbl->table_name, tbl->hashval); 363 return (DB_SUCCESS); 364 } 365 366 367 static int hashsizes[] = { /* hashtable sizes */ 368 11, 369 53, 370 113, 371 337, 372 977, 373 2053, 374 4073, 375 8011, 376 16001, 377 0 378 }; 379 380 // prevents wrap around numbers from being passed 381 #define CALLOC_LIMIT 536870911 382 383 /* Returns the next size to use for the hash table */ 384 static unsigned int 385 get_next_hashsize(long unsigned oldsize) 386 { 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; 395 } 396 if (newsize == 0) 397 newsize = oldsize * 2 + 1; /* just double */ 398 } 399 return (newsize); 400 } 401 402 /* 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. 407 */ 408 static void 409 grow_dictionary(db_dict_desc_p dd) 410 { 411 unsigned int oldsize, i, new_size; 412 db_table_desc_p * oldtab, *newtab; 413 414 oldsize = dd->tables.tables_len; 415 oldtab = dd->tables.tables_val; 416 417 new_size = get_next_hashsize(oldsize); 418 419 if (new_size > CALLOC_LIMIT) { 420 FATAL("db_dictionary::grow: table size exceeds calloc limit", 421 DB_MEMORY_LIMIT); 422 } 423 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); 429 } 430 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); 434 } 435 delete oldtab; // delete old hashtable 436 } 437 438 dd->tables.tables_val = newtab; 439 dd->tables.tables_len = new_size; 440 } 441 442 #define HASHSHIFT 3 443 #define HASHMASK 0x1f 444 445 static u_int 446 get_hashval(char *value) 447 { 448 int i, len; 449 u_int hval = 0; 450 451 len = strlen(value); 452 for (i = 0; i < len; i++) { 453 hval = ((hval<<HASHSHIFT)^hval); 454 hval += (value[i] & HASHMASK); 455 } 456 457 return (hval); 458 } 459 460 static db_status 461 enumerate_dictionary(db_dict_desc *dd, db_status (*func) (db_table_desc*)) 462 { 463 int i; 464 db_table_desc *bucket; 465 db_status status; 466 467 if (dd == NULL) 468 return (DB_SUCCESS); 469 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); 476 } 477 } 478 479 return (DB_SUCCESS); 480 } 481 482 483 /* 484 * Look up target table_name in hashtable and return its db_table_desc. 485 * Return NULL if not found. 486 */ 487 static db_table_desc * 488 search_dictionary(db_dict_desc *dd, char *target) 489 { 490 register unsigned long hval; 491 unsigned long bucket; 492 493 if (target == NULL || dd == NULL || dd->tables.tables_len == 0) 494 return (NULL); 495 496 hval = get_hashval(target); 497 bucket = hval % dd->tables.tables_len; 498 499 db_table_desc_p fst = dd->tables.tables_val[bucket]; 500 501 if (fst != NULL) 502 return (search_bucket(fst, hval, target)); 503 else 504 return (NULL); 505 } 506 507 /* 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. 512 */ 513 static db_status 514 remove_from_dictionary(db_dict_desc *dd, char *target, bool_t remove_storage) 515 { 516 register unsigned long hval; 517 unsigned long bucket; 518 register db_table_desc *fst; 519 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); 535 } 536 537 /* 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. 543 * 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). 551 */ 552 static db_status 553 add_to_dictionary(db_dict_desc_p dd, db_table_desc *td) 554 { 555 register unsigned long hval; 556 char *target; 557 558 if (dd == NULL) 559 return (DB_NOTFOUND); 560 561 if (td == NULL) 562 return (DB_NOTFOUND); 563 target = td->table_name; 564 if (target == NULL) 565 return (DB_NOTUNIQUE); 566 567 hval = get_hashval(target); 568 569 if (dd->tables.tables_val == NULL) 570 grow_dictionary(dd); 571 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); 581 } 582 583 /* increase hash table size if number of entries equals table size */ 584 if (++(dd->count) > dd->tables.tables_len) 585 grow_dictionary(dd); 586 587 return (DB_SUCCESS); 588 } 589 590 /* ******************* pickling routines for dictionary ******************* */ 591 592 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) 596 { 597 return (xdr_db_dict_desc_p(x, (db_dict_desc_p *) tbl)); 598 } 599 600 class pickle_dict_desc: public pickle_file { 601 public: 602 pickle_dict_desc(char *f, pickle_mode m) : pickle_file(f, m) {} 603 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)); } 607 }; 608 609 /* ************************ dictionary methods *************************** */ 610 611 db_dictionary::db_dictionary() 612 { 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); 623 } 624 625 /* 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. 635 */ 636 int 637 db_dictionary::db_clone_bucket(db_table_desc *bucket, db_table_desc **clone) 638 { 639 u_long size; 640 XDR xdrs; 641 char *bufin = NULL; 642 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); 652 } 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); 662 } 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); 671 } 672 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); 683 } 684 free(bufin); 685 xdr_destroy(&xdrs); 686 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_clone_bucket"); 687 return (1); 688 } 689 690 691 int 692 db_dictionary::change_table_name(db_table_desc *clone, char *tok, char *repl) 693 { 694 char *newname; 695 char *loc_end, *loc_beg; 696 697 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::change_table_name"); 698 while (clone) { 699 /* 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. 706 */ 707 if (strlen(tok) == 0) { 708 strcat(clone->table_name, repl); 709 clone = clone->next; 710 continue; 711 } 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); 720 } 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); 731 } 732 clone = clone->next; 733 } 734 WRITEUNLOCK(this, DB_LOCK_ERROR, 735 "wu db_dictionary::change_table_name"); 736 return (1); 737 } 738 739 740 #ifdef curdict 741 #undef curdict 742 #endif 743 /* 744 * A function to initialize the temporary dictionary from the real 745 * dictionary. 746 */ 747 bool_t 748 db_dictionary::inittemp(char *dictname, db_dictionary& curdict) 749 { 750 int status; 751 db_table_desc_p *newtab; 752 753 db_shutdown(); 754 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); 760 } 761 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); 769 } 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); 778 } 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); 787 } 788 789 sprintf(tmpfilename, "%s.tmp", filename); 790 sprintf(logfilename, "%s.log", filename); 791 unlink(tmpfilename); /* get rid of partial checkpoints */ 792 dictionary = NULL; 793 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); 804 } 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); 815 } 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; 822 823 if (initialized == TRUE) { 824 changed = FALSE; 825 reset_log(); 826 } 827 828 WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp"); 829 return (initialized); 830 } 831 832 833 /* 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. 838 */ 839 db_status 840 db_dictionary::massage_dict(char *newdictname, char *tok, char *repl) 841 { 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; 848 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); 856 } 857 858 if ((tbl_count = dictionary->count) == 0) { 859 WRITEUNLOCK(this, DB_SUCCESS, 860 "wu db_dictionary::massage_dict"); 861 return (DB_SUCCESS); 862 } 863 864 /* First checkpoint */ 865 if ((status = checkpoint()) != DB_SUCCESS) { 866 WRITEUNLOCK(this, status, "wu db_dictionary::massage_dict"); 867 return (status); 868 } 869 870 #ifdef DEBUG 871 enumerate_dictionary(dictionary, &print_table); 872 #endif 873 874 /* Initialize the free dictionary so that we can start populating it */ 875 FreeDictionary->inittemp(newdictname, *this); 876 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); 888 } 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); 894 } 895 /* 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. 899 */ 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); 908 } 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); 916 } 917 np = next_np; 918 } 919 } 920 } 921 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); 928 } 929 930 /* 931 * Now, shutdown the inuse dictionary and update the FreeDictionary 932 * and InUseDictionary pointers as well. Also, delete the old dictionary 933 * file. 934 */ 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); 942 } 943 944 945 db_status 946 db_dictionary::merge_dict(db_dictionary& tempdict, char *tok, char *repl) 947 { 948 949 db_status dbstat = DB_SUCCESS; 950 951 db_table_desc *tbl = NULL, *clone = NULL, *next_td = NULL; 952 int retval, i; 953 954 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::merge_dict"); 955 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); 965 } 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); 977 } 978 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); 988 } 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); 996 } 997 } 998 clone = next_td; 999 } 1000 } 1001 /* 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. 1005 */ 1006 if (dbstat == DB_SUCCESS) 1007 changed = TRUE; 1008 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::merge_dict"); 1009 return (dbstat); 1010 } 1011 1012 int 1013 db_dictionary::copyfile(char *infile, char *outfile) 1014 { 1015 db_table_desc *tbl = NULL; 1016 db *dbase; 1017 int ret; 1018 1019 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile"); 1020 /* 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. 1025 */ 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); 1033 /* 1034 * Read-lock again, and get a 'tbl' we can use since we're 1035 * still holding the lock. 1036 */ 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); 1043 } 1044 } 1045 ret = tbl->database->dump(outfile) ? DB_SUCCESS : DB_INTERNAL_ERROR; 1046 READUNLOCK(this, ret, "ru db_dictionary::copyfile"); 1047 return (ret); 1048 } 1049 1050 1051 bool_t 1052 db_dictionary::extract_entries(db_dictionary& tempdict, char **fs, int fscnt) 1053 { 1054 int i, retval; 1055 db_table_desc *tbl, *clone; 1056 db_table_desc tbl_ent; 1057 db_status dbstat; 1058 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; 1075 } 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); 1084 } 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); 1091 } 1092 } 1093 if (tempdict.dump() != DB_SUCCESS) { 1094 READUNLOCK(this, FALSE, 1095 "ru db_dictionary::extract_entries"); 1096 return (FALSE); 1097 } 1098 READUNLOCK(this, FALSE, 1099 "ru db_dictionary::extract_entries"); 1100 return (TRUE); 1101 } 1102 1103 1104 /* 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. 1110 */ 1111 bool_t 1112 db_dictionary::init(char *file) 1113 { 1114 int status; 1115 1116 WRITELOCK(this, FALSE, "w db_dictionary::init"); 1117 db_shutdown(); 1118 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); 1126 } 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); 1135 } 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); 1144 } 1145 1146 sprintf(tmpfilename, "%s.tmp", filename); 1147 sprintf(logfilename, "%s.log", filename); 1148 unlink(tmpfilename); /* get rid of partial checkpoints */ 1149 dictionary = NULL; 1150 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); 1161 } 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; 1169 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); 1179 } 1180 1181 WRITEUNLOCK(this, initialized, "wu db_dictionary::init"); 1182 return (initialized); 1183 } 1184 1185 /* 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. 1191 */ 1192 static bool_t 1193 apply_log_entry(db_dictlog_entry *j, char *dictchar, int *count) 1194 { 1195 db_dictionary *dict = (db_dictionary*) dictchar; 1196 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; 1209 1210 case DB_REMOVE_TABLE: 1211 dict->delete_table_aux(j->get_table_name(), 1212 INMEMORY_ONLY); 1213 // ignore status 1214 break; 1215 1216 default: 1217 WARNING("db::apply_log_entry: unknown action_type"); 1218 WRITEUNLOCK(dict, FALSE, "wu apply_log_entry"); 1219 return (FALSE); 1220 } 1221 db_update_version.assign(j->get_version()); 1222 } 1223 1224 WRITEUNLOCK(dict, TRUE, "wu apply_log_entry"); 1225 return (TRUE); 1226 } 1227 1228 int 1229 db_dictionary::incorporate_log(char *file_name) 1230 { 1231 db_dictlog f(file_name, PICKLE_READ); 1232 int ret; 1233 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); 1240 } 1241 1242 1243 /* Frees memory of filename and tables. Has no effect on disk storage. */ 1244 db_status 1245 db_dictionary::db_shutdown() 1246 { 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? */ 1252 } 1253 1254 if (filename) { 1255 delete filename; 1256 filename = NULL; 1257 } 1258 if (tmpfilename) { 1259 delete tmpfilename; 1260 tmpfilename = NULL; 1261 } 1262 if (logfilename) { 1263 delete logfilename; 1264 logfilename = NULL; 1265 } 1266 if (dictionary) { 1267 delete_dictionary(dictionary); 1268 dictionary = NULL; 1269 } 1270 initialized = FALSE; 1271 changed = FALSE; 1272 reset_log(); 1273 1274 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_shutdown"); 1275 return (DB_SUCCESS); 1276 } 1277 1278 /* 1279 * Dump contents of this dictionary (minus the database representations) 1280 * to its file. Returns 0 if operation succeeds, -1 otherwise. 1281 */ 1282 int 1283 db_dictionary::dump() 1284 { 1285 int status; 1286 1287 READLOCK(this, -1, "r db_dictionary::dump"); 1288 if (!initialized) { 1289 READUNLOCK(this, -1, "ru db_dictionary::dump"); 1290 return (-1); 1291 } 1292 1293 unlink(tmpfilename); /* get rid of partial dumps */ 1294 pickle_dict_desc f(tmpfilename, PICKLE_WRITE); 1295 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; 1302 } 1303 1304 READUNLOCK(this, -1, "ru db_dictionary::dump"); 1305 return (status); 1306 } 1307 1308 /* 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 1316 */ 1317 db_status 1318 db_dictionary::checkpoint() 1319 { 1320 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::checkpoint"); 1321 1322 if (changed == FALSE) { 1323 WRITEUNLOCK(this, DB_LOCK_ERROR, 1324 "wu db_dictionary::checkpoint"); 1325 return (DB_SUCCESS); 1326 } 1327 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; 1332 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); 1341 } 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); 1348 } 1349 1350 /* close existing logfile and delete its structure */ 1351 int 1352 db_dictionary::reset_log() 1353 { 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: "); 1362 } 1363 } 1364 delete logfile; 1365 logfile = NULL; 1366 } 1367 logfile_opened = FALSE; 1368 WRITEUNLOCK(this, -1, "wu db_dictionary::reset_log"); 1369 return (0); 1370 } 1371 1372 /* close existing logfile, but leave its structure if exists */ 1373 int 1374 db_dictionary::close_log() 1375 { 1376 WRITELOCK(this, -1, "w db_dictionary::close_log"); 1377 if (logfile != NULL && logfile_opened == TRUE) { 1378 logfile->close(); 1379 } 1380 logfile_opened = FALSE; 1381 WRITEUNLOCK(this, -1, "wu db_dictionary::close_log"); 1382 return (0); 1383 } 1384 1385 /* open logfile, creating its structure if it does not exist */ 1386 int 1387 db_dictionary::open_log() 1388 { 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); 1397 } 1398 } 1399 1400 if (logfile_opened == TRUE) { 1401 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log"); 1402 return (0); 1403 } 1404 1405 if ((logfile->open()) == NULL) { 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); 1411 } 1412 1413 logfile_opened = TRUE; 1414 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log"); 1415 return (0); 1416 } 1417 1418 /* 1419 * closes any open log files for all tables in dictionary or 'tab'. 1420 * "tab" is an optional argument. 1421 */ 1422 static int close_standby_list(); 1423 1424 db_status 1425 db_dictionary::db_standby(char *tab) 1426 { 1427 db_table_desc *tbl; 1428 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); 1434 } 1435 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); 1441 } 1442 1443 if ((tbl = find_table_desc(tab)) == NULL) { 1444 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby"); 1445 return (DB_BADTABLE); 1446 } 1447 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); 1452 } 1453 1454 /* 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. 1457 */ 1458 db_table_desc* 1459 db_dictionary::find_table_desc(char *tab) 1460 { 1461 db_table_desc *ret; 1462 1463 READLOCK(this, NULL, "r db_dictionary::find_table_desc"); 1464 if (initialized) 1465 ret = search_dictionary(dictionary, tab); 1466 else 1467 ret = NULL; 1468 1469 READUNLOCK(this, ret, "r db_dictionary::find_table_desc"); 1470 return (ret); 1471 } 1472 1473 db_table_desc * 1474 db_dictionary::find_table_desc(char *tab, bool_t searchDeferred) { 1475 db_table_desc *ret = NULL; 1476 1477 READLOCK(this, NULL, "r db_dictionary::find_table_desc_d"); 1478 1479 /* If desired, look in the deferred dictionary first */ 1480 if (initialized && searchDeferred && deferred.dictionary != NULL) 1481 ret = search_dictionary(deferred.dictionary, tab); 1482 1483 /* No result yet => search the "normal" dictionary */ 1484 if (ret == NULL) 1485 ret = find_table_desc(tab); 1486 1487 READUNLOCK(this, ret, "r db_dictionary::find_table_desc_d"); 1488 return (ret); 1489 } 1490 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)); 1495 } 1496 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)); 1501 } 1502 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"; 1511 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); 1516 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)); 1526 } 1527 } else { 1528 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1529 "%s: DB error %d for \"%s\"", 1530 myself, dstat, NIL(tab)); 1531 } 1532 return (0); 1533 } 1534 1535 /* Try the dictionary again */ 1536 res = find_table_noLDAP(tab, where, searchDeferred, doLoad); 1537 1538 return (res); 1539 } 1540 1541 /* 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. 1547 */ 1548 db * 1549 db_dictionary::find_table_noLDAP(char *tab, db_table_desc **where, 1550 bool_t searchDeferred, bool_t doLoad) 1551 { 1552 if (!initialized) 1553 return (NULL); 1554 1555 db_table_desc* tbl; 1556 db *dbase = NULL; 1557 int lret; 1558 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 1564 } 1565 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 1570 } 1571 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 1579 } 1580 1581 // need to load in/init database 1582 dbase = new db(tab); 1583 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); 1589 } 1590 1591 /* 1592 * Lock the newly created 'dbase', so we can release the general 1593 * db_dictionary lock. 1594 */ 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); 1601 } 1602 /* Assign tbl->database, and then release the 'this' lock */ 1603 tbl->database = dbase; 1604 WRITEUNLOCK(this, NULL, "wu db_dictionary::find_table"); 1605 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); 1610 } 1611 1612 delete dbase; 1613 tbl->database = NULL; 1614 WARNING("db_dictionary::find_table: could not load database"); 1615 return (NULL); 1616 } 1617 1618 /* Log action to be taken on the dictionary and update db_update_version. */ 1619 1620 db_status 1621 db_dictionary::log_action(int action, char *tab, table_obj *tobj) 1622 { 1623 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::log_action"); 1624 1625 vers *newv = db_update_version.nextminor(); 1626 db_dictlog_entry le(action, newv, tab, tobj); 1627 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); 1633 } 1634 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); 1642 } 1643 1644 db_update_version.assign(newv); 1645 delete newv; 1646 changed = TRUE; 1647 1648 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::log_action"); 1649 return (DB_SUCCESS); 1650 } 1651 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. 1657 1658 db_status 1659 db_dictionary::delete_table_aux(char *tab, int mode) 1660 { 1661 db_status ret; 1662 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); 1668 } 1669 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); 1675 } 1676 1677 if (mode != INMEMORY_ONLY) { 1678 int need_free = 0; 1679 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); 1686 } 1687 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; 1693 } 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); 1700 } 1701 dbase->remove_files(); // remove physical files 1702 if (need_free) 1703 delete dbase; 1704 } 1705 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); 1710 } 1711 1712 /* 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. 1719 */ 1720 db_status 1721 db_dictionary::delete_table(char *tab) 1722 { 1723 return (delete_table_aux(tab, !INMEMORY_ONLY)); 1724 } 1725 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. 1734 // 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. 1738 1739 db_status 1740 db_dictionary::add_table_aux(char *tab, table_obj* tobj, int mode) 1741 { 1742 db_status ret; 1743 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); 1749 } 1750 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 1755 } 1756 1757 // create data structures for table 1758 db_table_desc *new_table = 0; 1759 db_status status = create_table_desc(tab, tobj, &new_table); 1760 1761 if (status != DB_SUCCESS) { 1762 WRITEUNLOCK(this, DB_LOCK_ERROR, 1763 "wu db_dictionary::add_table_aux"); 1764 return (status); 1765 } 1766 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); 1777 } 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); 1786 } 1787 1788 // update 'external' copy of dictionary 1789 status = log_action(DB_ADD_TABLE, tab, tobj); 1790 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); 1797 } 1798 } 1799 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); 1804 } 1805 1806 /* 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. 1813 */ 1814 db_status 1815 db_dictionary::add_table(char *tab, table_obj* tobj) 1816 { 1817 return (add_table_aux(tab, tobj, !INMEMORY_ONLY)); 1818 } 1819 1820 /* 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?). 1824 */ 1825 db_query* 1826 db_dictionary::translate_to_query(db_table_desc* tbl, int numattrs, 1827 nis_attr* attrlist) 1828 { 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); 1834 } 1835 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); 1842 } 1843 1844 if (q->size() == 0) { 1845 delete q; 1846 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query"); 1847 return (NULL); 1848 } 1849 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query"); 1850 return (q); 1851 } 1852 1853 static db_table_names gt_answer; 1854 static int gt_posn; 1855 1856 static db_status 1857 get_table_name(db_table_desc* tbl) 1858 { 1859 if (tbl) 1860 return (DB_BADTABLE); 1861 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); 1867 1868 return (DB_SUCCESS); 1869 } 1870 1871 1872 /* 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. 1877 */ 1878 db_table_names* 1879 db_dictionary::get_table_names() 1880 { 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); 1891 } 1892 1893 enumerate_dictionary(dictionary, &get_table_name); 1894 READUNLOCK(this, NULL, "ru db_dictionary::get_table_names"); 1895 return (>_answer); 1896 } 1897 1898 static db_status 1899 db_checkpoint_aux(db_table_desc *current) 1900 { 1901 db *dbase; 1902 int status; 1903 1904 if (current == NULL) 1905 return (DB_BADTABLE); 1906 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); 1913 } 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); 1920 } 1921 status = dbase->checkpoint(); 1922 delete dbase; // unload 1923 } else 1924 status = current->database->checkpoint(); 1925 1926 if (status == 0) 1927 return (DB_STORAGE_LIMIT); 1928 return (DB_SUCCESS); 1929 } 1930 1931 /* Like db_checkpoint_aux except only stops on LIMIT errors */ 1932 static db_status 1933 db_checkpoint_aux_cont(db_table_desc *current) 1934 { 1935 db_status status = db_checkpoint_aux(current); 1936 1937 if (status == DB_STORAGE_LIMIT || status == DB_MEMORY_LIMIT) 1938 return (status); 1939 else 1940 return (DB_SUCCESS); 1941 } 1942 1943 db_status 1944 db_dictionary::db_checkpoint(char *tab) 1945 { 1946 db_table_desc *tbl; 1947 db_status ret; 1948 bool_t init; 1949 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); 1955 1956 checkpoint(); // checkpoint dictionary first 1957 1958 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_checkpoint"); 1959 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); 1964 } 1965 1966 if ((tbl = find_table_desc(tab)) == NULL) { 1967 READUNLOCK(this, DB_LOCK_ERROR, 1968 "ru db_dictionary::db_checkpoint"); 1969 return (DB_BADTABLE); 1970 } 1971 1972 ret = db_checkpoint_aux(tbl); 1973 READUNLOCK(this, ret, "ru db_dictionary::db_checkpoint"); 1974 return (ret); 1975 } 1976 1977 /* *********************** db_standby **************************** */ 1978 /* Deal with list of tables that need to be 'closed' */ 1979 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); 1985 1986 /* 1987 * Returns 1 if all databases on the list could be closed, 0 1988 * otherwise. 1989 */ 1990 static int 1991 close_standby_list() 1992 { 1993 db *database; 1994 int i, ret; 1995 const char *myself = "close_standby_list"; 1996 1997 MUTEXLOCK(db_standby_list, "close_standby_list"); 1998 1999 if (db_standby_count == 0) { 2000 MUTEXUNLOCK(db_standby_list, "close_standby_list"); 2001 return (1); 2002 } 2003 2004 for (i = 0, ret = 0; i < db_standby_size; i++) { 2005 if ((database = db_standby_list[i])) { 2006 /* 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. 2010 */ 2011 int lockok; 2012 2013 TRYWRITELOCK(database, lockok, 2014 "try w db_dictionary::close_standby_list"); 2015 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; 2025 } 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 */ 2031 } 2032 } 2033 2034 MUTEXUNLOCK(db_standby_list, "close_standby_list"); 2035 2036 return (ret); 2037 } 2038 2039 /* 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. 2042 */ 2043 2044 int 2045 add_to_standby_list(db* database) 2046 { 2047 int i; 2048 const char *myself = "add_to_standby_list"; 2049 2050 MUTEXLOCK(db_standby_list, "add_to_standby_list"); 2051 2052 if (database == 0) { 2053 MUTEXUNLOCK(db_standby_list, "add_to_standby_list"); 2054 return (1); 2055 } 2056 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"); 2062 } 2063 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])); 2068 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); 2076 } 2077 2078 db_standby_list = ndsl; 2079 2080 for (i = db_standby_size; i < db_standby_size+OPENED_DBS_CHUNK; 2081 i++) 2082 db_standby_list[i] = 0; 2083 2084 db_standby_size += OPENED_DBS_CHUNK; 2085 } 2086 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); 2093 } 2094 } 2095 2096 MUTEXUNLOCK(db_standby_list, "add_to_standby_list"); 2097 2098 return (0); 2099 } 2100 2101 int 2102 remove_from_standby_list(db* database) 2103 { 2104 int i; 2105 2106 MUTEXLOCK(db_standby_list, "remove_from_standby_list"); 2107 2108 if (database == 0) { 2109 MUTEXUNLOCK(db_standby_list, "remove_from_standby_list"); 2110 return (1); 2111 } 2112 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); 2120 } 2121 } 2122 2123 MUTEXUNLOCK(db_standby_list, "remove_from_standby_list"); 2124 2125 return (0); 2126 } 2127 2128 /* Release space for copied dictionary */ 2129 static void 2130 db_release_dictionary(db_dict_desc_p d) { 2131 2132 int i; 2133 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; 2141 } 2142 } 2143 delete d; 2144 } 2145 2146 return; 2147 } 2148 2149 /* 2150 * Make a copy of the dictionary 2151 */ 2152 db_dict_desc_p 2153 db_dictionary::db_copy_dictionary(void) { 2154 2155 db_dict_desc_p tmp; 2156 int i, ok = 1, count = 0; 2157 2158 WRITELOCK(this, NULL, "db_dictionary::db_copy_dictionary w"); 2159 2160 if (dictionary == NULL) { 2161 WRITEUNLOCK(this, NULL, 2162 "db_dictionary::db_copy_dictionary wu"); 2163 return (NULL); 2164 } 2165 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); 2171 } 2172 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); 2181 } 2182 2183 tmp->impl_vers = dictionary->impl_vers; 2184 tmp->tables.tables_len = 0; 2185 tmp->count = 0; 2186 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; 2200 } 2201 tbl = n; 2202 tmp->count++; 2203 } else { 2204 ok = 0; 2205 } 2206 t->next = savenext; 2207 } 2208 tmp->tables.tables_len++; 2209 } 2210 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; 2221 } 2222 2223 return (tmp); 2224 } 2225 2226 /* 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. 2233 */ 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"; 2241 2242 if (tbl != NULL) { 2243 db_table_desc *clone, *savenext = tbl->next; 2244 /* 2245 * Only want to clone one db_table_desc, so temporarily 2246 * unlink the tail. 2247 */ 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); 2260 } 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; 2266 } 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; 2288 } 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; 2294 } 2295 } 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; 2308 } 2309 } 2310 } else { 2311 delete clone; 2312 /* Return value already set above */ 2313 } 2314 } else { 2315 ret = DB_INTERNAL_ERROR; 2316 } 2317 } else { 2318 ret = DB_NOTFOUND; 2319 } 2320 WRITEUNLOCK(this, ret, "wu db_dictionary::defer"); 2321 return (ret); 2322 } 2323 2324 /* 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. 2328 * 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. 2332 */ 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); 2339 2340 if (old == NULL) { 2341 WRITEUNLOCK(this, DB_NOTFOUND, "wu db_dictionary::rollback"); 2342 return (DB_NOTFOUND); 2343 } 2344 2345 /* 2346 * Remove old incarnation from deferred dictionary. We already hold 2347 * a pointer ('old') to it, so don't delete. 2348 */ 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); 2356 } 2357 2358 if (old->database != 0) 2359 old->database->unmarkDeferred(); 2360 2361 /* 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. 2365 */ 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 */ 2372 /* 2373 * Cut our losses; delete old incarnation, and leave 2374 * updated one in place. 2375 */ 2376 delete_table_desc(old); 2377 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback"); 2378 return (ret); 2379 } 2380 /* Throw away updates */ 2381 delete_table_desc(upd); 2382 } 2383 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); 2394 } 2395 2396 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback"); 2397 return (ret); 2398 } 2399 2400 /* 2401 * Commit changes. Done by simply removing and deleting the pre-resync 2402 * data from the deferred dictionary. 2403 */ 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); 2409 2410 if (old == NULL) { 2411 /* Fine (we hope); nothing to do */ 2412 WRITEUNLOCK(this, ret, "wu db_dictionary::commit"); 2413 return (DB_SUCCESS); 2414 } 2415 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 */ 2423 2424 WRITEUNLOCK(this, ret, "wu db_dictionary::commit"); 2425 return (ret); 2426 } 2427 2428 /* 2429 * The noWriteThrough flag is used to prevent modifies/updates to LDAP 2430 * while we're incorporating log data into the in-memory tables. 2431 */ 2432 void 2433 db_dictionary::setNoWriteThrough(void) { 2434 ASSERTWHELD(this->dict); 2435 noWriteThrough.flag++; 2436 } 2437 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 */ 2447 } 2448