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