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