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