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 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 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 = ""; 712 o.zo_group = ""; 713 o.zo_domain = ""; 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 } 938