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