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