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 * Copyright 2019 RackTop Systems. 29 */ 30 31 #include "db_headers.h" 32 #include "db_entry.h" 33 #include "db_dictionary.h" 34 #include "db_dictlog.h" 35 #include "db_vers.h" 36 #include "nisdb_mt.h" 37 #include "nisdb_rw.h" 38 #include "ldap_parse.h" 39 #include "ldap_map.h" 40 #include "nis_hashitem.h" 41 #include "ldap_util.h" 42 #include "nis_db.h" 43 #include <rpcsvc/nis.h> 44 45 #include <stdio.h> 46 #include <string.h> 47 #include <malloc.h> 48 #ifdef TDRPC 49 #include <sysent.h> 50 #endif 51 #include <unistd.h> 52 #include <syslog.h> 53 #include <rpc/rpc.h> 54 55 typedef bool_t (*db_func)(XDR *, db_table_desc *); 56 57 extern db_dictionary *InUseDictionary; 58 extern db_dictionary *FreeDictionary; 59 60 /* *************** dictionary version ****************** */ 61 62 #define DB_MAGIC 0x12340000 63 #define DB_MAJOR 0 64 #define DB_MINOR 10 65 #define DB_VERSION_0_9 (DB_MAGIC|(DB_MAJOR<<8)|9) 66 #define DB_ORIG_VERSION DB_VERSION_0_9 67 #define DB_CURRENT_VERSION (DB_MAGIC|DB_MAJOR<<8|DB_MINOR) 68 69 vers db_update_version; /* Note 'global' for all dbs. */ 70 71 #define INMEMORY_ONLY 1 72 73 /* 74 * Checks for valid version. For now, there are two: 75 * DB_VERSION_ORIG was the ORIGINAL one with major = 0, minor = 9 76 * DB_CURRENT_VERSION is the latest one with changes in the database format 77 * for entry objects and the change in the dictionary format. 78 * 79 * Our current implementation can support both versions. 80 */ 81 static inline bool_t 82 db_valid_version(u_int vers) 83 { 84 return ((vers == DB_CURRENT_VERSION) || (vers == DB_ORIG_VERSION)); 85 } 86 87 static char * 88 db_version_str(u_int vers) 89 { 90 static char vstr[128]; 91 u_int d_major = (vers&0x0000ff00)>>8; 92 u_int d_minor = (vers&0x000000ff); 93 94 sprintf(vstr, "SunSoft, SSM, Version %d.%d", d_major, d_minor); 95 return (vstr); 96 } 97 98 /* 99 * Special XDR version that checks for a valid version number. 100 * If we don't find an acceptable version, exit immediately instead 101 * of continuing to xdr rest of dictionary, which might lead to 102 * a core dump if the formats between versions are incompatible. 103 * In the future, there might be a switch to determine versions 104 * and their corresponding XDR routines for the rest of the dictionary. 105 */ 106 extern "C" { 107 bool_t 108 xdr_db_dict_version(XDR *xdrs, db_dict_version *objp) 109 { 110 if (xdrs->x_op == XDR_DECODE) { 111 if (!xdr_u_int(xdrs, (u_int*) objp) || 112 !db_valid_version(((u_int) *objp))) { 113 syslog(LOG_ERR, 114 "db_dictionary: invalid dictionary format! Expecting %s", 115 db_version_str(DB_CURRENT_VERSION)); 116 fprintf(stderr, 117 "db_dictionary: invalid dictionary format! Expecting %s\n", 118 db_version_str(DB_CURRENT_VERSION)); 119 exit(1); 120 } 121 } else if (!xdr_u_int(xdrs, (u_int*) objp)) 122 return (FALSE); 123 return (TRUE); 124 } 125 126 void 127 make_zero(vers* v) 128 { 129 v->zero(); 130 } 131 132 133 }; 134 135 136 /* ******************* dictionary data structures *************** */ 137 138 /* Delete contents of single db_table_desc pointed to by 'current.' */ 139 static void 140 delete_table_desc(db_table_desc *current) 141 { 142 if (current->table_name != NULL) delete current->table_name; 143 if (current->scheme != NULL) delete current->scheme; 144 if (current->database != NULL) delete current->database; 145 delete current; 146 } 147 148 /* Create new table descriptor using given table name and table_object. */ 149 db_status 150 db_dictionary::create_table_desc(char *tab, table_obj* zdesc, 151 db_table_desc** answer) 152 { 153 db_table_desc *newtab; 154 if ((newtab = new db_table_desc) == NULL) { 155 FATAL3( 156 "db_dictionary::add_table: could not allocate space for new table", 157 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 158 } 159 160 newtab->database = NULL; 161 newtab->table_name = NULL; 162 newtab->next = NULL; 163 164 if ((newtab->scheme = new db_scheme(zdesc)) == NULL) { 165 delete_table_desc(newtab); 166 FATAL3( 167 "db_dictionary::add_table: could not allocate space for scheme", 168 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 169 } 170 171 if (newtab->scheme->numkeys() == 0) { 172 WARNING( 173 "db_dictionary::add_table: could not translate table_obj to scheme"); 174 delete_table_desc(newtab); 175 return (DB_BADOBJECT); 176 } 177 178 if ((newtab->table_name = strdup(tab)) == NULL) { 179 delete_table_desc(newtab); 180 FATAL3( 181 "db_dictionary::add_table: could not allocate space for table name", 182 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 183 } 184 185 if (answer) 186 *answer = newtab; 187 return (DB_SUCCESS); 188 } 189 190 191 /* Delete list of db_table_desc pointed to by 'head.' */ 192 static void 193 delete_bucket(db_table_desc *head) 194 { 195 db_table_desc * nextone, *current; 196 197 for (current = head; current != NULL; current = nextone) { 198 nextone = current->next; // remember next 199 delete_table_desc(current); 200 } 201 } 202 203 static void 204 delete_dictionary(db_dict_desc *dict) 205 { 206 db_table_desc* bucket; 207 int i; 208 if (dict) { 209 if (dict->tables.tables_val) { 210 /* delete each bucket */ 211 for (i = 0; i < dict->tables.tables_len; i++) { 212 bucket = dict->tables.tables_val[i]; 213 if (bucket) 214 delete_bucket(bucket); 215 } 216 /* delete table */ 217 delete dict->tables.tables_val; 218 } 219 /* delete dictionary */ 220 delete dict; 221 } 222 } 223 224 /* Relocate bucket starting with this entry to new hashtable 'new_tab'. */ 225 static void 226 relocate_bucket(db_table_desc* bucket, 227 db_table_desc_p *new_tab, unsigned long hashsize) 228 { 229 db_table_desc_p np, next_np, *hp; 230 231 for (np = bucket; np != NULL; np = next_np) { 232 next_np = np->next; 233 hp = &new_tab[np->hashval % hashsize]; 234 np->next = *hp; 235 *hp = np; 236 } 237 } 238 239 /* 240 * Return pointer to entry with same hash value and table_name 241 * as those supplied. Returns NULL if not found. 242 */ 243 static db_status 244 enumerate_bucket(db_table_desc* bucket, db_status(*func)(db_table_desc *)) 245 { 246 db_table_desc_p np; 247 db_status status; 248 249 for (np = bucket; np != NULL; np = np->next) { 250 status = (func)(np); 251 if (status != DB_SUCCESS) 252 return (status); 253 } 254 return (DB_SUCCESS); 255 } 256 257 258 /* 259 * Return pointer to entry with same hash value and table_name 260 * as those supplied. Returns NULL if not found. 261 */ 262 static db_table_desc_p 263 search_bucket(db_table_desc* bucket, unsigned long hval, char *target) 264 { 265 db_table_desc_p np; 266 267 for (np = bucket; np != NULL; np = np->next) { 268 if (np->hashval == hval && 269 strcmp(np->table_name, target) == 0) { 270 break; 271 } 272 } 273 return (np); 274 } 275 276 277 /* 278 * Remove entry with the specified hashvalue and target table name. 279 * Returns 'TRUE' if successful, FALSE otherwise. 280 * If the entry being removed is at the head of the list, then 281 * the head is updated to reflect the removal. The storage for the 282 * entry is freed if desired. 283 */ 284 static bool_t 285 remove_from_bucket(db_table_desc_p bucket, 286 db_table_desc_p *head, unsigned long hval, char *target, 287 bool_t free_storage) 288 { 289 db_table_desc_p np, dp; 290 291 /* Search for it in the bucket */ 292 for (dp = np = bucket; np != NULL; np = np->next) { 293 if (np->hashval == hval && 294 strcmp(np->table_name, target) == 0) { 295 break; 296 } else { 297 dp = np; 298 } 299 } 300 301 if (np == NULL) 302 return (FALSE); // cannot delete if it is not there 303 304 if (dp == np) { 305 *head = np->next; // deleting head of bucket 306 } else { 307 dp->next = np->next; // deleting interior link 308 } 309 if (free_storage) 310 delete_table_desc(np); 311 312 return (TRUE); 313 } 314 315 316 /* 317 * Add given entry to the bucket pointed to by 'bucket'. 318 * If an entry with the same table_name is found, no addition 319 * is done. The entry is added to the head of the bucket. 320 */ 321 static bool_t 322 add_to_bucket(db_table_desc_p bucket, db_table_desc **head, db_table_desc_p td) 323 { 324 db_table_desc_p curr; 325 char *target_name; 326 unsigned long target_hval; 327 target_name = td->table_name; 328 target_hval = td->hashval; 329 330 /* Search for it in the bucket */ 331 for (curr = bucket; curr != NULL; curr = curr->next) { 332 if (curr->hashval == target_hval && 333 strcmp(curr->table_name, target_name) == 0) { 334 break; 335 } 336 } 337 338 if (curr != NULL) 339 return (FALSE); /* duplicates not allowed */ 340 341 curr = *head; 342 *head = td; 343 td->next = curr; 344 return (TRUE); 345 } 346 347 348 /* Print bucket starting with this entry. */ 349 static void 350 print_bucket(db_table_desc *head) 351 { 352 db_table_desc *np; 353 for (np = head; np != NULL; np = np->next) { 354 printf("%s: %d\n", np->table_name, np->hashval); 355 } 356 } 357 358 static db_status 359 print_table(db_table_desc *tbl) 360 { 361 if (tbl == NULL) 362 return (DB_BADTABLE); 363 printf("%s: %d\n", tbl->table_name, tbl->hashval); 364 return (DB_SUCCESS); 365 } 366 367 368 static int hashsizes[] = { /* hashtable sizes */ 369 11, 370 53, 371 113, 372 337, 373 977, 374 2053, 375 4073, 376 8011, 377 16001, 378 0 379 }; 380 381 // prevents wrap around numbers from being passed 382 #define CALLOC_LIMIT 536870911 383 384 /* Returns the next size to use for the hash table */ 385 static unsigned int 386 get_next_hashsize(long unsigned oldsize) 387 { 388 long unsigned newsize, n; 389 if (oldsize == 0) 390 newsize = hashsizes[0]; 391 else { 392 for (n = 0; newsize = hashsizes[n++]; ) 393 if (oldsize == newsize) { 394 newsize = hashsizes[n]; /* get next size */ 395 break; 396 } 397 if (newsize == 0) 398 newsize = oldsize * 2 + 1; /* just double */ 399 } 400 return (newsize); 401 } 402 403 /* 404 * Grow the current hashtable upto the next size. 405 * The contents of the existing hashtable is copied to the new one and 406 * relocated according to its hashvalue relative to the new size. 407 * Old table is deleted after the relocation. 408 */ 409 static void 410 grow_dictionary(db_dict_desc_p dd) 411 { 412 unsigned int oldsize, i, new_size; 413 db_table_desc_p * oldtab, *newtab; 414 415 oldsize = dd->tables.tables_len; 416 oldtab = dd->tables.tables_val; 417 418 new_size = get_next_hashsize(oldsize); 419 420 if (new_size > CALLOC_LIMIT) { 421 FATAL("db_dictionary::grow: table size exceeds calloc limit", 422 DB_MEMORY_LIMIT); 423 } 424 425 if ((newtab = (db_table_desc_p*) 426 calloc((unsigned int) new_size, 427 sizeof (db_table_desc_p))) == NULL) { 428 FATAL("db_dictionary::grow: cannot allocate space", 429 DB_MEMORY_LIMIT); 430 } 431 432 if (oldtab != NULL) { // must transfer contents of old to new 433 for (i = 0; i < oldsize; i++) { 434 relocate_bucket(oldtab[i], newtab, new_size); 435 } 436 delete oldtab; // delete old hashtable 437 } 438 439 dd->tables.tables_val = newtab; 440 dd->tables.tables_len = new_size; 441 } 442 443 #define HASHSHIFT 3 444 #define HASHMASK 0x1f 445 446 static u_int 447 get_hashval(char *value) 448 { 449 int i, len; 450 u_int hval = 0; 451 452 len = strlen(value); 453 for (i = 0; i < len; i++) { 454 hval = ((hval<<HASHSHIFT)^hval); 455 hval += (value[i] & HASHMASK); 456 } 457 458 return (hval); 459 } 460 461 static db_status 462 enumerate_dictionary(db_dict_desc *dd, db_status (*func) (db_table_desc*)) 463 { 464 int i; 465 db_table_desc *bucket; 466 db_status status; 467 468 if (dd == NULL) 469 return (DB_SUCCESS); 470 471 for (i = 0; i < dd->tables.tables_len; i++) { 472 bucket = dd->tables.tables_val[i]; 473 if (bucket) { 474 status = enumerate_bucket(bucket, func); 475 if (status != DB_SUCCESS) 476 return (status); 477 } 478 } 479 480 return (DB_SUCCESS); 481 } 482 483 484 /* 485 * Look up target table_name in hashtable and return its db_table_desc. 486 * Return NULL if not found. 487 */ 488 static db_table_desc * 489 search_dictionary(db_dict_desc *dd, char *target) 490 { 491 unsigned long hval; 492 unsigned long bucket; 493 494 if (target == NULL || dd == NULL || dd->tables.tables_len == 0) 495 return (NULL); 496 497 hval = get_hashval(target); 498 bucket = hval % dd->tables.tables_len; 499 500 db_table_desc_p fst = dd->tables.tables_val[bucket]; 501 502 if (fst != NULL) 503 return (search_bucket(fst, hval, target)); 504 else 505 return (NULL); 506 } 507 508 /* 509 * Remove the entry with the target table_name from the dictionary. 510 * If successful, return DB_SUCCESS; otherwise DB_NOTUNIQUE if target 511 * is null; DB_NOTFOUND if entry is not found. 512 * If successful, decrement count of number of entries in hash table. 513 */ 514 static db_status 515 remove_from_dictionary(db_dict_desc *dd, char *target, bool_t remove_storage) 516 { 517 unsigned long hval; 518 unsigned long bucket; 519 db_table_desc *fst; 520 521 if (target == NULL) 522 return (DB_NOTUNIQUE); 523 if (dd == NULL || dd->tables.tables_len == 0) 524 return (DB_NOTFOUND); 525 hval = get_hashval(target); 526 bucket = hval % dd->tables.tables_len; 527 fst = dd->tables.tables_val[bucket]; 528 if (fst == NULL) 529 return (DB_NOTFOUND); 530 if (remove_from_bucket(fst, &dd->tables.tables_val[bucket], 531 hval, target, remove_storage)) { 532 --(dd->count); 533 return (DB_SUCCESS); 534 } else 535 return (DB_NOTFOUND); 536 } 537 538 /* 539 * Add a new db_table_desc to the dictionary. 540 * Return DB_NOTUNIQUE, if entry with identical table_name 541 * already exists. If entry is added, return DB_SUCCESS. 542 * Increment count of number of entries in index table and grow table 543 * if number of entries equals size of table. 544 * 545 * Inputs: db_dict_desc_p dd pointer to dictionary to add to. 546 * db_table_desc *td pointer to table entry to be added. The 547 * db_table_desc.next field will be altered 548 * without regard to it's current setting. 549 * This means that if next points to a list of 550 * table entries, they may be either linked into 551 * the dictionary unexpectly or cut off (leaked). 552 */ 553 static db_status 554 add_to_dictionary(db_dict_desc_p dd, db_table_desc *td) 555 { 556 unsigned long hval; 557 char *target; 558 559 if (dd == NULL) 560 return (DB_NOTFOUND); 561 562 if (td == NULL) 563 return (DB_NOTFOUND); 564 target = td->table_name; 565 if (target == NULL) 566 return (DB_NOTUNIQUE); 567 568 hval = get_hashval(target); 569 570 if (dd->tables.tables_val == NULL) 571 grow_dictionary(dd); 572 573 db_table_desc_p fst; 574 unsigned long bucket; 575 bucket = hval % dd->tables.tables_len; 576 fst = dd->tables.tables_val[bucket]; 577 td->hashval = hval; 578 if (fst == NULL) { /* Empty bucket */ 579 dd->tables.tables_val[bucket] = td; 580 } else if (!add_to_bucket(fst, &dd->tables.tables_val[bucket], td)) { 581 return (DB_NOTUNIQUE); 582 } 583 584 /* increase hash table size if number of entries equals table size */ 585 if (++(dd->count) > dd->tables.tables_len) 586 grow_dictionary(dd); 587 588 return (DB_SUCCESS); 589 } 590 591 /* ******************* pickling routines for dictionary ******************* */ 592 593 594 /* Does the actual writing to/from file specific for db_dict_desc structure. */ 595 static bool_t 596 transfer_aux(XDR* x, pptr tbl) 597 { 598 return (xdr_db_dict_desc_p(x, (db_dict_desc_p *) tbl)); 599 } 600 601 class pickle_dict_desc: public pickle_file { 602 public: 603 pickle_dict_desc(char *f, pickle_mode m) : pickle_file(f, m) {} 604 605 /* Transfers db_dict_desc structure pointed to by dp to/from file. */ 606 int transfer(db_dict_desc_p * dp) 607 { return (pickle_file::transfer((pptr) dp, &transfer_aux)); } 608 }; 609 610 /* ************************ dictionary methods *************************** */ 611 612 db_dictionary::db_dictionary() 613 { 614 dictionary = NULL; 615 initialized = FALSE; 616 filename = NULL; 617 tmpfilename = NULL; 618 logfilename = NULL; 619 logfile = NULL; 620 logfile_opened = FALSE; 621 changed = FALSE; 622 INITRW(dict); 623 READLOCKOK(dict); 624 } 625 626 /* 627 * This routine clones an entire hash bucket chain. If you clone a 628 * data dictionary entry with the ->next pointer set, you will get a 629 * clone of that entry, as well as the entire linked list. This can cause 630 * pain if you then pass the cloned bucket to routines such as 631 * add_to_dictionary(), which do not expect nor handle dictionary hash 632 * entries with the ->next pointer set. You might either get duplicate 633 * entires or lose entries. If you wish to clone the entire bucket chain 634 * and add it to a new dictionary, loop through the db_table_desc->next list 635 * and call add_to_dictionary() for each item. 636 */ 637 int 638 db_dictionary::db_clone_bucket(db_table_desc *bucket, db_table_desc **clone) 639 { 640 u_long size; 641 XDR xdrs; 642 char *bufin = NULL; 643 644 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_clone_bucket"); 645 db_func use_this = xdr_db_table_desc; 646 size = xdr_sizeof((xdrproc_t) use_this, (void *) bucket); 647 bufin = (char *) calloc(1, (size_t) size * sizeof (char)); 648 if (!bufin) { 649 READUNLOCK(this, DB_MEMORY_LIMIT, 650 "db_dictionary::insert_modified_table: out of memory"); 651 FATAL3("db_dictionary::insert_modified_table: out of memory", 652 DB_MEMORY_LIMIT, 0); 653 } 654 xdrmem_create(&xdrs, bufin, (size_t) size, XDR_ENCODE); 655 if (!xdr_db_table_desc(&xdrs, bucket)) { 656 free(bufin); 657 xdr_destroy(&xdrs); 658 READUNLOCK(this, DB_MEMORY_LIMIT, 659 "db_dictionary::insert_modified_table: xdr encode failed"); 660 FATAL3( 661 "db_dictionary::insert_modified_table: xdr encode failed.", 662 DB_MEMORY_LIMIT, 0); 663 } 664 *clone = (db_table_desc *) calloc(1, (size_t) size * sizeof (char)); 665 if (!*clone) { 666 xdr_destroy(&xdrs); 667 free(bufin); 668 READUNLOCK(this, DB_MEMORY_LIMIT, 669 "db_dictionary::insert_modified_table: out of memory"); 670 FATAL3("db_dictionary::insert_modified_table: out of memory", 671 DB_MEMORY_LIMIT, 0); 672 } 673 674 xdrmem_create(&xdrs, bufin, (size_t) size, XDR_DECODE); 675 if (!xdr_db_table_desc(&xdrs, *clone)) { 676 free(bufin); 677 free(*clone); 678 xdr_destroy(&xdrs); 679 READUNLOCK(this, DB_MEMORY_LIMIT, 680 "db_dictionary::insert_modified_table: xdr encode failed"); 681 FATAL3( 682 "db_dictionary::insert_modified_table: xdr encode failed.", 683 DB_MEMORY_LIMIT, 0); 684 } 685 free(bufin); 686 xdr_destroy(&xdrs); 687 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_clone_bucket"); 688 return (1); 689 } 690 691 692 int 693 db_dictionary::change_table_name(db_table_desc *clone, char *tok, char *repl) 694 { 695 char *newname; 696 char *loc_end, *loc_beg; 697 698 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::change_table_name"); 699 while (clone) { 700 /* 701 * Special case for a tok="". This is used for the 702 * nisrestore(8), when restoring a replica in another 703 * domain. This routine is used to change the datafile 704 * names in the data.dict (see bugid #4031273). This will not 705 * effect massage_dict(), since it never generates an empty 706 * string for tok. 707 */ 708 if (strlen(tok) == 0) { 709 strcat(clone->table_name, repl); 710 clone = clone->next; 711 continue; 712 } 713 newname = (char *) calloc(1, sizeof (char) * 714 strlen(clone->table_name) + 715 strlen(repl) - strlen(tok) + 1); 716 if (!newname) { 717 WRITEUNLOCK(this, DB_MEMORY_LIMIT, 718 "db_dictionary::change_table_name: out of memory"); 719 FATAL3("db_dictionary::change_table_name: out of memory.", 720 DB_MEMORY_LIMIT, 0); 721 } 722 if (loc_beg = strstr(clone->table_name, tok)) { 723 loc_end = loc_beg + strlen(tok); 724 int s = loc_beg - clone->table_name; 725 memcpy(newname, clone->table_name, s); 726 strcat(newname + s, repl); 727 strcat(newname, loc_end); 728 free(clone->table_name); 729 clone->table_name = newname; 730 } else { 731 free(newname); 732 } 733 clone = clone->next; 734 } 735 WRITEUNLOCK(this, DB_LOCK_ERROR, 736 "wu db_dictionary::change_table_name"); 737 return (1); 738 } 739 740 741 #ifdef curdict 742 #undef curdict 743 #endif 744 /* 745 * A function to initialize the temporary dictionary from the real 746 * dictionary. 747 */ 748 bool_t 749 db_dictionary::inittemp(char *dictname, db_dictionary& curdict) 750 { 751 int status; 752 db_table_desc_p *newtab; 753 754 db_shutdown(); 755 756 WRITELOCK(this, FALSE, "w db_dictionary::inittemp"); 757 if (initialized) { 758 /* Someone else got in between db_shutdown() and lock() */ 759 WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp"); 760 return (TRUE); 761 } 762 763 pickle_dict_desc f(dictname, PICKLE_READ); 764 filename = strdup(dictname); 765 if (filename == NULL) { 766 WRITEUNLOCK(this, FALSE, 767 "db_dictionary::inittemp: could not allocate space"); 768 FATAL3("db_dictionary::inittemp: could not allocate space", 769 DB_MEMORY_LIMIT, FALSE); 770 } 771 int len = strlen(filename); 772 tmpfilename = new char[len+5]; 773 if (tmpfilename == NULL) { 774 delete filename; 775 WRITEUNLOCK(this, FALSE, 776 "db_dictionary::inittemp: could not allocate space"); 777 FATAL3("db_dictionary::inittemp: could not allocate space", 778 DB_MEMORY_LIMIT, FALSE); 779 } 780 logfilename = new char[len+5]; 781 if (logfilename == NULL) { 782 delete filename; 783 delete tmpfilename; 784 WRITEUNLOCK(this, FALSE, 785 "db_dictionary::inittemp: cannot allocate space"); 786 FATAL3("db_dictionary::inittemp: cannot allocate space", 787 DB_MEMORY_LIMIT, FALSE); 788 } 789 790 sprintf(tmpfilename, "%s.tmp", filename); 791 sprintf(logfilename, "%s.log", filename); 792 unlink(tmpfilename); /* get rid of partial checkpoints */ 793 dictionary = NULL; 794 795 if ((status = f.transfer(&dictionary)) < 0) { 796 initialized = FALSE; 797 } else if (status == 1) { /* no dictionary exists, create one */ 798 dictionary = new db_dict_desc; 799 if (dictionary == NULL) { 800 WRITEUNLOCK(this, FALSE, 801 "db_dictionary::inittemp: could not allocate space"); 802 FATAL3( 803 "db_dictionary::inittemp: could not allocate space", 804 DB_MEMORY_LIMIT, FALSE); 805 } 806 dictionary->tables.tables_len = 807 curdict.dictionary->tables.tables_len; 808 if ((newtab = (db_table_desc_p *) calloc( 809 (unsigned int) dictionary->tables.tables_len, 810 sizeof (db_table_desc_p))) == NULL) { 811 WRITEUNLOCK(this, FALSE, 812 "db_dictionary::inittemp: cannot allocate space"); 813 FATAL3( 814 "db_dictionary::inittemp: cannot allocate space", 815 DB_MEMORY_LIMIT, 0); 816 } 817 dictionary->tables.tables_val = newtab; 818 dictionary->count = 0; 819 dictionary->impl_vers = curdict.dictionary->impl_vers; 820 initialized = TRUE; 821 } else /* dictionary loaded successfully */ 822 initialized = TRUE; 823 824 if (initialized == TRUE) { 825 changed = FALSE; 826 reset_log(); 827 } 828 829 WRITEUNLOCK(this, FALSE, "wu db_dictionary::inittemp"); 830 return (initialized); 831 } 832 833 834 /* 835 * This method replaces the token string specified with the replacment 836 * string specified. It assumes that at least one and only one instance of 837 * the token exists. It is the responsibility of the caller to ensure that 838 * the above assumption stays valid. 839 */ 840 db_status 841 db_dictionary::massage_dict(char *newdictname, char *tok, char *repl) 842 { 843 int retval; 844 u_int i, tbl_count; 845 db_status status; 846 db_table_desc *bucket, *np, *clone, *next_np; 847 char tail[NIS_MAXNAMELEN]; 848 db_dictionary *tmpptr; 849 850 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::massage_dict"); 851 if (dictionary == NULL) { 852 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 853 "db_dictionary::massage_dict: uninitialized dictionary file"); 854 FATAL3( 855 "db_dictionary::massage_dict: uninitialized dictionary file.", 856 DB_INTERNAL_ERROR, DB_INTERNAL_ERROR); 857 } 858 859 if ((tbl_count = dictionary->count) == 0) { 860 WRITEUNLOCK(this, DB_SUCCESS, 861 "wu db_dictionary::massage_dict"); 862 return (DB_SUCCESS); 863 } 864 865 /* First checkpoint */ 866 if ((status = checkpoint()) != DB_SUCCESS) { 867 WRITEUNLOCK(this, status, "wu db_dictionary::massage_dict"); 868 return (status); 869 } 870 871 #ifdef DEBUG 872 enumerate_dictionary(dictionary, &print_table); 873 #endif 874 875 /* Initialize the free dictionary so that we can start populating it */ 876 FreeDictionary->inittemp(newdictname, *this); 877 878 for (i = 0; i < dictionary->tables.tables_len; i++) { 879 bucket = dictionary->tables.tables_val[i]; 880 if (bucket) { 881 np = bucket; 882 while (np != NULL) { 883 next_np = np->next; 884 retval = db_clone_bucket(np, &clone); 885 if (retval != 1) { 886 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 887 "wu db_dictionary::massage_dict"); 888 return (DB_INTERNAL_ERROR); 889 } 890 if (change_table_name(clone, tok, repl) == -1) { 891 delete_table_desc(clone); 892 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 893 "wu db_dictionary::massage_dict"); 894 return (DB_INTERNAL_ERROR); 895 } 896 /* 897 * We know we don't have a log file, so we will 898 * just add to the in-memory database and dump 899 * all of it once we are done. 900 */ 901 status = add_to_dictionary 902 (FreeDictionary->dictionary, 903 clone); 904 if (status != DB_SUCCESS) { 905 delete_table_desc(clone); 906 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 907 "wu db_dictionary::massage_dict"); 908 return (DB_INTERNAL_ERROR); 909 } 910 status = remove_from_dictionary(dictionary, 911 np->table_name, TRUE); 912 if (status != DB_SUCCESS) { 913 delete_table_desc(clone); 914 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 915 "wu db_dictionary::massage_dict"); 916 return (DB_INTERNAL_ERROR); 917 } 918 np = next_np; 919 } 920 } 921 } 922 923 if (FreeDictionary->dump() != DB_SUCCESS) { 924 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 925 "wu db_dictionary::massage_dict"); 926 FATAL3( 927 "db_dictionary::massage_dict: Unable to dump new dictionary.", 928 DB_INTERNAL_ERROR, DB_INTERNAL_ERROR); 929 } 930 931 /* 932 * Now, shutdown the inuse dictionary and update the FreeDictionary 933 * and InUseDictionary pointers as well. Also, delete the old dictionary 934 * file. 935 */ 936 unlink(filename); /* There shouldn't be a tmpfile or logfile */ 937 db_shutdown(); 938 tmpptr = InUseDictionary; 939 InUseDictionary = FreeDictionary; 940 FreeDictionary = tmpptr; 941 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::massage_dict"); 942 return (DB_SUCCESS); 943 } 944 945 946 db_status 947 db_dictionary::merge_dict(db_dictionary& tempdict, char *tok, char *repl) 948 { 949 950 db_status dbstat = DB_SUCCESS; 951 952 db_table_desc *tbl = NULL, *clone = NULL, *next_td = NULL; 953 int retval, i; 954 955 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::merge_dict"); 956 957 for (i = 0; i < tempdict.dictionary->tables.tables_len; ++i) { 958 tbl = tempdict.dictionary->tables.tables_val[i]; 959 if (!tbl) 960 continue; 961 retval = db_clone_bucket(tbl, &clone); 962 if (retval != 1) { 963 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 964 "wu db_dictionary::merge_dict"); 965 return (DB_INTERNAL_ERROR); 966 } 967 while (clone) { 968 next_td = clone->next; 969 clone->next = NULL; 970 if ((tok) && 971 (change_table_name(clone, tok, repl) == -1)) { 972 delete_table_desc(clone); 973 if (next_td) 974 delete_table_desc(next_td); 975 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 976 "wu db_dictionary::merge_dict"); 977 return (DB_INTERNAL_ERROR); 978 } 979 980 dbstat = add_to_dictionary(dictionary, clone); 981 if (dbstat == DB_NOTUNIQUE) { 982 /* Overide */ 983 dbstat = remove_from_dictionary(dictionary, 984 clone->table_name, TRUE); 985 if (dbstat != DB_SUCCESS) { 986 WRITEUNLOCK(this, dbstat, 987 "wu db_dictionary::merge_dict"); 988 return (dbstat); 989 } 990 dbstat = add_to_dictionary(dictionary, 991 clone); 992 } else { 993 if (dbstat != DB_SUCCESS) { 994 WRITEUNLOCK(this, dbstat, 995 "wu db_dictionary::merge_dict"); 996 return (dbstat); 997 } 998 } 999 clone = next_td; 1000 } 1001 } 1002 /* 1003 * If we were successful in merging the dictionaries, then mark the 1004 * dictionary changed, so that it will be properly checkpointed and 1005 * dumped to disk. 1006 */ 1007 if (dbstat == DB_SUCCESS) 1008 changed = TRUE; 1009 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::merge_dict"); 1010 return (dbstat); 1011 } 1012 1013 int 1014 db_dictionary::copyfile(char *infile, char *outfile) 1015 { 1016 db_table_desc *tbl = NULL; 1017 db *dbase; 1018 int ret; 1019 1020 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile"); 1021 /* 1022 * We need to hold the read-lock until the dump() is done. 1023 * However, we must avoid the lock migration (read -> write) 1024 * that would happen in find_table() if the db must be loaded. 1025 * Hence, look first look for an already loaded db. 1026 */ 1027 dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE); 1028 if (dbase == NULL) { 1029 /* Release the read-lock, and try again, allowing load */ 1030 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::copyfile"); 1031 dbase = find_table(infile, &tbl, TRUE, TRUE, TRUE); 1032 if (dbase == NULL) 1033 return (DB_NOTFOUND); 1034 /* 1035 * Read-lock again, and get a 'tbl' we can use since we're 1036 * still holding the lock. 1037 */ 1038 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::copyfile"); 1039 dbase = find_table(infile, &tbl, TRUE, TRUE, FALSE); 1040 if (dbase == NULL) { 1041 READUNLOCK(this, DB_NOTFOUND, 1042 "ru db_dictionary::copyfile"); 1043 return (DB_NOTFOUND); 1044 } 1045 } 1046 ret = tbl->database->dump(outfile) ? DB_SUCCESS : DB_INTERNAL_ERROR; 1047 READUNLOCK(this, ret, "ru db_dictionary::copyfile"); 1048 return (ret); 1049 } 1050 1051 1052 bool_t 1053 db_dictionary::extract_entries(db_dictionary& tempdict, char **fs, int fscnt) 1054 { 1055 int i, retval; 1056 db_table_desc *tbl, *clone; 1057 db_table_desc tbl_ent; 1058 db_status dbstat; 1059 1060 READLOCK(this, FALSE, "r db_dictionary::extract_entries"); 1061 for (i = 0; i < fscnt; ++i) { 1062 tbl = find_table_desc(fs[i]); 1063 if (!tbl) { 1064 syslog(LOG_DEBUG, 1065 "extract_entries: no dictionary entry for %s", 1066 fs[i]); 1067 READUNLOCK(this, FALSE, 1068 "ru db_dictionary::extract_entries"); 1069 return (FALSE); 1070 } else { 1071 tbl_ent.table_name = tbl->table_name; 1072 tbl_ent.hashval = tbl->hashval; 1073 tbl_ent.scheme = tbl->scheme; 1074 tbl_ent.database = tbl->database; 1075 tbl_ent.next = NULL; 1076 } 1077 retval = db_clone_bucket(&tbl_ent, &clone); 1078 if (retval != 1) { 1079 syslog(LOG_DEBUG, 1080 "extract_entries: unable to clone entry for %s", 1081 fs[i]); 1082 READUNLOCK(this, FALSE, 1083 "ru db_dictionary::extract_entries"); 1084 return (FALSE); 1085 } 1086 dbstat = add_to_dictionary(tempdict.dictionary, clone); 1087 if (dbstat != DB_SUCCESS) { 1088 delete_table_desc(clone); 1089 READUNLOCK(this, FALSE, 1090 "ru db_dictionary::extract_entries"); 1091 return (FALSE); 1092 } 1093 } 1094 if (tempdict.dump() != DB_SUCCESS) { 1095 READUNLOCK(this, FALSE, 1096 "ru db_dictionary::extract_entries"); 1097 return (FALSE); 1098 } 1099 READUNLOCK(this, FALSE, 1100 "ru db_dictionary::extract_entries"); 1101 return (TRUE); 1102 } 1103 1104 1105 /* 1106 * Initialize dictionary from contents in 'file'. 1107 * If there is already information in this dictionary, it is removed. 1108 * Therefore, regardless of whether the load from the file succeeds, 1109 * the contents of this dictionary will be altered. Returns 1110 * whether table has been initialized successfully. 1111 */ 1112 bool_t 1113 db_dictionary::init(char *file) 1114 { 1115 int status; 1116 1117 WRITELOCK(this, FALSE, "w db_dictionary::init"); 1118 db_shutdown(); 1119 1120 pickle_dict_desc f(file, PICKLE_READ); 1121 filename = strdup(file); 1122 if (filename == NULL) { 1123 WRITEUNLOCK(this, FALSE, 1124 "db_dictionary::init: could not allocate space"); 1125 FATAL3("db_dictionary::init: could not allocate space", 1126 DB_MEMORY_LIMIT, FALSE); 1127 } 1128 int len = strlen(filename); 1129 tmpfilename = new char[len+5]; 1130 if (tmpfilename == NULL) { 1131 delete filename; 1132 WRITEUNLOCK(this, FALSE, 1133 "db_dictionary::init: could not allocate space"); 1134 FATAL3("db_dictionary::init: could not allocate space", 1135 DB_MEMORY_LIMIT, FALSE); 1136 } 1137 logfilename = new char[len+5]; 1138 if (logfilename == NULL) { 1139 delete filename; 1140 delete tmpfilename; 1141 WRITEUNLOCK(this, FALSE, 1142 "db_dictionary::init: cannot allocate space"); 1143 FATAL3("db_dictionary::init: cannot allocate space", 1144 DB_MEMORY_LIMIT, FALSE); 1145 } 1146 1147 sprintf(tmpfilename, "%s.tmp", filename); 1148 sprintf(logfilename, "%s.log", filename); 1149 unlink(tmpfilename); /* get rid of partial checkpoints */ 1150 dictionary = NULL; 1151 1152 /* load dictionary */ 1153 if ((status = f.transfer(&dictionary)) < 0) { 1154 initialized = FALSE; 1155 } else if (status == 1) { /* no dictionary exists, create one */ 1156 dictionary = new db_dict_desc; 1157 if (dictionary == NULL) { 1158 WRITEUNLOCK(this, FALSE, 1159 "db_dictionary::init: could not allocate space"); 1160 FATAL3("db_dictionary::init: could not allocate space", 1161 DB_MEMORY_LIMIT, FALSE); 1162 } 1163 dictionary->tables.tables_len = 0; 1164 dictionary->tables.tables_val = NULL; 1165 dictionary->count = 0; 1166 dictionary->impl_vers = DB_CURRENT_VERSION; 1167 initialized = TRUE; 1168 } else /* dictionary loaded successfully */ 1169 initialized = TRUE; 1170 1171 if (initialized == TRUE) { 1172 int num_changes = 0; 1173 changed = FALSE; 1174 reset_log(); 1175 if ((num_changes = incorporate_log(logfilename)) < 0) 1176 syslog(LOG_ERR, 1177 "incorporation of dictionary logfile '%s' failed", 1178 logfilename); 1179 changed = (num_changes > 0); 1180 } 1181 1182 WRITEUNLOCK(this, initialized, "wu db_dictionary::init"); 1183 return (initialized); 1184 } 1185 1186 /* 1187 * Execute log entry 'j' on the dictionary identified by 'dict' if the 1188 * version of j is later than that of the dictionary. If 'j' is executed, 1189 * 'count' is incremented and the dictionary's verison is updated to 1190 * that of 'j'. 1191 * Returns TRUE always for valid log entries; FALSE otherwise. 1192 */ 1193 static bool_t 1194 apply_log_entry(db_dictlog_entry *j, char *dictchar, int *count) 1195 { 1196 db_dictionary *dict = (db_dictionary*) dictchar; 1197 1198 WRITELOCK(dict, FALSE, "w apply_log_entry"); 1199 if (db_update_version.earlier_than(j->get_version())) { 1200 ++ *count; 1201 #ifdef DEBUG 1202 j->print(); 1203 #endif /* DEBUG */ 1204 switch (j->get_action()) { 1205 case DB_ADD_TABLE: 1206 dict->add_table_aux(j->get_table_name(), 1207 j->get_table_object(), INMEMORY_ONLY); 1208 // ignore status 1209 break; 1210 1211 case DB_REMOVE_TABLE: 1212 dict->delete_table_aux(j->get_table_name(), 1213 INMEMORY_ONLY); 1214 // ignore status 1215 break; 1216 1217 default: 1218 WARNING("db::apply_log_entry: unknown action_type"); 1219 WRITEUNLOCK(dict, FALSE, "wu apply_log_entry"); 1220 return (FALSE); 1221 } 1222 db_update_version.assign(j->get_version()); 1223 } 1224 1225 WRITEUNLOCK(dict, TRUE, "wu apply_log_entry"); 1226 return (TRUE); 1227 } 1228 1229 int 1230 db_dictionary::incorporate_log(char *file_name) 1231 { 1232 db_dictlog f(file_name, PICKLE_READ); 1233 int ret; 1234 1235 WRITELOCK(this, -1, "w db_dictionary::incorporate_log"); 1236 setNoWriteThrough(); 1237 ret = f.execute_on_log(&(apply_log_entry), (char *) this); 1238 clearNoWriteThrough(); 1239 WRITEUNLOCK(this, -1, "wu db_dictionary::incorporate_log"); 1240 return (ret); 1241 } 1242 1243 1244 /* Frees memory of filename and tables. Has no effect on disk storage. */ 1245 db_status 1246 db_dictionary::db_shutdown() 1247 { 1248 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::db_shutdown"); 1249 if (!initialized) { 1250 WRITEUNLOCK(this, DB_LOCK_ERROR, 1251 "wu db_dictionary::db_shutdown"); 1252 return (DB_SUCCESS); /* DB_NOTFOUND? */ 1253 } 1254 1255 if (filename) { 1256 delete filename; 1257 filename = NULL; 1258 } 1259 if (tmpfilename) { 1260 delete tmpfilename; 1261 tmpfilename = NULL; 1262 } 1263 if (logfilename) { 1264 delete logfilename; 1265 logfilename = NULL; 1266 } 1267 if (dictionary) { 1268 delete_dictionary(dictionary); 1269 dictionary = NULL; 1270 } 1271 initialized = FALSE; 1272 changed = FALSE; 1273 reset_log(); 1274 1275 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_shutdown"); 1276 return (DB_SUCCESS); 1277 } 1278 1279 /* 1280 * Dump contents of this dictionary (minus the database representations) 1281 * to its file. Returns 0 if operation succeeds, -1 otherwise. 1282 */ 1283 int 1284 db_dictionary::dump() 1285 { 1286 int status; 1287 1288 READLOCK(this, -1, "r db_dictionary::dump"); 1289 if (!initialized) { 1290 READUNLOCK(this, -1, "ru db_dictionary::dump"); 1291 return (-1); 1292 } 1293 1294 unlink(tmpfilename); /* get rid of partial dumps */ 1295 pickle_dict_desc f(tmpfilename, PICKLE_WRITE); 1296 1297 status = f.transfer(&dictionary); /* dump table descs */ 1298 if (status != 0) { 1299 WARNING("db_dictionary::dump: could not write out dictionary"); 1300 } else if (rename(tmpfilename, filename) < 0) { 1301 WARNING_M("db_dictionary::dump: could not rename temp file: "); 1302 status = -1; 1303 } 1304 1305 READUNLOCK(this, -1, "ru db_dictionary::dump"); 1306 return (status); 1307 } 1308 1309 /* 1310 * Write out in-memory copy of dictionary to file. 1311 * 1. Update major version. 1312 * 2. Dump contents to temporary file. 1313 * 3. Rename temporary file to real dictionary file. 1314 * 4. Remove log file. 1315 * A checkpoint is done only if it has changed since the previous checkpoint. 1316 * Returns DB_SUCCESS if checkpoint was successful; error code otherwise 1317 */ 1318 db_status 1319 db_dictionary::checkpoint() 1320 { 1321 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::checkpoint"); 1322 1323 if (changed == FALSE) { 1324 WRITEUNLOCK(this, DB_LOCK_ERROR, 1325 "wu db_dictionary::checkpoint"); 1326 return (DB_SUCCESS); 1327 } 1328 1329 vers *oldv = new vers(db_update_version); // copy 1330 vers * newv = db_update_version.nextmajor(); // get next version 1331 db_update_version.assign(newv); // update version 1332 delete newv; 1333 1334 if (dump() != 0) { 1335 WARNING_M( 1336 "db_dictionary::checkpoint: could not dump dictionary: "); 1337 db_update_version.assign(oldv); // rollback 1338 delete oldv; 1339 WRITEUNLOCK(this, DB_INTERNAL_ERROR, 1340 "wu db_dictionary::checkpoint"); 1341 return (DB_INTERNAL_ERROR); 1342 } 1343 unlink(logfilename); /* should do atomic rename and log delete */ 1344 reset_log(); /* should check for what? */ 1345 delete oldv; 1346 changed = FALSE; 1347 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::checkpoint"); 1348 return (DB_SUCCESS); 1349 } 1350 1351 /* close existing logfile and delete its structure */ 1352 int 1353 db_dictionary::reset_log() 1354 { 1355 WRITELOCK(this, -1, "w db_dictionary::reset_log"); 1356 /* try to close old log file */ 1357 /* doesnot matter since we do synchronous writes only */ 1358 if (logfile != NULL) { 1359 if (logfile_opened == TRUE) { 1360 if (logfile->close() < 0) { 1361 WARNING_M( 1362 "db_dictionary::reset_log: could not close log file: "); 1363 } 1364 } 1365 delete logfile; 1366 logfile = NULL; 1367 } 1368 logfile_opened = FALSE; 1369 WRITEUNLOCK(this, -1, "wu db_dictionary::reset_log"); 1370 return (0); 1371 } 1372 1373 /* close existing logfile, but leave its structure if exists */ 1374 int 1375 db_dictionary::close_log() 1376 { 1377 WRITELOCK(this, -1, "w db_dictionary::close_log"); 1378 if (logfile != NULL && logfile_opened == TRUE) { 1379 logfile->close(); 1380 } 1381 logfile_opened = FALSE; 1382 WRITEUNLOCK(this, -1, "wu db_dictionary::close_log"); 1383 return (0); 1384 } 1385 1386 /* open logfile, creating its structure if it does not exist */ 1387 int 1388 db_dictionary::open_log() 1389 { 1390 WRITELOCK(this, -1, "w db_dictionary::open_log"); 1391 if (logfile == NULL) { 1392 if ((logfile = new db_dictlog(logfilename, PICKLE_APPEND)) == 1393 NULL) { 1394 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log"); 1395 FATAL3( 1396 "db_dictionary::reset_log: cannot allocate space", 1397 DB_MEMORY_LIMIT, -1); 1398 } 1399 } 1400 1401 if (logfile_opened == TRUE) { 1402 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log"); 1403 return (0); 1404 } 1405 1406 if ((logfile->open()) == FALSE) { 1407 WARNING_M("db_dictionary::open_log: could not open log file: "); 1408 delete logfile; 1409 logfile = NULL; 1410 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log"); 1411 return (-1); 1412 } 1413 1414 logfile_opened = TRUE; 1415 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log"); 1416 return (0); 1417 } 1418 1419 /* 1420 * closes any open log files for all tables in dictionary or 'tab'. 1421 * "tab" is an optional argument. 1422 */ 1423 static int close_standby_list(); 1424 1425 db_status 1426 db_dictionary::db_standby(char *tab) 1427 { 1428 db_table_desc *tbl; 1429 1430 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::db_standby"); 1431 if (!initialized) { 1432 WRITEUNLOCK(this, DB_BADDICTIONARY, 1433 "wu db_dictionary::db_standby"); 1434 return (DB_BADDICTIONARY); 1435 } 1436 1437 if (tab == NULL) { 1438 close_log(); // close dictionary log 1439 close_standby_list(); 1440 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby"); 1441 return (DB_SUCCESS); 1442 } 1443 1444 if ((tbl = find_table_desc(tab)) == NULL) { 1445 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby"); 1446 return (DB_BADTABLE); 1447 } 1448 1449 if (tbl->database != NULL) 1450 tbl->database->close_log(); 1451 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::db_standby"); 1452 return (DB_SUCCESS); 1453 } 1454 1455 /* 1456 * Returns db_table_desc of table name 'tab'. 'prev', if supplied, 1457 * is set to the entry located ahead of 'tab's entry in the dictionary. 1458 */ 1459 db_table_desc* 1460 db_dictionary::find_table_desc(char *tab) 1461 { 1462 db_table_desc *ret; 1463 1464 READLOCK(this, NULL, "r db_dictionary::find_table_desc"); 1465 if (initialized) 1466 ret = search_dictionary(dictionary, tab); 1467 else 1468 ret = NULL; 1469 1470 READUNLOCK(this, ret, "r db_dictionary::find_table_desc"); 1471 return (ret); 1472 } 1473 1474 db_table_desc * 1475 db_dictionary::find_table_desc(char *tab, bool_t searchDeferred) { 1476 db_table_desc *ret = NULL; 1477 1478 READLOCK(this, NULL, "r db_dictionary::find_table_desc_d"); 1479 1480 /* If desired, look in the deferred dictionary first */ 1481 if (initialized && searchDeferred && deferred.dictionary != NULL) 1482 ret = search_dictionary(deferred.dictionary, tab); 1483 1484 /* No result yet => search the "normal" dictionary */ 1485 if (ret == NULL) 1486 ret = find_table_desc(tab); 1487 1488 READUNLOCK(this, ret, "r db_dictionary::find_table_desc_d"); 1489 return (ret); 1490 } 1491 1492 db * 1493 db_dictionary::find_table(char *tab, db_table_desc **where) { 1494 /* Most operations should use the deferred dictionary if it exists */ 1495 return (find_table(tab, where, TRUE, TRUE, TRUE)); 1496 } 1497 1498 db * 1499 db_dictionary::find_table(char *tab, db_table_desc **where, 1500 bool_t searchDeferred) { 1501 return (find_table(tab, where, searchDeferred, TRUE, TRUE)); 1502 } 1503 1504 db * 1505 db_dictionary::find_table(char *tab, db_table_desc **where, 1506 bool_t searchDeferred, bool_t doLDAP, 1507 bool_t doLoad) { 1508 db *res; 1509 int lstat; 1510 db_status dstat; 1511 const char *myself = "db_dictionary::find_table"; 1512 1513 res = find_table_noLDAP(tab, where, searchDeferred, doLoad); 1514 /* If found, or shouldn't try LDAP, we're done */ 1515 if (res != 0 || !doLDAP) 1516 return (res); 1517 1518 /* See if we can retrieve the object from LDAP */ 1519 dstat = dbCreateFromLDAP(tab, &lstat); 1520 if (dstat != DB_SUCCESS) { 1521 if (dstat == DB_NOTFOUND) { 1522 if (lstat != LDAP_SUCCESS) { 1523 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1524 "%s: LDAP error for \"%s\": %s", 1525 myself, NIL(tab), 1526 ldap_err2string(lstat)); 1527 } 1528 } else { 1529 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1530 "%s: DB error %d for \"%s\"", 1531 myself, dstat, NIL(tab)); 1532 } 1533 return (0); 1534 } 1535 1536 /* Try the dictionary again */ 1537 res = find_table_noLDAP(tab, where, searchDeferred, doLoad); 1538 1539 return (res); 1540 } 1541 1542 /* 1543 * Return database structure of table named by 'tab'. 1544 * If 'where' is set, set it to the table_desc of 'tab.' 1545 * If the database is loaded in from stable store if it has not been loaded. 1546 * If it cannot be loaded, it is initialized using the scheme stored in 1547 * the table_desc. NULL is returned if the initialization fails. 1548 */ 1549 db * 1550 db_dictionary::find_table_noLDAP(char *tab, db_table_desc **where, 1551 bool_t searchDeferred, bool_t doLoad) 1552 { 1553 if (!initialized) 1554 return (NULL); 1555 1556 db_table_desc* tbl; 1557 db *dbase = NULL; 1558 int lret; 1559 1560 READLOCK(this, NULL, "r db_dictionary::find_table"); 1561 tbl = find_table_desc(tab, searchDeferred); 1562 if (tbl == NULL) { 1563 READUNLOCK(this, NULL, "ru db_dictionary::find_table"); 1564 return (NULL); // not found 1565 } 1566 1567 if (tbl->database != NULL || !doLoad) { 1568 if (tbl->database && where) *where = tbl; 1569 READUNLOCK(this, NULL, "ru db_dictionary::find_table"); 1570 return (tbl->database); // return handle 1571 } 1572 1573 READUNLOCK(this, NULL, "ru db_dictionary::find_table"); 1574 WRITELOCK(this, NULL, "w db_dictionary::find_table"); 1575 /* Re-check; some other thread might have loaded the db */ 1576 if (tbl->database != NULL) { 1577 if (where) *where = tbl; 1578 WRITEUNLOCK(this, NULL, "wu db_dictionary::find_table"); 1579 return (tbl->database); // return handle 1580 } 1581 1582 // need to load in/init database 1583 dbase = new db(tab); 1584 1585 if (dbase == NULL) { 1586 WRITEUNLOCK(this, NULL, 1587 "db_dictionary::find_table: could not allocate space"); 1588 FATAL3("db_dictionary::find_table: could not allocate space", 1589 DB_MEMORY_LIMIT, NULL); 1590 } 1591 1592 /* 1593 * Lock the newly created 'dbase', so we can release the general 1594 * db_dictionary lock. 1595 */ 1596 WRITELOCKNR(dbase, lret, "w dbase db_dictionary::find_table"); 1597 if (lret != 0) { 1598 WRITEUNLOCK(this, NULL, 1599 "db_dictionary::find_table: could not lock dbase"); 1600 FATAL3("db_dictionary::find_table: could not lock dbase", 1601 DB_LOCK_ERROR, NULL); 1602 } 1603 /* Assign tbl->database, and then release the 'this' lock */ 1604 tbl->database = dbase; 1605 WRITEUNLOCK(this, NULL, "wu db_dictionary::find_table"); 1606 1607 if (dbase->load()) { // try to load in database 1608 if (where) *where = tbl; 1609 WRITEUNLOCK(dbase, dbase, "wu dbase db_dictionary::find_table"); 1610 return (dbase); 1611 } 1612 1613 delete dbase; 1614 tbl->database = NULL; 1615 WARNING("db_dictionary::find_table: could not load database"); 1616 return (NULL); 1617 } 1618 1619 /* Log action to be taken on the dictionary and update db_update_version. */ 1620 1621 db_status 1622 db_dictionary::log_action(int action, char *tab, table_obj *tobj) 1623 { 1624 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::log_action"); 1625 1626 vers *newv = db_update_version.nextminor(); 1627 db_dictlog_entry le(action, newv, tab, tobj); 1628 1629 if (open_log() < 0) { 1630 delete newv; 1631 WRITEUNLOCK(this, DB_STORAGE_LIMIT, 1632 "wu db_dictionary::log_action"); 1633 return (DB_STORAGE_LIMIT); 1634 } 1635 1636 if (logfile->append(&le) < 0) { 1637 WARNING_M("db::log_action: could not add log entry: "); 1638 close_log(); 1639 delete newv; 1640 WRITEUNLOCK(this, DB_STORAGE_LIMIT, 1641 "wu db_dictionary::log_action"); 1642 return (DB_STORAGE_LIMIT); 1643 } 1644 1645 db_update_version.assign(newv); 1646 delete newv; 1647 changed = TRUE; 1648 1649 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db_dictionary::log_action"); 1650 return (DB_SUCCESS); 1651 } 1652 1653 // For a complete 'delete' operation, we want the following behaviour: 1654 // 1. If there is an entry in the log, the physical table exists and is 1655 // stable. 1656 // 2. If there is no entry in the log, the physical table may or may not 1657 // exist. 1658 1659 db_status 1660 db_dictionary::delete_table_aux(char *tab, int mode) 1661 { 1662 db_status ret; 1663 1664 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::delete_table_aux"); 1665 if (!initialized) { 1666 WRITEUNLOCK(this, DB_LOCK_ERROR, 1667 "wu db_dictionary::delete_table_aux"); 1668 return (DB_BADDICTIONARY); 1669 } 1670 1671 db_table_desc *tbl; 1672 if ((tbl = find_table_desc(tab)) == NULL) { // table not found 1673 WRITEUNLOCK(this, DB_LOCK_ERROR, 1674 "wu db_dictionary::delete_table_aux"); 1675 return (DB_NOTFOUND); 1676 } 1677 1678 if (mode != INMEMORY_ONLY) { 1679 int need_free = 0; 1680 1681 // Update log. 1682 db_status status = log_action(DB_REMOVE_TABLE, tab); 1683 if (status != DB_SUCCESS) { 1684 WRITEUNLOCK(this, status, 1685 "wu db_dictionary::delete_table_aux"); 1686 return (status); 1687 } 1688 1689 // Remove physical structures 1690 db *dbase = tbl->database; 1691 if (dbase == NULL) { // need to get desc to access files 1692 dbase = new db(tab); 1693 need_free = 1; 1694 } 1695 if (dbase == NULL) { 1696 WARNING( 1697 "db_dictionary::delete_table: could not create db structure"); 1698 WRITEUNLOCK(this, DB_MEMORY_LIMIT, 1699 "wu db_dictionary::delete_table_aux"); 1700 return (DB_MEMORY_LIMIT); 1701 } 1702 dbase->remove_files(); // remove physical files 1703 if (need_free) 1704 delete dbase; 1705 } 1706 1707 // Remove in-memory structures 1708 ret = remove_from_dictionary(dictionary, tab, TRUE); 1709 WRITEUNLOCK(this, ret, "wu db_dictionary::delete_table_aux"); 1710 return (ret); 1711 } 1712 1713 /* 1714 * Delete table with given name 'tab' from dictionary. 1715 * Returns error code if table does not exist or if dictionary has not been 1716 * initialized. Dictionary is updated to stable store if deletion is 1717 * successful. Fatal error occurs if dictionary cannot be saved. 1718 * Returns DB_SUCCESS if dictionary has been updated successfully. 1719 * Note that the files associated with the table are also removed. 1720 */ 1721 db_status 1722 db_dictionary::delete_table(char *tab) 1723 { 1724 return (delete_table_aux(tab, !INMEMORY_ONLY)); 1725 } 1726 1727 // For a complete 'add' operation, we want the following behaviour: 1728 // 1. If there is an entry in the log, then the physical table exists and 1729 // has been initialized properly. 1730 // 2. If there is no entry in the log, the physical table may or may not 1731 // exist. In this case, we don't really care because we cannot get at 1732 // it. The next time we add a table with the same name to the dictionary, 1733 // it will be initialized properly. 1734 // This mode is used when the table is first created. 1735 // 1736 // For an INMEMORY_ONLY operation, only the internal structure is created and 1737 // updated. This mode is used when the database gets loaded and the internal 1738 // dictionary gets updated from the log entries. 1739 1740 db_status 1741 db_dictionary::add_table_aux(char *tab, table_obj* tobj, int mode) 1742 { 1743 db_status ret; 1744 1745 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::add_table_aux"); 1746 if (!initialized) { 1747 WRITEUNLOCK(this, DB_LOCK_ERROR, 1748 "wu db_dictionary::add_table_aux"); 1749 return (DB_BADDICTIONARY); 1750 } 1751 1752 if (find_table_desc(tab) != NULL) { 1753 WRITEUNLOCK(this, DB_LOCK_ERROR, 1754 "wu db_dictionary::add_table_aux"); 1755 return (DB_NOTUNIQUE); // table already exists 1756 } 1757 1758 // create data structures for table 1759 db_table_desc *new_table = 0; 1760 db_status status = create_table_desc(tab, tobj, &new_table); 1761 1762 if (status != DB_SUCCESS) { 1763 WRITEUNLOCK(this, DB_LOCK_ERROR, 1764 "wu db_dictionary::add_table_aux"); 1765 return (status); 1766 } 1767 1768 if (mode != INMEMORY_ONLY) { 1769 // create physical structures for table 1770 new_table->database = new db(tab); 1771 if (new_table->database == NULL) { 1772 delete_table_desc(new_table); 1773 WRITEUNLOCK(this, DB_MEMORY_LIMIT, 1774 "db_dictionary::add_table: could not allocate space for db"); 1775 FATAL3( 1776 "db_dictionary::add_table: could not allocate space for db", 1777 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 1778 } 1779 if (new_table->database->init(new_table->scheme) == 0) { 1780 WARNING( 1781 "db_dictionary::add_table: could not initialize database from scheme"); 1782 new_table->database->remove_files(); 1783 delete_table_desc(new_table); 1784 WRITEUNLOCK(this, DB_STORAGE_LIMIT, 1785 "wu db_dictionary::add_table_aux"); 1786 return (DB_STORAGE_LIMIT); 1787 } 1788 1789 // update 'external' copy of dictionary 1790 status = log_action(DB_ADD_TABLE, tab, tobj); 1791 1792 if (status != DB_SUCCESS) { 1793 new_table->database->remove_files(); 1794 delete_table_desc(new_table); 1795 WRITEUNLOCK(this, status, 1796 "wu db_dictionary::add_table_aux"); 1797 return (status); 1798 } 1799 } 1800 1801 // finally, update in-memory copy of dictionary 1802 ret = add_to_dictionary(dictionary, new_table); 1803 WRITEUNLOCK(this, ret, "wu db_dictionary::add_table_aux"); 1804 return (ret); 1805 } 1806 1807 /* 1808 * Add table with given name 'tab' and description 'zdesc' to dictionary. 1809 * Returns error code if table already exists, or if no memory can be found 1810 * to store the descriptor, or if dictionary has not been intialized. 1811 * Dictionary is updated to stable store if addition is successful. 1812 * Fatal error occurs if dictionary cannot be saved. 1813 * Returns DB_SUCCESS if dictionary has been updated successfully. 1814 */ 1815 db_status 1816 db_dictionary::add_table(char *tab, table_obj* tobj) 1817 { 1818 return (add_table_aux(tab, tobj, !INMEMORY_ONLY)); 1819 } 1820 1821 /* 1822 * Translate given NIS attribute list to a db_query structure. 1823 * Return FALSE if dictionary has not been initialized, or 1824 * table does not have a scheme (which should be a fatal error?). 1825 */ 1826 db_query* 1827 db_dictionary::translate_to_query(db_table_desc* tbl, int numattrs, 1828 nis_attr* attrlist) 1829 { 1830 READLOCK(this, NULL, "r db_dictionary::translate_to_query"); 1831 if (!initialized || 1832 tbl->scheme == NULL || numattrs == 0 || attrlist == NULL) { 1833 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query"); 1834 return (NULL); 1835 } 1836 1837 db_query *q = new db_query(tbl->scheme, numattrs, attrlist); 1838 if (q == NULL) { 1839 READUNLOCK(this, NULL, 1840 "db_dictionary::translate: could not allocate space"); 1841 FATAL3("db_dictionary::translate: could not allocate space", 1842 DB_MEMORY_LIMIT, NULL); 1843 } 1844 1845 if (q->size() == 0) { 1846 delete q; 1847 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query"); 1848 return (NULL); 1849 } 1850 READUNLOCK(this, NULL, "ru db_dictionary::translate_to_query"); 1851 return (q); 1852 } 1853 1854 static db_table_names gt_answer; 1855 static int gt_posn; 1856 1857 static db_status 1858 get_table_name(db_table_desc* tbl) 1859 { 1860 if (tbl) 1861 return (DB_BADTABLE); 1862 1863 if (gt_posn < gt_answer.db_table_names_len) 1864 gt_answer.db_table_names_val[gt_posn++] = 1865 strdup(tbl->table_name); 1866 else 1867 return (DB_BADTABLE); 1868 1869 return (DB_SUCCESS); 1870 } 1871 1872 1873 /* 1874 * Return the names of tables in this dictionary. 1875 * XXX This routine is used only for testing only; 1876 * if to be used for real, need to free memory sensibly, or 1877 * caller of get_table_names should have freed them. 1878 */ 1879 db_table_names* 1880 db_dictionary::get_table_names() 1881 { 1882 READLOCK(this, NULL, "r db_dictionary::get_table_names"); 1883 gt_answer.db_table_names_len = dictionary->count; 1884 gt_answer.db_table_names_val = new db_table_namep[dictionary->count]; 1885 gt_posn = 0; 1886 if ((gt_answer.db_table_names_val) == NULL) { 1887 READUNLOCK(this, NULL, 1888 "db_dictionary::get_table_names: could not allocate space for names"); 1889 FATAL3( 1890 "db_dictionary::get_table_names: could not allocate space for names", 1891 DB_MEMORY_LIMIT, NULL); 1892 } 1893 1894 enumerate_dictionary(dictionary, &get_table_name); 1895 READUNLOCK(this, NULL, "ru db_dictionary::get_table_names"); 1896 return (>_answer); 1897 } 1898 1899 static db_status 1900 db_checkpoint_aux(db_table_desc *current) 1901 { 1902 db *dbase; 1903 int status; 1904 1905 if (current == NULL) 1906 return (DB_BADTABLE); 1907 1908 if (current->database == NULL) { /* need to load it in */ 1909 dbase = new db(current->table_name); 1910 if (dbase == NULL) { 1911 FATAL3( 1912 "db_dictionary::db_checkpoint: could not allocate space", 1913 DB_MEMORY_LIMIT, DB_MEMORY_LIMIT); 1914 } 1915 if (dbase->load() == 0) { 1916 syslog(LOG_ERR, 1917 "db_dictionary::db_checkpoint: could not load table %s", 1918 current->table_name); 1919 delete dbase; 1920 return (DB_BADTABLE); 1921 } 1922 status = dbase->checkpoint(); 1923 delete dbase; // unload 1924 } else 1925 status = current->database->checkpoint(); 1926 1927 if (status == 0) 1928 return (DB_STORAGE_LIMIT); 1929 return (DB_SUCCESS); 1930 } 1931 1932 /* Like db_checkpoint_aux except only stops on LIMIT errors */ 1933 static db_status 1934 db_checkpoint_aux_cont(db_table_desc *current) 1935 { 1936 db_status status = db_checkpoint_aux(current); 1937 1938 if (status == DB_STORAGE_LIMIT || status == DB_MEMORY_LIMIT) 1939 return (status); 1940 else 1941 return (DB_SUCCESS); 1942 } 1943 1944 db_status 1945 db_dictionary::db_checkpoint(char *tab) 1946 { 1947 db_table_desc *tbl; 1948 db_status ret; 1949 bool_t init; 1950 1951 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_checkpoint"); 1952 init = initialized; 1953 READUNLOCK(this, DB_LOCK_ERROR, "ru db_dictionary::db_checkpoint"); 1954 if (!init) 1955 return (DB_BADDICTIONARY); 1956 1957 checkpoint(); // checkpoint dictionary first 1958 1959 READLOCK(this, DB_LOCK_ERROR, "r db_dictionary::db_checkpoint"); 1960 1961 if (tab == NULL) { 1962 ret = enumerate_dictionary(dictionary, &db_checkpoint_aux_cont); 1963 READUNLOCK(this, ret, "ru db_dictionary::db_checkpoint"); 1964 return (ret); 1965 } 1966 1967 if ((tbl = find_table_desc(tab)) == NULL) { 1968 READUNLOCK(this, DB_LOCK_ERROR, 1969 "ru db_dictionary::db_checkpoint"); 1970 return (DB_BADTABLE); 1971 } 1972 1973 ret = db_checkpoint_aux(tbl); 1974 READUNLOCK(this, ret, "ru db_dictionary::db_checkpoint"); 1975 return (ret); 1976 } 1977 1978 /* *********************** db_standby **************************** */ 1979 /* Deal with list of tables that need to be 'closed' */ 1980 1981 #define OPENED_DBS_CHUNK 12 1982 static db **db_standby_list; 1983 static uint_t db_standby_size = 0; 1984 static uint_t db_standby_count = 0; 1985 DECLMUTEXLOCK(db_standby_list); 1986 1987 /* 1988 * Returns 1 if all databases on the list could be closed, 0 1989 * otherwise. 1990 */ 1991 static int 1992 close_standby_list() 1993 { 1994 db *database; 1995 int i, ret; 1996 const char *myself = "close_standby_list"; 1997 1998 MUTEXLOCK(db_standby_list, "close_standby_list"); 1999 2000 if (db_standby_count == 0) { 2001 MUTEXUNLOCK(db_standby_list, "close_standby_list"); 2002 return (1); 2003 } 2004 2005 for (i = 0, ret = 0; i < db_standby_size; i++) { 2006 if ((database = db_standby_list[i])) { 2007 /* 2008 * In order to avoid a potential dead-lock, we 2009 * check to see if close_log() would be able to 2010 * lock the db; if not, just skip the db. 2011 */ 2012 int lockok; 2013 2014 TRYWRITELOCK(database, lockok, 2015 "try w db_dictionary::close_standby_list"); 2016 2017 if (lockok == 0) { 2018 database->close_log(1); 2019 db_standby_list[i] = (db*)NULL; 2020 --db_standby_count; 2021 WRITEUNLOCK(database, db_standby_count == 0, 2022 "db_dictionary::close_standby_list"); 2023 if (db_standby_count == 0) { 2024 ret = 1; 2025 break; 2026 } 2027 } else if (lockok != EBUSY) { 2028 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2029 "%s: try-lock error %d", 2030 myself, lockok); 2031 } /* else it's EBUSY; skip to the next one */ 2032 } 2033 } 2034 2035 MUTEXUNLOCK(db_standby_list, "close_standby_list"); 2036 2037 return (ret); 2038 } 2039 2040 /* 2041 * Add given database to list of databases that have been opened for updates. 2042 * If size of list exceeds maximum, close opened databases first. 2043 */ 2044 2045 int 2046 add_to_standby_list(db* database) 2047 { 2048 int i; 2049 const char *myself = "add_to_standby_list"; 2050 2051 MUTEXLOCK(db_standby_list, "add_to_standby_list"); 2052 2053 if (database == 0) { 2054 MUTEXUNLOCK(db_standby_list, "add_to_standby_list"); 2055 return (1); 2056 } 2057 2058 /* Try to keep the list below OPENED_DBS_CHUNK */ 2059 if (db_standby_count >= OPENED_DBS_CHUNK) { 2060 MUTEXUNLOCK(db_standby_list, "add_to_standby_list"); 2061 close_standby_list(); 2062 MUTEXLOCK(db_standby_list, "add_to_standby_list"); 2063 } 2064 2065 if (db_standby_count >= db_standby_size) { 2066 db **ndsl = (db **)realloc(db_standby_list, 2067 (db_standby_size+OPENED_DBS_CHUNK) * 2068 sizeof (ndsl[0])); 2069 2070 if (ndsl == 0) { 2071 MUTEXUNLOCK(db_standby_list, "add_to_standby_list"); 2072 logmsg(MSG_NOMEM, LOG_ERR, 2073 "%s: realloc(%d) => NULL", 2074 myself, (db_standby_size+OPENED_DBS_CHUNK) * 2075 sizeof (ndsl[0])); 2076 return (0); 2077 } 2078 2079 db_standby_list = ndsl; 2080 2081 for (i = db_standby_size; i < db_standby_size+OPENED_DBS_CHUNK; 2082 i++) 2083 db_standby_list[i] = 0; 2084 2085 db_standby_size += OPENED_DBS_CHUNK; 2086 } 2087 2088 for (i = 0; i < db_standby_size; i++) { 2089 if (db_standby_list[i] == (db*)NULL) { 2090 db_standby_list[i] = database; 2091 ++db_standby_count; 2092 MUTEXUNLOCK(db_standby_list, "add_to_standby_list"); 2093 return (1); 2094 } 2095 } 2096 2097 MUTEXUNLOCK(db_standby_list, "add_to_standby_list"); 2098 2099 return (0); 2100 } 2101 2102 int 2103 remove_from_standby_list(db* database) 2104 { 2105 int i; 2106 2107 MUTEXLOCK(db_standby_list, "remove_from_standby_list"); 2108 2109 if (database == 0) { 2110 MUTEXUNLOCK(db_standby_list, "remove_from_standby_list"); 2111 return (1); 2112 } 2113 2114 for (i = 0; i < db_standby_size; i++) { 2115 if ((database == db_standby_list[i])) { 2116 db_standby_list[i] = (db*)NULL; 2117 --db_standby_count; 2118 MUTEXUNLOCK(db_standby_list, 2119 "remove_from_standby_list"); 2120 return (1); 2121 } 2122 } 2123 2124 MUTEXUNLOCK(db_standby_list, "remove_from_standby_list"); 2125 2126 return (0); 2127 } 2128 2129 /* Release space for copied dictionary */ 2130 static void 2131 db_release_dictionary(db_dict_desc_p d) { 2132 2133 int i; 2134 2135 if (d != NULL) { 2136 for (i = 0; i < d->tables.tables_len; i++) { 2137 db_table_desc_p n, t = d->tables.tables_val[i]; 2138 while (t != NULL) { 2139 n = t->next; 2140 delete_table_desc(t); 2141 t = n; 2142 } 2143 } 2144 delete d; 2145 } 2146 2147 return; 2148 } 2149 2150 /* 2151 * Make a copy of the dictionary 2152 */ 2153 db_dict_desc_p 2154 db_dictionary::db_copy_dictionary(void) { 2155 2156 db_dict_desc_p tmp; 2157 int i, ok = 1, count = 0; 2158 2159 WRITELOCK(this, NULL, "db_dictionary::db_copy_dictionary w"); 2160 2161 if (dictionary == NULL) { 2162 WRITEUNLOCK(this, NULL, 2163 "db_dictionary::db_copy_dictionary wu"); 2164 return (NULL); 2165 } 2166 2167 tmp = new db_dict_desc; 2168 if (tmp == NULL) { 2169 WRITEUNLOCK(this, NULL, 2170 "db_dictionary::db_copy_dictionary wu: no memory"); 2171 return (NULL); 2172 } 2173 2174 tmp->tables.tables_val = (db_table_desc_p *)calloc( 2175 tmp->tables.tables_len, 2176 sizeof (tmp->tables.tables_val[0])); 2177 if (tmp->tables.tables_val == NULL) { 2178 delete tmp; 2179 WRITEUNLOCK(this, NULL, 2180 "db_dictionary::db_copy_dictionary wu: no memory"); 2181 return (NULL); 2182 } 2183 2184 tmp->impl_vers = dictionary->impl_vers; 2185 tmp->tables.tables_len = 0; 2186 tmp->count = 0; 2187 2188 /* For each table ... */ 2189 for (i = 0; ok && i < dictionary->tables.tables_len; i++) { 2190 db_table_desc_p tbl = NULL, 2191 t = dictionary->tables.tables_val[i]; 2192 /* ... and each bucket in the chain ... */ 2193 while (ok && t != NULL) { 2194 db_table_desc_p n, savenext = t->next; 2195 t->next = NULL; 2196 if (db_clone_bucket(t, &n)) { 2197 if (tbl != NULL) { 2198 tbl->next = n; 2199 } else { 2200 tmp->tables.tables_val[i] = n; 2201 } 2202 tbl = n; 2203 tmp->count++; 2204 } else { 2205 ok = 0; 2206 } 2207 t->next = savenext; 2208 } 2209 tmp->tables.tables_len++; 2210 } 2211 2212 if (ok) { 2213 #ifdef NISDB_LDAP_DEBUG 2214 if ((tmp->tables.tables_len != 2215 dictionary->tables.tables_len) || 2216 (tmp->count != dictionary->count)) 2217 abort(); 2218 #endif /* NISDB_LDAP_DEBUG */ 2219 } else { 2220 db_release_dictionary(tmp); 2221 tmp = NULL; 2222 } 2223 2224 return (tmp); 2225 } 2226 2227 /* 2228 * Set deferred commit mode. To do this, we make a copy of the table 2229 * (structures and data), and put that on the deferred dictionary list. 2230 * This list is used for lookups during a resync, so clients continue 2231 * to see the pre-resync data. Meanwhile, any changes (including table 2232 * deletes) are done to the (temporarily hidden to clients) table in 2233 * the normal dictionary. 2234 */ 2235 db_status 2236 db_dictionary::defer(char *table) { 2237 db_status ret = DB_SUCCESS; 2238 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::defer"); 2239 db_table_desc *tbl = find_table_desc(table); 2240 int res; 2241 const char *myself = "db_dictionary::defer"; 2242 2243 if (tbl != NULL) { 2244 db_table_desc *clone, *savenext = tbl->next; 2245 /* 2246 * Only want to clone one db_table_desc, so temporarily 2247 * unlink the tail. 2248 */ 2249 tbl->next = NULL; 2250 res = db_clone_bucket(tbl, &clone); 2251 /* Restore link to tail */ 2252 tbl->next = savenext; 2253 if (res == 1) { 2254 db_status stat; 2255 if (deferred.dictionary == NULL) { 2256 deferred.dictionary = new db_dict_desc; 2257 if (deferred.dictionary == NULL) { 2258 WRITEUNLOCK(this, DB_MEMORY_LIMIT, 2259 "wu db_dictionary::defer"); 2260 return (DB_MEMORY_LIMIT); 2261 } 2262 deferred.dictionary->tables.tables_len = 0; 2263 deferred.dictionary->tables.tables_val = NULL; 2264 deferred.dictionary->count = 0; 2265 deferred.dictionary->impl_vers = 2266 DB_CURRENT_VERSION; 2267 } 2268 ret = DB_SUCCESS; 2269 /* Initialize and load the database for the clone */ 2270 if (clone->database == 0) { 2271 clone->database = new db(table); 2272 if (clone->database != 0) { 2273 if (clone->database->load()) { 2274 logmsg(MSG_NOTIMECHECK, 2275 #ifdef NISDB_LDAP_DEBUG 2276 LOG_WARNING, 2277 #else 2278 LOG_INFO, 2279 #endif /* NISDB_LDAP_DEBUG */ 2280 "%s: Clone DB for \"%s\" loaded", 2281 myself, NIL(table)); 2282 } else { 2283 logmsg(MSG_NOTIMECHECK, LOG_ERR, 2284 "%s: Error loading clone DB for \"%s\"", 2285 myself, NIL(table)); 2286 delete clone->database; 2287 clone->database = 0; 2288 ret = DB_INTERNAL_ERROR; 2289 } 2290 } else { 2291 logmsg(MSG_NOTIMECHECK, LOG_ERR, 2292 "%s: Unable to clone DB for \"%s\"", 2293 myself, NIL(table)); 2294 ret = DB_MEMORY_LIMIT; 2295 } 2296 } 2297 if (clone->database != 0) { 2298 clone->database->markDeferred(); 2299 stat = add_to_dictionary(deferred.dictionary, 2300 clone); 2301 ret = stat; 2302 if (stat != DB_SUCCESS) { 2303 delete clone->database; 2304 clone->database = 0; 2305 delete clone; 2306 if (stat == DB_NOTUNIQUE) { 2307 /* Already deferred */ 2308 ret = DB_SUCCESS; 2309 } 2310 } 2311 } else { 2312 delete clone; 2313 /* Return value already set above */ 2314 } 2315 } else { 2316 ret = DB_INTERNAL_ERROR; 2317 } 2318 } else { 2319 ret = DB_NOTFOUND; 2320 } 2321 WRITEUNLOCK(this, ret, "wu db_dictionary::defer"); 2322 return (ret); 2323 } 2324 2325 /* 2326 * Unset deferred commit mode and roll back changes; doesn't recover the 2327 * disk data, but that's OK, since we only want to be able to continue 2328 * serving the table until we can try a full dump again. 2329 * 2330 * The rollback is done by removing (and deleting) the updated table from 2331 * the dictionary, and then moving the saved table from the deferred 2332 * dictionary list to the actual one. 2333 */ 2334 db_status 2335 db_dictionary::rollback(char *table) { 2336 db_status ret = DB_SUCCESS; 2337 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::rollback"); 2338 db_table_desc *old = search_dictionary(deferred.dictionary, table); 2339 db_table_desc *upd = search_dictionary(dictionary, table); 2340 2341 if (old == NULL) { 2342 WRITEUNLOCK(this, DB_NOTFOUND, "wu db_dictionary::rollback"); 2343 return (DB_NOTFOUND); 2344 } 2345 2346 /* 2347 * Remove old incarnation from deferred dictionary. We already hold 2348 * a pointer ('old') to it, so don't delete. 2349 */ 2350 ret = remove_from_dictionary(deferred.dictionary, table, FALSE); 2351 if (ret != DB_SUCCESS) { 2352 #ifdef NISDB_LDAP_DEBUG 2353 abort(); 2354 #endif /* NISDB_LDAP_DEBUG */ 2355 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback"); 2356 return (ret); 2357 } 2358 2359 if (old->database != 0) 2360 old->database->unmarkDeferred(); 2361 2362 /* 2363 * Remove updated incarnation from dictionary. If 'upd' is NULL, 2364 * the table has been removed while we were in deferred mode, and 2365 * that's OK; we just need to retain the old incarnation. 2366 */ 2367 if (upd != NULL) { 2368 ret = remove_from_dictionary(dictionary, table, FALSE); 2369 if (ret != DB_SUCCESS) { 2370 #ifdef NISDB_LDAP_DEBUG 2371 abort(); 2372 #endif /* NISDB_LDAP_DEBUG */ 2373 /* 2374 * Cut our losses; delete old incarnation, and leave 2375 * updated one in place. 2376 */ 2377 delete_table_desc(old); 2378 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback"); 2379 return (ret); 2380 } 2381 /* Throw away updates */ 2382 delete_table_desc(upd); 2383 } 2384 2385 /* (Re-)insert old incarnation in the dictionary */ 2386 ret = add_to_dictionary(dictionary, old); 2387 if (ret != DB_SUCCESS) { 2388 #ifdef NISDB_LDAP_DEBUG 2389 abort(); 2390 #endif /* NISDB_LDAP_DEBUG */ 2391 /* At least avoid memory leak */ 2392 delete_table_desc(old); 2393 syslog(LOG_ERR, 2394 "db_dictionary::rollback: rollback error %d for \"%s\"", ret, table); 2395 } 2396 2397 WRITEUNLOCK(this, ret, "wu db_dictionary::rollback"); 2398 return (ret); 2399 } 2400 2401 /* 2402 * Commit changes. Done by simply removing and deleting the pre-resync 2403 * data from the deferred dictionary. 2404 */ 2405 db_status 2406 db_dictionary::commit(char *table) { 2407 db_status ret = DB_SUCCESS; 2408 WRITELOCK(this, DB_LOCK_ERROR, "w db_dictionary::commit"); 2409 db_table_desc *old = search_dictionary(deferred.dictionary, table); 2410 2411 if (old == NULL) { 2412 /* Fine (we hope); nothing to do */ 2413 WRITEUNLOCK(this, ret, "wu db_dictionary::commit"); 2414 return (DB_SUCCESS); 2415 } 2416 2417 ret = remove_from_dictionary(deferred.dictionary, table, FALSE); 2418 if (ret == DB_SUCCESS) 2419 delete_table_desc(old); 2420 #ifdef NISDB_LDAP_DEBUG 2421 else 2422 abort(); 2423 #endif /* NISDB_LDAP_DEBUG */ 2424 2425 WRITEUNLOCK(this, ret, "wu db_dictionary::commit"); 2426 return (ret); 2427 } 2428 2429 /* 2430 * The noWriteThrough flag is used to prevent modifies/updates to LDAP 2431 * while we're incorporating log data into the in-memory tables. 2432 */ 2433 void 2434 db_dictionary::setNoWriteThrough(void) { 2435 ASSERTWHELD(this->dict); 2436 noWriteThrough.flag++; 2437 } 2438 2439 void 2440 db_dictionary::clearNoWriteThrough(void) { 2441 ASSERTWHELD(this->dict); 2442 if (noWriteThrough.flag > 0) 2443 noWriteThrough.flag--; 2444 #ifdef NISDB_LDAP_DEBUG 2445 else 2446 abort(); 2447 #endif /* NISDB_LDAP_DEBUG */ 2448 } 2449