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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
31 #include <sys/param.h>
32 #include <sys/types.h>
34 #include "db_headers.h"
36 #include "db_mindex.h"
37 #include "db_pickle.h"
39 #include "nisdb_ldap.h"
40 #include "ldap_nisdbquery.h"
42 #include "ldap_ruleval.h"
43 #include "ldap_scheme.h"
44 #include "ldap_parse.h"
45 #include "nis_hashitem.h"
47 #include "ldap_glob.h"
49 /* Pass through configuration information to the table */
51 db_mindex::configure(char *tablePath
) {
52 if (tablePath
== NULL
)
57 objPath
.ptr
= strdup(tablePath
);
60 return (table
->configure(tablePath
));
62 /* Defer table config until we have a table instance */
63 return (objPath
.ptr
!= NULL
);
68 * The noWriteThrough flag is used to prevent modifies/updates to LDAP
69 * while we're incorporating log data into the in-memory tables.
72 db_mindex::setNoWriteThrough(void) {
73 ASSERTWHELD(this->mindex
);
74 noWriteThrough
.flag
++;
78 db_mindex::clearNoWriteThrough(void) {
79 ASSERTWHELD(this->mindex
);
80 if (noWriteThrough
.flag
> 0)
81 noWriteThrough
.flag
--;
82 #ifdef NISDB_LDAP_DEBUG
85 #endif /* NISDB_LDAP_DEBUG */
89 * The noLDAPquery flag is used to prevent recursive LDAP queries when
90 * satisfy_query() is re-entered as we add an entry from queryLDAP().
93 db_mindex::setNoLDAPquery(void) {
94 ASSERTWHELD(this->mindex
);
99 db_mindex::clearNoLDAPquery(void) {
100 ASSERTWHELD(this->mindex
);
101 if (noLDAPquery
.flag
> 0)
103 #ifdef NISDB_LDAP_DEBUG
106 #endif /* NISDB_LDAP_DEBUG */
110 * The initialLoad flag tells us if an add or remove is done as part of
111 * the initial load of data, in which case we should use the initial TTLs.
114 db_mindex::setInitialLoad(void) {
115 ASSERTWHELD(this->mindex
);
120 db_mindex::clearInitialLoad(void) {
121 ASSERTWHELD(this->mindex
);
122 if (initialLoad
.flag
> 0)
124 #ifdef NISDB_LDAP_DEBUG
127 #endif /* NISDB_LDAP_DEBUG */
131 db_mindex::setDbPtr(void *ptr
) {
136 db_mindex::getDbPtr(void) {
141 db_mindex::getTable(void) {
145 static void setOid(nis_object
*obj
);
147 extern void db_free_result(db_result
*);
150 updateMappingObj(__nis_table_mapping_t
*t
, char **objNameP
,
152 zotypes type
= NIS_BOGUS_OBJ
;
154 const char *myself
= "updateMappingObj";
157 objName
= t
->objName
;
158 else if (objNameP
!= 0)
161 return (NIS_BOGUS_OBJ
);
165 int lstat
= LDAP_SUCCESS
;
166 nis_object
*o
= dbFindObject(objName
, &stat
);
168 /* If not found in the local DB, try LDAP */
170 if (stat
!= DB_NOTFOUND
) {
171 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
172 "%s: DB err %d for \"%s\"",
173 myself
, stat
, NIL(objName
));
175 o
= ldapFindObj(t
, objName
, &lstat
);
176 /* If found, refresh/create the local copy */
179 rstat
= dbRefreshObj(objName
, o
);
180 if (rstat
!= DB_SUCCESS
)
181 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
182 "%s: DB error %d refreshing \"%s\"",
183 myself
, rstat
, NIL(objName
));
188 type
= o
->zo_data
.zo_type
;
190 *objNameP
= sdup(myself
, T
, objName
);
191 if (*objNameP
== 0) {
192 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
193 "%s: Unable to copy object name (\"%s\")",
194 myself
, NIL(objName
));
198 if (!setMappingObjTypeEtc(t
, o
))
199 nis_destroy_object(o
);
202 nis_destroy_object(o
);
204 } else if (lstat
!= LDAP_SUCCESS
) {
205 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
206 "%s: LDAP err %d for \"%s\"",
207 myself
, lstat
, NIL(objName
));
214 static __nis_table_mapping_t
*
215 mappingFromObj(nis_object
*obj
, int *statP
) {
216 __nis_table_mapping_t
*t
;
217 __nis_buffer_t b
= {0, 0};
219 const char *myself
= "mappingFromObj";
221 if (obj
== 0 || obj
->zo_data
.zo_type
== NIS_ENTRY_OBJ
)
225 * Convert full object name to the db table path used as
226 * key for the mapping hash list.
228 bp2buf(myself
, &b
, "%s.%s",
229 NIL(obj
->zo_name
), NIL(obj
->zo_domain
));
230 objPath
= internalTableName(b
.buf
);
232 if (slen(objPath
) <= 0) {
234 *statP
= LDAP_OPERATIONS_ERROR
;
239 t
= (__nis_table_mapping_t
*)__nis_find_item_mt(objPath
,
240 &ldapMappingList
, 0, 0);
247 static __nis_table_mapping_t
*
248 selectMapping(db_table
*table
, nis_object
*obj
, db_query
*qin
,
249 bool_t wantWrite
, bool_t
*asObjP
, int *statP
) {
250 __nis_table_mapping_t
*t
;
251 __nis_buffer_t b
= {0, 0};
252 bool_t doLDAP
, asObj
;
253 int stat
= LDAP_SUCCESS
;
254 char *objPath
= 0, buf
[MAXPATHLEN
+NIS_MAXNAMELEN
+1];
255 const char *myself
= "db_mindex::selectMapping";
258 * If 'table' is NULL, we try to find a mapping for 'obj'.
259 * We expect this to happen when our caller wants to write
260 * the object from a directory entry to LDAP.
266 *statP
= LDAP_SUCCESS
;
268 t
= mappingFromObj(obj
, statP
);
274 * Should the object type in the mapping be NIS_BOGUS_OBJ,
275 * we need to determine what kind of object this is.
277 if (t
->objType
== NIS_BOGUS_OBJ
) {
278 t
->objType
= updateMappingObj(t
, 0, 0);
279 if (t
->objType
== NIS_BOGUS_OBJ
) {
281 *statP
= LDAP_OPERATIONS_ERROR
;
287 * If caller wants a mapping suitable for writing,
288 * check that we're the master for this object.
295 * If the object type for the mapping is NIS_BOGUS_OBJ, then
296 * we haven't yet been able to determine what kind of object this
297 * is. Try to fix that now.
299 if (table
->mapping
.objType
== NIS_BOGUS_OBJ
) {
300 table
->mapping
.objType
= updateMappingObj(table
->mapping
.tm
,
301 &table
->mapping
.objName
,
302 &table
->mapping
.isMaster
);
303 table
->mapping
.expireType
= table
->mapping
.objType
;
307 * Depending on the object type (table->mapping.objType):
309 * table Use table->mapping.tm to query LDAP
310 * for entries per 'qin'.
312 * directory Use 'qin' and table->mapping.objName
313 * to retrieve a mapping entry, and then
314 * query LDAP for the corresponding object.
315 * 'qin' == NULL means reading/writing the
316 * entire directory object, plus the names
317 * of the directory entries.
319 * bogus Not mapping this object. However, we may
320 * still be mapping the object 'obj'.
322 * other Shouldn't happen; illegal.
324 switch (table
->mapping
.objType
) {
326 t
= table
->mapping
.tm
;
328 doLDAP
= table
->mapping
.isMaster
&&
329 table
->mapping
.toLDAP
;
331 doLDAP
= table
->mapping
.fromLDAP
;
334 case NIS_DIRECTORY_OBJ
: {
344 * We expect the query to have one component, containing
345 * the directory entry name. If there's no query, we want
346 * an enumeration of the entries in the directory. They're
347 * stored with the XDR:ed directory object in LDAP, so
348 * asObj should be TRUE.
351 t
= table
->mapping
.tm
;
353 doLDAP
= table
->mapping
.isMaster
&&
354 table
->mapping
.toLDAP
;
356 doLDAP
= table
->mapping
.fromLDAP
;
362 if (nqc
!= 1 || (qc
= qin
->queryloc()) == 0 ||
363 qc
[0].index_value
== 0) {
364 stat
= LDAP_PARAM_ERROR
;
367 qc
[0].index_value
->get_value(&sub
, &len
);
368 if (sub
== 0 || len
<= 0) {
369 stat
= LDAP_PARAM_ERROR
;
373 /* Append directory name to dir entry name */
374 sbc2buf(myself
, sub
, len
, &b
);
375 bp2buf(myself
, &b
, ".%s", table
->mapping
.objName
);
377 /* Convert to the DB internal name */
378 objPath
= internal_table_name(b
.buf
, buf
);
380 if (slen(objPath
) <= 0) {
381 stat
= LDAP_OPERATIONS_ERROR
;
385 /* Look for the corresponding table mapping */
386 t
= (__nis_table_mapping_t
*)__nis_find_item_mt(
387 objPath
, &ldapMappingList
, 0, 0);
392 /* Update object mapping information */
393 if (t
->objType
== NIS_BOGUS_OBJ
)
394 (void) updateMappingObj(t
, 0, 0);
397 * Should check the objectDN's in 't', but leave that to
398 * underlying functions.
401 doLDAP
= t
->isMaster
;
408 t
= mappingFromObj(obj
, statP
);
432 * Replace or remove the table entry identified by 'e'. 'tableName' is
433 * the name of the table (which could be a directory) in which the entry
434 * resides. 'obj' is an un-XDR:ed copy of the object in 'e', optionally
435 * supplied to save re-doing unpacking of the entry object. 'tobj' is
436 * a pointer to the table object; needed for table entries, but not
437 * for directory entries.
439 * 'ttime' contains the current time, to be supplied for the trans log
442 * Returns LDAP_SUCCESS when entry successfully added/modified/deleted,
443 * LDAP_COMPARE_TRUE if an entry to be added/modified was the same as
444 * an already existing one, and a suitable error otherwise.
447 db_mindex::updateTableEntry(entry_object
*e
, int replace
, char *tableName
,
448 nis_object
*obj
, nis_object
*tobj
, uint32_t ttime
,
450 int stat
, freeObj
= 0;
451 db_index_entry
*dbie
;
456 nis_object
*oldObj
= 0;
457 const char *myself
= "db_mindex::updateTableEntry";
459 if (table
== 0 || e
== 0)
460 return (LDAP_PARAM_ERROR
);
462 qi
= extract_index_values_from_object(e
);
464 logmsg(MSG_NOMEM
, LOG_ERR
,
465 "%s: Out of memory for query index",
467 return (LDAP_NO_MEMORY
);
470 dbie
= satisfy_query(qi
, &count
, &valid
, FALSE
);
471 if (dbie
!= 0 && (count
!= 1 || !valid
)) {
472 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
473 "%s: count=%d, valid=%s",
474 myself
, count
, valid
? "TRUE" : "FALSE");
476 return (LDAP_OPERATIONS_ERROR
);
480 * Need a copy of the old object in order to log a removal
481 * (this is true even if we're modifying an existing entry).
484 oldObj
= unmakePseudoEntryObj(
485 table
->get_entry(dbie
->getlocation()), tobj
);
487 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
488 "%s: Error getting object from old pseudo-entry for \"%s\" in \"%s\"",
489 myself
, NIL(obj
->zo_name
),
492 return (LDAP_OPERATIONS_ERROR
);
497 /* Need the object from the entry */
498 if (dbie
!= 0 && obj
== 0) {
499 obj
= unmakePseudoEntryObj(e
, tobj
);
501 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
502 "%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
503 myself
, NIL(obj
->zo_name
),
506 nis_destroy_object(oldObj
);
507 return (LDAP_OPERATIONS_ERROR
);
512 /* Is the new object a dup of the old ? */
513 if (dbie
!= 0 && sameNisPlusObj(oldObj
, obj
)) {
514 /* Yes, it's a dup, so just update the timestamp */
515 table
->touchEntry(dbie
->getlocation());
518 nis_destroy_object(obj
);
519 nis_destroy_object(oldObj
);
520 return (LDAP_COMPARE_TRUE
);
523 * Not a dup, so go ahead and add it. Provided
524 * that 'qi' isn't NULL (which we've already
525 * checked), DB_ADD(_NOSYNC) does the right
526 * thing even if matching entries already
529 dbres
= ((db
*)dbptr
.ptr
)->log_action(DB_ADD_NOSYNC
,
532 stat
= LDAP_OPERATIONS_ERROR
;
533 else if (dbres
->status
== DB_SUCCESS
)
536 stat
= LDAP_OPERATIONS_ERROR
;
537 db_free_result(dbres
);
539 } else { /* Removing */
540 /* If the object doesn't exist, we're done */
543 return (LDAP_SUCCESS
);
546 dbres
= ((db
*)dbptr
.ptr
)->log_action(DB_REMOVE_NOSYNC
, qi
, 0);
548 stat
= LDAP_OPERATIONS_ERROR
;
549 else if (dbres
->status
== DB_SUCCESS
)
552 stat
= LDAP_OPERATIONS_ERROR
;
553 db_free_result(dbres
);
556 /* Log the operation */
557 if (stat
== LDAP_SUCCESS
) {
559 nis_attr
*attr
, attrbuf
[NIS_MAXCOLUMNS
];
561 /* If we haven't begun the transaction yet, do so now */
563 *xid
= beginTransaction();
565 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
566 "%s: Error starting transaction for \"%s\"",
567 myself
, NIL(tableName
));
570 nis_destroy_object(oldObj
);
571 return (LDAP_OPERATIONS_ERROR
);
575 if (replace
&& obj
== 0) {
576 obj
= unmakePseudoEntryObj(e
, tobj
);
578 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
579 "%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
580 myself
, NIL(obj
->zo_name
),
584 nis_destroy_object(oldObj
);
585 return (LDAP_OPERATIONS_ERROR
);
591 * The log stores nis_attr information, so we need to
592 * convert the scheme-query to a nis_attr array.
594 attr
= schemeQuery2nisAttr(qi
, attrbuf
, scheme
,
595 table
->mapping
.tm
, &numAttrs
);
597 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
598 "%s: Error converting index query to nis_attr for \"%s\" in \"%s\"",
599 myself
, NIL(obj
->zo_name
), NIL(tableName
));
601 nis_destroy_object(obj
);
603 nis_destroy_object(oldObj
);
605 return (LDAP_OPERATIONS_ERROR
);
610 * While the DB can handle a modify (replace)
611 * operation, the trans log stores this as a
612 * remove followed by an add (which allows
613 * backing out the change by removing the new
614 * object incarnation, and adding the old one).
617 ret
= addUpdate(REM_IBASE
, tableName
,
618 numAttrs
, attr
, oldObj
, 0, ttime
);
622 ret
= addUpdate(ADD_IBASE
, tableName
,
623 numAttrs
, attr
, obj
, 0, ttime
);
624 } else { /* Removal */
625 ret
= addUpdate(REM_IBASE
, tableName
, numAttrs
, attr
,
629 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
630 "%s: Error adding trans log entry for \"%s\" in \"%s\"",
631 myself
, NIL(obj
->zo_name
), NIL(tableName
));
632 stat
= LDAP_OPERATIONS_ERROR
;
639 nis_destroy_object(oldObj
);
641 nis_destroy_object(obj
);
647 db_mindex::touchEntry(entry_object
*e
) {
651 if (table
== 0 || e
== 0)
654 qi
= extract_index_values_from_object(e
);
658 ret
= touchEntry(qi
);
666 db_mindex::touchEntry(db_query
*q
) {
667 db_index_entry
*dbie
;
671 dbie
= satisfy_query(q
, &count
, &valid
, FALSE
);
672 if (dbie
!= 0 && count
== 1 && valid
)
673 table
->touchEntry(dbie
->getlocation());
681 * Compose an object name from column zero of 'e' and 't->objName',
682 * and return the mapping for that object, if any. Also set '*name'
683 * to point to the dir entry name in 'e'. Note that this is a pointer
684 * to existing data, and shouldn't be freed other than as part of
687 static __nis_table_mapping_t
*
688 findDirEntryMapping(__nis_table_mapping_t
*t
, entry_object
*e
, char **name
) {
689 __nis_table_mapping_t
*x
;
691 const char *myself
= "findDirEntryMapping";
692 __nis_buffer_t b
= {0, 0};
694 if (e
== 0 || e
->en_cols
.en_cols_len
!= 2 ||
695 e
->en_cols
.en_cols_val
== 0)
698 entryName
= e
->en_cols
.en_cols_val
[1].ec_value
.ec_value_val
;
702 if (t
== 0 || entryName
== 0 || t
->objName
== 0)
705 bp2buf(myself
, &b
, "%s.%s", entryName
, t
->objName
);
706 if (b
.len
== 0 || b
.buf
== 0)
709 x
= (__nis_table_mapping_t
*)__nis_find_item_mt(b
.buf
,
710 &ldapMappingList
, 0, 0);
718 * Query LDAP per the supplied (scheme-) query 'qin'. If 'doAsynch' is
719 * set, and the query is an enumeration (qin == 0), the query will be
720 * performed in a detached thread, and complete asynchronously. In this
721 * case, the return status reflects the setup and launch of the
722 * detached thread; the query will complete asynchronously.
724 * Returns an appropriate LDAP status code.
727 db_mindex::queryLDAP(db_query
*qin
, char *dbId
, int doAsynch
) {
728 __nis_table_mapping_t
*t
;
729 int i
, na
, nq
= 0, stat
, stat2
, numAttrs
, ret
;
732 bool_t asObj
, doEnum
;
738 const char *myself
= "db_mindex::queryLDAP";
740 if (!useLDAPrespository
|| table
== 0)
741 return (LDAP_SUCCESS
);
744 * Instances from the deferred dictionary shouldn't change,
745 * there's no point in querying LDAP.
747 if (table
->mapping
.isDeferredTable
)
748 return (LDAP_SUCCESS
);
750 t
= selectMapping(table
, 0, qin
, FALSE
, &asObj
, &stat
);
755 #ifdef NISDB_LDAP_DEBUG
756 printf("%s: %s (%s)\n",
757 myself
, NIL(t
->objName
), (asObj
? "object" : "entry"));
758 #endif /* NISDB_LDAP_DEBUG */
761 q
= schemeQuery2Query(qin
, scheme
);
763 return (LDAP_PARAM_ERROR
);
764 #ifdef NISDB_LDAP_DEBUG
766 #endif /* NISDB_LDAP_DEBUG */
769 #ifdef NISDB_LDAP_DEBUG
770 printf("\tenumerating %s%s%s\n",
771 dbId
? dbId
: "", dbId
? ":" : "", NIL(t
->objName
));
772 #endif /* NISDB_LDAP_DEBUG */
776 * Do we have any active mappings for this particular query and
777 * dbId ? If not, we're done.
779 * Note that we don't care about the return value from
780 * selectTableMapping(), just wheter or not there are
781 * any valid mappings.
784 sfree(selectTableMapping(t
, q
, 0, asObj
, dbId
, &i
));
787 return (LDAP_SUCCESS
);
790 /* Is the object a directory ? */
797 stat
= objFromLDAP(t
, &o
, &ea
, &nea
);
800 if (stat
== LDAP_NO_SUCH_OBJECT
) {
801 /* Positive failure; remove the object */
802 dstat
= dbDeleteObj(t
->objName
);
803 if (dstat
== DB_SUCCESS
|| dstat
== DB_NOTFOUND
) {
806 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
807 "%s: DB error %d deleting \"%s\"",
808 myself
, dstat
, NIL(t
->objName
));
809 stat
= LDAP_OPERATIONS_ERROR
;
815 } else if (stat
!= LDAP_SUCCESS
) {
819 /* OK; this entry just isn't mapped */
821 return (LDAP_SUCCESS
);
826 * We're updating one particular entry (described
827 * by 't') in the directory 'table->mapping.tm'.
831 dstat
= dbRefreshObj(t
->objName
, o
);
832 if (dstat
== DB_SUCCESS
) {
835 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
836 "%s: DB error %d updating \"%s\" in \"%s\"",
837 myself
, NIL(t
->objName
),
838 NIL(table
->mapping
.tm
->objName
));
839 stat
= LDAP_OPERATIONS_ERROR
;
842 freeEntryObjArray(ea
, numEa
);
844 nis_destroy_object(o
);
852 * q == 0, so we're enumerating. Update the list of
857 * Need to disable write-through to LDAP, for which we need
858 * a lock on our db_mindex ('this'); we're also updating the
859 * table, so we need a write lock on that as well.
861 WRITELOCKNR(this, stat
, "w db_mindex::queryLDAP");
863 WRITELOCKNR(table
, stat2
,
864 "table w db_mindex::queryLDAP");
866 if (stat
!= 0 || stat2
!= 0) {
867 nis_destroy_object(dirObj
);
868 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
869 "%s: lock error %d", myself
,
870 stat
!= 0 ? stat
: stat2
);
871 return (LDAP_OPERATIONS_ERROR
);
876 table
->setEnumMode(0);
878 for (i
= 0, na
= 0; i
< numEa
; i
++) {
880 __nis_table_mapping_t
*x
;
888 * We've got a list of dir entries. In the general,
889 * case, some are new, and some already exist.
890 * We definitely want to add the new ones, and to
891 * that end, we need a copy of the object for the
892 * entry. By definition, if an entry is new, we
893 * don't yet have a copy of the object for it, so
894 * it's LDAP or nothing.
896 * If the entry already exists, try to update the
897 * entry object. In this case, we again only need
898 * to look in LDAP for the object; if there already
899 * is one in the DB, it's in the dir entry which we
902 * So, whether adding or replacing, try to get the
905 * If we can't get a copy of the object, there's not
906 * much point in adding or updating (since a dir
907 * entry just consists of the entry object and name),
908 * so we continue to the next entry.
910 * However, in that case, we do need to touch the
911 * dir entry; otherwise, it will be removed later
915 x
= findDirEntryMapping(t
, ea
[i
], &name
);
917 if (x
== 0 || (st
= objFromLDAP(x
, &o
, 0, 0)) !=
920 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
921 "%s: Unable to obtain object for \"%s\" in \"%s\": %s",
922 myself
, NIL(name
), NIL(t
->objName
),
923 ldap_err2string(st
));
925 nis_destroy_object(o
);
926 if (!touchEntry(ea
[i
])) {
927 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
928 "%s: Inconsistency: LDAP-derived directory \"%s\" "
929 "contains entry \"%s\", which is unknown locally, "
930 "and has no LDAP mapping",
931 myself
, NIL(t
->objName
),
937 if (ea
[i
]->en_cols
.en_cols_len
!= 2 ||
938 ea
[i
]->en_cols
.en_cols_val
== 0 ||
939 ea
[i
]->en_cols
.en_cols_val
[0].
940 ec_value
.ec_value_val
!= 0 ||
941 ea
[i
]->en_cols
.en_cols_val
[0].
942 ec_value
.ec_value_len
!= 0) {
943 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
944 "%s: Illegal entry_obj col 0 for \"%s\" in \"%s\"",
945 myself
, NIL(name
), NIL(t
->objName
));
946 nis_destroy_object(o
);
952 e
= makePseudoEntryObj(o
, ea
[i
], 0);
954 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
955 "%s: Unable to create pseudo entry object for \"%s\" in \"%s\"",
956 myself
, NIL(name
), NIL(t
->objName
));
957 nis_destroy_object(o
);
962 st
= updateTableEntry(e
, 1, t
->objName
, o
, 0,
963 o
->zo_oid
.mtime
, &xid
);
964 if (st
== LDAP_SUCCESS
) {
966 } else if (st
== LDAP_COMPARE_TRUE
) {
967 /* OK, same as existing entry */
970 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
971 "%s: Error updating directory entry for \"%s\" in \"%s\": %s",
972 myself
, NIL(name
), NIL(t
->objName
),
973 ldap_err2string(st
));
974 if (stat
== LDAP_SUCCESS
)
978 /* Free the XDR buffer */
979 sfree(e
->en_cols
.en_cols_val
[0].
980 ec_value
.ec_value_val
);
982 ea
[i
]->en_cols
.en_cols_val
[0].
983 ec_value
.ec_value_val
= 0;
984 ea
[i
]->en_cols
.en_cols_val
[0].
985 ec_value
.ec_value_len
= 0;
986 nis_destroy_object(o
);
989 freeEntryObjArray(ea
, numEa
);
991 /* Get list of entries to remove */
992 ea
= table
->endEnumMode(&numEa
);
994 uint32_t nowt
= time(0);
996 for (i
= 0; i
< numEa
; i
++) {
1002 st
= updateTableEntry(ea
[i
], 0, t
->objName
, 0,
1004 if (st
== LDAP_SUCCESS
) {
1007 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
1008 "%s: Error removing directory entry for \"%s\": %s",
1009 myself
, NIL(t
->objName
),
1010 ldap_err2string(st
));
1011 if (stat
== LDAP_SUCCESS
)
1017 if (stat
== LDAP_SUCCESS
) {
1019 (void) gettimeofday(&now
, 0);
1020 table
->mapping
.enumExpire
= now
.tv_sec
+
1025 (void) ((db
*)dbptr
.ptr
)->sync_log();
1027 if (xid
!= 0 && na
> 0 && stat
== LDAP_SUCCESS
) {
1028 ret
= endTransaction(xid
, dirObj
);
1030 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
1031 "%s: Error ending transaction for \"%s\"",
1032 myself
, NIL(t
->objName
));
1033 stat
= LDAP_OPERATIONS_ERROR
;
1035 } else if (xid
!= 0) {
1036 ret
= abort_transaction(xid
);
1038 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
1039 "%s: Error aborting transaction for \"%s\"",
1040 myself
, NIL(t
->objName
));
1043 nis_destroy_object(dirObj
);
1048 clearNoWriteThrough();
1050 WRITEUNLOCK2(table
, this,
1052 "table wu db_mindex::queryLDAP",
1053 "wu db_mindex::queryLDAP");
1059 * In order to ping replicas, if any, we need to find the
1060 * directory containing the table to be updated. If we
1061 * can't find the directory object, we're sunk, so let's
1065 dirObj
= findObj(t
->obj
->zo_domain
, &dstat
, &stat
);
1067 if (stat
== LDAP_SUCCESS
)
1068 stat
= LDAP_OPERATIONS_ERROR
;
1075 stat
= entriesFromLDAP(t
, qin
, q
, dbId
, dirObj
, doAsynch
);
1080 extern db
*tableDB(char *);
1083 * Remove the LDAP entry/entries corresponding to 'qin'/'obj'.
1086 db_mindex::removeLDAP(db_query
*qin
, nis_object
*obj
) {
1087 __nis_table_mapping_t
*t
;
1092 if (!useLDAPrespository
|| table
== 0)
1093 return (LDAP_SUCCESS
);
1095 /* Instances from the deferred dictionary should not update LDAP */
1096 if (table
->mapping
.isDeferredTable
)
1097 return (LDAP_SUCCESS
);
1099 t
= selectMapping(table
, 0, qin
, TRUE
, &asObj
, &stat
);
1100 if (t
== 0 && stat
!= LDAP_SUCCESS
)
1103 #ifdef NISDB_LDAP_DEBUG
1105 printf("removeLDAP: %s\n", NIL(t
->objName
));
1106 #endif /* NISDB_LDAP_DEBUG */
1111 * selectMapping() gave us the mapping for the
1112 * directory entry. However, if 't' is NULL, this
1113 * could be due to the directory itself not being
1114 * mapped, in which case we must obtain the mapping
1118 t
= selectMapping(0, obj
, 0, TRUE
, &asObj
,
1120 if (t
== 0 && stat
!= LDAP_SUCCESS
)
1125 stat
= deleteLDAPobj(t
);
1127 * If we were successful, update the object
1128 * stored with the mapping.
1130 if (stat
== LDAP_SUCCESS
)
1131 (void) replaceMappingObj(t
, 0);
1137 * Since it's a directory entry we've removed, we also
1138 * need to update the directory object itself.
1140 stat
= storeLDAP(0, 0, 0, 0, 0);
1142 q
= schemeQuery2Query(qin
, scheme
);
1144 return (LDAP_PARAM_ERROR
);
1145 #ifdef NISDB_LDAP_DEBUG
1147 #endif /* NISDB_LDAP_DEBUG */
1148 stat
= mapToLDAP(t
, 1, &q
, 0, 0, 0, 0);
1153 * This isn't the way to remove the LDAP entries
1154 * corresponding to the entire table.
1156 #ifdef NISDB_LDAP_DEBUG
1158 #endif /* NISDB_LDAP_DEBUG */
1159 stat
= LDAP_PARAM_ERROR
;
1166 * Helper function for storeLDAP() which handles updates for objects
1167 * other than table entries.
1170 db_mindex::storeObjLDAP(__nis_table_mapping_t
*t
, nis_object
*o
) {
1171 int stat
, assigned
= 0;
1173 int numEa
, doUnlock
= 0;
1176 const char *myself
= "db_mindex::storeObjLDAP";
1178 if (t
== 0 || o
== 0)
1179 return (LDAP_SUCCESS
);
1182 * If the object to be stored is anything other than a
1183 * directory, we can just go ahead and write it to LDAP.
1184 * A directory object, however, also needs a directory
1185 * entry list, so we should to get hold of the db_table
1186 * that goes with the directory.
1188 if (o
->zo_data
.zo_type
== NIS_DIRECTORY_OBJ
) {
1189 dbase
= tableDB(t
->objName
);
1191 dbm
= dbase
->mindex();
1192 if (dbase
== 0 || dbm
== 0 || dbm
->table
== 0) {
1193 /* By definition, no dir entries */
1202 * Read-lock the table so that 'tab'
1203 * doesn't change while we're using it.
1205 READLOCK(dbm
->table
, LDAP_OPERATIONS_ERROR
,
1206 "r table db_mindex::storeLDAP");
1209 tea
= dbm
->table
->gettab();
1210 ntea
= dbm
->table
->getsize();
1213 * There may be empty slots in the table 'tab'
1214 * array, so get rid of those.
1216 if (tea
!= 0 && ntea
> 0) {
1217 ea
= (entry_object
**)am(myself
,
1218 ntea
* sizeof (ea
[0]));
1220 READUNLOCK(dbm
->table
, LDAP_NO_MEMORY
,
1221 "ru table db_mindex::storeLDAP");
1222 return (LDAP_NO_MEMORY
);
1224 for (i
= 0, numEa
= 0; i
< ntea
; i
++) {
1231 /* No non-empty slots */
1234 READUNLOCK(dbm
->table
,
1235 LDAP_OPERATIONS_ERROR
,
1236 "ru table db_mindex::storeLDAP");
1242 READUNLOCK(dbm
->table
,
1243 LDAP_OPERATIONS_ERROR
,
1244 "ru table db_mindex::storeLDAP");
1253 stat
= objToLDAP(t
, o
, ea
, numEa
);
1258 READUNLOCK(dbm
->table
, stat
,
1259 "ru table db_mindex::storeLDAP");
1267 * Store data specified by the index-query 'qin' to LDAP. If 'obj' is
1268 * non-null, it's a pointer to the pseudo-entry object corresponding to
1269 * 'qin'. As a short-cut/convenience, the caller can instead supply
1270 * the actual nis_object 'o'; if 'o' is NULL, it's derived from 'obj'.
1272 * 'oldObj' is used for table entries if the store operation is
1273 * an update, and the corresponding NIS+ operation was a delete followed
1274 * by an add. In this case, oldObj contains the pre-delete incarnation of
1275 * the entry object to be modified.
1277 * The 'dbId' string is used to select one dbId for mapping chains
1278 * that contain more than one.
1280 * Returns an LDAP status code.
1283 db_mindex::storeLDAP(db_query
*qin
, entry_object
*obj
, nis_object
*o
,
1284 entry_obj
*oldObj
, char *dbId
) {
1285 __nis_table_mapping_t
*t
;
1287 db_query
*q
, *qo
, **qa
;
1288 __nis_rule_value_t
*rv
= 0;
1290 const char *myself
= "db_mindex::storeLDAP";
1292 if (!useLDAPrespository
|| table
== 0)
1293 return (LDAP_SUCCESS
);
1295 /* Instances from the deferred dictionary should not update LDAP */
1296 if (table
->mapping
.isDeferredTable
)
1297 return (LDAP_SUCCESS
);
1299 t
= selectMapping(table
, 0, qin
, TRUE
, &asObj
, &stat
);
1300 if (t
== 0 && stat
!= LDAP_SUCCESS
)
1303 #ifdef NISDB_LDAP_DEBUG
1305 printf("storeLDAP: %s%s%s\n",
1306 dbId
? dbId
: "", dbId
? ":" : "", NIL(t
->objName
));
1307 #endif /* NISDB_LDAP_DEBUG */
1310 * selectMapping() didn't have the object to look at, so we
1311 * must check if this is a directory entry or not.
1315 if (o
->zo_data
.zo_type
== NIS_ENTRY_OBJ
)
1317 } else if (obj
!= 0) {
1318 if (obj
->en_type
== 0 ||
1319 strcmp(obj
->en_type
, "IN_DIRECTORY") != 0)
1325 bool_t freeO
= FALSE
;
1328 * If we don't have a mapping, that's probably because
1329 * the directory (represented by 'this') isn't mapped.
1330 * Try to get a mapping from 'o' or 'obj'.
1333 if (o
== 0 && obj
!= 0) {
1334 o
= unmakePseudoEntryObj(obj
, 0);
1336 return (LDAP_OPERATIONS_ERROR
);
1340 t
= selectMapping(0, o
, 0, TRUE
, &asObj
, &stat
);
1343 nis_destroy_object(o
);
1350 * If we found a mapping for the 'table' in this db_mindex,
1356 o
= unmakePseudoEntryObj(obj
, 0);
1361 o
= dbFindObject(t
->objName
, &dstat
);
1363 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
1364 "%s: DB error %d finding \"%s\"",
1371 return (LDAP_OPERATIONS_ERROR
);
1373 stat
= storeObjLDAP(t
, o
);
1376 * Store the object with the mapping. If 'o' was
1377 * supplied by the caller, we first need to make
1381 o
= nis_clone_object(o
, 0);
1383 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
1384 "%s: Unable to refresh mapping object for \"%s\"",
1385 myself
, NIL(t
->objName
));
1388 if (!replaceMappingObj(t
, o
))
1389 nis_destroy_object(o
);
1393 * Object now either destroyed or stored in 't'.
1394 * Set pointer to NULL in order to avoid freeing
1399 if (stat
!= LDAP_SUCCESS
)
1403 if (freeO
&& o
!= 0) {
1404 nis_destroy_object(o
);
1409 * If the entry object 'obj' has the type "IN_DIRECTORY",
1410 * then it's a directory entry, and we should check if
1411 * the directory is mapped to LDAP, and update the dir
1412 * entry list accordingly.
1414 if (obj
== 0 || obj
->en_type
== 0 ||
1415 strcmp(obj
->en_type
, "IN_DIRECTORY") != 0)
1416 return (LDAP_SUCCESS
);
1418 /* Does it have a mapping ? */
1419 t
= selectMapping(table
, 0, 0, TRUE
, &asObj
, &stat
);
1423 stat
= storeObjLDAP(t
, t
->obj
);
1428 /* Store table entries. If we don't have a mapping, we're done. */
1430 return (LDAP_SUCCESS
);
1432 if (qin
!= NULL
&& obj
!= NULL
) {
1433 db_index_entry
*dbie
;
1434 int i
, size
, nq
= 0;
1437 db_query qbuf
, **qold
;
1439 rv
= (__nis_rule_value_t
*)am(myself
, sizeof (*rv
));
1440 qa
= (db_query
**)am(myself
, sizeof (qa
[0]));
1443 * Note that only qold[0] is a unique query pointer.
1444 * All the other qold[i]'s are copies of qa[i].
1445 * Hence, we only free qold[0], as well as qold
1448 qold
= (db_query
**)am(myself
, sizeof (qold
[0]));
1452 if (rv
== 0 || qa
== 0 || (oldObj
!= 0 && qold
== 0)) {
1456 return (LDAP_NO_MEMORY
);
1459 q
= schemeQuery2Query(qin
, scheme
);
1463 return (LDAP_PARAM_ERROR
);
1466 qa
[0] = pseudoEntryObj2Query(obj
, t
->obj
, &rv
[0]);
1468 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
1469 "%s: Unable to obtain query representation of new entry object for \"%s\"",
1470 myself
, NIL(t
->dbId
));
1475 return (LDAP_OPERATIONS_ERROR
);
1478 qold
[0] = pseudoEntryObj2Query(oldObj
, t
->obj
, 0);
1480 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
1481 "%s: Unable to obtain query representation of old entry object for \"%s\"",
1482 myself
, NIL(t
->dbId
));
1488 return (LDAP_OPERATIONS_ERROR
);
1495 * In order to support many-to-one NIS+ to LDAP mapping,
1496 * we need to find all possible matches in the NIS+ DB,
1497 * and then merge to produce a single update. mapToLDAP()
1498 * takes care of the merging, so our job is to collect
1499 * the matches. Worst case is that we need to search
1500 * individually for each component in 'qin', so that's
1503 * mapToLDAP() has a mode that only performs an update
1504 * for the first DN, and that's what we want. In order
1505 * to make sure that it's the correct DN, we leave the
1506 * original query as the first one passed to mapToLDAP().
1511 /* For each component of 'qin' */
1512 for (i
= 0; i
< size
; i
++) {
1513 db_query
*qc
, **qat
, **qoldt
;
1515 __nis_rule_value_t
*rvt
;
1517 qc
= queryFromComponent(qin
, i
, &qbuf
);
1521 dbie
= satisfy_query_dbonly(qc
, &count
, FALSE
, &valid
);
1522 if (dbie
== 0 || !valid
|| count
<= 0)
1525 rvt
= (__nis_rule_value_t
*)realloc(rv
,
1526 (nq
+count
) * sizeof (rv
[0]));
1527 qat
= (db_query
**)realloc(qa
,
1528 (nq
+count
) * sizeof (qa
[0]));
1530 qoldt
= (db_query
**)realloc(qold
,
1531 (nq
+count
) * sizeof (qold
[0]));
1532 if (rvt
== 0 || qat
== 0 || (qold
!= 0 && qoldt
== 0)) {
1534 freeQueries(qa
, nq
);
1536 freeQueries(qat
, nq
);
1538 freeRuleValue(rv
, nq
);
1540 freeRuleValue(rvt
, nq
);
1543 freeQueries(qold
, 1);
1545 freeQueries(qoldt
, 1);
1548 (void) memset(&qbuf
, 0, sizeof (qbuf
));
1549 logmsg(MSG_NOMEM
, LOG_ERR
,
1550 "%s: realloc(%d) failed",
1551 myself
, (nq
+count
) * sizeof (void *));
1552 return (LDAP_NO_MEMORY
);
1558 (void) memset(&rv
[nq
], 0, count
* sizeof (rv
[0]));
1559 (void) memset(&qa
[nq
], 0, count
* sizeof (qa
[0]));
1562 (void) memset(&qold
[nq
], 0,
1563 count
* sizeof (qold
[0]));
1566 for (j
= 0; j
< count
; j
++) {
1567 qa
[nq
] = pseudoEntryObj2Query(
1568 table
->get_entry(dbie
->getlocation()),
1571 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
1572 "%s: Could not create query from entry obj for \"%s\"",
1573 myself
, NIL(t
->objName
));
1574 freeQueries(qa
, nq
);
1575 freeQueries(qold
, 1);
1576 freeRuleValue(rv
, nq
);
1578 (void) memset(&qbuf
, 0, sizeof (qbuf
));
1579 return (LDAP_PARAM_ERROR
);
1584 dbie
= dbie
->getnextresult();
1590 stat
= mapToLDAP(t
, nq
, (qold
!= 0 ? qold
: qa
), qa
, rv
, 1,
1593 freeQueries(qa
, nq
);
1594 freeRuleValue(rv
, nq
);
1596 freeQueries(qold
, 1);
1597 (void) memset(&qbuf
, 0, sizeof (qbuf
));
1599 } else if (qin
== 0 && obj
== 0 && t
->objType
== NIS_TABLE_OBJ
) {
1603 READLOCK(table
, LDAP_OPERATIONS_ERROR
,
1604 "r table db_mindex::storeLDAP");
1606 tab
= table
->gettab();
1607 ntab
= table
->getsize();
1608 if (tab
== 0 || ntab
<= 0) {
1609 READUNLOCK(table
, LDAP_OPERATIONS_ERROR
,
1610 "ru table db_mindex::storeLDAP");
1611 return (LDAP_SUCCESS
);
1614 qa
= (db_query
**)am(myself
, ntab
* sizeof (qa
[0]));
1615 rv
= (__nis_rule_value_t
*)am(myself
, ntab
* sizeof (rv
[0]));
1616 if (qa
== 0 || rv
== 0) {
1619 READUNLOCK(table
, LDAP_OPERATIONS_ERROR
,
1620 "ru table db_mindex::storeLDAP");
1621 return (LDAP_NO_MEMORY
);
1624 for (i
= 0; i
< ntab
; i
++) {
1628 qa
[i
] = pseudoEntryObj2Query(tab
[i
], t
->obj
, &rv
[i
]);
1631 freeRuleValue(rv
, i
);
1632 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
1633 "%s: Could not create query from entry for \"%s\"",
1634 myself
, NIL(t
->objName
));
1635 READUNLOCK(table
, LDAP_OPERATIONS_ERROR
,
1636 "ru table db_mindex::storeLDAP");
1637 return (LDAP_OPERATIONS_ERROR
);
1641 stat
= mapToLDAP(t
, ntab
, qa
, qa
, rv
, 0, dbId
);
1643 freeQueries(qa
, ntab
);
1644 freeRuleValue(rv
, ntab
);
1646 if (stat
== LDAP_SUCCESS
) {
1650 * Since we've just successfully uploaded everthing
1651 * in this table, we now consider our local copy
1652 * up-to-date as well.
1655 (void) gettimeofday(&now
, 0);
1656 WRITELOCKNR(table
, lstat
,
1657 "w table db_mindex::storeLDAP");
1659 table
->mapping
.enumExpire
= now
.tv_sec
+
1662 WRITEUNLOCKNR(table
, lstat
,
1663 "wu table db_mindex::storeLDAP");
1666 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
1667 "%s: %sock error %d for \"%s\"%s",
1668 myself
, lck
?"L":"Unl", lstat
,
1671 "; unable to update enumeration expiration":
1676 READUNLOCK(table
, stat
,
1677 "ru table db_mindex::storeLDAP");
1683 * Sets the oid (i.e., the creation and modification times) for the
1684 * specified object. In order to avoid retrieving the old incarnation
1685 * (if any) from the DB first, we're punting and setting both mtime
1686 * and ctime to the current time.
1689 setOid(nis_object
*obj
) {
1691 obj
->zo_oid
.ctime
= obj
->zo_oid
.mtime
= time(0);