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