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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * nis_db.cc 23 * 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 29 #include <sys/param.h> 30 #include <strings.h> 31 #include <syslog.h> 32 #include "nisdb_mt.h" 33 #include "db_headers.h" 34 #include "db_entry.h" 35 #include "db.h" 36 #include "db_dictionary.h" 37 #include "db_pickle.h" 38 #include "nis_db.h" 39 #include "nis_ldap.h" 40 #include "ldap_util.h" 41 #include "ldap_parse.h" 42 #include "ldap_glob.h" 43 #include "ldap_xdr.h" 44 #include "ldap_glob.h" 45 46 db_dictionary curdict; 47 db_dictionary tempdict; /* a temporary one */ 48 49 db_dictionary *InUseDictionary = &curdict; 50 db_dictionary *FreeDictionary = &tempdict; 51 52 extern "C" { 53 static db_result *db_add_entry_x(char *tab, int numattrs, 54 nis_attr *attrname, entry_obj * newobj, 55 int skiplog, int nosync); 56 db_status db_table_exists(char *table_name); 57 58 /* 59 * (Imported from rpc.nisd/nis_xx_proc.c) 60 * 61 * 'tbl_prototype' is used to create a table that holds a directory. 62 */ 63 static table_col cols[2] = { 64 {(char *)"object", TA_BINARY+TA_XDR, 0}, 65 {(char *)"name", TA_CASE+TA_SEARCHABLE, 0} 66 }; 67 68 table_obj tbl_prototype = { (char *)"DIRECTORY", 2, ' ', {2, &cols[0]}, NULL }; 69 } 70 71 /* 72 * Free resources associated with a db_result structure 73 */ 74 void 75 db_free_result(db_result *dr) 76 { 77 int i; 78 79 if (dr == 0) 80 return; 81 82 /* Can't have valid objects */ 83 if (dr->status != DB_SUCCESS) { 84 free(dr); 85 return; 86 } 87 88 for (i = 0; i < dr->objects.objects_len; i++) 89 free_entry(dr->objects.objects_val[i]); 90 free(dr->objects.objects_val); 91 free(dr); 92 } 93 94 95 /* Return an empty db_result structure with its status field set to 's'. */ 96 db_result* 97 empty_result(db_status s) 98 { 99 db_result * res = new db_result; 100 if (res != NULL) { 101 res->status = s; 102 res->nextinfo.db_next_desc_len = 0; 103 res->nextinfo.db_next_desc_val = NULL; 104 res->objects.objects_len = 0; 105 res->objects.objects_val = NULL; 106 } else { 107 WARNING("nis_db::empty_result: cannot allocate space"); 108 } 109 return (res); 110 } 111 112 static db_result* 113 set_result(db_result* res, db_status s) 114 { 115 if (res != NULL) { 116 res->status = s; 117 } 118 return (res); 119 } 120 121 /* 122 * Given a FQ object name for a table or directory, return the (db *) 123 * corresponding to the object. 124 */ 125 db * 126 tableDB(char *tableName) { 127 db_table_desc *tbl = 0; 128 char *intName; 129 db *dbase; 130 131 intName = internalTableName(tableName); 132 if (intName == 0) 133 return (0); 134 135 dbase = InUseDictionary->find_table(intName, &tbl); 136 137 sfree(intName); 138 139 return (dbase); 140 } 141 142 extern "C" { 143 144 bool_t 145 db_in_dict_file(char *name) 146 { 147 return ((bool_t) InUseDictionary->find_table_desc(name)); 148 149 } 150 151 const char 152 *db_perror(db_status dbstat) 153 { 154 const char *str = NULL; 155 156 switch (dbstat) { 157 case DB_SUCCESS: 158 str = "Success"; 159 break; 160 case DB_NOTFOUND: 161 str = "Not Found"; 162 break; 163 case DB_BADTABLE: 164 str = "Bad Table"; 165 break; 166 case DB_BADQUERY: 167 str = "Bad Query"; 168 break; 169 case DB_BADOBJECT: 170 str = "Bad Object"; 171 break; 172 case DB_MEMORY_LIMIT: 173 str = "Memory limit exceeded"; 174 break; 175 case DB_STORAGE_LIMIT: 176 str = "Database storage limit exceeded"; 177 break; 178 case DB_INTERNAL_ERROR: 179 str = "Database internal error"; 180 break; 181 case DB_SYNC_FAILED: 182 str = "Sync of log file failed"; 183 break; 184 default: 185 str = "Unknown Error"; 186 break; 187 } 188 return (str); 189 } 190 191 bool_t 192 db_extract_dict_entries(char *newdict, char **fs, int fscnt) 193 { 194 /* 195 * Use the "FreeDictionary" ptr for the backup 196 * dictionary. 197 */ 198 if (!FreeDictionary->inittemp(newdict, *InUseDictionary)) 199 return (FALSE); 200 return (InUseDictionary->extract_entries (*FreeDictionary, 201 fs, fscnt)); 202 } 203 204 bool_t 205 db_copy_file(char *infile, char *outfile) 206 { 207 return (InUseDictionary->copyfile(infile, outfile)); 208 209 } 210 211 212 /* 213 * The tok and repl parameters will allow us to merge two dictionaries 214 * that reference tables from different domains (master/replica in live 215 * in different domains). If set to NULL, then the dictionary merge is 216 * done as normal (no name changing). 217 */ 218 db_status 219 db_begin_merge_dict(char *newdict, char *tok, char *repl) 220 { 221 db_status dbstat; 222 223 /* 224 * It is assumed that InUseDictionary has already been initialized. 225 */ 226 dbstat = InUseDictionary->checkpoint(); 227 if (dbstat != DB_SUCCESS) 228 return (dbstat); 229 230 /* 231 * Use the "FreeDictionary" ptr for the backup 232 * dictionary. 233 */ 234 if (!FreeDictionary->init(newdict)) 235 return (DB_INTERNAL_ERROR); 236 237 return (InUseDictionary->merge_dict(*FreeDictionary, 238 tok, repl)); 239 } 240 241 242 db_status 243 db_end_merge_dict() 244 { 245 db_status dbstat; 246 247 dbstat = InUseDictionary->checkpoint(); 248 if (dbstat != DB_SUCCESS) { 249 return (dbstat); 250 } 251 dbstat = InUseDictionary->db_shutdown(); 252 if (dbstat != DB_SUCCESS) { 253 return (dbstat); 254 } 255 dbstat = FreeDictionary->db_shutdown(); 256 if (dbstat != DB_SUCCESS) { 257 return (dbstat); 258 } 259 return (dbstat); 260 } 261 262 263 264 db_status 265 db_abort_merge_dict() 266 { 267 db_status dbstat; 268 269 dbstat = InUseDictionary->db_shutdown(); 270 if (dbstat != DB_SUCCESS) 271 return (dbstat); 272 dbstat = FreeDictionary->db_shutdown(); 273 if (dbstat != DB_SUCCESS) 274 return (dbstat); 275 } 276 277 278 /* 279 * Initialize system (dictionary) using file 'filename'. If system cannot 280 * be read from file, it is initialized to be empty. Returns TRUE if 281 * initialization succeeds, FALSE otherwise. 282 * This function must be called before any other. 283 */ 284 bool_t 285 db_initialize(char * filename) 286 { 287 return (InUseDictionary->init(filename)); 288 } 289 290 291 /* 292 * Massage the dictionary file by replacing the specified token with the 293 * the replacement string. This function is needed to provide backwards 294 * compatibility for providing a transportable dictionary file. The idea 295 * is that rpc.nisd will call this function when it wants to change the 296 * /var/nis/<hostname> strings with something like /var/nis/data. 297 * 298 */ 299 db_status 300 db_massage_dict(char *newdictname, char *tok, char *repl) 301 { 302 return (InUseDictionary->massage_dict(newdictname, tok, repl)); 303 } 304 305 306 307 /* 308 * Create new table using given table name and table descriptor. 309 * Returns DB_SUCCESS if successful; appropriate error code otherwise. 310 */ 311 db_status 312 db_create_table(char * table_name, table_obj * table_desc) 313 { 314 return (InUseDictionary->add_table(table_name, table_desc)); 315 } 316 317 /* 318 * Destroys table named by 'table_name.' Returns DB_SUCCESS if successful, 319 * error code otherwise. Note that currently, the removed table is no 320 * longer accessible from this interface and all files associated with it 321 * are removed from stable storage. 322 */ 323 db_status 324 db_destroy_table(char * table_name) 325 { 326 return (InUseDictionary->delete_table(table_name)); 327 } 328 329 330 /* 331 * Return a copy of the first entry in the specified table, that satisfies 332 * the given attributes. The returned structure 'db_result' contains the status, 333 * the copy of the object, and a 'db_next_desc' to be used for the 'next' 334 * operation. 335 */ 336 db_result * 337 db_first_entry(char * table_name, int numattrs, nis_attr * attrname) 338 { 339 db_result * safety = empty_result(DB_SUCCESS); 340 db_table_desc * tbl = NULL; 341 db * dbase = InUseDictionary->find_table(table_name, &tbl); 342 343 if (tbl == NULL || dbase == NULL) 344 return (set_result(safety, DB_BADTABLE)); 345 else { 346 db_result * res = NULL; 347 db_query *query = NULL; 348 349 if (numattrs != 0) { 350 query = InUseDictionary->translate_to_query(tbl, 351 numattrs, attrname); 352 if (query == NULL) 353 return (set_result(safety, 354 DB_BADQUERY)); 355 } 356 res = dbase->execute(DB_FIRST, query, NULL, NULL); 357 if (query) delete query; 358 if (safety) delete safety; 359 return (res); 360 } 361 } 362 363 /* 364 * Return a copy of the next entry in the specified table as specified by 365 * the 'next_desc'. The returned structure 'db_result' contains the status, 366 * a copy of the object, and a db_next_desc to be used for a subsequent 367 * 'next' operation. 368 */ 369 db_result * 370 db_next_entry(char * table_name, db_next_desc * next_desc) 371 { 372 db_result * safety = empty_result(DB_SUCCESS); 373 db * dbase = InUseDictionary->find_table(table_name); 374 375 if (dbase != NULL) { 376 if (safety) delete safety; 377 return (dbase->execute(DB_NEXT, NULL, NULL, next_desc)); 378 } else 379 return (set_result(safety, DB_BADTABLE)); 380 } 381 382 /* 383 * Indicate to the system that you are no longer interested in the rest of the 384 * results identified by [next_desc]. After executing this operation, the 385 * [next_desc] is no longer valid (cannot be used as an argument for next). 386 */ 387 388 db_result * 389 db_reset_next_entry(char * table_name, db_next_desc * next_desc) 390 { 391 db_result * safety = empty_result(DB_SUCCESS); 392 db * dbase = InUseDictionary->find_table(table_name); 393 394 if (dbase != NULL) { 395 if (safety) delete safety; 396 return (dbase->execute(DB_RESET_NEXT, 397 NULL, NULL, next_desc)); 398 } else 399 return (set_result(safety, DB_BADTABLE)); 400 } 401 402 /* 403 * Returns copies of entries that satisfy the given attributes from table. 404 * Returns the status and entries in a db_result structure. 405 * If no attributes are specified, DB_BADQUERY is returned. 406 */ 407 db_result * 408 __db_list_entries(char * table_name, int numattrs, nis_attr * attrname, 409 bool_t useDeferred) 410 { 411 db_result * safety = empty_result(DB_SUCCESS); 412 db_table_desc * tbl = NULL; 413 db * dbase = InUseDictionary->find_table(table_name, &tbl, 414 useDeferred); 415 416 if (tbl == NULL || dbase == NULL) 417 return (set_result(safety, DB_BADTABLE)); 418 else { 419 db_result * res = NULL; 420 if (numattrs != 0) { 421 db_query *query; 422 query = InUseDictionary->translate_to_query(tbl, 423 numattrs, attrname); 424 if (query == NULL) 425 return (set_result(safety, 426 DB_BADQUERY)); 427 res = dbase->execute(DB_LOOKUP, query, 428 NULL, NULL); 429 delete query; 430 } else { 431 res = dbase->execute(DB_ALL, NULL, NULL, NULL); 432 } 433 if (safety) delete safety; 434 return (res); 435 } 436 } 437 438 db_result * 439 db_list_entries(char *table_name, int numattrs, nis_attr *attrname) { 440 return (__db_list_entries(table_name, numattrs, attrname, TRUE)); 441 } 442 443 /* 444 * Input: A fully qualified object name (example: "x.y.z"). 445 * Output: Returns the first level of the object name ("x"). 446 * If 'tableP' is non-NULL, '*tableP' will contain 447 * the internal table name for "y.z". 448 * 449 * Both the return value and '*tableP' must be freed by the caller. 450 */ 451 char * 452 entryName(const char *msg, char *objName, char **tableP) { 453 char *name, *table, *dir; 454 const char *myself = "entryName"; 455 456 if (msg == 0) 457 msg = myself; 458 459 name = sdup(msg, T, objName); 460 if (name == 0) 461 return (0); 462 463 dir = strchr(name, '.'); 464 if (dir == 0) { 465 sfree(name); 466 return (0); 467 } 468 *(dir++) = '\0'; 469 470 if (tableP == 0) 471 return (name); 472 473 table = internalTableName(dir); 474 if (table == 0) { 475 sfree(name); 476 return (0); 477 } 478 479 *tableP = table; 480 481 return (name); 482 } 483 484 #define RETSTAT(obj, status) \ 485 { \ 486 if (statP != 0) \ 487 *statP = status; \ 488 return (obj); \ 489 } 490 491 /* 492 * Given a fully qualified object name, retrive a copy of the object, 493 * using the NIS+ DB only (i.e., no LDAP). Avoids using nis_leaf_of() 494 * etc., since they aren't re-entrant. 495 */ 496 nis_object * 497 dbFindObject(char *objName, db_status *statP) { 498 char buf[MAXPATHLEN+NIS_MAXNAMELEN+1]; 499 char *name, *table = 0; 500 nis_attr attr; 501 db *dbase; 502 db_result *res; 503 db_table_desc *tbl = 0; 504 db_query *query; 505 db_mindex *mindex; 506 nis_object *o; 507 int lstat; 508 const char *myself = "dbFindObject"; 509 510 if (objName == 0) 511 RETSTAT(0, DB_BADQUERY); 512 513 /* The root dir is treated specially */ 514 table = internalTableName(objName); 515 if (table == 0) 516 RETSTAT(0, DB_BADQUERY); 517 if (strcmp(ROOTDIRFILE, table) == 0) { 518 sfree(table); 519 520 o = get_root_object(); 521 if (o == 0) 522 RETSTAT(0, DB_NOTFOUND); 523 524 RETSTAT(o, DB_SUCCESS); 525 } 526 527 /* If not the root dir, find the directory where the entry lives */ 528 529 sfree(table); 530 name = entryName(myself, objName, &table); 531 if (name == 0 || table == 0) { 532 sfree(name); 533 RETSTAT(0, DB_MEMORY_LIMIT); 534 } 535 536 dbase = InUseDictionary->find_table_noLDAP(table, &tbl, TRUE, TRUE); 537 sfree(table); 538 if (dbase != 0) 539 mindex = dbase->mindex(); 540 if (dbase == 0 || tbl == 0 || mindex == 0) { 541 sfree(name); 542 RETSTAT(0, DB_BADTABLE); 543 } 544 545 WRITELOCKNR(mindex, lstat, "mindex w dbFindObject"); 546 if (lstat != 0) { 547 sfree(name); 548 RETSTAT(0, DB_LOCK_ERROR); 549 } 550 551 attr.zattr_ndx = (char *)"name"; 552 attr.zattr_val.zattr_val_val = name; 553 attr.zattr_val.zattr_val_len = slen(name) + 1; 554 555 query = InUseDictionary->translate_to_query(tbl, 1, &attr); 556 if (query == 0) { 557 sfree(name); 558 WRITEUNLOCKNR(mindex, lstat, "mindex wu dbFindObject"); 559 RETSTAT(0, DB_BADQUERY); 560 } 561 562 /* Only want to look in the local DB */ 563 mindex->setNoLDAPquery(); 564 565 res = dbase->execute(DB_LOOKUP, query, 0, 0); 566 567 mindex->clearNoLDAPquery(); 568 569 delete query; 570 571 sfree(name); 572 573 WRITEUNLOCKNR(mindex, lstat, "mindex wu dbFindObject"); 574 if (lstat != 0) { 575 db_free_result(res); 576 RETSTAT(0, DB_LOCK_ERROR); 577 } 578 579 if (res == 0) 580 RETSTAT(0, DB_MEMORY_LIMIT); 581 582 if (res->status != DB_SUCCESS) { 583 db_status st = res->status; 584 585 db_free_result(res); 586 RETSTAT(0, st); 587 } 588 589 if (res->objects.objects_len != 1 || res->objects.objects_val == 0 || 590 res->objects.objects_val[0] == 0) { 591 db_free_result(res); 592 RETSTAT(0, DB_BADOBJECT); 593 } 594 595 o = unmakePseudoEntryObj(res->objects.objects_val[0], 0); 596 597 db_free_result(res); 598 599 if (o == 0) { 600 RETSTAT(0, DB_BADOBJECT); 601 } 602 603 RETSTAT(o, DB_SUCCESS); 604 } 605 606 /* 607 * Return the object specified by 't' or 'objName' from LDAP. Set 608 * the LDAP status in '*statP'. 609 */ 610 nis_object * 611 ldapFindObj(__nis_table_mapping_t *t, char *objName, int *statP) { 612 nis_object *o; 613 int stat; 614 const char *myself = "ldapFindObj"; 615 616 if (t == 0) { 617 char *table, tbuf[MAXPATHLEN + NIS_MAXNAMELEN + 1]; 618 619 if (objName == 0) { 620 if (statP != 0) 621 *statP = LDAP_PARAM_ERROR; 622 return (0); 623 } 624 625 /* Look for mapping */ 626 table = internal_table_name(objName, tbuf); 627 if (table == 0) { 628 if (statP != 0) 629 *statP = LDAP_PARAM_ERROR; 630 return (0); 631 } 632 633 t = (__nis_table_mapping_t *)__nis_find_item_mt(table, 634 &ldapMappingList, 0, 0); 635 if (t == 0) { 636 /* Not really an error; just not mapped */ 637 *statP = LDAP_SUCCESS; 638 return (0); 639 } 640 } 641 642 o = 0; 643 stat = objFromLDAP(t, &o, 0, 0); 644 645 if (statP != 0) 646 *statP = stat; 647 648 return (o); 649 } 650 651 /* 652 * Look for the specified object, first locally, then in LDAP. 653 */ 654 nis_object * 655 findObj(char *name, db_status *statP, int *lstatP) { 656 nis_object *o; 657 db_status stat = DB_SUCCESS; 658 int lstat = LDAP_SUCCESS; 659 const char *myself = "findObj"; 660 661 o = dbFindObject(name, &stat); 662 663 if (o == 0) { 664 if (stat != DB_NOTFOUND) 665 logmsg(MSG_NOTIMECHECK, LOG_INFO, 666 "%s: DB error %d looking for \"%s\"", 667 myself, stat, NIL(name)); 668 669 o = ldapFindObj(0, name, &lstat); 670 if (o == 0) { 671 if (lstat != LDAP_SUCCESS && 672 lstat != LDAP_NO_SUCH_OBJECT) 673 logmsg(MSG_NOTIMECHECK, LOG_INFO, 674 "%s: LDAP error looking for \"%s\": %s", 675 myself, NIL(name), 676 ldap_err2string(lstat)); 677 } 678 } 679 680 if (statP != 0) 681 *statP = stat; 682 if (lstatP != 0) 683 *lstatP = lstat; 684 685 return (o); 686 } 687 688 /* 689 * Delete the specified object from the local DB. 690 */ 691 db_status 692 dbDeleteObj(char *objName) { 693 nisdb_tsd_t *tsd = __nisdb_get_tsd(); 694 nis_object *o; 695 db_status stat; 696 nisdb_obj_del_t *nod, *tmp; 697 int xid; 698 const char *myself = "dbDeleteObj"; 699 700 if (objName == 0) 701 return (DB_SUCCESS); 702 703 /* 704 * Since in-structure locks can't completely protect 705 * during structure deletion, we just note that the 706 * object should be deleted, and leave that for a 707 * (slightly) later time in rpc.nisd, where we can 708 * use the rpc.nisd's table/directory locks for 709 * protection. 710 */ 711 712 if (tsd == 0) 713 return (DB_INTERNAL_ERROR); 714 715 o = dbFindObject(objName, &stat); 716 if (o == 0) { 717 if (stat == DB_NOTFOUND) 718 return (DB_SUCCESS); 719 else 720 return (stat); 721 } 722 723 /* 724 * In order to prevent a chicken-and-egg problem (if the 725 * object doesn't exist in LDAP, is that because we just 726 * haven't written it to LDAP yet, or because it's been 727 * removed), we only allow object deletion if we're the 728 * master for it. 729 */ 730 731 nod = (nisdb_obj_del_t *)am(myself, sizeof (*nod)); 732 if (nod == 0) { 733 nis_destroy_object(o); 734 return (DB_MEMORY_LIMIT); 735 } 736 737 nod->objType = o->zo_data.zo_type; 738 nis_destroy_object(o); 739 740 nod->objName = sdup(myself, T, objName); 741 if (nod->objName == 0) { 742 sfree(nod); 743 return (DB_MEMORY_LIMIT); 744 } 745 746 /* Check for a dup */ 747 for (tmp = tsd->objDelList; tmp != 0; 748 tmp = (nisdb_obj_del_t *)tmp->next) { 749 if (strcmp(nod->objName, tmp->objName) == 0) { 750 sfree(nod->objName); 751 sfree(nod); 752 return (DB_SUCCESS); 753 } 754 } 755 756 /* Insert at start of list */ 757 nod->next = tsd->objDelList; 758 tsd->objDelList = nod; 759 760 return (DB_SUCCESS); 761 } 762 763 /* 764 * Touch (i.e., update the expiration time for) the specified object. 765 */ 766 db_status 767 dbTouchObj(char *objName) { 768 char *ent, *table; 769 db *dbase; 770 db_table_desc *tbl = 0; 771 db_mindex *mindex; 772 nis_attr attr; 773 db_query *query; 774 db_status stat; 775 const char *myself = "dbTouchObj"; 776 777 table = internalTableName(objName); 778 if (table == 0) 779 return (DB_BADQUERY); 780 781 if (strcmp(ROOTDIRFILE, table) == 0) { 782 sfree(table); 783 784 if (touchRootDir() == 0) 785 return (DB_SUCCESS); 786 else 787 return (DB_INTERNAL_ERROR); 788 } 789 790 sfree(table); 791 table = 0; 792 ent = entryName(myself, objName, &table); 793 if (ent == 0 || table == 0) { 794 sfree(ent); 795 return (DB_MEMORY_LIMIT); 796 } 797 798 dbase = InUseDictionary->find_table(table, &tbl, TRUE); 799 if (dbase != 0) 800 mindex = dbase->mindex(); 801 if (dbase == 0 || tbl == 0 || mindex == 0) { 802 sfree(ent); 803 sfree(table); 804 return (DB_BADTABLE); 805 } 806 807 attr.zattr_ndx = (char *)"name"; 808 attr.zattr_val.zattr_val_val = ent; 809 attr.zattr_val.zattr_val_len = slen(ent) + 1; 810 811 query = InUseDictionary->translate_to_query(tbl, 1, &attr); 812 if (query == 0) { 813 sfree(ent); 814 sfree(table); 815 return (DB_BADQUERY); 816 } 817 818 mindex->touchEntry(query); 819 820 sfree(ent); 821 sfree(table); 822 delete query; 823 824 return (DB_SUCCESS); 825 } 826 827 /* 828 * Create a NIS_TABLE_OBJ. 829 * Borrows heavily from rpc.nisd/nis_db.c:__create_table(). 830 */ 831 db_status 832 dbCreateTable(char *intName, nis_object *obj) { 833 table_col tc[NIS_MAXCOLUMNS+1]; 834 table_obj tobj, *t; 835 int i; 836 const char *myself = "dbCreateTable"; 837 838 if (intName == 0 || obj == 0) 839 return (DB_BADTABLE); 840 841 t = &(obj->TA_data); 842 843 /* Make sure there are searchable columns */ 844 for (i = 0; i < t->ta_cols.ta_cols_len; i++) { 845 if (t->ta_cols.ta_cols_val[i].tc_flags & TA_SEARCHABLE) 846 break; 847 } 848 if (i >= t->ta_cols.ta_cols_len) { 849 logmsg(MSG_NOTIMECHECK, LOG_INFO, 850 "%s: No searchable columns in \"%s\" (\"%s\")", 851 myself, NIL(obj->zo_name), NIL(intName)); 852 return (DB_BADTABLE); 853 } 854 855 tobj = *t; 856 /* Shift columns one step right */ 857 for (i = 0; i < tobj.ta_cols.ta_cols_len; i++) { 858 tc[i+1] = tobj.ta_cols.ta_cols_val[i]; 859 } 860 tc[0].tc_name = 0; 861 tc[0].tc_flags = TA_XDR | TA_BINARY; 862 tc[0].tc_rights = 0; 863 tobj.ta_cols.ta_cols_len += 1; 864 tobj.ta_cols.ta_cols_val = tc; 865 866 return (db_create_table(intName, &tobj)); 867 } 868 869 #define TABLE_COL(o, n) o->TA_data.ta_cols.ta_cols_val[n] 870 871 /* 872 * Refresh (if necessary, create), the specified object in the local DB. 873 */ 874 db_status 875 dbRefreshObj(char *name, nis_object *o) { 876 char *objName; 877 __nis_buffer_t b = {0, 0}; 878 nis_object *curObj; 879 db_status stat; 880 char *ent, *table, *objTable; 881 int rstat, isDir = 0, isTable = 0; 882 const char *myself = "refreshObj"; 883 884 if (o == 0) 885 /* Delete it */ 886 return (dbDeleteObj(name)); 887 888 /* We don't work on entry objects */ 889 if (o->zo_data.zo_type == NIS_ENTRY_OBJ) 890 return (DB_BADOBJECT); 891 892 if (name != 0) 893 objName = name; 894 else { 895 bp2buf(myself, &b, "%s.%s", NIL(o->zo_name), NIL(o->zo_domain)); 896 objName = b.buf; 897 } 898 899 curObj = dbFindObject(objName, &stat); 900 if (curObj == 0 && stat != DB_NOTFOUND) { 901 sfree(b.buf); 902 return (stat); 903 } 904 905 /* 906 * If the object doesn't change, just touch it to update the 907 * expiration time. 908 */ 909 if (curObj != 0) { 910 if (sameNisPlusObj(o, curObj)) { 911 sfree(b.buf); 912 nis_destroy_object(curObj); 913 return (dbTouchObj(objName)); 914 } 915 916 /* Otherwise, check that the name and type is the same */ 917 if (o->zo_data.zo_type != curObj->zo_data.zo_type || 918 o->zo_name == 0 || curObj->zo_name == 0 || 919 o->zo_domain == 0 || curObj->zo_domain == 0 || 920 strcmp(o->zo_name, curObj->zo_name) != 0 || 921 strcmp(o->zo_domain, curObj->zo_domain) != 0) { 922 sfree(b.buf); 923 nis_destroy_object(curObj); 924 return (DB_BADOBJECT); 925 } 926 927 /* 928 * If the object is a table, we can't allow the scheme 929 * to change. 930 */ 931 if (o->zo_data.zo_type == NIS_TABLE_OBJ) { 932 int i; 933 934 if (o->TA_data.ta_maxcol != 935 curObj->TA_data.ta_maxcol) { 936 sfree(b.buf); 937 nis_destroy_object(curObj); 938 return (DB_BADOBJECT); 939 } 940 941 for (i = 0; i < o->TA_data.ta_maxcol; i++) { 942 if ((TABLE_COL(o, i).tc_flags & 943 TA_SEARCHABLE) != 944 (TABLE_COL(curObj, i).tc_flags & 945 TA_SEARCHABLE)) { 946 sfree(b.buf); 947 nis_destroy_object(curObj); 948 return (DB_BADOBJECT); 949 } 950 } 951 } 952 } else { 953 /* 954 * If we're creating a directory object, make a note 955 * so that we can add it to the serving list and create 956 * the disk file. Similarly, if creating a table, we 957 * also need to create the disk file. 958 */ 959 if (o->zo_data.zo_type == NIS_DIRECTORY_OBJ) 960 isDir = 1; 961 else if (o->zo_data.zo_type == NIS_TABLE_OBJ) 962 isTable = 1; 963 } 964 965 objTable = internalTableName(objName); 966 if (objTable == 0) { 967 sfree(b.buf); 968 if (curObj != 0) 969 nis_destroy_object(curObj); 970 return (DB_BADQUERY); 971 } 972 973 if (strcmp(ROOTDIRFILE, objTable) == 0) { 974 sfree(objTable); 975 976 rstat = update_root_object((nis_name)ROOTOBJFILE, o); 977 if (rstat == 1) 978 stat = DB_SUCCESS; 979 else 980 stat = DB_INTERNAL_ERROR; 981 } else { 982 nis_attr attr; 983 entry_object *e, eo; 984 entry_col ec[2]; 985 db *dbase; 986 db_table_desc *tbl = 0; 987 db_mindex *mindex; 988 db_result *dbres; 989 int lstat; 990 991 /* Find parent */ 992 ent = entryName(myself, objName, &table); 993 if (ent == 0 || table == 0) { 994 sfree(b.buf); 995 sfree(objTable); 996 sfree(ent); 997 if (curObj != 0) 998 nis_destroy_object(curObj); 999 return (DB_MEMORY_LIMIT); 1000 } 1001 1002 /* 1003 * Calling vanilla find_table() here (which might go to 1004 * LDAP and recurse back to ourselves) so that it should 1005 * work to create a hierarchy of directories. 1006 */ 1007 dbase = InUseDictionary->find_table(table, &tbl, TRUE); 1008 if (dbase != 0) 1009 mindex = dbase->mindex(); 1010 if (dbase == 0 || tbl == 0 || mindex == 0) { 1011 sfree(b.buf); 1012 sfree(objTable); 1013 sfree(ent); 1014 sfree(table); 1015 if (curObj != 0) 1016 nis_destroy_object(curObj); 1017 return (DB_BADTABLE); 1018 } 1019 1020 /* Construct suitable nis_attr and entry_object */ 1021 attr.zattr_ndx = (char *)"name"; 1022 attr.zattr_val.zattr_val_val = ent; 1023 attr.zattr_val.zattr_val_len = slen(ent) + 1; 1024 1025 ec[1].ec_flags = 0; 1026 ec[1].ec_value.ec_value_val = ent; 1027 ec[1].ec_value.ec_value_len = attr.zattr_val.zattr_val_len; 1028 1029 eo.en_type = (char *)"IN_DIRECTORY"; 1030 eo.en_cols.en_cols_val = ec; 1031 eo.en_cols.en_cols_len = 2; 1032 1033 e = makePseudoEntryObj(o, &eo, 0); 1034 if (e == 0) { 1035 sfree(objTable); 1036 sfree(table); 1037 sfree(ent); 1038 if (curObj != 0) 1039 nis_destroy_object(curObj); 1040 return (DB_INTERNAL_ERROR); 1041 } 1042 1043 /* Only want to update the local DB */ 1044 1045 WRITELOCKNR(mindex, lstat, "mindex w dbRefreshObj"); 1046 if (lstat != 0) { 1047 sfree(objTable); 1048 sfree(table); 1049 sfree(ent); 1050 if (curObj != 0) 1051 nis_destroy_object(curObj); 1052 return (DB_LOCK_ERROR); 1053 } 1054 mindex->setNoWriteThrough(); 1055 mindex->setNoLDAPquery(); 1056 1057 dbres = db_add_entry_x(table, 1, &attr, e, 0, 0); 1058 1059 mindex->clearNoLDAPquery(); 1060 mindex->clearNoWriteThrough(); 1061 WRITEUNLOCKNR(mindex, lstat, "mindex wu dbRefreshObj"); 1062 if (lstat != 0) { 1063 sfree(objTable); 1064 sfree(table); 1065 sfree(ent); 1066 if (curObj != 0) 1067 nis_destroy_object(curObj); 1068 db_free_result(dbres); 1069 return (DB_LOCK_ERROR); 1070 } 1071 1072 sfree(ent); 1073 sfree(table); 1074 1075 if (dbres == 0) 1076 stat = DB_MEMORY_LIMIT; 1077 else 1078 stat = dbres->status; 1079 1080 db_free_result(dbres); 1081 1082 /* 1083 * If successful so far, add the transaction. 1084 */ 1085 if (stat == DB_SUCCESS) { 1086 int xid, st; 1087 db_status ds; 1088 nis_object *dirObj; 1089 1090 /* Find the directory where this is added */ 1091 dirObj = dbFindObject(o->zo_domain, &ds); 1092 if (dirObj == 0) { 1093 sfree(objTable); 1094 if (curObj != 0) 1095 nis_destroy_object(curObj); 1096 return (ds); 1097 } 1098 1099 xid = beginTransaction(); 1100 if (xid == 0) { 1101 sfree(objTable); 1102 if (curObj != 0) 1103 nis_destroy_object(curObj); 1104 nis_destroy_object(dirObj); 1105 return (DB_INTERNAL_ERROR); 1106 } 1107 1108 st = addUpdate((curObj == 0) ? ADD_NAME : MOD_NAME_NEW, 1109 objName, 0, 0, o, curObj, 0); 1110 if (st != 0) { 1111 (void) abort_transaction(xid); 1112 sfree(objTable); 1113 if (curObj != 0) 1114 nis_destroy_object(curObj); 1115 nis_destroy_object(dirObj); 1116 return (DB_INTERNAL_ERROR); 1117 } 1118 1119 st = endTransaction(xid, dirObj); 1120 if (st != 0) 1121 stat = DB_INTERNAL_ERROR; 1122 1123 if (curObj != 0) 1124 nis_destroy_object(curObj); 1125 nis_destroy_object(dirObj); 1126 } 1127 1128 /* 1129 * If it's a table or directory, create the DB file. 1130 * If a directory, also add it to the serving list. 1131 */ 1132 if (stat == DB_SUCCESS &&(isDir || isTable)) { 1133 if (isDir) { 1134 stat = db_create_table(objTable, 1135 &tbl_prototype); 1136 } else { 1137 stat = dbCreateTable(objTable, o); 1138 } 1139 } 1140 sfree(objTable); 1141 } 1142 1143 sfree(b.buf); 1144 1145 return (stat); 1146 } 1147 1148 /* 1149 * Replace the object stored with the mapping 't'. Return TRUE if 1150 * at least one object was replaced, FALSE otherwise. 1151 */ 1152 bool_t 1153 replaceMappingObj(__nis_table_mapping_t *t, nis_object *n) { 1154 __nis_table_mapping_t *x; 1155 nis_object *old = 0; 1156 int assigned = 0; 1157 1158 /* 1159 * The alternate mappings are usually mostly copies 1160 * of the original, so we try to make sure that we 1161 * don't free the same nis_object twice. 1162 */ 1163 for (x = t; x != 0; x = (__nis_table_mapping_t *)x->next) { 1164 if (old == 0) { 1165 old = x->obj; 1166 if (x->obj != 0) 1167 nis_destroy_object(x->obj); 1168 } else { 1169 if (x->obj != old && x->obj != 0) 1170 nis_destroy_object(x->obj); 1171 } 1172 x->obj = n; 1173 assigned++; 1174 } 1175 1176 return (assigned > 0); 1177 } 1178 1179 /* 1180 * Set object type, column info, and obj for the specified 1181 * mapping 't' from the object 'o'. Returns zero if 'o' was unused, 1182 * and should be freed by the caller, larger than zero otherwise. 1183 */ 1184 int 1185 setMappingObjTypeEtc(__nis_table_mapping_t *t, nis_object *o) { 1186 __nis_table_mapping_t *x; 1187 int ls, ret; 1188 int i; 1189 1190 if (t == 0 || o == 0) 1191 return (0); 1192 1193 t->objType = o->zo_data.zo_type; 1194 for (x = t; x != 0; x = (__nis_table_mapping_t *)x->next) { 1195 if (x != t) { 1196 x->objType = t->objType; 1197 } 1198 if (x->objType == NIS_TABLE_OBJ) { 1199 /* 1200 * If we have rules, this mapping is for table entries, 1201 * and we need the column names. Otherwise, remove the 1202 * column names (if any). 1203 */ 1204 1205 for (i = 0; i < x->numColumns; i++) 1206 sfree(x->column[i]); 1207 sfree(x->column); 1208 x->column = 0; 1209 x->numColumns = 0; 1210 } 1211 } 1212 ret = replaceMappingObj(t, o); 1213 1214 return (ret); 1215 } 1216 1217 /* 1218 * Retrieve the specified object (internal DB name) from LDAP, and 1219 * refresh/create as appropriate. 1220 */ 1221 db_status 1222 dbCreateFromLDAP(char *intName, int *ldapStat) { 1223 __nis_table_mapping_t *t; 1224 int lstat, doDestroy; 1225 nis_object *obj = 0; 1226 db_status dstat; 1227 const char *myself = "dbCreateFromLDAP"; 1228 1229 if (!useLDAPrespository) { 1230 if (ldapStat != 0) 1231 *ldapStat = LDAP_SUCCESS; 1232 return (DB_SUCCESS); 1233 } 1234 1235 t = (__nis_table_mapping_t *)__nis_find_item_mt(intName, 1236 &ldapMappingList, 1237 0, 0); 1238 1239 /* No mapping isn't a failure */ 1240 if (t == 0) { 1241 if (ldapStat != 0) 1242 *ldapStat = LDAP_SUCCESS; 1243 return (DB_NOTFOUND); 1244 } 1245 1246 lstat = objFromLDAP(t, &obj, 0, 0); 1247 if (ldapStat != 0) 1248 *ldapStat = lstat; 1249 if (lstat != LDAP_SUCCESS) 1250 return (DB_NOTFOUND); 1251 1252 /* 1253 * If the LDAP operation was successful, but 'obj' is NULL, 1254 * there's no mapping for this object, and we're done. 1255 */ 1256 if (obj == 0) 1257 return (DB_SUCCESS); 1258 1259 /* Update the mapping with object info */ 1260 doDestroy = setMappingObjTypeEtc(t, obj) == 0; 1261 1262 dstat = dbRefreshObj(t->objName, obj); 1263 1264 if (doDestroy) 1265 nis_destroy_object(obj); 1266 1267 return (dstat); 1268 } 1269 1270 /* 1271 * Up- (fromLDAP==0) or down- (fromLDAP==1) load all LDAP mapped data. 1272 * Returns an LDAP error status. 1273 */ 1274 int 1275 loadAllLDAP(int fromLDAP, void *cookie, db_status *dstatP) { 1276 __nis_table_mapping_t *t, *start; 1277 int stat = LDAP_SUCCESS; 1278 db_status dstat = DB_SUCCESS; 1279 db *dbase; 1280 db_table_desc *tbl = 0; 1281 db_mindex *mindex; 1282 const char *myself = "loadAllLDAP"; 1283 1284 /* 1285 * If the 'cookie' and '*cookie' are non-NULL, start scanning 1286 * the mappings from '*cookie'. When we return with an error, 1287 * we set '*cookie' to point to the mapping being processed. 1288 * This enables our caller to react appropriately, and retry 1289 * if desired. 1290 * 1291 * The cookie is opaque to our caller, who's only allowed to 1292 * initialize *cookie to NULL. 1293 */ 1294 if (cookie != 0) { 1295 start = *((__nis_table_mapping_t **)cookie); 1296 if (start == 0) 1297 start = ldapMappingSeq; 1298 } else { 1299 start = ldapMappingSeq; 1300 } 1301 1302 for (t = start; t != 0; t = (__nis_table_mapping_t *)t->seqNext) { 1303 __nis_table_mapping_t **tp; 1304 int nm; 1305 1306 if (fromLDAP) { 1307 /* Are there any mappings for the object proper ? */ 1308 tp = selectTableMapping(t, 0, 0, 1, t->dbId, &nm); 1309 if (tp != 0 && nm > 0) { 1310 dstat = dbCreateFromLDAP(t->objPath, &stat); 1311 if (dstat != DB_SUCCESS) { 1312 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1313 "%s: DB error %d creating \"%s\": %s", 1314 myself, dstat, NIL(t->objName), 1315 ldap_err2string(stat)); 1316 if (cookie != 0) 1317 *((__nis_table_mapping_t **) 1318 cookie) = t; 1319 if (dstatP != 0) 1320 *dstatP = dstat; 1321 else if (stat == LDAP_SUCCESS) 1322 stat = LDAP_OPERATIONS_ERROR; 1323 sfree(tp); 1324 return (stat); 1325 } 1326 } 1327 sfree(tp); 1328 1329 /* Any mappings for table entries ? */ 1330 tp = selectTableMapping(t, 0, 0, 0, t->dbId, &nm); 1331 if (tp == 0 || nm <= 0) { 1332 sfree(tp); 1333 continue; 1334 } 1335 sfree(tp); 1336 1337 /* 1338 * The object itself must exist in the local 1339 * DB by now. Get the db_mindex and let 1340 * db_mindex::queryLDAP() do the work; if 1341 * the object isn't a table, queryLDAP() 1342 * will do nothing and return success. 1343 */ 1344 dbase = InUseDictionary->find_table(t->objPath, 1345 &tbl, TRUE); 1346 if (dbase != 0) 1347 mindex = dbase->mindex(); 1348 if (dbase == 0 || tbl == 0 || mindex == 0) { 1349 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1350 "%s: No local DB entry for \"%s\" (%s:%s)", 1351 myself, NIL(t->objPath), 1352 NIL(t->dbId), NIL(t->objName)); 1353 if (cookie != 0) 1354 *((__nis_table_mapping_t **)cookie) = 1355 t; 1356 if (dstatP != 0) 1357 *dstatP = DB_BADTABLE; 1358 return ((dstatP != 0) ? 1359 LDAP_SUCCESS : LDAP_OPERATIONS_ERROR); 1360 } 1361 mindex->setInitialLoad(); 1362 stat = mindex->queryLDAP(0, t->dbId, 0); 1363 mindex->clearInitialLoad(); 1364 if (stat != LDAP_SUCCESS) { 1365 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1366 "%s: LDAP error retrieving entries for %s:%s: %s", 1367 myself, NIL(t->dbId), NIL(t->objName), 1368 ldap_err2string(stat)); 1369 if (cookie != 0) 1370 *((__nis_table_mapping_t **)cookie) = 1371 t; 1372 if (dstatP != 0) 1373 *dstatP = DB_SUCCESS; 1374 return (stat); 1375 } 1376 } else { 1377 nis_object *obj; 1378 char *ent, *objPath; 1379 int freeObjPath = 0; 1380 1381 /* 1382 * Up-loading to LDAP, so the object must 1383 * already exist in the local DB. 1384 */ 1385 obj = dbFindObject(t->objName, &dstat); 1386 if (obj == 0) { 1387 if (dstat == DB_NOTFOUND) 1388 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1389 "%s: No local DB object for \"%s\" (%s:%s); skipping up-load", 1390 myself, NIL(t->objPath), 1391 NIL(t->dbId), 1392 NIL(t->objName)); 1393 else 1394 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1395 "%s: DB error %d for \"%s\" (%s:%s); skipping up-load", 1396 myself, dstat, 1397 NIL(t->objPath), 1398 NIL(t->dbId), 1399 NIL(t->objName)); 1400 continue; 1401 } 1402 1403 /* 1404 * If it's a table or directory, there will be 1405 * a dictionary entry for the object itself. 1406 * Otherwise, we need the dictionary entry for 1407 * the parent directory. 1408 * 1409 * For a table, we need the db_mindex for both the 1410 * table object itself, as well as for the parent 1411 * directory (in order to store table entries). 1412 * We start with the latter. 1413 */ 1414 if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) { 1415 objPath = t->objPath; 1416 ent = 0; 1417 } else { 1418 objPath = 0; 1419 ent = entryName(myself, t->objName, 1420 &objPath); 1421 if (ent == 0 || objPath == 0) { 1422 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1423 "%s: Error deriving entry/DB-table names for %s:%s; skipping up-load", 1424 myself, NIL(t->dbId), 1425 NIL(t->objName)); 1426 sfree(ent); 1427 sfree(objPath); 1428 nis_destroy_object(obj); 1429 obj = 0; 1430 continue; 1431 } 1432 freeObjPath = 1; 1433 } 1434 1435 dbase = InUseDictionary->find_table(objPath, 1436 &tbl, TRUE); 1437 if (dbase != 0) 1438 mindex = dbase->mindex(); 1439 if (dbase == 0 || tbl == 0 || mindex == 0) { 1440 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1441 "%s: No local DB entry for \"%s\" (%s:%s); skipping up-load", 1442 myself, objPath, 1443 NIL(t->dbId), NIL(t->objName)); 1444 sfree(ent); 1445 if (freeObjPath) 1446 sfree(objPath); 1447 nis_destroy_object(obj); 1448 obj = 0; 1449 continue; 1450 } 1451 1452 /* 1453 * Our next action(s) depend on the object type: 1454 * 1455 * directory Store dir object 1456 * 1457 * table Store table obj, as well 1458 * as any entries in the 1459 * table 1460 * 1461 * other Store object; we need to 1462 * build a db_query specifying 1463 * the first-level name of the 1464 * object. 1465 * 1466 * storeLDAP() will just do nothing and return 1467 * success if we try to, say, store a table object 1468 * when only the table entries are mapped. Hence, 1469 * we don't have to worry about those distinctions 1470 * here. 1471 */ 1472 if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) { 1473 stat = mindex->storeLDAP(0, 0, obj, 0, t->dbId); 1474 } else { 1475 nis_attr attr; 1476 db_query *q; 1477 1478 attr.zattr_ndx = (char *)"name"; 1479 attr.zattr_val.zattr_val_val = ent; 1480 attr.zattr_val.zattr_val_len = slen(ent) + 1; 1481 1482 q = new db_query(mindex->getScheme(), 1, &attr); 1483 if (q == 0) { 1484 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1485 "%s: error creating db_query for \"%s\" in \"%s\"; skipping up-load", 1486 myself, ent, objPath); 1487 sfree(ent); 1488 if (freeObjPath) 1489 sfree(objPath); 1490 nis_destroy_object(obj); 1491 obj = 0; 1492 continue; 1493 } 1494 1495 stat = mindex->storeLDAP(q, 0, obj, 0, t->dbId); 1496 1497 delete q; 1498 1499 } 1500 1501 sfree(ent); 1502 if (freeObjPath) 1503 sfree(objPath); 1504 1505 if (stat != LDAP_SUCCESS) { 1506 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1507 "%s: Error storing %s:%s to LDAP: %s", 1508 myself, NIL(t->dbId), NIL(t->objName), 1509 ldap_err2string(stat)); 1510 nis_destroy_object(obj); 1511 obj = 0; 1512 if (cookie != 0) 1513 *((__nis_table_mapping_t **) 1514 cookie) = t; 1515 if (dstatP != 0) 1516 *dstatP = DB_SUCCESS; 1517 return (stat); 1518 } 1519 1520 /* Any mappings for table entries ? */ 1521 tp = selectTableMapping(t, 0, 0, 0, t->dbId, &nm); 1522 if (tp == 0 || nm <= 0) { 1523 sfree(tp); 1524 nis_destroy_object(obj); 1525 obj = 0; 1526 continue; 1527 } 1528 sfree(tp); 1529 1530 /* 1531 * If it's a table, we also need to store the table 1532 * entries. 1533 */ 1534 if (obj->zo_data.zo_type == NIS_TABLE_OBJ) { 1535 tbl = 0; 1536 dbase = InUseDictionary->find_table(t->objPath, 1537 &tbl, TRUE); 1538 if (dbase != 0) 1539 mindex = dbase->mindex(); 1540 if (dbase == 0 || tbl == 0 || mindex == 0) { 1541 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1542 "%s: No local DB entry for \"%s\" (%s:%s); skipping entry up-load", 1543 myself, NIL(t->objPath), 1544 NIL(t->dbId), NIL(t->objName)); 1545 nis_destroy_object(obj); 1546 obj = 0; 1547 continue; 1548 } 1549 1550 stat = mindex->storeLDAP(0, 0, obj, 0, t->dbId); 1551 1552 if (stat != LDAP_SUCCESS) { 1553 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1554 "%s: Error storing %s:%s entries to LDAP: %s", 1555 myself, NIL(t->dbId), 1556 NIL(t->objName), 1557 ldap_err2string(stat)); 1558 nis_destroy_object(obj); 1559 obj = 0; 1560 if (cookie != 0) 1561 *((__nis_table_mapping_t **) 1562 cookie) = t; 1563 if (dstatP != 0) 1564 *dstatP = DB_SUCCESS; 1565 return (stat); 1566 } 1567 } 1568 nis_destroy_object(obj); 1569 obj = 0; 1570 } 1571 } 1572 1573 if (dstatP != 0) 1574 *dstatP = dstat; 1575 return (stat); 1576 } 1577 1578 /* 1579 * Object identified by given attribute name is added to specified table. 1580 * If object already exists, it is replaced. If more than one object 1581 * matches the given attribute name, DB_NOTUNIQUE is returned. 1582 */ 1583 static 1584 db_result * 1585 db_add_entry_x(char * tab, int numattrs, nis_attr * attrname, 1586 entry_obj * newobj, int skiplog, int nosync) 1587 { 1588 db_result * safety = empty_result(DB_SUCCESS); 1589 db_table_desc * tbl = NULL; 1590 db * dbase = InUseDictionary->find_table(tab, &tbl, FALSE); 1591 1592 if (tbl == NULL || dbase == NULL) { 1593 return (set_result(safety, DB_BADTABLE)); 1594 } else if (skiplog) { 1595 db_result * res; 1596 res = dbase->execute(DB_ADD_NOLOG, NULL, 1597 (entry_object *) newobj, NULL); 1598 if (safety) delete safety; 1599 return (res); 1600 } else { 1601 db_result *res; 1602 db_query * 1603 query = InUseDictionary->translate_to_query(tbl, 1604 numattrs, attrname); 1605 if (query == NULL) 1606 return (set_result(safety, DB_BADQUERY)); 1607 if (nosync) 1608 res = dbase->execute(DB_ADD_NOSYNC, 1609 query, (entry_object *) newobj, NULL); 1610 else 1611 res = dbase->execute(DB_ADD, query, 1612 (entry_object *) newobj, NULL); 1613 delete query; 1614 if (safety) delete safety; 1615 return (res); 1616 } 1617 } 1618 1619 db_result * 1620 db_add_entry(char * tab, int numattrs, nis_attr * attrname, 1621 entry_obj * newobj) 1622 { 1623 return (db_add_entry_x(tab, numattrs, attrname, newobj, 0, 0)); 1624 } 1625 1626 db_result * 1627 __db_add_entry_nolog(char * tab, int numattrs, nis_attr * attrname, 1628 entry_obj * newobj) 1629 { 1630 return (db_add_entry_x(tab, numattrs, attrname, newobj, 1, 0)); 1631 } 1632 1633 db_result * 1634 __db_add_entry_nosync(char * tab, int numattrs, nis_attr * attrname, 1635 entry_obj * newobj) 1636 { 1637 return (db_add_entry_x(tab, numattrs, attrname, newobj, 0, 1)); 1638 } 1639 1640 /* 1641 * Remove object identified by given attributes from specified table. 1642 * If no attribute is supplied, all entries in table are removed. 1643 * If attributes identify more than one object, all objects are removed. 1644 */ 1645 1646 db_result * 1647 db_remove_entry_x(char * table_name, int num_attrs, nis_attr * attrname, 1648 int nosync) 1649 { 1650 db_result * safety = empty_result(DB_SUCCESS); 1651 db_table_desc * tbl = NULL; 1652 db * dbase = InUseDictionary->find_table(table_name, &tbl, FALSE); 1653 db_result * res; 1654 1655 if (tbl == NULL || dbase == NULL) 1656 return (set_result(safety, DB_BADTABLE)); 1657 else { 1658 if (num_attrs != 0) { 1659 db_query *query; 1660 query = InUseDictionary->translate_to_query(tbl, 1661 num_attrs, attrname); 1662 if (query == NULL) 1663 return (set_result(safety, 1664 DB_BADQUERY)); 1665 if (nosync) 1666 res = dbase->execute(DB_REMOVE_NOSYNC, 1667 query, NULL, NULL); 1668 else 1669 res = dbase->execute(DB_REMOVE, query, 1670 NULL, NULL); 1671 delete query; 1672 } else { 1673 if (nosync) 1674 res = dbase->execute(DB_REMOVE_NOSYNC, 1675 NULL, NULL, NULL); 1676 else 1677 res = dbase->execute(DB_REMOVE, 1678 NULL, NULL, NULL); 1679 } 1680 if (safety) delete safety; 1681 return (res); 1682 } 1683 } 1684 1685 db_result * 1686 db_remove_entry(char * table_name, int num_attrs, nis_attr * attrname) 1687 { 1688 return (db_remove_entry_x(table_name, num_attrs, attrname, 0)); 1689 } 1690 1691 db_result * 1692 __db_remove_entry_nosync(char * table_name, int num_attrs, nis_attr * attrname) 1693 { 1694 return (db_remove_entry_x(table_name, num_attrs, attrname, 1)); 1695 } 1696 1697 /* Return a copy of the version of specified table. */ 1698 vers * 1699 db_version(char * table_name) 1700 { 1701 db * dbase = InUseDictionary->find_table(table_name); 1702 1703 if (dbase == NULL) 1704 return (NULL); 1705 vers* v = new vers(dbase->get_version()); 1706 if (v == NULL) 1707 WARNING("nis_db::db_version: cannot allocate space"); 1708 return (v); 1709 } 1710 1711 /* Return log entries since (later than) given version 'v' of table. */ 1712 db_log_list * 1713 db_log_entries_since(char * table_name, vers * v) 1714 { 1715 db * dbase = InUseDictionary->find_table(table_name); 1716 1717 if (dbase == NULL) 1718 return (NULL); 1719 return (dbase->get_log_entries_since(v)); 1720 } 1721 1722 db_status 1723 db_sync_log(char *table_name) { 1724 1725 db * dbase = InUseDictionary->find_table(table_name); 1726 1727 if (dbase == NULL) 1728 return (DB_BADTABLE); 1729 return (dbase->sync_log()); 1730 } 1731 1732 /* 1733 * Apply the given update specified in 'entry' to the specified table. 1734 * Returns DB_SUCCESS if update was executed. 1735 * Returns DB_NOTFOUND if update occurs too early to be applied. 1736 */ 1737 db_status 1738 db_apply_log_entry(char * table_name, db_log_entry * entry) 1739 { 1740 db * dbase = InUseDictionary->find_table(table_name, NULL, FALSE); 1741 1742 if (dbase == NULL) 1743 return (DB_BADTABLE); 1744 if (dbase->execute_log_entry(entry)) 1745 return (DB_SUCCESS); /* got executed */ 1746 else 1747 return (DB_NOTFOUND); /* not executed */ 1748 } 1749 1750 /* 1751 * Checkpoint specified table (i.e. incorporate logged updates to main 1752 * database file). If table_name is NULL, checkpoint all tables that 1753 * needs it. 1754 */ 1755 db_status 1756 db_checkpoint(char * table_name) 1757 { 1758 return (InUseDictionary->db_checkpoint(table_name)); 1759 } 1760 1761 /* Print names of tables in system. */ 1762 void 1763 db_print_table_names() 1764 { 1765 int i; 1766 db_table_names * answer = InUseDictionary->get_table_names(); 1767 1768 if (answer != NULL) { 1769 for (i = 0; i < answer->db_table_names_len; i++) { 1770 printf("%s\n", answer->db_table_names_val[i]); 1771 delete answer->db_table_names_val[i]; 1772 } 1773 delete answer->db_table_names_val; 1774 delete answer; 1775 } 1776 } 1777 1778 /* Print statistics of specified table to stdout. */ 1779 db_status 1780 db_stats(char * table_name) 1781 { 1782 db_table_desc * tbl = NULL; 1783 db *dbase = InUseDictionary->find_table(table_name, &tbl); 1784 1785 if (tbl == NULL || dbase == NULL || tbl->scheme == NULL) 1786 return (DB_BADTABLE); 1787 1788 dbase->print(); 1789 tbl->scheme->print(); 1790 return (DB_SUCCESS); 1791 } 1792 1793 1794 /* Print statistics of indices of specified table to stdout. */ 1795 db_status 1796 db_print_all_indices(char * table_name) 1797 { 1798 db * dbase = InUseDictionary->find_table(table_name); 1799 1800 if (dbase == NULL) 1801 return (DB_BADTABLE); 1802 dbase->print_all_indices(); 1803 return (DB_SUCCESS); 1804 } 1805 1806 /* Print specified index of table to stdout. */ 1807 db_status 1808 db_print_index(char * table_name, int which) 1809 { 1810 db * dbase = InUseDictionary->find_table(table_name); 1811 1812 if (dbase == NULL) 1813 return (DB_BADTABLE); 1814 dbase->print_index(which); 1815 return (DB_SUCCESS); 1816 } 1817 1818 /* close open files */ 1819 db_status 1820 db_standby(char * table_name) 1821 { 1822 return (InUseDictionary->db_standby(table_name)); 1823 } 1824 1825 /* Returns DB_SUCCESS if table exists; DB_BADTABLE if table does not exist. */ 1826 db_status 1827 db_table_exists(char * table_name) 1828 { 1829 db_table_desc *dbtab = InUseDictionary->find_table_desc(table_name); 1830 1831 if (dbtab == NULL) 1832 return (DB_BADTABLE); 1833 return (DB_SUCCESS); 1834 } 1835 1836 /* 1837 * Returns DB_SUCCESS if table exists; DB_BADTABLE if table does not exist. 1838 * If table already loaded, unload it. 1839 */ 1840 db_status 1841 db_unload_table(char * table_name) 1842 { 1843 db_table_desc * 1844 dbtab = InUseDictionary->find_table_desc(table_name); 1845 if (dbtab == NULL) 1846 return (DB_BADTABLE); 1847 // unload 1848 if (dbtab->database != NULL) { 1849 delete dbtab->database; 1850 dbtab->database = NULL; 1851 } 1852 return (DB_SUCCESS); 1853 } 1854 1855 /* 1856 * Put the specified table in deferred mode, which means that updates go 1857 * to the original table, but reads are satisfied out of a copy (which we 1858 * make here). Thus, "defer" refers to the table as seen by read requests, 1859 * since for them, changes are deferred. 1860 */ 1861 db_status 1862 __db_defer(char *table_name) { 1863 db_status stat; 1864 1865 stat = InUseDictionary->defer(table_name); 1866 return (stat); 1867 } 1868 1869 /* 1870 * Commit deferred changes for the specified table. I.e., make visible 1871 * any updates made since the table was deferred. 1872 */ 1873 db_status 1874 __db_commit(char *table_name) { 1875 db_status stat; 1876 1877 stat = InUseDictionary->commit(table_name); 1878 return (stat); 1879 } 1880 1881 /* 1882 * Rollback, i.e., return to the state before we entered deferred mode. 1883 */ 1884 db_status 1885 __db_rollback(char *table_name) { 1886 db_status stat; 1887 1888 stat = InUseDictionary->rollback(table_name); 1889 return (stat); 1890 } 1891 1892 db_status 1893 __db_configure(char *table_name) { 1894 db_status stat; 1895 char tablePath[MAXPATHLEN + NIS_MAXNAMELEN + 1]; 1896 db *dbase = InUseDictionary->find_table(table_name, NULL); 1897 1898 if (dbase == NULL || table_name == 0) 1899 return (DB_BADTABLE); 1900 1901 if (strlen(table_name) >= sizeof (tablePath)) 1902 return (DB_BADQUERY); 1903 1904 if (internal_table_name(table_name, tablePath) == 0) 1905 return (DB_STORAGE_LIMIT); 1906 1907 if (dbase->configure(tablePath)) 1908 stat = DB_SUCCESS; 1909 else 1910 stat = DB_INTERNAL_ERROR; 1911 1912 return (stat); 1913 } 1914 1915 /* 1916 * During some rpc.nisd operations (such as when recovering the trans.log), 1917 * we don't want to use the LDAP repository, so we provide a main switch. 1918 * Note that we expect this to be used only when rpc.nisd is single-threaded, 1919 * so there is no need for synchronization when reading or modifying the 1920 * value of the main switch. 1921 */ 1922 int useLDAPrespository = 1; 1923 1924 void 1925 __db_disallowLDAP(void) { 1926 useLDAPrespository = 0; 1927 } 1928 1929 void 1930 __db_allowLDAP(void) { 1931 useLDAPrespository = 1; 1932 } 1933 1934 } /* extern "C" */