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 } 940