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
db_free_result(db_result * dr)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*
empty_result(db_status s)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*
set_result(db_result * res,db_status s)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 *
tableDB(char * tableName)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
db_in_dict_file(char * name)147 db_in_dict_file(char *name)
148 {
149 return (InUseDictionary->find_table_desc(name) != NULL);
150
151 }
152
153 const char
db_perror(db_status dbstat)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
db_extract_dict_entries(char * newdict,char ** fs,int fscnt)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
db_copy_file(char * infile,char * outfile)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
db_begin_merge_dict(char * newdict,char * tok,char * repl)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
db_end_merge_dict()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
db_abort_merge_dict()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
db_initialize(char * filename)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
db_massage_dict(char * newdictname,char * tok,char * repl)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
db_create_table(char * table_name,table_obj * table_desc)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
db_destroy_table(char * table_name)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 *
db_first_entry(char * table_name,int numattrs,nis_attr * attrname)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 *
db_next_entry(char * table_name,db_next_desc * next_desc)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 *
db_reset_next_entry(char * table_name,db_next_desc * next_desc)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 *
__db_list_entries(char * table_name,int numattrs,nis_attr * attrname,bool_t useDeferred)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 *
db_list_entries(char * table_name,int numattrs,nis_attr * attrname)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 *
entryName(const char * msg,char * objName,char ** tableP)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 *
dbFindObject(char * objName,db_status * statP)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 *
ldapFindObj(__nis_table_mapping_t * t,char * objName,int * statP)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 *
findObj(char * name,db_status * statP,int * lstatP)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
dbDeleteObj(char * objName)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
dbTouchObj(char * objName)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
dbCreateTable(char * intName,nis_object * obj)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
dbRefreshObj(char * name,nis_object * o)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
replaceMappingObj(__nis_table_mapping_t * t,nis_object * n)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
setMappingObjTypeEtc(__nis_table_mapping_t * t,nis_object * o)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
dbCreateFromLDAP(char * intName,int * ldapStat)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
loadAllLDAP(int fromLDAP,void * cookie,db_status * dstatP)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 *
db_add_entry_x(char * tab,int numattrs,nis_attr * attrname,entry_obj * newobj,int skiplog,int nosync)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 *
db_add_entry(char * tab,int numattrs,nis_attr * attrname,entry_obj * newobj)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 *
__db_add_entry_nolog(char * tab,int numattrs,nis_attr * attrname,entry_obj * newobj)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 *
__db_add_entry_nosync(char * tab,int numattrs,nis_attr * attrname,entry_obj * newobj)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 *
db_remove_entry_x(char * table_name,int num_attrs,nis_attr * attrname,int nosync)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 *
db_remove_entry(char * table_name,int num_attrs,nis_attr * attrname)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 *
__db_remove_entry_nosync(char * table_name,int num_attrs,nis_attr * attrname)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 *
db_version(char * table_name)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 *
db_log_entries_since(char * table_name,vers * v)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
db_sync_log(char * table_name)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
db_apply_log_entry(char * table_name,db_log_entry * entry)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
db_checkpoint(char * table_name)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
db_print_table_names()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
db_stats(char * table_name)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
db_print_all_indices(char * table_name)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
db_print_index(char * table_name,int which)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
db_standby(char * table_name)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
db_table_exists(char * table_name)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
db_unload_table(char * table_name)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
__db_defer(char * table_name)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
__db_commit(char * table_name)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
__db_rollback(char * table_name)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
__db_configure(char * table_name)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
__db_disallowLDAP(void)1926 __db_disallowLDAP(void) {
1927 useLDAPrespository = 0;
1928 }
1929
1930 void
__db_allowLDAP(void)1931 __db_allowLDAP(void) {
1932 useLDAPrespository = 1;
1933 }
1934
1935 } /* extern "C" */
1936