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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <string.h> 29 #include <errno.h> 30 #include <syslog.h> 31 #include <procfs.h> 32 #include <unistd.h> 33 #include <fcntl.h> 34 #include <libintl.h> 35 #include <atomic.h> 36 #include <pthread.h> 37 #include <sys/mman.h> 38 #include <time.h> 39 #include "solaris-int.h" 40 #include "ns_connmgmt.h" 41 #include "ns_cache_door.h" 42 #include "ns_internal.h" 43 44 /* 45 * Access (reference, shutdown, or reload) the current connection 46 * management control structure conn_mgmt_t. 47 */ 48 #define NS_CONN_MGMT_OP_REF 1 49 #define NS_CONN_MGMT_OP_SHUTDOWN 2 50 #define NS_CONN_MGMT_OP_RELOAD_CONFIG 3 51 #define NS_CONN_MGMT_OP_NEW_CONFIG 4 52 #define NS_CONN_MGMT_OP_LIB_INIT 5 53 54 static ns_conn_mgmt_t *access_conn_mgmt(int); 55 static ns_conn_mgmt_t *release_conn_mgmt(ns_conn_mgmt_t *, boolean_t); 56 static int close_conn_mt(ns_conn_mt_t *, int, ns_ldap_error_t **, 57 ns_conn_user_t *); 58 static int close_conn_mt_when_nouser(ns_conn_mt_t *cm); 59 void shutdown_all_conn_mt(ns_conn_mgmt_t *cmg); 60 static int conn_signal(ns_conn_mt_t *); 61 static int conn_wait(ns_conn_mt_t *, ns_conn_user_t *); 62 static void close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg); 63 static ns_conn_mgmt_t *proc_server_change(ns_server_status_change_t *chg, 64 ns_conn_mgmt_t *cmg); 65 static void get_preferred_servers(boolean_t, boolean_t, ns_conn_mgmt_t *); 66 static void start_thread(); 67 68 static ns_conn_mgmt_t *ns_connmgmt = NULL; 69 static ns_conn_mgmt_t *ns_connmgmt_parent = NULL; 70 static mutex_t ns_connmgmt_lock = DEFAULTMUTEX; 71 static boolean_t ns_connmgmt_shutting_down = B_FALSE; 72 73 #define NS_CONN_MSG_NO_CONN_MGMT gettext( \ 74 "libsldap: unable to allocate the connection management control") 75 #define NS_CONN_MSG_NO_MTC_KEY gettext( \ 76 "libsldap: unable to allocate the TSD key for per-thread ldap error") 77 #define NS_CONN_MSG_NO_CMG_KEY gettext( \ 78 "libsldap: unable to allocate the TSD key for connection management") 79 #define NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded") 80 #define NS_CONN_MSG_RELOADED gettext( \ 81 "libsldap: configuration has been reloaded") 82 #define NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \ 83 "libsldap: library unloaded or configuration has been reloaded") 84 #define NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \ 85 "libsldap: received incorrect data from ldap_cachemgr") 86 #define NS_CONN_MSG_MEMORY_ERROR gettext( \ 87 "libsldap: unable to allocate memory") 88 #define NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \ 89 "libsldap: unable to start the server monitor thread (%s)") 90 #define NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \ 91 "libsldap: server down reported by ldap_cachemgr") 92 93 static int ns_conn_free = 1; 94 #define NS_CONN_UNLOCK_AND_FREE(free, cm, cmg) \ 95 { \ 96 (void) mutex_unlock(&(cm)->lock); \ 97 if (free == 1) \ 98 cmg = free_conn_mt((cm), 1); \ 99 if (cmg != NULL) \ 100 (void) mutex_unlock(&(cmg)->lock); \ 101 } 102 103 #define NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \ 104 { \ 105 char *msg = NULL; \ 106 (void) mutex_lock(&(cmg)->lock); \ 107 if ((cmg)->shutting_down == B_TRUE) \ 108 msg = NS_CONN_MSG_SHUTDOWN; \ 109 else if ((cmg)->cfg_reloaded == B_TRUE) \ 110 msg = NS_CONN_MSG_RELOADED; \ 111 if (msg != NULL) { \ 112 (*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \ 113 (void) mutex_unlock(&(cmg)->lock); \ 114 return (NS_LDAP_OP_FAILED); \ 115 } \ 116 } 117 118 /* 119 * TSD keys ns_mtckey and ns_cmgkey are for sharing ldap connections 120 * and their associated connection management structure among 121 * multiple threads. The pointers to the per-thread ldap error 122 * information and the connection management structure are 123 * saved in ns_mtckey and ns_cmgkey. 124 */ 125 thread_key_t ns_mtckey = THR_ONCE_KEY; 126 thread_key_t ns_cmgkey = THR_ONCE_KEY; 127 128 /* Per thread LDAP error resides in thread-specific data (ns_mtckey) */ 129 struct ldap_error { 130 int le_errno; 131 char *le_matched; 132 char *le_errmsg; 133 }; 134 135 /* NULL struct ldap_error */ 136 static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL}; 137 138 /* destructor: free the ldap error data in the thread specific area */ 139 static void 140 ns_mtckey_cleanup(void *key) { 141 struct ldap_error *le = (struct ldap_error *)key; 142 143 if (le == NULL) 144 return; 145 if (le->le_matched != NULL) { 146 ldap_memfree(le->le_matched); 147 } 148 if (le->le_errmsg != NULL) { 149 ldap_memfree(le->le_errmsg); 150 } 151 free(le); 152 } 153 154 /* Free/detach the thread specific data structures */ 155 static void 156 conn_tsd_free() { 157 void *tsd = NULL; 158 int rc; 159 160 /* free the per-thread ldap error info */ 161 rc = thr_getspecific(ns_mtckey, &tsd); 162 if (rc == 0 && tsd != NULL) 163 ns_mtckey_cleanup(tsd); 164 (void) thr_setspecific(ns_mtckey, NULL); 165 166 /* detach the connection management control */ 167 (void) thr_setspecific(ns_cmgkey, NULL); 168 } 169 170 /* per-thread callback function for allocating a mutex */ 171 static void * 172 ns_mutex_alloc(void) 173 { 174 mutex_t *mutexp = NULL; 175 176 if ((mutexp = malloc(sizeof (mutex_t))) != NULL) { 177 if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) { 178 free(mutexp); 179 mutexp = NULL; 180 } 181 } 182 return (mutexp); 183 } 184 185 /* per-thread callback function for freeing a mutex */ 186 static void 187 ns_mutex_free(void *mutexp) 188 { 189 (void) mutex_destroy((mutex_t *)mutexp); 190 free(mutexp); 191 } 192 193 /* 194 * Function for setting up thread-specific data 195 * where per thread LDAP error and the pointer 196 * to the active connection management control 197 * are stored. 198 */ 199 static int 200 conn_tsd_setup(ns_conn_mgmt_t *cmg) 201 { 202 void *tsd; 203 int rc; 204 205 rc = thr_setspecific(ns_cmgkey, cmg); 206 if (rc != 0) /* must be ENOMEM */ 207 return (-1); 208 209 /* return success if the ns_mtckey TSD is already set */ 210 rc = thr_getspecific(ns_mtckey, &tsd); 211 if (rc == 0 && tsd != NULL) 212 return (0); 213 214 /* allocate and set the ns_mtckey TSD */ 215 tsd = (void *) calloc(1, sizeof (struct ldap_error)); 216 if (tsd == NULL) 217 return (-1); 218 rc = thr_setspecific(ns_mtckey, tsd); 219 if (rc != 0) { /* must be ENOMEM */ 220 free(tsd); 221 return (-1); 222 } 223 return (0); 224 } 225 226 /* Callback function for setting the per thread LDAP error */ 227 /*ARGSUSED*/ 228 static void 229 set_ld_error(int err, char *matched, char *errmsg, void *dummy) 230 { 231 struct ldap_error *le; 232 int eno; 233 234 if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) { 235 syslog(LOG_ERR, gettext( 236 "libsldap: set_ld_error: thr_getspecific failed (%s)."), 237 strerror(eno)); 238 return; 239 } 240 241 /* play safe, do nothing if TSD pointer is NULL */ 242 if (le == NULL) { 243 syslog(LOG_INFO, gettext( 244 "libsldap: set_ld_error: TSD pointer is NULL.")); 245 return; 246 } 247 248 le->le_errno = err; 249 250 if (le->le_matched != NULL) { 251 ldap_memfree(le->le_matched); 252 le->le_matched = NULL; 253 } 254 le->le_matched = matched; 255 256 if (le->le_errmsg != NULL) { 257 ldap_memfree(le->le_errmsg); 258 le->le_errmsg = NULL; 259 } 260 le->le_errmsg = errmsg; 261 } 262 263 /* check and allocate the thread-specific data for using a MT connection */ 264 static int 265 conn_tsd_check(ns_conn_mgmt_t *cmg) 266 { 267 if (conn_tsd_setup(cmg) != 0) 268 return (NS_LDAP_MEMORY); 269 270 return (NS_LDAP_SUCCESS); 271 } 272 273 /* Callback function for getting the per thread LDAP error */ 274 /*ARGSUSED*/ 275 static int 276 get_ld_error(char **matched, char **errmsg, void *dummy) 277 { 278 struct ldap_error *le; 279 int eno; 280 281 if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) { 282 syslog(LOG_ERR, gettext( 283 "libsldap: get_ld_error: thr_getspecific failed (%s)"), 284 strerror(eno)); 285 return (eno); 286 } 287 288 /* play safe, return NULL error data, if TSD pointer is NULL */ 289 if (le == NULL) 290 le = &ldap_error_NULL; 291 292 if (matched != NULL) { 293 *matched = le->le_matched; 294 } 295 if (errmsg != NULL) { 296 *errmsg = le->le_errmsg; 297 } 298 return (le->le_errno); 299 } 300 301 /* Callback function for setting per thread errno */ 302 static void 303 set_errno(int err) 304 { 305 errno = err; 306 } 307 308 /* Callback function for getting per thread errno */ 309 static int 310 get_errno(void) 311 { 312 return (errno); 313 } 314 315 /* set up an ldap session 'ld' for sharing among multiple threads */ 316 static int 317 setup_mt_conn(LDAP *ld) 318 { 319 320 struct ldap_thread_fns tfns; 321 struct ldap_extra_thread_fns extrafns; 322 int rc; 323 324 /* 325 * Set the function pointers for dealing with mutexes 326 * and error information 327 */ 328 (void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns)); 329 tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc; 330 tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free; 331 tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock; 332 tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock; 333 tfns.ltf_get_errno = get_errno; 334 tfns.ltf_set_errno = set_errno; 335 tfns.ltf_get_lderrno = get_ld_error; 336 tfns.ltf_set_lderrno = set_ld_error; 337 tfns.ltf_lderrno_arg = NULL; 338 339 /* 340 * Set up the ld to use those function pointers 341 */ 342 rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS, 343 (void *) &tfns); 344 if (rc < 0) { 345 syslog(LOG_INFO, gettext("libsldap: ldap_set_option " 346 "(LDAP_OPT_THREAD_FN_PTRS)")); 347 return (0); 348 } 349 350 /* 351 * Set the function pointers for working with semaphores 352 */ 353 (void) memset(&extrafns, '\0', 354 sizeof (struct ldap_extra_thread_fns)); 355 extrafns.ltf_threadid_fn = (void * (*)(void))thr_self; 356 extrafns.ltf_mutex_trylock = NULL; 357 extrafns.ltf_sema_alloc = NULL; 358 extrafns.ltf_sema_free = NULL; 359 extrafns.ltf_sema_wait = NULL; 360 extrafns.ltf_sema_post = NULL; 361 362 /* Set up the ld to use those function pointers */ 363 rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS, 364 (void *) &extrafns); 365 if (rc < 0) { 366 syslog(LOG_INFO, gettext("libsldap: ldap_set_option " 367 "(LDAP_OPT_EXTRA_THREAD_FN_PTRS)")); 368 return (0); 369 } 370 371 return (1); 372 } 373 374 /* set up an MT connection for sharing among multiple threads */ 375 static int 376 setup_mt_ld(LDAP *ld, ns_conn_mgmt_t *cmg) 377 { 378 thread_t t = thr_self(); 379 380 /* set up the per-thread data for using the MT connection */ 381 if (conn_tsd_setup(cmg) == -1) { 382 syslog(LOG_WARNING, 383 gettext("libsldap: tid= %d: unable to set up TSD\n"), t); 384 return (-1); 385 } 386 387 if (setup_mt_conn(ld) == 0) { 388 /* multiple threads per connection not supported */ 389 syslog(LOG_WARNING, gettext("libsldap: tid= %d: multiple " 390 "threads per connection not supported\n"), t); 391 conn_tsd_free(); 392 return (-1); 393 } 394 return (0); 395 } 396 397 /* 398 * Check name and UID of process, if it is nscd. 399 * 400 * Input: 401 * pid : PID of checked process 402 * check_uid : check if UID == 0 403 * Output: 404 * B_TRUE : nscd detected 405 * B_FALSE : nscd not confirmed 406 */ 407 static boolean_t 408 check_nscd_proc(pid_t pid, boolean_t check_uid) 409 { 410 psinfo_t pinfo; 411 char fname[MAXPATHLEN]; 412 ssize_t ret; 413 int fd; 414 415 if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) { 416 if ((fd = open(fname, O_RDONLY)) >= 0) { 417 ret = read(fd, &pinfo, sizeof (psinfo_t)); 418 (void) close(fd); 419 if ((ret == sizeof (psinfo_t)) && 420 (strcmp(pinfo.pr_fname, "nscd") == 0)) { 421 if (check_uid && (pinfo.pr_uid != 0)) 422 return (B_FALSE); 423 return (B_TRUE); 424 } 425 } 426 } 427 return (B_FALSE); 428 } 429 430 /* 431 * Check if this process is peruser nscd. 432 */ 433 boolean_t 434 __s_api_peruser_proc(void) 435 { 436 pid_t my_ppid; 437 static mutex_t nscdLock = DEFAULTMUTEX; 438 static pid_t checkedPpid = (pid_t)-1; 439 static boolean_t isPeruserNscd = B_FALSE; 440 441 my_ppid = getppid(); 442 443 /* 444 * Already checked before for this process? If yes, return cached 445 * response. 446 */ 447 if (my_ppid == checkedPpid) { 448 return (isPeruserNscd); 449 } 450 451 (void) mutex_lock(&nscdLock); 452 453 /* Check once more incase another thread has just complete this. */ 454 if (my_ppid == checkedPpid) { 455 (void) mutex_unlock(&nscdLock); 456 return (isPeruserNscd); 457 } 458 459 /* Reinitialize to be sure there is no residue after fork. */ 460 isPeruserNscd = B_FALSE; 461 462 /* Am I the nscd process? */ 463 if (check_nscd_proc(getpid(), B_FALSE)) { 464 /* Is my parent the nscd process with UID == 0. */ 465 isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE); 466 } 467 468 /* Remember for whom isPeruserNscd is. */ 469 checkedPpid = my_ppid; 470 471 (void) mutex_unlock(&nscdLock); 472 return (isPeruserNscd); 473 } 474 475 /* 476 * Check if this process is main nscd. 477 */ 478 boolean_t 479 __s_api_nscd_proc(void) 480 { 481 pid_t my_pid; 482 static mutex_t nscdLock = DEFAULTMUTEX; 483 static pid_t checkedPid = (pid_t)-1; 484 static boolean_t isMainNscd = B_FALSE; 485 486 /* 487 * Don't bother checking if this process isn't root, this cannot 488 * be main nscd. 489 */ 490 if (getuid() != 0) 491 return (B_FALSE); 492 493 my_pid = getpid(); 494 495 /* 496 * Already checked before for this process? If yes, return cached 497 * response. 498 */ 499 if (my_pid == checkedPid) { 500 return (isMainNscd); 501 } 502 503 (void) mutex_lock(&nscdLock); 504 505 /* Check once more incase another thread has just done this. */ 506 if (my_pid == checkedPid) { 507 (void) mutex_unlock(&nscdLock); 508 return (isMainNscd); 509 } 510 511 /* 512 * Am I the nscd process? UID is already checked, not needed from 513 * psinfo. 514 */ 515 isMainNscd = check_nscd_proc(my_pid, B_FALSE); 516 517 /* Remember for whom isMainNscd is. */ 518 checkedPid = my_pid; 519 520 (void) mutex_unlock(&nscdLock); 521 return (isMainNscd); 522 } 523 524 /* 525 * initialize a connection management control structure conn_mgmt_t 526 */ 527 ns_conn_mgmt_t * 528 init_conn_mgmt() 529 { 530 ns_conn_mgmt_t *cmg; 531 532 cmg = (ns_conn_mgmt_t *)calloc(1, sizeof (*cmg)); 533 if (cmg == NULL) { 534 syslog(LOG_ERR, NS_CONN_MSG_NO_CONN_MGMT); 535 return (NULL); 536 } 537 538 /* is this process nscd or peruser nscd ? */ 539 cmg->is_nscd = __s_api_nscd_proc(); 540 cmg->is_peruser_nscd = __s_api_peruser_proc(); 541 542 /* 543 * assume the underlying libldap allows multiple threads sharing 544 * the same ldap connection (MT connection) 545 */ 546 cmg->ldap_mt = B_TRUE; 547 /* state is inactive until MT connection is required/requested */ 548 cmg->state = NS_CONN_MGMT_INACTIVE; 549 550 (void) mutex_init(&cmg->lock, USYNC_THREAD, NULL); 551 (void) mutex_init(&cmg->cfg_lock, USYNC_THREAD, NULL); 552 cmg->pid = getpid(); 553 554 /* for nscd or peruser nscd, MT connection is required */ 555 if (cmg->is_nscd == B_TRUE || cmg->is_peruser_nscd == B_TRUE) 556 cmg->state = NS_CONN_MGMT_ACTIVE; 557 558 /* 559 * reference (or initialize) the current Native LDAP configuration and 560 * if in nscd process, make it never refreshed 561 */ 562 cmg->config = __s_api_get_default_config_global(); 563 if (cmg->config == NULL) 564 cmg->config = __s_api_loadrefresh_config_global(); 565 if (cmg->config != NULL) { 566 /* 567 * main nscd get config change notice from ldap_cachemgr 568 * so won't times out and refresh the config 569 */ 570 if (cmg->is_nscd == B_TRUE) 571 (cmg->config)->paramList[NS_LDAP_EXP_P].ns_tm = 0; 572 cmg->cfg_cookie = cmg->config->config_cookie; 573 } 574 575 return (cmg); 576 } 577 578 static void 579 mark_shutdown_or_reloaded(int op) 580 { 581 ns_conn_mgmt_t *cmg = ns_connmgmt; 582 583 (void) mutex_lock(&cmg->lock); 584 if (op == NS_CONN_MGMT_OP_SHUTDOWN) 585 cmg->shutting_down = B_TRUE; 586 else 587 cmg->cfg_reloaded = B_TRUE; 588 atomic_inc_uint(&cmg->ref_cnt); 589 cmg->state = NS_CONN_MGMT_DETACHED; 590 591 if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG) 592 __s_api_init_config_global(NULL); 593 594 (void) mutex_unlock(&cmg->lock); 595 } 596 597 /* 598 * Return a pointer to the current connection management. If 599 * it has not been created, or is requested to recreate, then 600 * create and return the pointer. It is possible, the current 601 * one is created by the parent before fork, create a new 602 * one too in such a case. 603 */ 604 static ns_conn_mgmt_t * 605 get_current_conn_mgmt(int op) 606 { 607 ns_conn_mgmt_t *cmg = ns_connmgmt; 608 static pid_t checked_pid = (pid_t)-1; 609 pid_t mypid; 610 611 mypid = getpid(); 612 if (cmg == NULL || checked_pid != mypid) { 613 checked_pid = mypid; 614 615 /* 616 * if current conn_mgmt not created yet or is from parent 617 * or is requested to recreate, create it 618 */ 619 if (cmg == NULL || cmg->pid != mypid) { 620 if (cmg != NULL) { 621 /* 622 * We don't want to free the conn_mgmt 623 * allocated by the parent, since 624 * there may be ldap connections 625 * still being used. So leave it 626 * alone but keep it referenced, 627 * so that it will not be flagged 628 * as a piece of leaked memory. 629 */ 630 ns_connmgmt_parent = cmg; 631 /* 632 * avoid lint warning; does not 633 * change the conn_mgmt in parent 634 */ 635 ns_connmgmt_parent->state = 636 NS_CONN_MGMT_DETACHED; 637 } 638 ns_connmgmt = init_conn_mgmt(); 639 cmg = ns_connmgmt; 640 /* 641 * ensure it will not be destroyed until explicitly 642 * shut down or reloaded 643 */ 644 if (op == NS_CONN_MGMT_OP_REF) 645 atomic_inc_uint(&cmg->ref_cnt); 646 } 647 } 648 649 return (cmg); 650 } 651 652 static ns_conn_mgmt_t * 653 access_conn_mgmt(int op) 654 { 655 ns_conn_mgmt_t *cmg = NULL; 656 ns_conn_mgmt_t *cmg_prev; 657 658 (void) mutex_lock(&ns_connmgmt_lock); 659 660 /* 661 * connection management is not available when the libsldap is being 662 * unloaded or shut down 663 */ 664 if (ns_connmgmt_shutting_down == B_TRUE) { 665 (void) mutex_unlock(&ns_connmgmt_lock); 666 return (NULL); 667 } 668 669 if (op == NS_CONN_MGMT_OP_SHUTDOWN) { 670 ns_connmgmt_shutting_down = B_TRUE; 671 if (ns_connmgmt != NULL) { 672 cmg = ns_connmgmt; 673 mark_shutdown_or_reloaded(op); 674 ns_connmgmt = NULL; 675 } 676 (void) mutex_unlock(&ns_connmgmt_lock); 677 return (cmg); 678 } 679 680 if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG || 681 op == NS_CONN_MGMT_OP_NEW_CONFIG) { 682 cmg_prev = ns_connmgmt; 683 mark_shutdown_or_reloaded(op); 684 /* 685 * the previous cmg (cmg_prev) will be freed later 686 * when its ref count reaches zero 687 */ 688 ns_connmgmt = NULL; 689 } 690 691 cmg = get_current_conn_mgmt(op); 692 if (cmg == NULL) { 693 (void) mutex_unlock(&ns_connmgmt_lock); 694 return (NULL); 695 } 696 697 atomic_inc_uint(&cmg->ref_cnt); 698 if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG || 699 op == NS_CONN_MGMT_OP_NEW_CONFIG) 700 cmg = cmg_prev; 701 else { /* op is NS_CONN_MGMT_OP_REF or NS_CONN_MGMT_OP_LIB_INIT */ 702 if (cmg->config == NULL) 703 cmg->config = __s_api_get_default_config(); 704 } 705 706 (void) mutex_unlock(&ns_connmgmt_lock); 707 return (cmg); 708 } 709 710 /* 711 * free a connection management control 712 */ 713 static void 714 free_conn_mgmt(ns_conn_mgmt_t *cmg) 715 { 716 union { 717 ldap_data_t s_d; 718 char s_b[1024]; 719 } space; 720 ldap_data_t *sptr; 721 int ndata; 722 int adata; 723 int rc; 724 ldap_get_chg_cookie_t cookie; 725 726 if (cmg == NULL) 727 return; 728 cookie = cmg->cfg_cookie; 729 730 __s_api_free2dArray(cmg->pservers); 731 /* destroy the previous config or release the current one */ 732 if (cmg->config != NULL) { 733 if (cmg->state == NS_CONN_MGMT_DETACHED) 734 __s_api_destroy_config(cmg->config); 735 else 736 __s_api_release_config(cmg->config); 737 } 738 739 /* stop the server status/config-change monitor thread */ 740 if (cmg->procchg_started == B_TRUE) { 741 if (cmg->procchg_tid != thr_self()) { 742 if (cmg->procchg_door_call == B_TRUE) { 743 adata = sizeof (ldap_call_t) + 1; 744 ndata = sizeof (space); 745 space.s_d.ldap_call.ldap_callnumber = 746 GETSTATUSCHANGE; 747 space.s_d.ldap_call.ldap_u.get_change.op = 748 NS_STATUS_CHANGE_OP_STOP; 749 space.s_d.ldap_call.ldap_u.get_change.cookie = 750 cookie; 751 sptr = &space.s_d; 752 rc = __ns_ldap_trydoorcall(&sptr, &ndata, 753 &adata); 754 if (rc != NS_CACHE_SUCCESS) 755 syslog(LOG_INFO, 756 gettext("libsldap: " 757 "free_conn_mgmt():" 758 " stopping door call " 759 " GETSTATUSCHANGE failed " 760 " (rc = %d)"), rc); 761 } 762 (void) pthread_cancel(cmg->procchg_tid); 763 cmg->procchg_started = B_FALSE; 764 } 765 } 766 767 free(cmg); 768 } 769 770 static ns_conn_mgmt_t * 771 release_conn_mgmt(ns_conn_mgmt_t *cmg, boolean_t unlock_cmg) 772 { 773 if (cmg == NULL) 774 return (NULL); 775 if (atomic_dec_uint_nv(&cmg->ref_cnt) == 0) { 776 if (cmg->state == NS_CONN_MGMT_DETACHED) { 777 if (unlock_cmg == B_TRUE) 778 (void) mutex_unlock(&cmg->lock); 779 free_conn_mgmt(cmg); 780 return (NULL); 781 } else { 782 syslog(LOG_WARNING, 783 gettext("libsldap: connection management " 784 " has a refcount of zero but the state " 785 " is not DETACHED (%d)"), cmg->state); 786 cmg = NULL; 787 } 788 } 789 return (cmg); 790 } 791 792 /* 793 * exposed function for initializing a connection management control structure 794 */ 795 ns_conn_mgmt_t * 796 __s_api_conn_mgmt_init() 797 { 798 if (thr_keycreate_once(&ns_mtckey, ns_mtckey_cleanup) != 0) { 799 syslog(LOG_WARNING, NS_CONN_MSG_NO_MTC_KEY); 800 return (NULL); 801 } 802 803 if (thr_keycreate_once(&ns_cmgkey, NULL) != 0) { 804 syslog(LOG_WARNING, NS_CONN_MSG_NO_CMG_KEY); 805 return (NULL); 806 } 807 808 return (access_conn_mgmt(NS_CONN_MGMT_OP_LIB_INIT)); 809 } 810 811 /* initialize a connection user */ 812 ns_conn_user_t * 813 __s_api_conn_user_init(int type, void *userinfo, boolean_t referral) 814 { 815 ns_conn_user_t *cu; 816 ns_conn_mgmt_t *cmg; 817 818 /* delete the reference to the previously used conn_mgmt */ 819 (void) thr_setspecific(ns_cmgkey, NULL); 820 821 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF); 822 if (cmg == NULL) 823 return (NULL); 824 825 if (cmg->state != NS_CONN_MGMT_ACTIVE && 826 cmg->state != NS_CONN_MGMT_INACTIVE) { 827 atomic_dec_uint(&cmg->ref_cnt); 828 return (NULL); 829 } 830 831 cu = (ns_conn_user_t *)calloc(1, sizeof (*cu)); 832 if (cu == NULL) { 833 atomic_dec_uint(&cmg->ref_cnt); 834 return (NULL); 835 } 836 837 cu->type = type; 838 cu->state = NS_CONN_USER_ALLOCATED; 839 cu->tid = thr_self(); 840 cu->userinfo = userinfo; 841 cu->referral = referral; 842 cu->ns_rc = NS_LDAP_SUCCESS; 843 cu->conn_mgmt = cmg; 844 845 (void) conn_tsd_setup(cmg); 846 847 return (cu); 848 } 849 850 /* 851 * Free the resources used by a connection user. 852 * The caller should ensure this conn_user is 853 * not associated with any conn_mt, i.e., 854 * not in any conn_mt's linked list of conn_users. 855 * The caller needs to free the userinfo member 856 * as well. 857 */ 858 void 859 __s_api_conn_user_free(ns_conn_user_t *cu) 860 { 861 ns_conn_mgmt_t *cmg; 862 863 if (cu == NULL) 864 return; 865 866 cu->state = NS_CONN_USER_FREED; 867 if (cu->ns_error != NULL) 868 (void) __ns_ldap_freeError(&cu->ns_error); 869 870 cmg = cu->conn_mgmt; 871 conn_tsd_free(); 872 (void) release_conn_mgmt(cmg, B_FALSE); 873 (void) free(cu); 874 } 875 876 /* 877 * Initialize an MT connection control structure 878 * that will be used to represent an ldap connection 879 * to be shared among multiple threads and to hold 880 * and manage all the conn_users using the ldap 881 * connection. 882 */ 883 static ns_conn_mt_t * 884 init_conn_mt(ns_conn_mgmt_t *cmg, ns_ldap_error_t **ep) 885 { 886 ns_conn_mt_t *cm; 887 ns_conn_mgmt_t *cmg_a; 888 889 cm = (ns_conn_mt_t *)calloc(1, sizeof (*cm)); 890 if (cm == NULL) { 891 if (ep != NULL) 892 *ep = __s_api_make_error(NS_LDAP_MEMORY, NULL); 893 return (NULL); 894 } 895 896 cmg_a = access_conn_mgmt(NS_CONN_MGMT_OP_REF); 897 if (cmg_a != cmg) { 898 if (cmg_a != NULL) { 899 (void) release_conn_mgmt(cmg_a, B_FALSE); 900 if (ep != NULL) 901 *ep = __s_api_make_error(NS_LDAP_OP_FAILED, 902 NS_CONN_MSG_SHUTDOWN_RELOADED); 903 } 904 return (NULL); 905 } 906 907 (void) mutex_init(&cm->lock, USYNC_THREAD, NULL); 908 cm->state = NS_CONN_MT_CONNECTING; 909 cm->tid = thr_self(); 910 cm->pid = getpid(); 911 cm->next = NULL; 912 cm->cu_head = NULL; 913 cm->cu_tail = NULL; 914 cm->conn = NULL; 915 cm->conn_mgmt = cmg; 916 917 return (cm); 918 } 919 920 /* 921 * Free an MT connection control structure, assume conn_mgmt is locked. 922 * 'unlock_cmg' is passed to release_conn_mgmt() to indicate the 923 * cmg needs to be unlocked or not. 924 */ 925 static ns_conn_mgmt_t * 926 free_conn_mt(ns_conn_mt_t *cm, int unlock_cmg) 927 { 928 ns_conn_mgmt_t *cmg; 929 930 if (cm == NULL) 931 return (NULL); 932 if (cm->ns_error != NULL) 933 (void) __ns_ldap_freeError(&cm->ns_error); 934 if (cm->conn != NULL) { 935 if (cm->conn->ld != NULL) 936 (void) ldap_unbind(cm->conn->ld); 937 __s_api_freeConnection(cm->conn); 938 } 939 cmg = cm->conn_mgmt; 940 free(cm); 941 return (release_conn_mgmt(cmg, unlock_cmg)); 942 } 943 944 /* add a connection user to an MT connection */ 945 static void 946 add_cu2cm(ns_conn_user_t *cu, ns_conn_mt_t *cm) 947 { 948 949 if (cm->cu_head == NULL) { 950 cm->cu_head = cu; 951 cm->cu_tail = cu; 952 } else { 953 cm->cu_tail->next = cu; 954 cm->cu_tail = cu; 955 } 956 cm->cu_cnt++; 957 } 958 959 /* add an MT connection to the connection management */ 960 static void 961 add_cm2cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg) 962 { 963 /* 964 * add connection opened for WRITE to top of list 965 * for garbage collection purpose. This is to 966 * ensure the connection will be closed after a 967 * certain amount of time (60 seconds). 968 */ 969 if (cmg->cm_head == NULL) { 970 cmg->cm_head = cm; 971 cmg->cm_tail = cm; 972 } else { 973 if (cm->opened_for == NS_CONN_USER_WRITE) { 974 cm->next = cmg->cm_head; 975 cmg->cm_head = cm; 976 } else { 977 cmg->cm_tail->next = cm; 978 cmg->cm_tail = cm; 979 } 980 } 981 cmg->cm_cnt++; 982 } 983 984 /* delete a connection user from an MT connection */ 985 static void 986 del_cu4cm(ns_conn_user_t *cu, ns_conn_mt_t *cm) 987 { 988 ns_conn_user_t *pu, *u; 989 990 if (cu == NULL || cm->cu_head == NULL || cm->cu_cnt == 0) 991 return; 992 993 /* only one conn_user on list */ 994 if (cm->cu_head == cm->cu_tail) { 995 if (cu == cm->cu_head) { 996 cm->cu_head = cm->cu_tail = NULL; 997 cm->cu_cnt = 0; 998 cu->next = NULL; 999 } 1000 return; 1001 } 1002 1003 /* more than one and cu is the first one */ 1004 if (cu == cm->cu_head) { 1005 cm->cu_head = cu->next; 1006 cm->cu_cnt--; 1007 cu->next = NULL; 1008 return; 1009 } 1010 1011 pu = cm->cu_head; 1012 for (u = cm->cu_head->next; u; u = u->next) { 1013 if (cu == u) 1014 break; 1015 pu = u; 1016 } 1017 if (pu != cm->cu_tail) { 1018 pu->next = cu->next; 1019 if (pu->next == NULL) 1020 cm->cu_tail = pu; 1021 cm->cu_cnt--; 1022 cu->next = NULL; 1023 } else { 1024 syslog(LOG_INFO, gettext( 1025 "libsldap: del_cu4cm(): connection user not found")); 1026 } 1027 } 1028 1029 /* delete an MT connection from the connection management control structure */ 1030 static void 1031 del_cm4cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg) 1032 { 1033 ns_conn_mt_t *pm, *m; 1034 1035 if (cm == NULL || cmg->cm_head == NULL || cmg->cm_cnt == 0) 1036 return; 1037 1038 /* only one conn_mt on list */ 1039 if (cmg->cm_head == cmg->cm_tail) { 1040 if (cm == cmg->cm_head) { 1041 cmg->cm_head = cmg->cm_tail = NULL; 1042 cmg->cm_cnt = 0; 1043 cm->next = NULL; 1044 } 1045 return; 1046 } 1047 1048 /* more than one and cm is the first one */ 1049 if (cm == cmg->cm_head) { 1050 cmg->cm_head = cm->next; 1051 cmg->cm_cnt--; 1052 cm->next = NULL; 1053 return; 1054 } 1055 1056 pm = cmg->cm_head; 1057 for (m = cmg->cm_head->next; m; m = m->next) { 1058 if (cm == m) 1059 break; 1060 pm = m; 1061 } 1062 if (pm != cmg->cm_tail) { 1063 pm->next = cm->next; 1064 if (pm->next == NULL) 1065 cmg->cm_tail = pm; 1066 cmg->cm_cnt--; 1067 cm->next = NULL; 1068 } else { 1069 syslog(LOG_INFO, gettext( 1070 "libsldap: del_cm4cmg(): MT connection not found")); 1071 } 1072 } 1073 1074 /* 1075 * compare to see if the server and credential for authentication match 1076 * those used by an MT connection 1077 */ 1078 static boolean_t 1079 is_server_cred_matched(const char *server, const ns_cred_t *cred, 1080 ns_conn_mt_t *cm) 1081 { 1082 Connection *cp = cm->conn; 1083 1084 /* check server first */ 1085 if (server != NULL && *server != 0) { 1086 if (strcasecmp(server, cp->serverAddr) != 0) 1087 return (B_FALSE); 1088 } 1089 1090 if (cred == NULL) 1091 return (B_TRUE); 1092 1093 /* then check cred */ 1094 return (__s_api_is_auth_matched(cp->auth, cred)); 1095 } 1096 1097 /* 1098 * Wait until a pending MT connection becomes available. 1099 * Return 1 if so, 0 if error. 1100 * 1101 * Assume the current conn_mgmt and the input conn_mt 1102 * are locked. 1103 */ 1104 static int 1105 wait_for_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t *cm) 1106 { 1107 1108 cu->state = NS_CONN_USER_WAITING; 1109 add_cu2cm(cu, cm); 1110 cu->conn_mt = cm; 1111 1112 (void) mutex_unlock(&cm->lock); 1113 /* 1114 * It could take some time so we don't want to hold 1115 * cm->conn_mgmt across the wait 1116 */ 1117 (void) mutex_unlock(&(cm->conn_mgmt)->lock); 1118 1119 (void) mutex_lock(&cm->lock); 1120 /* check one more time see if need to wait */ 1121 if (cm->state == NS_CONN_MT_CONNECTING) { 1122 (void) conn_wait(cm, cu); 1123 1124 /* cm->lock is locked again at this point */ 1125 1126 cu->state = NS_CONN_USER_WOKEUP; 1127 } 1128 1129 if (cm->state == NS_CONN_MT_CONNECTED) 1130 return (1); 1131 else { 1132 del_cu4cm(cu, cm); 1133 cu->conn_mt = NULL; 1134 cu->bad_mt_conn = B_FALSE; 1135 return (0); 1136 } 1137 } 1138 1139 /* 1140 * Check and see if the input MT connection '*cm' should be closed. 1141 * In two cases, it should be closed. If a preferred server is 1142 * found to be up when ldap_cachemgr is queried and reported back. 1143 * Or when the server being used for the connection is found to 1144 * be down. Return B_FALSE if the connection is not closed (or not marked 1145 * to be closed), otherwise unlock mutex (*cm)->lock and return B_TRUE. 1146 * This function assumes conn_mgmt cmg and conn_mt *cm are locked. 1147 */ 1148 static boolean_t 1149 check_and_close_conn(ns_conn_mgmt_t *cmg, ns_conn_mt_t **cm, 1150 ns_conn_user_t *cu) { 1151 1152 int rc; 1153 int j; 1154 int svridx = -1; 1155 int upidx = -1; 1156 int free_cm; 1157 ns_server_info_t sinfo; 1158 ns_ldap_error_t *errorp = NULL; 1159 1160 /* 1161 * check only if preferred servers are defined 1162 */ 1163 if (cmg->pservers_loaded == B_FALSE) 1164 get_preferred_servers(B_FALSE, B_FALSE, cmg); 1165 if (cmg->pservers == NULL) 1166 return (B_FALSE); 1167 1168 /* 1169 * ask ldap_cachemgr for the first available server 1170 */ 1171 rc = __s_api_requestServer(NS_CACHE_NEW, NULL, 1172 &sinfo, &errorp, NS_CACHE_ADDR_IP); 1173 if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) { 1174 (void) __ns_ldap_freeError(&errorp); 1175 return (B_FALSE); 1176 } 1177 1178 /* 1179 * Did ldap_cachemgr return a preferred server ? 1180 */ 1181 for (j = 0; cmg->pservers[j] != NULL; j++) { 1182 if (strcasecmp(sinfo.server, cmg->pservers[j]) != 0) 1183 continue; 1184 upidx = j; 1185 break; 1186 } 1187 1188 /* 1189 * Is the server being used a preferred one ? 1190 */ 1191 for (j = 0; cmg->pservers[j] != NULL; j++) { 1192 if (strcasecmp(cmg->pservers[j], (*cm)->conn->serverAddr) != 0) 1193 continue; 1194 svridx = j; 1195 break; 1196 } 1197 1198 /* 1199 * Need to fall back to a down-but-now-up preferred server ? 1200 * A preferred server falls back to a more preferred one. 1201 * A regular one falls back to any preferred ones. So if 1202 * both are preferred ones and same index, or both 1203 * are not preferred ones, then no need to close the 1204 * connection. 1205 */ 1206 if ((upidx == -1 && svridx == -1) || 1207 (upidx != -1 && svridx != -1 && upidx == svridx)) { 1208 __s_api_free_server_info(&sinfo); 1209 return (B_FALSE); 1210 } 1211 1212 /* 1213 * otherwise, 4 cases, all may need to close the connection: 1214 * For case 1 and 2, both servers are preferred ones: 1215 * 1. ldap_cachemgr returned a better one to use (upidx < svridx) 1216 * 2. the server being used is down (upidx > svridx) 1217 * 3. ldap_cachemgr returned a preferred one, but the server 1218 * being used is not, so need to fall back to the preferred server 1219 * 4. ldap_cachemgr returned a non-preferred one, but the server 1220 * being used is a preferred one, so it must be down (since 1221 * ldap_cachemgr always returns a preferred one when possible). 1222 * For case 1 & 3, close the READ connection when no user uses it. 1223 * For 2 and 4, close the connection with error rc, LDAP_SERVER_DOWN. 1224 */ 1225 if (upidx != -1 && (svridx == -1 || upidx < svridx)) { /* case 1 & 3 */ 1226 /* fallback does not make sense for WRITE/referred connection */ 1227 if ((*cm)->opened_for == NS_CONN_USER_WRITE || 1228 (*cm)->referral == B_TRUE) { 1229 __s_api_free_server_info(&sinfo); 1230 return (B_FALSE); 1231 } 1232 free_cm = close_conn_mt_when_nouser(*cm); 1233 if (cmg->shutting_down == B_FALSE) 1234 cu->retry = B_TRUE; 1235 } else { 1236 ns_ldap_error_t *ep; 1237 ep = __s_api_make_error(LDAP_SERVER_DOWN, 1238 NS_CONN_MSG_DOWN_FROM_CACHEMGR); 1239 /* cu has not been attached to cm yet, use NULL as cu pointer */ 1240 free_cm = close_conn_mt(*cm, LDAP_SERVER_DOWN, &ep, NULL); 1241 if (cmg->shutting_down == B_FALSE) 1242 cu->retry = B_TRUE; 1243 (void) __ns_ldap_freeError(&ep); 1244 } 1245 1246 (void) mutex_unlock(&(*cm)->lock); 1247 if (free_cm == 1) { 1248 (void) free_conn_mt(*cm, 0); 1249 *cm = NULL; 1250 } 1251 1252 __s_api_free_server_info(&sinfo); 1253 1254 return (B_TRUE); 1255 } 1256 1257 /* 1258 * Check to see if a conn_mt matches the connection criteria from 1259 * a conn_user. Return B_TRUE if yes, B_FALSE, otherwise. The input 1260 * conn_mt pointer (*cmt) may be freed and *cmt will be set to NULL 1261 * to indicate so. 1262 * conn_mt *cmt and conn_mgmt cm->conn_mgmt are assumed locked. 1263 * cm->lock is unlocked at exit if rc is B_FALSE. 1264 */ 1265 static boolean_t 1266 match_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t **cmt, 1267 ns_conn_mt_state_t st, const char *server, 1268 const ns_cred_t *cred) 1269 { 1270 boolean_t matched = B_FALSE; 1271 boolean_t drop_conn; 1272 int free_cm = 0; 1273 ns_conn_mt_t *cm = *cmt; 1274 ns_conn_mgmt_t *cmg = cm->conn_mgmt; 1275 1276 if (cm->state != st || cm->close_when_nouser == B_TRUE || 1277 cm->detached == B_TRUE || cm->pid != getpid() || 1278 cm->referral != cu->referral) { 1279 (void) mutex_unlock(&cm->lock); 1280 return (B_FALSE); 1281 } 1282 1283 /* 1284 * if a conn_mt opened for WRITE is idle 1285 * long enough, then close it. To improve 1286 * the performance of applications, such 1287 * as ldapaddent, a WRITE connection is 1288 * given a short time to live in the 1289 * connection pool, expecting the write 1290 * requests to come in a quick succession. 1291 * To save resource, the connection will 1292 * be closed if idle more than 60 seconds. 1293 */ 1294 if (cm->opened_for == NS_CONN_USER_WRITE && 1295 cu->type != NS_CONN_USER_WRITE && cm->cu_cnt == 0 && 1296 ((time(NULL) - cm->access_time) > 60)) { 1297 /* 1298 * NS_LDAP_INTERNAL is irrelevant here. There no 1299 * conn_user to consume the rc 1300 */ 1301 free_cm = close_conn_mt(cm, NS_LDAP_INTERNAL, NULL, NULL); 1302 (void) mutex_unlock(&cm->lock); 1303 if (free_cm == 1) { 1304 (void) free_conn_mt(cm, 0); 1305 *cmt = NULL; 1306 } 1307 return (B_FALSE); 1308 } 1309 1310 switch (cu->type) { 1311 case NS_CONN_USER_SEARCH: 1312 case NS_CONN_USER_GETENT: 1313 if (cm->opened_for == NS_CONN_USER_SEARCH || 1314 cm->opened_for == NS_CONN_USER_GETENT) 1315 matched = B_TRUE; 1316 break; 1317 1318 case NS_CONN_USER_WRITE: 1319 if (cm->opened_for == NS_CONN_USER_WRITE) 1320 matched = B_TRUE; 1321 break; 1322 1323 default: 1324 matched = B_FALSE; 1325 break; 1326 } 1327 1328 if (matched == B_TRUE && ((server != NULL || cred != NULL) && 1329 is_server_cred_matched(server, cred, cm) == B_FALSE)) 1330 matched = B_FALSE; 1331 1332 if (matched != B_FALSE) { 1333 /* 1334 * Check and drop the 'connected' connection if 1335 * necessary. Main nscd gets status changes from 1336 * the ldap_cachemgr daemon directly via the 1337 * GETSTATUSCHANGE door call, the standalone 1338 * function works in a no ldap_cachemgr environment, 1339 * so no need to check and drop connections. 1340 */ 1341 if (cm->state == NS_CONN_MT_CONNECTED && 1342 cmg->is_nscd == B_FALSE && !__s_api_isStandalone()) { 1343 drop_conn = check_and_close_conn(cmg, &cm, cu); 1344 if (drop_conn == B_TRUE) { 1345 if (cm == NULL) 1346 *cmt = NULL; 1347 return (B_FALSE); 1348 } 1349 } 1350 1351 /* check if max. users using or waiting for the connection */ 1352 if ((cm->state == NS_CONN_MT_CONNECTED && 1353 cm->cu_max != NS_CONN_MT_USER_NO_MAX && 1354 cm->cu_cnt >= cm->cu_max) || 1355 (cm->state == NS_CONN_MT_CONNECTING && 1356 cm->cu_max != NS_CONN_MT_USER_NO_MAX && 1357 cm->waiter_cnt >= cm->cu_max - 1)) 1358 matched = B_FALSE; 1359 } 1360 1361 if (matched == B_FALSE) 1362 (void) mutex_unlock(&cm->lock); 1363 1364 return (matched); 1365 } 1366 1367 /* 1368 * obtain an MT connection from the connection management for a conn_user 1369 * 1370 * Input: 1371 * server : server name or IP address 1372 * flags : libsldap API flags 1373 * cred : pointer to the user credential 1374 * cu : pointer to the conn_user structure 1375 * Output: 1376 * session : hold pointer to the Connection structure 1377 * errorp : hold pointer to error info (ns_ldap_error_t) 1378 */ 1379 int 1380 __s_api_conn_mt_get(const char *server, const int flags, const ns_cred_t *cred, 1381 Connection **session, ns_ldap_error_t **errorp, ns_conn_user_t *cu) 1382 { 1383 int rc; 1384 int i; 1385 ns_conn_mt_t *cn; 1386 ns_conn_mt_state_t st; 1387 ns_conn_mgmt_t *cmg; 1388 1389 if (errorp == NULL || cu == NULL || session == NULL) 1390 return (NS_LDAP_INVALID_PARAM); 1391 1392 *session = NULL; 1393 cmg = cu->conn_mgmt; 1394 1395 /* 1396 * for pam_ldap, always try opening a new connection 1397 */ 1398 if (cu->type == NS_CONN_USER_AUTH) 1399 return (NS_LDAP_NOTFOUND); 1400 1401 /* if need a new conn, then don't reuse */ 1402 if (flags & NS_LDAP_NEW_CONN) 1403 return (NS_LDAP_NOTFOUND); 1404 1405 if (flags & NS_LDAP_KEEP_CONN) 1406 cu->keep_conn = B_TRUE; 1407 1408 /* 1409 * We want to use MT connection only if keep-connection flag is 1410 * set or if MT was requested (or active) 1411 */ 1412 if (!((cmg->state == NS_CONN_MGMT_INACTIVE && 1413 cu->keep_conn == B_TRUE) || cmg->state == NS_CONN_MGMT_ACTIVE)) 1414 return (NS_LDAP_NOTFOUND); 1415 1416 /* MT connection will be used now (if possible/available) */ 1417 cu->use_mt_conn = B_TRUE; 1418 1419 NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errorp); 1420 1421 /* first look for a connection already open */ 1422 st = NS_CONN_MT_CONNECTED; 1423 cu->state = NS_CONN_USER_FINDING; 1424 for (i = 0; i < 2; i++) { 1425 for (cn = cmg->cm_head; cn; cn = cn->next) { 1426 (void) mutex_lock(&cn->lock); 1427 rc = match_conn_mt(cu, &cn, st, server, cred); 1428 if (rc == B_FALSE && cn != NULL) /* not found */ 1429 continue; 1430 if (cn == NULL) { /* not found and cn freed */ 1431 /* 1432 * as the conn_mt list could 1433 * be different due to cn's 1434 * deletion, scan the entire 1435 * conn_mt list again 1436 */ 1437 st = NS_CONN_MT_CONNECTED; 1438 i = -1; 1439 break; 1440 } 1441 1442 /* return a connected one if found */ 1443 if (cn->state == NS_CONN_MT_CONNECTED) { 1444 *session = cn->conn; 1445 add_cu2cm(cu, cn); 1446 cu->conn_mt = cn; 1447 cu->state = NS_CONN_USER_CONNECTED; 1448 (void) mutex_unlock(&cn->lock); 1449 (void) mutex_unlock(&cmg->lock); 1450 return (NS_LDAP_SUCCESS); 1451 } 1452 1453 /* 1454 * if cn is not connecting, or allow only 1455 * one user, skip it 1456 */ 1457 if (cn->state != NS_CONN_MT_CONNECTING || 1458 cn->cu_max == 1) { 1459 (void) mutex_unlock(&cn->lock); 1460 continue; 1461 } 1462 1463 /* wait for the connecting conn_mt */ 1464 if (wait_for_conn_mt(cu, cn) != 1) { 1465 /* 1466 * NS_LDAP_NOTFOUND signals that the function 1467 * __s_api_check_libldap_MT_conn_support() 1468 * detected that the lower libldap library 1469 * does not support MT connection, so return 1470 * NS_LDAP_NOTFOUND to let the caller to 1471 * open a non-MT conneciton. Otherwise, 1472 * connect error occurred, return 1473 * NS_CONN_USER_CONNECT_ERROR 1474 */ 1475 if (cn->ns_rc != NS_LDAP_NOTFOUND) 1476 cu->state = NS_CONN_USER_CONNECT_ERROR; 1477 else { 1478 cu->state = NS_CONN_USER_FINDING; 1479 cu->use_mt_conn = B_FALSE; 1480 } 1481 (void) mutex_unlock(&cn->lock); 1482 1483 /* cmg->lock unlocked by wait_for_conn_mt() */ 1484 1485 return (cn->ns_rc); 1486 } 1487 1488 /* return the newly available conn_mt */ 1489 *session = cn->conn; 1490 cu->state = NS_CONN_USER_CONNECTED; 1491 (void) mutex_unlock(&cn->lock); 1492 1493 /* cmg->lock unlocked by wait_for_conn_mt() */ 1494 1495 return (NS_LDAP_SUCCESS); 1496 } 1497 1498 /* next, look for a connecting conn_mt */ 1499 if (i == 0) 1500 st = NS_CONN_MT_CONNECTING; 1501 } 1502 1503 /* no connection found, start opening one */ 1504 cn = init_conn_mt(cmg, errorp); 1505 if (cn == NULL) { 1506 (void) mutex_unlock(&cmg->lock); 1507 return ((*errorp)->status); 1508 } 1509 cu->conn_mt = cn; 1510 cn->opened_for = cu->type; 1511 cn->referral = cu->referral; 1512 if (cmg->ldap_mt == B_TRUE) 1513 cn->cu_max = NS_CONN_MT_USER_MAX; 1514 else 1515 cn->cu_max = 1; 1516 add_cm2cmg(cn, cmg); 1517 (void) mutex_unlock(&cmg->lock); 1518 1519 return (NS_LDAP_NOTFOUND); 1520 } 1521 1522 1523 /* 1524 * add an MT connection to the connection management 1525 * 1526 * Input: 1527 * con : pointer to the Connection info 1528 * cu : pointer to the conn_user structure 1529 * Output: 1530 * ep : hold pointer to error info (ns_ldap_error_t) 1531 */ 1532 int 1533 __s_api_conn_mt_add(Connection *con, ns_conn_user_t *cu, ns_ldap_error_t **ep) 1534 { 1535 ns_conn_mgmt_t *cmg = cu->conn_mgmt; 1536 ns_conn_mt_t *cm = cu->conn_mt; 1537 1538 /* if the conn_mgmt is being shut down, return error */ 1539 NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep); 1540 1541 /* 1542 * start the change monitor thread only if it 1543 * hasn't been started and the process is the 1544 * main nscd (not peruser nscd) 1545 */ 1546 if (cmg->procchg_started == B_FALSE && cmg->is_nscd == B_TRUE) { 1547 start_thread(cmg); 1548 cmg->procchg_started = B_TRUE; 1549 } 1550 (void) mutex_lock(&cm->lock); 1551 cm->conn = con; 1552 cm->state = NS_CONN_MT_CONNECTED; 1553 cm->pid = getpid(); 1554 cm->create_time = time(NULL); 1555 cm->access_time = cm->create_time; 1556 cm->opened_for = cu->type; 1557 add_cu2cm(cu, cm); 1558 cu->conn_mt = cm; 1559 cu->state = NS_CONN_USER_CONNECTED; 1560 if (cmg->ldap_mt == B_TRUE) 1561 cm->cu_max = NS_CONN_MT_USER_MAX; 1562 else 1563 cm->cu_max = 1; 1564 1565 /* wake up the waiters if any */ 1566 (void) conn_signal(cm); 1567 1568 (void) mutex_unlock(&cm->lock); 1569 (void) mutex_unlock(&cmg->lock); 1570 1571 return (NS_LDAP_SUCCESS); 1572 } 1573 1574 /* 1575 * return an MT connection to the pool when a conn user is done using it 1576 * 1577 * Input: 1578 * cu : pointer to the conn_user structure 1579 * Output: NONE 1580 */ 1581 void 1582 __s_api_conn_mt_return(ns_conn_user_t *cu) 1583 { 1584 ns_conn_mt_t *cm; 1585 ns_conn_mgmt_t *cmg; 1586 1587 if (cu == NULL || cu->use_mt_conn == B_FALSE) 1588 return; 1589 cm = cu->conn_mt; 1590 if (cm == NULL) 1591 return; 1592 cmg = cu->conn_mgmt; 1593 1594 (void) mutex_lock(&cm->lock); 1595 del_cu4cm(cu, cm); 1596 cu->state = NS_CONN_USER_DISCONNECTED; 1597 cu->conn_mt = NULL; 1598 cu->bad_mt_conn = B_FALSE; 1599 1600 /* 1601 * if this MT connection is no longer needed, or not usable, and 1602 * no more conn_user uses it, then close it. 1603 */ 1604 1605 if ((cm->close_when_nouser == B_TRUE || 1606 cm->state != NS_CONN_MT_CONNECTED) && cm->cu_cnt == 0) { 1607 (void) mutex_unlock(&cm->lock); 1608 (void) mutex_lock(&cmg->lock); 1609 (void) mutex_lock(&cm->lock); 1610 del_cm4cmg(cm, cmg); 1611 /* use ns_conn_free (instead of 1) to avoid lint warning */ 1612 NS_CONN_UNLOCK_AND_FREE(ns_conn_free, cm, cmg); 1613 } else { 1614 if (cm->state == NS_CONN_MT_CONNECTED && cm->cu_cnt == 0 && 1615 cm->conn != NULL && cm->conn->ld != NULL) { 1616 struct timeval zerotime; 1617 LDAPMessage *res; 1618 1619 zerotime.tv_sec = zerotime.tv_usec = 0L; 1620 /* clean up remaining results just in case */ 1621 while (ldap_result(cm->conn->ld, LDAP_RES_ANY, 1622 LDAP_MSG_ALL, &zerotime, &res) > 0) { 1623 if (res != NULL) 1624 (void) ldap_msgfree(res); 1625 } 1626 } 1627 (void) mutex_unlock(&cm->lock); 1628 } 1629 } 1630 1631 /* save error info (rc and ns_ldap_error_t) in the conn_mt */ 1632 static void 1633 err2cm(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp) { 1634 ns_ldap_error_t *ep; 1635 1636 cm->ns_rc = rc; 1637 cm->ns_error = NULL; 1638 if (errorp != NULL && *errorp != NULL) { 1639 ep = __s_api_copy_error(*errorp); 1640 if (ep == NULL) 1641 cm->ns_rc = NS_LDAP_MEMORY; 1642 else 1643 cm->ns_error = ep; 1644 } 1645 } 1646 1647 /* copy error info (rc and ns_ldap_error_t) from conn_mt to conn_user */ 1648 static void 1649 err_from_cm(ns_conn_user_t *cu, ns_conn_mt_t *cm) { 1650 ns_ldap_error_t *ep; 1651 1652 cu->ns_rc = cm->ns_rc; 1653 if (cu->ns_error != NULL) 1654 (void) __ns_ldap_freeError(&cu->ns_error); 1655 cu->ns_error = NULL; 1656 if (cm->ns_rc != NS_LDAP_SUCCESS && cm->ns_error != NULL) { 1657 ep = __s_api_copy_error(cm->ns_error); 1658 if (ep == NULL) 1659 cu->ns_rc = NS_LDAP_MEMORY; 1660 else 1661 cu->ns_error = ep; 1662 } 1663 } 1664 1665 /* copy error info (rc and ns_ldap_error_t) from caller to conn_user */ 1666 static void 1667 err_from_caller(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp) { 1668 1669 cu->ns_rc = rc; 1670 if (errorp != NULL) { 1671 if (cu->ns_error != NULL) 1672 (void) __ns_ldap_freeError(&cu->ns_error); 1673 cu->ns_error = *errorp; 1674 *errorp = NULL; 1675 } else 1676 cu->ns_error = NULL; 1677 } 1678 1679 /* 1680 * remove an MT connection from the connection management when failed to open 1681 * 1682 * Input: 1683 * cu : pointer to the conn_user structure 1684 * rc : error code 1685 * errorp : pointer to pointer to error info (ns_ldap_error_t) 1686 * Output: 1687 * errorp : set to NULL, if none NULL cm, callers do not need to free it 1688 */ 1689 void 1690 __s_api_conn_mt_remove(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp) 1691 { 1692 ns_conn_mgmt_t *cmg; 1693 ns_conn_mt_t *cm; 1694 int free_cm = 0; 1695 1696 if (cu == NULL || cu->use_mt_conn == B_FALSE) 1697 return; 1698 if ((cm = cu->conn_mt) == NULL) 1699 return; 1700 cmg = cu->conn_mgmt; 1701 1702 (void) mutex_lock(&cmg->lock); 1703 (void) mutex_lock(&cm->lock); 1704 if (cm->state != NS_CONN_MT_CONNECT_ERROR) { 1705 cm->state = NS_CONN_MT_CONNECT_ERROR; 1706 cm->ns_rc = rc; 1707 if (errorp != NULL) { 1708 cm->ns_error = *errorp; 1709 *errorp = NULL; 1710 } 1711 } 1712 1713 /* all the conn_users share the same error rc and ns_ldap_error_t */ 1714 err_from_cm(cu, cm); 1715 /* wake up the waiters if any */ 1716 (void) conn_signal(cm); 1717 1718 del_cu4cm(cu, cm); 1719 cu->conn_mt = NULL; 1720 cu->bad_mt_conn = B_FALSE; 1721 if (cm->cu_cnt == 0) { 1722 del_cm4cmg(cm, cmg); 1723 free_cm = 1; 1724 } 1725 1726 NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg); 1727 } 1728 1729 /* 1730 * check to see if the underlying libldap supports multi-threaded client 1731 * (MT connections) 1732 */ 1733 int 1734 __s_api_check_libldap_MT_conn_support(ns_conn_user_t *cu, LDAP *ld, 1735 ns_ldap_error_t **ep) 1736 { 1737 int rc; 1738 ns_conn_mgmt_t *cmg; 1739 1740 /* if no need to check, just return success */ 1741 if (cu->conn_mt == NULL || cu->use_mt_conn == B_FALSE) 1742 return (NS_LDAP_SUCCESS); 1743 1744 cmg = cu->conn_mgmt; 1745 rc = setup_mt_ld(ld, cmg); 1746 1747 if (cmg->do_mt_conn == B_FALSE) { 1748 /* 1749 * If the conn_mgmt is being shut down, return error. 1750 * if cmg is usable, cmg->lock will be locked. Otherwise, 1751 * this function will return with rc NS_LDAP_OP_FAILED. 1752 */ 1753 NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep); 1754 if (cmg->do_mt_conn == B_FALSE) { 1755 if (rc < 0) 1756 cmg->ldap_mt = B_FALSE; 1757 else { 1758 cmg->ldap_mt = B_TRUE; 1759 if (cmg->is_nscd == B_TRUE || 1760 cmg->is_peruser_nscd == B_TRUE) { 1761 cmg->do_mt_conn = B_TRUE; 1762 cmg->state = NS_CONN_MGMT_ACTIVE; 1763 } 1764 } 1765 } 1766 (void) mutex_unlock(&cmg->lock); 1767 } 1768 1769 if (rc < 0) 1770 __s_api_conn_mt_remove(cu, NS_LDAP_NOTFOUND, NULL); 1771 return (NS_LDAP_SUCCESS); 1772 } 1773 1774 /* 1775 * Close an MT connection. 1776 * Assume cm not null and locked, assume conn_mgmt is also locked. 1777 * Return -1 if error, 1 if the cm should be freed, otherwise 0. 1778 */ 1779 static int 1780 close_conn_mt(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp, 1781 ns_conn_user_t *cu) 1782 { 1783 ns_conn_mgmt_t *cmg = cm->conn_mgmt; 1784 ns_conn_mt_t *m; 1785 ns_conn_user_t *u; 1786 1787 if ((cm->state != NS_CONN_MT_CONNECTED && cm->state != 1788 NS_CONN_MT_CLOSING) || cmg->cm_head == NULL || cmg->cm_cnt == 0) 1789 return (-1); 1790 1791 /* if the conn_mt is not in the MT connection pool, nothing to do */ 1792 for (m = cmg->cm_head; m; m = m->next) { 1793 if (cm == m) 1794 break; 1795 } 1796 if (m == NULL) 1797 return (-1); 1798 1799 if (cm->state == NS_CONN_MT_CONNECTED) { /* first time in here */ 1800 cm->state = NS_CONN_MT_CLOSING; 1801 /* 1802 * If more cu exist to consume the error info, copy 1803 * it to the cm. If the caller calls on behalf of 1804 * a cu, cu won't be NULL. Check to see if there's 1805 * more cu that needs the error info. If caller does 1806 * not have a specific cu attached to it (e.g., 1807 * shutdown_all_conn_mt()), cu is NULL, check if at 1808 * least one cu exists. 1809 */ 1810 if ((cu != NULL && cm->cu_cnt > 1) || 1811 (cu == NULL && cm->cu_cnt > 0)) { 1812 err2cm(cm, rc, errorp); 1813 /* wake up waiter (conn_user) if any */ 1814 (void) conn_signal(cm); 1815 } 1816 1817 /* for each conn_user using the conn_mt, set bad_mt_conn flag */ 1818 if (cm->cu_head != NULL) { 1819 for (u = cm->cu_head; u; u = u->next) { 1820 u->bad_mt_conn = B_TRUE; 1821 if (cmg->shutting_down == B_FALSE) 1822 u->retry = B_TRUE; 1823 } 1824 } 1825 } 1826 1827 /* detach the conn_mt if no more conn_user left */ 1828 if ((cu != NULL && cm->cu_cnt == 1) || 1829 (cu == NULL && cm->cu_cnt == 0)) { 1830 del_cm4cmg(cm, cmg); 1831 cm->detached = B_TRUE; 1832 return (1); 1833 } 1834 1835 return (0); 1836 } 1837 1838 /* 1839 * An MT connection becomes bad, close it and free resources. 1840 * This function is called with a ns_conn_user_t representing 1841 * a user of the MT connection. 1842 * 1843 * Input: 1844 * cu : pointer to the conn_user structure 1845 * rc : error code 1846 * errorp : pointer to pointer to error info (ns_ldap_error_t) 1847 * Output: 1848 * errorp : set to NULL (if no error), callers do not need to free it 1849 */ 1850 void 1851 __s_api_conn_mt_close(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp) 1852 { 1853 ns_conn_mgmt_t *cmg; 1854 ns_conn_mt_t *cm; 1855 int free_cm = 0; 1856 1857 if (cu == NULL || cu->use_mt_conn == B_FALSE) 1858 return; 1859 1860 if (cu->state != NS_CONN_USER_CONNECTED || (cm = cu->conn_mt) == NULL) 1861 return; 1862 cmg = cu->conn_mgmt; 1863 1864 (void) mutex_lock(&cmg->lock); 1865 (void) mutex_lock(&cm->lock); 1866 1867 /* close the MT connection if possible */ 1868 free_cm = close_conn_mt(cm, rc, errorp, cu); 1869 if (free_cm == -1) { /* error case */ 1870 (void) mutex_unlock(&cm->lock); 1871 (void) mutex_unlock(&cmg->lock); 1872 return; 1873 } 1874 1875 if (rc != NS_LDAP_SUCCESS) { /* error info passed in, use it */ 1876 err_from_caller(cu, rc, errorp); 1877 } else { /* error not passed in, use those saved in the conn_mt */ 1878 err_from_cm(cu, cm); 1879 } 1880 1881 /* detach the conn_user from the conn_mt */ 1882 del_cu4cm(cu, cm); 1883 cu->conn_mt = NULL; 1884 cu->bad_mt_conn = B_FALSE; 1885 if (cmg->shutting_down == B_FALSE) 1886 cu->retry = B_TRUE; 1887 NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg); 1888 } 1889 1890 /* 1891 * Close an MT connection when the associated server is known to be 1892 * down. This function is called with a ns_conn_mt_t representing 1893 * the MT connection. That is, the caller is not a conn_user 1894 * thread but rather the procchg thread. 1895 */ 1896 static void 1897 close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg) 1898 { 1899 ns_conn_mgmt_t *cmg; 1900 int free_cm = 0; 1901 ns_ldap_error_t *ep; 1902 1903 if (cm == NULL) 1904 return; 1905 cmg = cm->conn_mgmt; 1906 1907 ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep)); 1908 if (ep != NULL) { 1909 ep->status = rc; 1910 if (errmsg != NULL) 1911 ep->message = strdup(errmsg); /* OK if returns NULL */ 1912 } 1913 1914 (void) mutex_lock(&cmg->lock); 1915 (void) mutex_lock(&cm->lock); 1916 1917 /* close the MT connection if possible */ 1918 free_cm = close_conn_mt(cm, LDAP_SERVER_DOWN, &ep, NULL); 1919 if (free_cm == -1) { /* error case */ 1920 (void) mutex_unlock(&cm->lock); 1921 (void) mutex_unlock(&cmg->lock); 1922 return; 1923 } 1924 (void) __ns_ldap_freeError(&ep); 1925 1926 NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg); 1927 } 1928 1929 /* 1930 * Close an MT connection when there is a better server to connect to. 1931 * Mark the connection as to-be-closed-when-no-one-using so that 1932 * any outstanding ldap operations can run to completion. 1933 * Assume that both the conn_mt and conn_mgmt are locked. 1934 * Return 1 if the conn_mt should be freed. 1935 */ 1936 static int 1937 close_conn_mt_when_nouser(ns_conn_mt_t *cm) 1938 { 1939 int free_cm = 0; 1940 1941 if (cm->cu_cnt == 0) { 1942 del_cm4cmg(cm, cm->conn_mgmt); 1943 free_cm = 1; 1944 } else { 1945 cm->close_when_nouser = B_TRUE; 1946 } 1947 1948 return (free_cm); 1949 } 1950 1951 /* 1952 * Retrieve the configured preferred server list. 1953 * This function locked the conn_mgmt and does not 1954 * unlock at exit. 1955 */ 1956 static void 1957 get_preferred_servers(boolean_t lock, boolean_t reload, ns_conn_mgmt_t *cmg) 1958 { 1959 ns_ldap_error_t *errorp = NULL; 1960 void **pservers = NULL; 1961 1962 if (lock == B_TRUE) 1963 (void) mutex_lock(&cmg->lock); 1964 1965 /* if already done, and no reload, then return */ 1966 if (cmg->pservers_loaded == B_TRUE && reload == B_FALSE) 1967 return; 1968 1969 if (cmg->pservers != NULL) { 1970 (void) __ns_ldap_freeParam((void ***)&cmg->pservers); 1971 cmg->pservers = NULL; 1972 } 1973 1974 if (__ns_ldap_getParam(NS_LDAP_SERVER_PREF_P, 1975 &pservers, &errorp) == NS_LDAP_SUCCESS) { 1976 cmg->pservers = (char **)pservers; 1977 cmg->pservers_loaded = B_TRUE; 1978 } else { 1979 (void) __ns_ldap_freeError(&errorp); 1980 (void) __ns_ldap_freeParam(&pservers); 1981 } 1982 } 1983 1984 /* 1985 * This function handles the config or server status change notification 1986 * from the ldap_cachemgr. 1987 */ 1988 static ns_conn_mgmt_t * 1989 proc_server_change(ns_server_status_change_t *chg, ns_conn_mgmt_t *cmg) 1990 { 1991 int cnt, i, j, k, n; 1992 boolean_t loop = B_TRUE; 1993 boolean_t cmg_locked = B_FALSE; 1994 char *s; 1995 ns_conn_mt_t *cm; 1996 ns_conn_mgmt_t *ocmg; 1997 1998 /* if config changed, reload the configuration */ 1999 if (chg->config_changed == B_TRUE) { 2000 /* reload the conn_mgmt and Native LDAP config */ 2001 ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_RELOAD_CONFIG); 2002 shutdown_all_conn_mt(ocmg); 2003 /* release the one obtained from access_conn_mgmt(RELOAD) */ 2004 (void) release_conn_mgmt(ocmg, B_FALSE); 2005 /* release the one obtained when ocmg was created */ 2006 (void) release_conn_mgmt(ocmg, B_FALSE); 2007 return (ocmg); 2008 } 2009 2010 if ((cnt = chg->num_server) == 0) 2011 return (cmg); 2012 2013 /* handle down servers first */ 2014 for (i = 0; i < cnt; i++) { 2015 2016 if (chg->changes[i] != NS_SERVER_DOWN) 2017 continue; 2018 s = chg->servers[i]; 2019 2020 /* 2021 * look for a CONNECTED MT connection using 2022 * the same server s, and close it 2023 */ 2024 while (loop) { 2025 if (cmg_locked == B_FALSE) { 2026 (void) mutex_lock(&cmg->lock); 2027 cmg_locked = B_TRUE; 2028 } 2029 for (cm = cmg->cm_head; cm; cm = cm->next) { 2030 (void) mutex_lock(&cm->lock); 2031 2032 if (cm->state == NS_CONN_MT_CONNECTED && 2033 cm->conn != NULL && 2034 strcasecmp(cm->conn->serverAddr, s) == 0) { 2035 (void) mutex_unlock(&cm->lock); 2036 break; 2037 } 2038 2039 (void) mutex_unlock(&cm->lock); 2040 } 2041 if (cm != NULL) { 2042 (void) mutex_unlock(&cmg->lock); 2043 cmg_locked = B_FALSE; 2044 close_conn_mt_by_procchg(cm, LDAP_SERVER_DOWN, 2045 NS_CONN_MSG_DOWN_FROM_CACHEMGR); 2046 /* 2047 * Process the next cm using server s. 2048 * Start from the head of the cm linked 2049 * list again, as the cm list may change 2050 * after close_conn_mt_by_procchg() is done. 2051 */ 2052 continue; 2053 } 2054 2055 /* 2056 * No (more) MT connection using the down server s. 2057 * Process the next server on the list. 2058 */ 2059 break; 2060 } /* while loop */ 2061 } 2062 2063 /* 2064 * Next handle servers whose status changed to up. 2065 * Get the preferred server list first if not done yet. 2066 * get_preferred_servers() leaves conn_mgmt locked. 2067 */ 2068 get_preferred_servers(cmg_locked == B_FALSE ? B_TRUE : B_FALSE, 2069 B_FALSE, cmg); 2070 cmg_locked = B_TRUE; 2071 /* 2072 * if no preferred server configured, we don't switch MT connection 2073 * to a more preferred server (i.e., fallback), so just return 2074 */ 2075 if (cmg->pservers == NULL) { 2076 (void) mutex_unlock(&cmg->lock); 2077 return (cmg); 2078 } 2079 2080 /* for each server that is up now */ 2081 for (i = 0; i < cnt; i++) { 2082 if (chg->changes[i] != NS_SERVER_UP) 2083 continue; 2084 s = chg->servers[i]; 2085 2086 /* 2087 * look for a CONNECTED MT connection which uses 2088 * a server less preferred than s, and treat it 2089 * as 'fallback needed' by calling 2090 * close_conn_mt_when_nouser() 2091 */ 2092 k = -1; 2093 loop = B_TRUE; 2094 while (loop) { 2095 if (cmg_locked == B_FALSE) { 2096 (void) mutex_lock(&cmg->lock); 2097 cmg_locked = B_TRUE; 2098 } 2099 2100 /* Is s a preferred server ? */ 2101 if (k == -1) { 2102 for (j = 0; cmg->pservers[j] != NULL; j++) { 2103 if (strcasecmp(cmg->pservers[j], 2104 s) == 0) { 2105 k = j; 2106 break; 2107 } 2108 } 2109 } 2110 /* skip s if not a preferred server */ 2111 if (k == -1) { 2112 break; 2113 } 2114 2115 /* check each MT connection */ 2116 for (cm = cmg->cm_head; cm; cm = cm->next) { 2117 (void) mutex_lock(&cm->lock); 2118 /* 2119 * Find an MT connection that is connected and 2120 * not marked, but leave WRITE or REFERRAL 2121 * connections alone, since fallback does not 2122 * make sense for them. 2123 */ 2124 if (cm->state == NS_CONN_MT_CONNECTED && 2125 cm->close_when_nouser == B_FALSE && 2126 cm->conn != NULL && cm->opened_for != 2127 NS_CONN_USER_WRITE && 2128 cm->referral == B_FALSE) { 2129 n = -1; 2130 /* 2131 * j < k ??? should we close 2132 * an active MT that is using s ? 2133 * ie could s went down and up 2134 * again, but cm is bound prior to 2135 * the down ? Play safe here, 2136 * and check j <= k. 2137 */ 2138 for (j = 0; j <= k; j++) { 2139 if (strcasecmp( 2140 cm->conn->serverAddr, 2141 cmg->pservers[j]) == 0) { 2142 n = j; 2143 break; 2144 } 2145 } 2146 /* 2147 * s is preferred, if its location 2148 * in the preferred server list is 2149 * ahead of that of the server 2150 * used by the cm (i.e., no match 2151 * found before s) 2152 */ 2153 if (n == -1) { /* s is preferred */ 2154 int fr = 0; 2155 fr = close_conn_mt_when_nouser( 2156 cm); 2157 NS_CONN_UNLOCK_AND_FREE(fr, 2158 cm, cmg); 2159 cmg_locked = B_FALSE; 2160 /* 2161 * break, not continue, 2162 * because we need to 2163 * check the entire cm 2164 * list again. The call 2165 * above may change the 2166 * cm list. 2167 */ 2168 break; 2169 } 2170 } 2171 (void) mutex_unlock(&cm->lock); 2172 } 2173 /* if no (more) cm using s, check next server */ 2174 if (cm == NULL) 2175 loop = B_FALSE; 2176 } /* while loop */ 2177 } 2178 if (cmg_locked == B_TRUE) 2179 (void) mutex_unlock(&cmg->lock); 2180 return (cmg); 2181 } 2182 2183 /* Shut down all MT connection managed by the connection management */ 2184 void 2185 shutdown_all_conn_mt(ns_conn_mgmt_t *cmg) 2186 { 2187 ns_ldap_error_t *ep; 2188 ns_conn_mt_t *cm; 2189 int free_cm = 0; 2190 boolean_t done = B_FALSE; 2191 2192 ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep)); 2193 if (ep != NULL) { /* if NULL, not a problem */ 2194 /* OK if returns NULL */ 2195 ep->message = strdup(NS_CONN_MSG_SHUTDOWN_RELOADED); 2196 } 2197 2198 (void) mutex_lock(&cmg->lock); 2199 while (cmg->cm_head != NULL && done == B_FALSE) { 2200 for (cm = cmg->cm_head; cm; cm = cm->next) { 2201 (void) mutex_lock(&cm->lock); 2202 if (cm->next == NULL) 2203 done = B_TRUE; 2204 /* shut down each conn_mt, ignore errors */ 2205 free_cm = close_conn_mt(cm, LDAP_OTHER, &ep, NULL); 2206 (void) mutex_unlock(&cm->lock); 2207 if (free_cm == 1) { 2208 (void) free_conn_mt(cm, 0); 2209 /* 2210 * conn_mt may change, so start from 2211 * top of list again 2212 */ 2213 break; 2214 } 2215 } 2216 } 2217 (void) mutex_unlock(&cmg->lock); 2218 (void) __ns_ldap_freeError(&ep); 2219 } 2220 2221 /* free all the resources used by the connection management */ 2222 void 2223 __s_api_shutdown_conn_mgmt() 2224 { 2225 ns_conn_mgmt_t *cmg; 2226 2227 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_SHUTDOWN); 2228 if (cmg == NULL) /* already being SHUT done */ 2229 return; 2230 2231 (void) shutdown_all_conn_mt(cmg); 2232 (void) release_conn_mgmt(cmg, B_FALSE); 2233 2234 /* then destroy the conn_mgmt */ 2235 (void) release_conn_mgmt(cmg, B_FALSE); 2236 } 2237 2238 2239 /* 2240 * Reinitialize the libsldap connection management after 2241 * a new native LDAP configuration is received. 2242 */ 2243 void 2244 __s_api_reinit_conn_mgmt_new_config(ns_config_t *new_cfg) 2245 { 2246 ns_conn_mgmt_t *cmg; 2247 ns_conn_mgmt_t *ocmg; 2248 2249 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF); 2250 if (cmg == NULL) 2251 return; 2252 if (cmg->config == new_cfg || cmg->state == NS_CONN_MGMT_DETACHED) { 2253 (void) release_conn_mgmt(cmg, B_FALSE); 2254 return; 2255 } 2256 2257 /* reload the conn_mgmt and native LDAP config */ 2258 ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_NEW_CONFIG); 2259 if (ocmg == cmg) 2260 shutdown_all_conn_mt(ocmg); 2261 /* release the one obtained from access_conn_mgmt(RELOAD) */ 2262 (void) release_conn_mgmt(ocmg, B_FALSE); 2263 /* release the one obtained when ocmg was created */ 2264 (void) release_conn_mgmt(ocmg, B_FALSE); 2265 /* release the one obtained when this function is entered */ 2266 (void) release_conn_mgmt(cmg, B_FALSE); 2267 } 2268 2269 /* 2270 * Prepare to retry ldap search operation if needed. 2271 * Return 1 if retry is needed, otherwise 0. 2272 * If first time in, return 1. If not, return 1 if: 2273 * - not a NS_CONN_USER_GETENT conn_user AND 2274 * - have not retried 3 times yet AND 2275 * - previous search failed AND 2276 * - the retry flag is set in the ns_conn_user_t or config was reloaded 2277 */ 2278 int 2279 __s_api_setup_retry_search(ns_conn_user_t **conn_user, 2280 ns_conn_user_type_t type, int *try_cnt, int *rc, 2281 ns_ldap_error_t **errorp) 2282 { 2283 boolean_t retry; 2284 ns_conn_user_t *cu = *conn_user; 2285 ns_conn_mgmt_t *cmg; 2286 2287 if (*try_cnt > 0 && cu != NULL) { 2288 /* 2289 * if called from firstEntry(), keep conn_mt for 2290 * the subsequent getnext requests 2291 */ 2292 if (cu->type == NS_CONN_USER_GETENT && *rc == NS_LDAP_SUCCESS) 2293 return (0); 2294 cmg = cu->conn_mgmt; 2295 retry = cu->retry; 2296 if (cu->conn_mt != NULL) 2297 __s_api_conn_mt_return(cu); 2298 if (cmg != NULL && cmg->cfg_reloaded == B_TRUE) 2299 retry = B_TRUE; 2300 __s_api_conn_user_free(cu); 2301 *conn_user = NULL; 2302 2303 if (*rc == NS_LDAP_SUCCESS || retry != B_TRUE) 2304 return (0); 2305 } 2306 2307 *try_cnt = *try_cnt + 1; 2308 if (*try_cnt > NS_LIST_TRY_MAX) 2309 return (0); 2310 2311 *conn_user = __s_api_conn_user_init(type, NULL, B_FALSE); 2312 if (*conn_user == NULL) { 2313 if (*try_cnt == 1) { /* first call before any retry */ 2314 *rc = NS_LDAP_MEMORY; 2315 *errorp = NULL; 2316 } 2317 /* for 1+ try, use previous rc and errorp */ 2318 return (0); 2319 } 2320 2321 /* free ldap_error_t from previous search */ 2322 if (*try_cnt > 1 && rc != NS_LDAP_SUCCESS && *errorp != NULL) 2323 (void) __ns_ldap_freeError(errorp); 2324 2325 return (1); 2326 } 2327 2328 /* prepare to get the next entry for an enumeration */ 2329 int 2330 __s_api_setup_getnext(ns_conn_user_t *cu, int *ns_err, 2331 ns_ldap_error_t **errorp) 2332 { 2333 int rc; 2334 ns_conn_mgmt_t *cmg; 2335 2336 /* 2337 * if using an MT connection, ensure the thread-specific data are set, 2338 * but if the MT connection is no longer good, return the error saved. 2339 */ 2340 if (cu->conn_mt != NULL && (cmg = cu->conn_mgmt) != NULL) { 2341 2342 if (cu->bad_mt_conn == B_TRUE) { 2343 __s_api_conn_mt_close(cu, 0, NULL); 2344 *ns_err = cu->ns_rc; 2345 *errorp = cu->ns_error; 2346 cu->ns_error = NULL; 2347 return (*ns_err); 2348 } 2349 2350 rc = conn_tsd_check(cmg); 2351 if (rc != NS_LDAP_SUCCESS) { 2352 *errorp = NULL; 2353 return (rc); 2354 } 2355 } 2356 2357 return (NS_LDAP_SUCCESS); 2358 } 2359 2360 /* wait for an MT connection to become available */ 2361 static int 2362 conn_wait(ns_conn_mt_t *conn_mt, ns_conn_user_t *conn_user) 2363 { 2364 ns_conn_waiter_t mywait; 2365 ns_conn_waiter_t *head = &conn_mt->waiter; 2366 2367 (void) cond_init(&(mywait.waitcv), USYNC_THREAD, 0); 2368 mywait.key = conn_user; 2369 mywait.signaled = 0; 2370 mywait.next = head->next; 2371 mywait.prev = head; 2372 if (mywait.next) 2373 mywait.next->prev = &mywait; 2374 head->next = &mywait; 2375 atomic_inc_uint(&conn_mt->waiter_cnt); 2376 2377 while (!mywait.signaled) 2378 (void) cond_wait(&(mywait.waitcv), &conn_mt->lock); 2379 if (mywait.prev) 2380 mywait.prev->next = mywait.next; 2381 if (mywait.next) 2382 mywait.next->prev = mywait.prev; 2383 return (0); 2384 } 2385 2386 /* signal that an MT connection is now available */ 2387 static int 2388 conn_signal(ns_conn_mt_t *conn_mt) 2389 { 2390 int c = 0; 2391 ns_conn_waiter_t *head = &conn_mt->waiter; 2392 ns_conn_waiter_t *tmp = head->next; 2393 2394 while (tmp) { 2395 (void) cond_signal(&(tmp->waitcv)); 2396 tmp->signaled = 1; 2397 atomic_dec_uint(&conn_mt->waiter_cnt); 2398 c++; 2399 tmp = tmp->next; 2400 } 2401 2402 return (c); 2403 } 2404 2405 /* 2406 * wait and process the server status and/or config change notification 2407 * from ldap_cachemgr 2408 */ 2409 static void * 2410 get_server_change(void *arg) 2411 { 2412 union { 2413 ldap_data_t s_d; 2414 char s_b[DOORBUFFERSIZE]; 2415 } space; 2416 ldap_data_t *sptr = &space.s_d; 2417 int ndata; 2418 int adata; 2419 char *ptr; 2420 int ds_cnt; 2421 int door_rc; 2422 int which; 2423 int retry = 0; 2424 boolean_t loop = B_TRUE; 2425 char *c, *oc; 2426 int dslen = strlen(DOORLINESEP); 2427 char dsep = DOORLINESEP_CHR; 2428 char chg_data[DOORBUFFERSIZE]; 2429 char **servers = NULL; 2430 boolean_t getchg_not_supported = B_FALSE; 2431 ns_conn_mgmt_t *ocmg = (ns_conn_mgmt_t *)arg; 2432 ns_conn_mgmt_t *cmg; 2433 ns_server_status_t *status = NULL; 2434 ns_server_status_change_t chg = { 0 }; 2435 ldap_get_change_out_t *get_chg; 2436 ldap_get_chg_cookie_t cookie; 2437 ldap_get_chg_cookie_t new_cookie; 2438 2439 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF); 2440 if (cmg != ocmg) 2441 thr_exit(NULL); 2442 /* cmg is locked before called */ 2443 cmg->procchg_tid = thr_self(); 2444 2445 /* make sure the thread specific data are set */ 2446 (void) conn_tsd_setup(cmg); 2447 cookie = cmg->cfg_cookie; 2448 2449 while (loop) { 2450 2451 if (chg.servers != NULL) 2452 free(chg.servers); 2453 if (chg.changes != NULL) 2454 free(chg.changes); 2455 if (sptr != &space.s_d) 2456 (void) munmap((char *)sptr, sizeof (space)); 2457 2458 /* 2459 * If the attached conn_mgmt has been deleted, 2460 * then exit. The new conn_mgmt will starts it 2461 * own monitor thread later. If libsldap is being 2462 * unloaded or configuration reloaded, OR 2463 * ldap_cachemgr rejected the GETSTATUSCHANGE door 2464 * call, then exit as well. 2465 */ 2466 if (cmg == NULL || cmg->state == NS_CONN_MGMT_DETACHED || 2467 getchg_not_supported == B_TRUE) { 2468 2469 if (cmg != NULL) { 2470 cmg->procchg_started = B_FALSE; 2471 (void) release_conn_mgmt(cmg, B_FALSE); 2472 } 2473 2474 conn_tsd_free(); 2475 thr_exit(NULL); 2476 } 2477 2478 (void) memset(space.s_b, 0, DOORBUFFERSIZE); 2479 (void) memset(&chg, 0, sizeof (chg)); 2480 adata = sizeof (ldap_call_t) + 1; 2481 ndata = sizeof (space); 2482 space.s_d.ldap_call.ldap_callnumber = GETSTATUSCHANGE; 2483 space.s_d.ldap_call.ldap_u.get_change.op = 2484 NS_STATUS_CHANGE_OP_START; 2485 space.s_d.ldap_call.ldap_u.get_change.cookie = cookie; 2486 sptr = &space.s_d; 2487 door_rc = __ns_ldap_trydoorcall_getfd(); 2488 cmg->procchg_door_call = B_TRUE; 2489 if (release_conn_mgmt(cmg, B_FALSE) == NULL) { 2490 conn_tsd_free(); 2491 thr_exit(NULL); 2492 } 2493 2494 if (door_rc == NS_CACHE_SUCCESS) 2495 door_rc = __ns_ldap_trydoorcall_send(&sptr, &ndata, 2496 &adata); 2497 2498 /* 2499 * Check and see if the conn_mgmt is still current. 2500 * If not, no need to continue. 2501 */ 2502 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF); 2503 if (cmg != NULL) 2504 cmg->procchg_door_call = B_FALSE; 2505 if (cmg != ocmg) { 2506 if (cmg != NULL) { 2507 cmg->procchg_started = B_FALSE; 2508 (void) release_conn_mgmt(cmg, B_FALSE); 2509 } 2510 conn_tsd_free(); 2511 thr_exit(NULL); 2512 } 2513 2514 if (door_rc != NS_CACHE_SUCCESS) { 2515 if (door_rc == NS_CACHE_NOSERVER) { 2516 if (retry++ > 10) 2517 getchg_not_supported = B_TRUE; 2518 else { 2519 /* 2520 * ldap_cachemgr may be down, give 2521 * it time to restart 2522 */ 2523 (void) sleep(2); 2524 } 2525 } else if (door_rc == NS_CACHE_NOTFOUND) 2526 getchg_not_supported = B_TRUE; 2527 continue; 2528 } else 2529 retry = 0; 2530 2531 /* copy info from door call return structure */ 2532 get_chg = &sptr->ldap_ret.ldap_u.changes; 2533 ptr = get_chg->data; 2534 /* configuration change ? */ 2535 if (get_chg->type == NS_STATUS_CHANGE_TYPE_CONFIG) { 2536 chg.config_changed = B_TRUE; 2537 cmg = proc_server_change(&chg, cmg); 2538 continue; 2539 } 2540 2541 /* server status changes ? */ 2542 if (get_chg->type == NS_STATUS_CHANGE_TYPE_SERVER) { 2543 /* 2544 * first check cookies, if don't match, config 2545 * has changed 2546 */ 2547 new_cookie = get_chg->cookie; 2548 if (new_cookie.mgr_pid != cookie.mgr_pid || 2549 new_cookie.seq_num != cookie.seq_num) { 2550 chg.config_changed = B_TRUE; 2551 cmg = proc_server_change(&chg, cmg); 2552 continue; 2553 } 2554 2555 (void) strlcpy(chg_data, ptr, sizeof (chg_data)); 2556 chg.num_server = get_chg->server_count; 2557 2558 servers = (char **)calloc(chg.num_server, 2559 sizeof (char *)); 2560 if (servers == NULL) { 2561 syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR); 2562 continue; 2563 } 2564 status = (ns_server_status_t *)calloc(chg.num_server, 2565 sizeof (int)); 2566 if (status == NULL) { 2567 syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR); 2568 free(servers); 2569 continue; 2570 } 2571 ds_cnt = 0; 2572 which = 0; 2573 oc = ptr; 2574 for (c = ptr; which != 2; c++) { 2575 /* look for DOORLINESEP or end of string */ 2576 if (*c != dsep && *c != '\0') 2577 continue; 2578 if (*c == dsep) { /* DOORLINESEP */ 2579 *c = '\0'; /* current value */ 2580 c += dslen; /* skip to next value */ 2581 } 2582 if (which == 0) { /* get server info */ 2583 servers[ds_cnt] = oc; 2584 oc = c; 2585 which = 1; /* get status next */ 2586 continue; 2587 } 2588 /* which == 1, get up/down status */ 2589 if (strcmp(NS_SERVER_CHANGE_UP, oc) == 0) { 2590 status[ds_cnt] = NS_SERVER_UP; 2591 } else if (strcmp(NS_SERVER_CHANGE_DOWN, 2592 oc) == 0) 2593 status[ds_cnt] = NS_SERVER_DOWN; 2594 else { 2595 syslog(LOG_INFO, 2596 NS_CONN_MSG_BAD_CACHEMGR_DATA); 2597 continue; 2598 } 2599 oc = c; 2600 ds_cnt++; 2601 if (*c == '\0') 2602 which = 2; /* exit the loop */ 2603 else 2604 which = 0; /* get server info next */ 2605 } 2606 chg.servers = servers; 2607 chg.changes = status; 2608 cmg = proc_server_change(&chg, cmg); 2609 continue; 2610 } 2611 } 2612 2613 return (NULL); 2614 } 2615 2616 /* start the thread handling the change notification from ldap_cachemgr */ 2617 static void 2618 start_thread(ns_conn_mgmt_t *cmg) { 2619 2620 int errnum; 2621 2622 /* 2623 * start a thread to get and process config and server status changes 2624 */ 2625 if (thr_create(NULL, NULL, get_server_change, 2626 (void *)cmg, THR_DETACHED, NULL) != 0) { 2627 errnum = errno; 2628 syslog(LOG_WARNING, NS_CONN_MSG_NO_PROCCHG_THREAD, 2629 strerror(errnum)); 2630 } 2631 } 2632