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