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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <time.h>
  28 #include <sys/time.h>
  29 #include <lber.h>
  30 #include <ldap.h>
  31 #include <signal.h>
  32 #include <pthread.h>
  33 #include "db_headers.h"
  34 #include "db.h"
  35 #include "db_mindex.h"
  36 #include "db_dictionary.h"
  37 #include "nisdb_mt.h"
  38 #include "ldap_map.h"
  39 #include "ldap_glob.h"
  40 #include "ldap_util.h"
  41 
  42 
  43 extern db_dictionary    *InUseDictionary;
  44 
  45 
  46 extern "C" {
  47 
  48 typedef struct {
  49         db_mindex               *mindex;
  50         __nis_table_mapping_t   *t;
  51         db_query                *qin;
  52         db_query                *q;
  53         char                    *dbId;
  54         nis_object              *dirObj;
  55         int                     isDeferred;
  56         char                    *tableName;
  57 } __entries_from_ldap_arg_t;
  58 
  59 static void     *entriesFromLDAPthread(void *);
  60 
  61 }
  62 
  63 int             entriesFromLDAPreal(__entries_from_ldap_arg_t *);
  64 
  65 #ifdef  SET_ENTRY_FLAGS
  66 static uint_t
  67 entryFlagsFromTable(uint_t tf) {
  68         uint_t  ef = 0;
  69 
  70         if ((tf & TA_BINARY) != 0)
  71                 ef |= EN_BINARY;
  72         if ((tf & TA_CRYPT) != 0)
  73                 ef |= EN_CRYPT;
  74         if ((tf & TA_XDR) != 0)
  75                 ef |= EN_XDR;
  76         if ((tf & TA_ASN1) != 0)
  77                 ef |= EN_ASN1;
  78 
  79         return (ef);
  80 }
  81 #endif  /* SET_ENTRY_FLAGS */
  82 
  83 static void                    setOid(nis_object *obj);
  84 
  85 /*
  86  * Retrieve container entries from LDAP per 't' and 'qin'/'q'.
  87  * This is a helper function for db_mindex::queryLDAP(); see
  88  * that function for details of the parameters (except doAsynch).
  89  *
  90  * If 'doAsynch' is set, and the retrieval is an enumeration
  91  * (qin == NULL), the retrieval is performed in a detached
  92  * thread. In this case, the return code just reflects the
  93  * setup and launch of the detached thread. Retrieval will
  94  * complete asynchronously.
  95  */
  96 int
  97 db_mindex::entriesFromLDAP(__nis_table_mapping_t *t, db_query *qin, db_query *q,
  98                         char *dbId, nis_object *dirObj, int doAsynch) {
  99         __entries_from_ldap_arg_t       *arg;
 100         int                             stat;
 101         db_status                       dstat;
 102         const char                      *myself = "db_mindex::entriesFromLDAP";
 103 
 104         arg = (__entries_from_ldap_arg_t *)am(myself, sizeof (*arg));
 105         if (arg == 0) {
 106                 freeQuery(q);
 107                 if (dirObj != 0)
 108                         nis_destroy_object(dirObj);
 109                 return (LDAP_NO_MEMORY);
 110         }
 111 
 112         arg->mindex = this;
 113         arg->t = t;
 114         arg->qin = qin;
 115         arg->q = q;
 116         arg->dbId = dbId;
 117         arg->dirObj = dirObj;
 118         arg->tableName = t->objName;
 119 
 120         /*
 121          * Check if an enumeration thread is running; if so, then regardless
 122          * of whether or not the current operation is an enumeration, we
 123          * just return success, and let our caller get the data from the
 124          * existing (deferred) DB.
 125          */
 126         (void) mutex_lock(&table->mapping.enumLock);
 127         if (table->mapping.enumTid != 0) {
 128                 int     doReturn = 0;
 129 
 130                 stat = pthread_kill(table->mapping.enumTid, 0);
 131                 if (stat == ESRCH) {
 132                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 133         "%s: Enumeration thread %d not found for \"%s\"; exit status = %d (%s)",
 134                                 myself, table->mapping.enumTid,
 135                                 NIL(t->objName), table->mapping.enumStat,
 136                                 ldap_err2string(table->mapping.enumStat));
 137                         /* Reflect the fact that no enum thread is running */
 138                         table->mapping.enumTid = 0;
 139                         table->mapping.enumStat = -1;
 140                         /* Cleanup deferred mode */
 141                         if (table->mapping.enumDeferred) {
 142                                 dstat = InUseDictionary->commit(t->objPath);
 143                                 if (dstat == DB_SUCCESS) {
 144                                         table->mapping.enumDeferred = 0;
 145                                 } else {
 146                                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
 147                                         "%s: DB error %d committing \"%s\"",
 148                                                 myself, dstat, NIL(t->objName));
 149                                 }
 150                         }
 151                 } else if (stat == 0) {
 152                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
 153                         "%s: Enumeration thread %d already running for \"%s\"",
 154                                 myself, table->mapping.enumTid,
 155                                 NIL(t->objName));
 156                         stat = LDAP_SUCCESS;
 157                         doReturn = 1;
 158                 } else {
 159                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
 160                 "%s: Error %d looking for enumeration thread %d for \"%s\"",
 161                                 myself, stat, table->mapping.enumTid,
 162                                 NIL(t->objName));
 163                         doReturn = 1;
 164                         stat = LDAP_OPERATIONS_ERROR;
 165                 }
 166                 if (doReturn) {
 167                         (void) mutex_unlock(&table->mapping.enumLock);
 168                         sfree(arg);
 169                         freeQuery(q);
 170                         if (dirObj != 0)
 171                                 nis_destroy_object(dirObj);
 172                         return (stat);
 173                 }
 174         }
 175 
 176         /*
 177          * If we're enumerating (and hence expect that retrieving all data,
 178          * and updating the local DB, might take a while), create a deferred-
 179          * update table that clients can use while we are updating the real
 180          * one.
 181          */
 182         if (doAsynch && qin == 0) {
 183                 if ((dstat = InUseDictionary->defer(t->objPath)) ==
 184                                 DB_SUCCESS) {
 185                         arg->isDeferred = 1;
 186                         table->mapping.enumDeferred = 1;
 187                 } else {
 188                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 189                         "%s: Unable to defer updates for \"%s\" (status=%d);"
 190                                         " updating in place",
 191                                 myself, NIL(t->objName), dstat);
 192                         arg->isDeferred = 0;
 193                         table->mapping.enumDeferred = 0;
 194                 }
 195         } else {
 196                 arg->isDeferred = 0;
 197                 table->mapping.enumDeferred = 0;
 198         }
 199 
 200         /* If enumerating, perform the operation in a separate thread */
 201         if (doAsynch && qin == 0) {
 202                 pthread_t       tid;
 203                 pthread_attr_t  attr;
 204 
 205                 (void) pthread_attr_init(&attr);
 206 #ifdef  FORCE_SYNCHRONOUS
 207 #else
 208                 (void) pthread_attr_setdetachstate(&attr,
 209                                                 PTHREAD_CREATE_DETACHED);
 210 #endif  /* FORCE_SYNCHRONOUS */
 211                 stat = pthread_create(&tid, &attr, entriesFromLDAPthread, arg);
 212                 if (stat != 0) {
 213                         (void) mutex_unlock(&table->mapping.enumLock);
 214                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 215                         "%s: Error %d creating new thread; using current one",
 216                                 myself, stat);
 217                         stat = (int)entriesFromLDAPthread(arg);
 218                         return (stat);
 219                 }
 220 
 221                 table->mapping.enumTid = tid;
 222                 table->mapping.enumStat = -1;
 223 
 224                 /*
 225                  * We're now returning to the caller, who will get data
 226                  * from:
 227                  *
 228                  *      The deferred DB, if an enumeration thread already
 229                  *      was running, and deferred mode was on, or
 230                  *
 231                  *      The original DB, if we just started an enumeration
 232                  *      thread. In this case, our caller (several levels up)
 233                  *      is holding a lock on the db_mindex/db_table, which
 234                  *      means that the enum thread will have to wait for
 235                  *      our caller once it's done the LDAP retrieval, and
 236                  *      wants to update the DB.
 237                  */
 238                 (void) mutex_unlock(&table->mapping.enumLock);
 239                 stat = LDAP_SUCCESS;
 240 #ifdef  FORCE_SYNCHRONOUS
 241                 {
 242                         int     tstat;
 243 
 244                         stat = pthread_join(tid, (void **)&tstat);
 245                         if (stat == 0) {
 246                                 stat = tstat;
 247                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 248                                         "%s: thread %d => %d",
 249                                         myself, tid, tstat);
 250                         } else {
 251                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 252                                         "%s: pthread_join(%d) => %d",
 253                                         myself, tid, stat);
 254                                 stat = LDAP_OPERATIONS_ERROR;
 255                         }
 256                 }
 257 #endif  /* FORCE_SYNCHRONOUS */
 258         } else {
 259                 (void) mutex_unlock(&table->mapping.enumLock);
 260                 stat = (int)entriesFromLDAPthread(arg);
 261         }
 262 
 263         return (stat);
 264 }
 265 
 266 extern "C" {
 267 
 268 /*
 269  * We use this 'extern "C"' function in order to make sure that
 270  * pthread_create() doesn't have any problems trying to invoke a
 271  * C++ function.
 272  */
 273 static void *
 274 entriesFromLDAPthread(void *voidarg) {
 275         __entries_from_ldap_arg_t       *arg;
 276         int                             stat;
 277         db                              *dbase;
 278         db_table_desc                   *tbl = 0;
 279         char                            *tableName;
 280 
 281         arg = (__entries_from_ldap_arg_t *)voidarg;
 282 
 283         /* Lock to prevent removal */
 284         (void) __nis_lock_db_table(arg->tableName, 1, 0,
 285                                         "entriesFromLDAPthread");
 286 
 287         /*
 288          * It's possible that the db_mindex for the table has changed,
 289          * or disappeared, between now and the time when our parent
 290          * thread released its lock on the table. Hence, we search the
 291          * dictionary to re-acquire the 'db', and the db_mindex.
 292          */
 293         tableName = internalTableName(arg->tableName);
 294         if (tableName != 0) {
 295 #ifdef  NISDB_LDAP_DEBUG
 296                 db_mindex       *oldMindex = arg->mindex;
 297 #endif  /* NISDB_LDAP_DEBUG */
 298 
 299                 dbase = InUseDictionary->find_table(tableName, &tbl, FALSE);
 300                 if (dbase != 0)
 301                         arg->mindex = dbase->mindex();
 302                 else
 303                         arg->mindex = 0;
 304 #ifdef  NISDB_LDAP_DEBUG
 305                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 306                         "entriesFromLDAPthread: %s -> %s -> 0x%x (0x%x)",
 307                         NIL(arg->tableName), NIL(tableName),
 308                         arg->mindex, oldMindex);
 309 #endif  /* NISDB_LDAP_DEBUG */
 310                 sfree(tableName);
 311                 tableName = 0;
 312         }
 313 
 314         stat = entriesFromLDAPreal(arg);
 315 
 316         (void) __nis_ulock_db_table(arg->tableName, 1, 0,
 317                                         "entriesFromLDAPthread");
 318 
 319         freeQuery(arg->q);
 320         if (arg->dirObj != 0)
 321                 nis_destroy_object(arg->dirObj);
 322         sfree(arg);
 323         return ((void *)stat);
 324 }
 325 
 326 }
 327 
 328 int
 329 entriesFromLDAPreal(__entries_from_ldap_arg_t *arg) {
 330         db_mindex                       *mindex;
 331         db_table                        *table;
 332         __nis_table_mapping_t           *t;
 333         db_query                        *q, *qin;
 334         char                            *dbId;
 335         nis_object                      *dirObj;
 336         int                             i, na, nau, nq = 0, xid = 0;
 337         int                             ret, stat = LDAP_SUCCESS, stat2, stat3;
 338         int                             lstat;
 339         __nis_obj_attr_t                **oa = 0;
 340         db_query                        **res;
 341         entry_object                    **ea;
 342         long                            numEa;
 343         bool_t                          doEnum;
 344         db_status                       dstat;
 345         struct timeval                  start;
 346         const char                      *myself =
 347                                         "db_mindex::entriesFromLDAPreal";
 348 
 349         if (arg == 0)
 350                 return (LDAP_PARAM_ERROR);
 351         mindex = arg->mindex;
 352         t = arg->t;
 353         q = arg->q;
 354         qin = arg->qin;
 355         dbId = arg->dbId;
 356         dirObj = arg->dirObj;
 357 
 358         table = (mindex != 0) ? mindex->getTable() : 0;
 359 
 360         if (mindex == 0 || t == 0 || table == 0) {
 361                 /* We haven't done anything, so rollback should be OK */
 362                 if (arg->isDeferred && t != 0) {
 363                         dstat = InUseDictionary->rollback(t->objPath);
 364                         if (dstat != DB_SUCCESS) {
 365                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 366                                 "%s: DB error %d rolling back \"%s\"",
 367                                         myself, dstat, NIL(t->objName));
 368                                 /*
 369                                  * Had rollback succeeded, the 'table'
 370                                  * would have disappeared. However, since
 371                                  * rollback failed, we need to update the
 372                                  * table->mapping.enum* fields.
 373                                  */
 374                                 if (table != 0) {
 375                                         (void) mutex_lock(&table->
 376                                                         mapping.enumLock);
 377                                         table->mapping.enumStat =
 378                                                         LDAP_PARAM_ERROR;
 379                                         table->mapping.enumTime = 0;
 380                                         table->mapping.enumEntries = 0;
 381                                         table->mapping.enumTid = 0;
 382                                         (void) mutex_unlock(&table->
 383                                                         mapping.enumLock);
 384                                 }
 385                         }
 386                 }
 387                 return (LDAP_PARAM_ERROR);
 388         }
 389 
 390         if (qin == 0)
 391                 logmsg(MSG_NOTIMECHECK, LOG_INFO, "%s: enumerating \"%s%s%s\"",
 392                         myself, dbId ? dbId : "", dbId ? ":" : "",
 393                         NIL(t->objName));
 394 
 395         (void) gettimeofday(&start, 0);
 396 
 397         /* Getting table entries */
 398         res = mapFromLDAP(t, q, &nq, dbId, &stat, &oa);
 399 #ifdef  NISDB_LDAP_DEBUG
 400         logmsg(MSG_ALWAYS, LOG_INFO,
 401                 "%s: mapFromLDAP() => 0x%x, status=%d %s; nq = %d",
 402                 myself, res, stat, stat == LDAP_SUCCESS ? "" :
 403                 ldap_err2string(stat), nq);
 404 #endif  /* NISDB_LDAP_DEBUG */
 405 
 406         /*
 407          * Keep track of the number of NIS+ entries we got back;
 408          * note that the number of LDAP entries may have been
 409          * smaller or larger.
 410          */
 411         (void) mutex_lock(&table->mapping.enumLock);
 412         table->mapping.enumEntries = nq;
 413         (void) mutex_unlock(&table->mapping.enumLock);
 414 
 415         /*
 416          * If we get LDAP_NO_SUCH_OBJECT, we need to delete the entries
 417          * in the table, so we can't just return.
 418          */
 419         if (res == 0 && stat != LDAP_NO_SUCH_OBJECT) {
 420                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 421                         "%s: mapFromLDAP() => 0x0, status=%d (%s)",
 422                         myself, stat, ldap_err2string(stat));
 423                 if (arg->isDeferred) {
 424                         dstat = InUseDictionary->rollback(t->objPath);
 425                         if (dstat != DB_SUCCESS) {
 426                                 struct timeval  end;
 427 
 428                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 429                                 "%s: DB error %d rolling back \"%s\"",
 430                                         myself, dstat, NIL(t->objName));
 431                                 /*
 432                                  * Had rollback succeeded, the 'table'
 433                                  * would have disappeared. However, since
 434                                  * rollback failed, we need to update the
 435                                  * table->mapping.enum* fields.
 436                                  */
 437                                 (void) mutex_lock(&table->mapping.enumLock);
 438                                 table->mapping.enumStat = stat;
 439                                 (void) gettimeofday(&end, 0);
 440                                 end.tv_sec -= start.tv_sec;
 441                                 end.tv_usec -= start.tv_usec;
 442                                 if (end.tv_usec < 0) {
 443                                         end.tv_usec += 1000000;
 444                                         end.tv_sec -= 1;
 445                                 }
 446                                 table->mapping.enumTime =
 447                                         1000000*end.tv_sec + end.tv_usec;
 448                                 table->mapping.enumTid = 0;
 449                                 (void) mutex_unlock(&table->mapping.enumLock);
 450                         }
 451                 }
 452                 return (stat);
 453         }
 454 
 455         /*
 456          * Need to disable write-through to LDAP, for which we need a lock
 457          * on our db_mindex ('mindex'); we're also updating the table, so
 458          * we need a write lock on that as well. However, before locking the
 459          * mindex, we need to maintain lock integrity by acquiring the
 460          * trans log lock. Note that actually beginning a transaction is
 461          * expensive, so we defer that until we know that we really need
 462          * to update.
 463          */
 464         lstat = lockTransLog(myself, 1, 1);
 465         if (lstat != 0) {
 466                 if (lstat == EBUSY)
 467                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
 468                 "%s: transaction log busy; no LDAP update for \"%s\"",
 469                                 myself, NIL(t->objName));
 470                 else
 471                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
 472         "%s: Error %d locking transaction log; no LDAP update for \"%s\"",
 473                                 myself, lstat, NIL(t->objName));
 474                 if (arg->isDeferred) {
 475                         dstat = InUseDictionary->rollback(t->objPath);
 476                         if (dstat != DB_SUCCESS) {
 477                                 struct timeval  end;
 478 
 479                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 480                                 "%s: DB error %d rolling back \"%s\"",
 481                                         myself, dstat, NIL(t->objName));
 482                                 /*
 483                                  * Had rollback succeeded, the 'table'
 484                                  * would have disappeared. However, since
 485                                  * rollback failed, we need to update the
 486                                  * table->mapping.enum* fields.
 487                                  */
 488                                 (void) mutex_lock(&table->mapping.enumLock);
 489                                 table->mapping.enumStat = LDAP_OPERATIONS_ERROR;
 490                                 (void) gettimeofday(&end, 0);
 491                                 end.tv_sec -= start.tv_sec;
 492                                 end.tv_usec -= start.tv_usec;
 493                                 if (end.tv_usec < 0) {
 494                                         end.tv_usec += 1000000;
 495                                         end.tv_sec -= 1;
 496                                 }
 497                                 table->mapping.enumTime = 1000000*end.tv_sec +
 498                                         end.tv_usec;
 499                                 table->mapping.enumTid = 0;
 500                                 (void) mutex_unlock(&table->mapping.enumLock);
 501                         }
 502                 }
 503                 return (LDAP_OPERATIONS_ERROR);
 504         }
 505 
 506         /*
 507          * If we have any updates, we'll call db::sync_log, which write-
 508          * locks the 'db' instance. In order to avoid a dead-lock with
 509          * threads performing a DB lookup (which will lock the 'db' and
 510          * then the 'db_mindex'), we need hence need to lock in the
 511          * following order:
 512          *
 513          *      trans.log       (already holding that one)
 514          *      db
 515          *      db_mindex
 516          *      db_table
 517          */
 518         TRYWRITELOCK(((db *)mindex->getDbPtr()), stat,
 519                 "w db db_mindex::entriesFromLDAPreal");
 520         if (stat == 0) {
 521                 TRYWRITELOCK(mindex, stat2, "w db_mindex::entriesFromLDAPreal");
 522                 if (stat2 == 0) {
 523                         TRYWRITELOCK(table, stat3,
 524                                 "table w db_mindex::entriesFromLDAPreal");
 525                 }
 526         }
 527 
 528         if (stat != 0 || stat2 != 0 || stat3 != 0) {
 529                 if (stat != 0) {
 530                         if (stat == EBUSY)
 531                                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 532                                 "%s: 'db' busy; no LDAP update for \"%s\"",
 533                                         myself, NIL(t->objName));
 534                         else
 535                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 536                         "%s: 'db' lock error %d; no LDAP update for \"%s\"",
 537                                         myself, stat, NIL(t->objName));
 538                 } else if (stat2 != 0) {
 539                         if (stat2 == EBUSY)
 540                                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 541                         "%s: 'db_mindex' busy; no LDAP update for \"%s\"",
 542                                         myself, NIL(t->objName));
 543                         else
 544                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 545                 "%s: 'db_mindex' lock error %d; no LDAP update for \"%s\"",
 546                                         myself, stat2, NIL(t->objName));
 547                 } else {
 548                         if (stat3 == EBUSY)
 549                                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 550                         "%s: 'db_table' busy; no LDAP update for \"%s\"",
 551                                         myself, NIL(t->objName));
 552                         else
 553                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 554                 "%s: 'db_table' lock error %d; no LDAP update for \"%s\"",
 555                                         myself, stat3, NIL(t->objName));
 556                 }
 557                 freeQueries(res, nq);
 558                 if (arg->isDeferred) {
 559                         dstat = InUseDictionary->rollback(t->objPath);
 560                         if (dstat != DB_SUCCESS) {
 561                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 562                                 "%s: DB error %d rolling back \"%s\"",
 563                                         myself, dstat, NIL(t->objName));
 564                                 /*
 565                                  * Had rollback succeeded, the 'table'
 566                                  * would have disappeared. However, since
 567                                  * rollback failed, we need to update the
 568                                  * table->mapping.enum* fields.
 569                                  */
 570                                 (void) mutex_lock(&table->mapping.enumLock);
 571                                 table->mapping.enumStat = LDAP_OPERATIONS_ERROR;
 572                                 table->mapping.enumTid = 0;
 573                                 (void) mutex_unlock(&table->mapping.enumLock);
 574                         }
 575                 }
 576                 if (stat == 0) {
 577                         if (stat2 == 0) {
 578                                 WRITEUNLOCK2(mindex, ((db *)mindex->getDbPtr()),
 579                                         LDAP_OPERATIONS_ERROR,
 580                                         LDAP_OPERATIONS_ERROR,
 581                                         "db_mindex::entriesFromLDAPreal wu",
 582                                         "db_mindex::entriesFromLDAPreal wu db");
 583                         } else {
 584                                 WRITEUNLOCK(((db *)mindex->getDbPtr()),
 585                                         LDAP_OPERATIONS_ERROR,
 586                                         "db_mindex::entriesFromLDAPreal wu db");
 587                         }
 588                 }
 589                 unlockTransLog(myself, 1);
 590                 return (LDAP_OPERATIONS_ERROR);
 591         }
 592 
 593         stat = LDAP_SUCCESS;
 594         mindex->setNoWriteThrough();
 595         mindex->setNoLDAPquery();
 596         if (qin == 0) {
 597                 table->setEnumMode(0);
 598                 doEnum = TRUE;
 599 
 600                 /*
 601                  * If there is no non-indexed table mapping, we must filter
 602                  * the enum mode (i.e., deletion candidates) array to only
 603                  * contain those entries that match the indexes.
 604                  */
 605                 if (haveIndexedMapping(t)) {
 606                         entry_object    **tea = table->gettab();
 607                         long            i, ntea = table->getsize();
 608 
 609 
 610                         /*
 611                          * Walk through the entry array, and remove any enum
 612                          * array entry that _doesn't_ match the index(es).
 613                          */
 614                         for (i = 0; i < ntea; i++) {
 615                                 db_query                *q;
 616                                 __nis_table_mapping_t   **tp;
 617                                 int                     numMatches;
 618 
 619                                 if (tea[i] == 0)
 620                                         continue;
 621 
 622                                 q = pseudoEntryObj2Query(tea[i], 0, 0);
 623                                 if (q == 0)
 624                                         continue;
 625 
 626                                 tp = selectTableMapping(t, q, 0, 0, dbId,
 627                                                         &numMatches);
 628                                 if (tp == 0 || numMatches <= 0)
 629                                         table->enumTouch(i);
 630 
 631                                 sfree(tp);
 632 
 633                                 freeQuery(q);
 634                         }
 635                 }
 636 
 637                 logmsg(MSG_NOTIMECHECK, LOG_INFO, "%s: %d entries from LDAP",
 638                         myself, nq);
 639         } else {
 640                 db_index_entry  *dbie;
 641                 long            i, count;
 642                 bool_t          valid;
 643 
 644                 /*
 645                  * Find the entries in the DB that currently match the
 646                  * query, and add them to the enum array. Those that
 647                  * remain untouched when we've processed the LDAP data
 648                  * don't currently exist in LDAP, and should be deleted
 649                  * from the DB.
 650                  */
 651                 dbie = mindex->satisfy_query_dbonly(qin, &count, FALSE, &valid);
 652                 if (dbie != 0 && valid && count > 0) {
 653                         table->setEnumMode(count);
 654                         doEnum = TRUE;
 655                         for (i = 0; i < count; i++) {
 656                                 table->enumSetup(dbie->getlocation(), i);
 657                                 dbie = dbie->getnextresult();
 658                                 if (dbie == 0)
 659                                         break;
 660                         }
 661                 } else {
 662                         doEnum = FALSE;
 663                 }
 664         }
 665 
 666         entry_col       ec[NIS_MAXCOLUMNS+1];
 667         for (i = 0, na = 0; i < nq; i++) {
 668                 entry_object    eo, *e;
 669                 table_col       *tc;
 670                 nis_object      o, *to;
 671                 int             j, nc;
 672                 db_qcomp        *qc;
 673 
 674                 if (res[i] == 0)
 675                         continue;
 676 
 677 #ifdef  NISDB_LDAP_DEBUG
 678                 printQuery(res[i], t);
 679                 printObjAttr(oa[i]);
 680 #endif  /* NISDB_LDAP_DEBUG */
 681 
 682                 /* Assemble an object from the query and attributes */
 683                 (void) memset(&o, 0, sizeof (o));
 684                 if (oa[i] != 0) {
 685                         o.zo_owner = oa[i]->zo_owner;
 686                         o.zo_group = oa[i]->zo_group;
 687                         o.zo_domain = oa[i]->zo_domain;
 688                         o.zo_access = oa[i]->zo_access;
 689                         o.zo_ttl = oa[i]->zo_ttl;
 690                 }
 691                 if ((to = t->obj) != 0) {
 692                         o.zo_name = to->zo_name;
 693                         o.zo_data.objdata_u.en_data.en_type =
 694                                 to->zo_data.objdata_u.ta_data.ta_type;
 695                         tc = to->zo_data.objdata_u.ta_data.ta_cols.ta_cols_val;
 696                         if (to->zo_data.objdata_u.ta_data.ta_cols.ta_cols_len
 697                                         != t->numColumns)
 698                                 tc = 0;
 699                         if (o.zo_owner == 0)
 700                                 o.zo_owner = to->zo_owner;
 701                         if (o.zo_group == 0)
 702                                 o.zo_group = to->zo_group;
 703                         if (o.zo_domain == 0)
 704                                 o.zo_domain = to->zo_domain;
 705                         if (o.zo_access == 0)
 706                                 o.zo_access = to->zo_access;
 707                         if (o.zo_ttl == 0)
 708                                 o.zo_ttl = to->zo_ttl;
 709                 } else {
 710                         tc = 0;
 711                         o.zo_owner = (nis_name)"";
 712                         o.zo_group = (nis_name)"";
 713                         o.zo_domain = (nis_name)"";
 714                 }
 715 
 716                 o.zo_data.zo_type = NIS_ENTRY_OBJ;
 717                 o.zo_data.objdata_u.en_data.en_cols.en_cols_len =
 718                         t->numColumns + 1;
 719                 o.zo_data.objdata_u.en_data.en_cols.en_cols_val = ec;
 720 
 721                 (void) memset(&ec, 0, sizeof (ec));
 722                 nc = res[i]->size();
 723                 qc = res[i]->queryloc();
 724                 if (qc == 0) {
 725                         freeQuery(res[i]);
 726                         continue;
 727                 }
 728                 for (j = 0; j < nc; j++) {
 729                         int     ic = 1+ qc[j].which_index;
 730                         if (ic < 1 || ic > t->numColumns)
 731                                 continue;
 732 #ifdef  SET_ENTRY_FLAGS
 733                         if (tc != 0)
 734                                 ec[ic].ec_flags =
 735                                         entryFlagsFromTable(tc[ic-1].tc_flags);
 736 #else
 737                         /*
 738                          * In theory, the entry flags should be derived
 739                          * from the table flags. However, that doesn't
 740                          * seem to be the way that the DB code has done
 741                          * things so far, so leave the entry flags unset.
 742                          */
 743 #endif  /* SET_ENTRY_FLAGS */
 744                         qc[j].index_value->get_value(
 745                                         &ec[ic].ec_value.ec_value_val,
 746                                         (int *)&ec[ic].ec_value.ec_value_len);
 747                 }
 748 
 749                 setOid(&o);
 750                 e = makePseudoEntryObj(&o, &eo, t->obj);
 751                 if (e == 0) {
 752                         freeQuery(res[i]);
 753                         continue;
 754                 }
 755 
 756                 /*
 757                  * 'o' is currently a pseudo-object of type entry, with
 758                  * column zero used for an XDR:ed version of the entry_obj,
 759                  * column one the real column zero of the entry, etc.
 760                  * We now need a real NIS_ENTRY_OBJ object, so move the
 761                  * entry_col array one step left.
 762                  */
 763                 o.zo_data.objdata_u.en_data.en_cols.en_cols_len = t->numColumns;
 764                 o.zo_data.objdata_u.en_data.en_cols.en_cols_val = &ec[1];
 765 
 766                 stat = mindex->updateTableEntry(e, 1, t->objName, &o, t->obj,
 767                                                 o.zo_oid.mtime, &xid);
 768                 /*
 769                  * LDAP_SUCCESS =>   Entry added or modified
 770                  * LDAP_COMPARE_TRUE =>      Entry same as existing one
 771                  * other =>          Error
 772                  */
 773                 if (stat == LDAP_SUCCESS) {
 774                         na++;
 775                 } else if (stat == LDAP_COMPARE_TRUE) {
 776                         stat = LDAP_SUCCESS;
 777                 } else {
 778                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 779                                 "%s: Error adding entry to \"%s\": %s",
 780                                 myself, NIL(t->objName),
 781                                 ldap_err2string(stat));
 782                 }
 783 
 784                 if (e->en_cols.en_cols_val != 0)
 785                         sfree(e->en_cols.en_cols_val[0].ec_value.ec_value_val);
 786 
 787                 freeQuery(res[i]);
 788         }
 789 
 790         sfree(res);
 791 
 792         /* Take care of deletes if we enumerated the table */
 793         if (doEnum) {
 794                 ea = table->endEnumMode(&numEa);
 795                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 796                         "%s: %d entries added/updated", myself, na);
 797                 nau = na;
 798         } else
 799                 ea = 0;
 800         if (ea != 0) {
 801                 uint32_t        nowt = time(0);
 802 
 803                 for (i = 0; i < numEa; i++) {
 804                         int     st;
 805 
 806                         if (ea[i] == 0)
 807                                 continue;
 808 
 809                         st = mindex->updateTableEntry(ea[i], 0, t->objName, 0,
 810                                                 t->obj, nowt, &xid);
 811                         if (st == LDAP_SUCCESS) {
 812                                 na++;
 813                         } else {
 814                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 815                         "%s: Error removing directory entry for \"%s\": %s",
 816                                         myself, NIL(t->objName),
 817                                         ldap_err2string(st));
 818                                 if (stat == LDAP_SUCCESS)
 819                                         stat = st;
 820                         }
 821                 }
 822                 if (stat == LDAP_SUCCESS) {
 823                         struct timeval  now;
 824                         (void) gettimeofday(&now, 0);
 825                         table->mapping.enumExpire = now.tv_sec +
 826                                 table->mapping.ttl;
 827                 }
 828                 if (doEnum)
 829                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
 830                                 "%s: %d entries deleted", myself, na-nau);
 831         }
 832 
 833         sfree(ea);
 834 
 835         /* If we called log_action() successfully, we need to sync the log */
 836         if (na > 0)
 837                 (void) ((db *)mindex->getDbPtr())->sync_log();
 838 
 839         if (xid != 0 && na > 0 && stat == LDAP_SUCCESS)
 840                 ret = endTransaction(xid, dirObj);
 841         else if (xid != 0)
 842                 ret = abort_transaction(xid);
 843         else
 844                 ret = 0;
 845         if (ret != 0) {
 846                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 847                         "%s: Error %s transaction for \"%s\"",
 848                         myself, (na > 0 && stat == LDAP_SUCCESS) ?
 849                                         "ending" : "aborting",
 850                         NIL(t->objName));
 851                 stat = LDAP_OPERATIONS_ERROR;
 852         }
 853 
 854         mindex->clearNoLDAPquery();
 855         mindex->clearNoWriteThrough();
 856         freeObjAttr(oa, nq);
 857 
 858 #ifdef  NISDB_LDAP_DEBUG
 859         printbuf();
 860 #endif  /* NISDB_LDAP_DEBUG */
 861 
 862         if (doEnum)
 863                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 864                         "%s: enumeration \"%s\" done", myself, NIL(t->objName));
 865 
 866         if (arg->isDeferred) {
 867                 /*
 868                  * Rollback doesn't recover data written to disk, so
 869                  * we should commit even if we're returning failure.
 870                  */
 871                 dstat = InUseDictionary->commit(t->objPath);
 872                 if (dstat != DB_SUCCESS) {
 873                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 874                                 "%s: DB error %d committing \"%s\"",
 875                                 myself, dstat, NIL(t->objName));
 876                 }
 877         }
 878         (void) mutex_lock(&table->mapping.enumLock);
 879         if (arg->isDeferred && dstat == DB_SUCCESS)
 880                 table->mapping.enumDeferred = 0;
 881         table->mapping.enumStat = stat;
 882         {
 883                 struct timeval  end;
 884 
 885                 (void) gettimeofday(&end, 0);
 886                 end.tv_sec -= start.tv_sec;
 887                 end.tv_usec -= start.tv_usec;
 888                 if (end.tv_usec < 0) {
 889                         end.tv_usec += 1000000;
 890                         end.tv_sec -= 1;
 891                 }
 892                 table->mapping.enumTime = 1000000*end.tv_sec + end.tv_usec;
 893                 logmsg(MSG_NOTIMECHECK,
 894 #ifdef  NISDB_LDAP_DEBUG
 895                         LOG_WARNING,
 896 #else
 897                         LOG_INFO,
 898 #endif  /* NISDB_LDAP_DEBUG */
 899                         "%s: %d entries in %ld usec => %ld usec/entry",
 900                         NIL(t->objName), table->mapping.enumEntries,
 901                         table->mapping.enumTime,
 902                         table->mapping.enumTime/
 903                         (table->mapping.enumEntries != 0 ?
 904                                 table->mapping.enumEntries : 1));
 905         }
 906         table->mapping.enumTid = 0;
 907         (void) mutex_unlock(&table->mapping.enumLock);
 908 
 909         WRITEUNLOCKNR(table, stat3, "table wu db_mindex::entriesFromLDAPreal");
 910         WRITEUNLOCKNR(mindex, stat2, "db_mindex::entriesFromLDAPreal wu");
 911         WRITEUNLOCKNR(((db *)mindex->getDbPtr()), lstat,
 912                 "db db_mindex::entriesFromLDAPreal wu");
 913         unlockTransLog(myself, 1);
 914         if (stat3 != 0)
 915                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 916                         "%s: Error %d unlocking db_table", myself, stat3);
 917         if (stat2 != 0)
 918                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 919                         "%s: Error %d unlocking db_mindex", myself, stat2);
 920         if (lstat != 0)
 921                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 922                         "%s: Error %d unlocking db", myself, lstat);
 923 
 924         return (stat);
 925 }
 926 /*
 927  * Sets the oid (i.e., the creation and modification times) for the
 928  * specified object. In order to avoid retrieving the old incarnation
 929  * (if any) from the DB first, we're punting and setting both mtime
 930  * and ctime to the current time.
 931  */
 932 static void
 933 setOid(nis_object *obj) {
 934         if (obj != 0) {
 935                 obj->zo_oid.ctime = obj->zo_oid.mtime = time(0);
 936         }
 937 }