dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libsldap / common / ns_connmgmt.c
blob0958c9210a9be9ea3e849b9dddff5d6990c7cb7b
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <string.h>
27 #include <errno.h>
28 #include <syslog.h>
29 #include <procfs.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <libintl.h>
33 #include <atomic.h>
34 #include <pthread.h>
35 #include <sys/mman.h>
36 #include <time.h>
37 #include "solaris-int.h"
38 #include "ns_connmgmt.h"
39 #include "ns_cache_door.h"
40 #include "ns_internal.h"
43 * Access (reference, shutdown, or reload) the current connection
44 * management control structure conn_mgmt_t.
46 #define NS_CONN_MGMT_OP_REF 1
47 #define NS_CONN_MGMT_OP_SHUTDOWN 2
48 #define NS_CONN_MGMT_OP_RELOAD_CONFIG 3
49 #define NS_CONN_MGMT_OP_NEW_CONFIG 4
50 #define NS_CONN_MGMT_OP_LIB_INIT 5
52 static ns_conn_mgmt_t *access_conn_mgmt(int);
53 static ns_conn_mgmt_t *release_conn_mgmt(ns_conn_mgmt_t *, boolean_t);
54 static int close_conn_mt(ns_conn_mt_t *, int, ns_ldap_error_t **,
55 ns_conn_user_t *);
56 static int close_conn_mt_when_nouser(ns_conn_mt_t *cm);
57 void shutdown_all_conn_mt(ns_conn_mgmt_t *cmg);
58 static int conn_signal(ns_conn_mt_t *);
59 static int conn_wait(ns_conn_mt_t *, ns_conn_user_t *);
60 static void close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg);
61 static ns_conn_mgmt_t *proc_server_change(ns_server_status_change_t *chg,
62 ns_conn_mgmt_t *cmg);
63 static void get_preferred_servers(boolean_t, boolean_t, ns_conn_mgmt_t *);
64 static void start_thread();
66 static ns_conn_mgmt_t *ns_connmgmt = NULL;
67 static ns_conn_mgmt_t *ns_connmgmt_parent = NULL;
68 static mutex_t ns_connmgmt_lock = DEFAULTMUTEX;
69 static boolean_t ns_connmgmt_shutting_down = B_FALSE;
71 #define NS_CONN_MSG_NO_CONN_MGMT gettext( \
72 "libsldap: unable to allocate the connection management control")
73 #define NS_CONN_MSG_NO_MTC_KEY gettext( \
74 "libsldap: unable to allocate the TSD key for per-thread ldap error")
75 #define NS_CONN_MSG_NO_CMG_KEY gettext( \
76 "libsldap: unable to allocate the TSD key for connection management")
77 #define NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded")
78 #define NS_CONN_MSG_RELOADED gettext( \
79 "libsldap: configuration has been reloaded")
80 #define NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \
81 "libsldap: library unloaded or configuration has been reloaded")
82 #define NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \
83 "libsldap: received incorrect data from ldap_cachemgr")
84 #define NS_CONN_MSG_MEMORY_ERROR gettext( \
85 "libsldap: unable to allocate memory")
86 #define NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \
87 "libsldap: unable to start the server monitor thread (%s)")
88 #define NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \
89 "libsldap: server down reported by ldap_cachemgr")
91 static int ns_conn_free = 1;
92 #define NS_CONN_UNLOCK_AND_FREE(free, cm, cmg) \
93 { \
94 (void) mutex_unlock(&(cm)->lock); \
95 if (free == 1) \
96 cmg = free_conn_mt((cm), 1); \
97 if (cmg != NULL) \
98 (void) mutex_unlock(&(cmg)->lock); \
101 #define NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \
103 char *msg = NULL; \
104 (void) mutex_lock(&(cmg)->lock); \
105 if ((cmg)->shutting_down == B_TRUE) \
106 msg = NS_CONN_MSG_SHUTDOWN; \
107 else if ((cmg)->cfg_reloaded == B_TRUE) \
108 msg = NS_CONN_MSG_RELOADED; \
109 if (msg != NULL) { \
110 (*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \
111 (void) mutex_unlock(&(cmg)->lock); \
112 return (NS_LDAP_OP_FAILED); \
117 * TSD keys ns_mtckey and ns_cmgkey are for sharing ldap connections
118 * and their associated connection management structure among
119 * multiple threads. The pointers to the per-thread ldap error
120 * information and the connection management structure are
121 * saved in ns_mtckey and ns_cmgkey.
123 thread_key_t ns_mtckey = THR_ONCE_KEY;
124 thread_key_t ns_cmgkey = THR_ONCE_KEY;
126 /* Per thread LDAP error resides in thread-specific data (ns_mtckey) */
127 struct ldap_error {
128 int le_errno;
129 char *le_matched;
130 char *le_errmsg;
133 /* NULL struct ldap_error */
134 static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
136 /* destructor: free the ldap error data in the thread specific area */
137 static void
138 ns_mtckey_cleanup(void *key) {
139 struct ldap_error *le = (struct ldap_error *)key;
141 if (le == NULL)
142 return;
143 if (le->le_matched != NULL) {
144 ldap_memfree(le->le_matched);
146 if (le->le_errmsg != NULL) {
147 ldap_memfree(le->le_errmsg);
149 free(le);
152 /* Free/detach the thread specific data structures */
153 static void
154 conn_tsd_free() {
155 void *tsd = NULL;
156 int rc;
158 /* free the per-thread ldap error info */
159 rc = thr_getspecific(ns_mtckey, &tsd);
160 if (rc == 0 && tsd != NULL)
161 ns_mtckey_cleanup(tsd);
162 (void) thr_setspecific(ns_mtckey, NULL);
164 /* detach the connection management control */
165 (void) thr_setspecific(ns_cmgkey, NULL);
168 /* per-thread callback function for allocating a mutex */
169 static void *
170 ns_mutex_alloc(void)
172 mutex_t *mutexp = NULL;
174 if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
175 if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
176 free(mutexp);
177 mutexp = NULL;
180 return (mutexp);
183 /* per-thread callback function for freeing a mutex */
184 static void
185 ns_mutex_free(void *mutexp)
187 (void) mutex_destroy((mutex_t *)mutexp);
188 free(mutexp);
192 * Function for setting up thread-specific data
193 * where per thread LDAP error and the pointer
194 * to the active connection management control
195 * are stored.
197 static int
198 conn_tsd_setup(ns_conn_mgmt_t *cmg)
200 void *tsd;
201 int rc;
203 rc = thr_setspecific(ns_cmgkey, cmg);
204 if (rc != 0) /* must be ENOMEM */
205 return (-1);
207 /* return success if the ns_mtckey TSD is already set */
208 rc = thr_getspecific(ns_mtckey, &tsd);
209 if (rc == 0 && tsd != NULL)
210 return (0);
212 /* allocate and set the ns_mtckey TSD */
213 tsd = (void *) calloc(1, sizeof (struct ldap_error));
214 if (tsd == NULL)
215 return (-1);
216 rc = thr_setspecific(ns_mtckey, tsd);
217 if (rc != 0) { /* must be ENOMEM */
218 free(tsd);
219 return (-1);
221 return (0);
224 /* Callback function for setting the per thread LDAP error */
225 /*ARGSUSED*/
226 static void
227 set_ld_error(int err, char *matched, char *errmsg, void *dummy)
229 struct ldap_error *le;
230 int eno;
232 if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
233 syslog(LOG_ERR, gettext(
234 "libsldap: set_ld_error: thr_getspecific failed (%s)."),
235 strerror(eno));
236 return;
239 /* play safe, do nothing if TSD pointer is NULL */
240 if (le == NULL) {
241 syslog(LOG_INFO, gettext(
242 "libsldap: set_ld_error: TSD pointer is NULL."));
243 return;
246 le->le_errno = err;
248 if (le->le_matched != NULL) {
249 ldap_memfree(le->le_matched);
250 le->le_matched = NULL;
252 le->le_matched = matched;
254 if (le->le_errmsg != NULL) {
255 ldap_memfree(le->le_errmsg);
256 le->le_errmsg = NULL;
258 le->le_errmsg = errmsg;
261 /* check and allocate the thread-specific data for using a MT connection */
262 static int
263 conn_tsd_check(ns_conn_mgmt_t *cmg)
265 if (conn_tsd_setup(cmg) != 0)
266 return (NS_LDAP_MEMORY);
268 return (NS_LDAP_SUCCESS);
271 /* Callback function for getting the per thread LDAP error */
272 /*ARGSUSED*/
273 static int
274 get_ld_error(char **matched, char **errmsg, void *dummy)
276 struct ldap_error *le;
277 int eno;
279 if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
280 syslog(LOG_ERR, gettext(
281 "libsldap: get_ld_error: thr_getspecific failed (%s)"),
282 strerror(eno));
283 return (eno);
286 /* play safe, return NULL error data, if TSD pointer is NULL */
287 if (le == NULL)
288 le = &ldap_error_NULL;
290 if (matched != NULL) {
291 *matched = le->le_matched;
293 if (errmsg != NULL) {
294 *errmsg = le->le_errmsg;
296 return (le->le_errno);
299 /* Callback function for setting per thread errno */
300 static void
301 set_errno(int err)
303 errno = err;
306 /* Callback function for getting per thread errno */
307 static int
308 get_errno(void)
310 return (errno);
313 /* set up an ldap session 'ld' for sharing among multiple threads */
314 static int
315 setup_mt_conn(LDAP *ld)
318 struct ldap_thread_fns tfns;
319 struct ldap_extra_thread_fns extrafns;
320 int rc;
323 * Set the function pointers for dealing with mutexes
324 * and error information
326 (void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
327 tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
328 tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
329 tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
330 tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
331 tfns.ltf_get_errno = get_errno;
332 tfns.ltf_set_errno = set_errno;
333 tfns.ltf_get_lderrno = get_ld_error;
334 tfns.ltf_set_lderrno = set_ld_error;
335 tfns.ltf_lderrno_arg = NULL;
338 * Set up the ld to use those function pointers
340 rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
341 (void *) &tfns);
342 if (rc < 0) {
343 syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
344 "(LDAP_OPT_THREAD_FN_PTRS)"));
345 return (0);
349 * Set the function pointers for working with semaphores
351 (void) memset(&extrafns, '\0',
352 sizeof (struct ldap_extra_thread_fns));
353 extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
354 extrafns.ltf_mutex_trylock = NULL;
355 extrafns.ltf_sema_alloc = NULL;
356 extrafns.ltf_sema_free = NULL;
357 extrafns.ltf_sema_wait = NULL;
358 extrafns.ltf_sema_post = NULL;
360 /* Set up the ld to use those function pointers */
361 rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
362 (void *) &extrafns);
363 if (rc < 0) {
364 syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
365 "(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"));
366 return (0);
369 return (1);
372 /* set up an MT connection for sharing among multiple threads */
373 static int
374 setup_mt_ld(LDAP *ld, ns_conn_mgmt_t *cmg)
376 thread_t t = thr_self();
378 /* set up the per-thread data for using the MT connection */
379 if (conn_tsd_setup(cmg) == -1) {
380 syslog(LOG_WARNING,
381 gettext("libsldap: tid= %d: unable to set up TSD\n"), t);
382 return (-1);
385 if (setup_mt_conn(ld) == 0) {
386 /* multiple threads per connection not supported */
387 syslog(LOG_WARNING, gettext("libsldap: tid= %d: multiple "
388 "threads per connection not supported\n"), t);
389 conn_tsd_free();
390 return (-1);
392 return (0);
396 * Check name and UID of process, if it is nscd.
398 * Input:
399 * pid : PID of checked process
400 * check_uid : check if UID == 0
401 * Output:
402 * B_TRUE : nscd detected
403 * B_FALSE : nscd not confirmed
405 static boolean_t
406 check_nscd_proc(pid_t pid, boolean_t check_uid)
408 psinfo_t pinfo;
409 char fname[MAXPATHLEN];
410 ssize_t ret;
411 int fd;
413 if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
414 if ((fd = open(fname, O_RDONLY)) >= 0) {
415 ret = read(fd, &pinfo, sizeof (psinfo_t));
416 (void) close(fd);
417 if ((ret == sizeof (psinfo_t)) &&
418 (strcmp(pinfo.pr_fname, "nscd") == 0)) {
419 if (check_uid && (pinfo.pr_uid != 0))
420 return (B_FALSE);
421 return (B_TRUE);
425 return (B_FALSE);
429 * Check if this process is peruser nscd.
431 boolean_t
432 __s_api_peruser_proc(void)
434 pid_t my_ppid;
435 static mutex_t nscdLock = DEFAULTMUTEX;
436 static pid_t checkedPpid = (pid_t)-1;
437 static boolean_t isPeruserNscd = B_FALSE;
439 my_ppid = getppid();
442 * Already checked before for this process? If yes, return cached
443 * response.
445 if (my_ppid == checkedPpid) {
446 return (isPeruserNscd);
449 (void) mutex_lock(&nscdLock);
451 /* Check once more incase another thread has just complete this. */
452 if (my_ppid == checkedPpid) {
453 (void) mutex_unlock(&nscdLock);
454 return (isPeruserNscd);
457 /* Reinitialize to be sure there is no residue after fork. */
458 isPeruserNscd = B_FALSE;
460 /* Am I the nscd process? */
461 if (check_nscd_proc(getpid(), B_FALSE)) {
462 /* Is my parent the nscd process with UID == 0. */
463 isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
466 /* Remember for whom isPeruserNscd is. */
467 checkedPpid = my_ppid;
469 (void) mutex_unlock(&nscdLock);
470 return (isPeruserNscd);
474 * Check if this process is main nscd.
476 boolean_t
477 __s_api_nscd_proc(void)
479 pid_t my_pid;
480 static mutex_t nscdLock = DEFAULTMUTEX;
481 static pid_t checkedPid = (pid_t)-1;
482 static boolean_t isMainNscd = B_FALSE;
485 * Don't bother checking if this process isn't root, this cannot
486 * be main nscd.
488 if (getuid() != 0)
489 return (B_FALSE);
491 my_pid = getpid();
494 * Already checked before for this process? If yes, return cached
495 * response.
497 if (my_pid == checkedPid) {
498 return (isMainNscd);
501 (void) mutex_lock(&nscdLock);
503 /* Check once more incase another thread has just done this. */
504 if (my_pid == checkedPid) {
505 (void) mutex_unlock(&nscdLock);
506 return (isMainNscd);
510 * Am I the nscd process? UID is already checked, not needed from
511 * psinfo.
513 isMainNscd = check_nscd_proc(my_pid, B_FALSE);
515 /* Remember for whom isMainNscd is. */
516 checkedPid = my_pid;
518 (void) mutex_unlock(&nscdLock);
519 return (isMainNscd);
523 * initialize a connection management control structure conn_mgmt_t
525 ns_conn_mgmt_t *
526 init_conn_mgmt()
528 ns_conn_mgmt_t *cmg;
530 cmg = (ns_conn_mgmt_t *)calloc(1, sizeof (*cmg));
531 if (cmg == NULL) {
532 syslog(LOG_ERR, NS_CONN_MSG_NO_CONN_MGMT);
533 return (NULL);
536 /* is this process nscd or peruser nscd ? */
537 cmg->is_nscd = __s_api_nscd_proc();
538 cmg->is_peruser_nscd = __s_api_peruser_proc();
541 * assume the underlying libldap allows multiple threads sharing
542 * the same ldap connection (MT connection)
544 cmg->ldap_mt = B_TRUE;
545 /* state is inactive until MT connection is required/requested */
546 cmg->state = NS_CONN_MGMT_INACTIVE;
548 (void) mutex_init(&cmg->lock, USYNC_THREAD, NULL);
549 (void) mutex_init(&cmg->cfg_lock, USYNC_THREAD, NULL);
550 cmg->pid = getpid();
552 /* for nscd or peruser nscd, MT connection is required */
553 if (cmg->is_nscd == B_TRUE || cmg->is_peruser_nscd == B_TRUE)
554 cmg->state = NS_CONN_MGMT_ACTIVE;
557 * reference (or initialize) the current Native LDAP configuration and
558 * if in nscd process, make it never refreshed
560 cmg->config = __s_api_get_default_config_global();
561 if (cmg->config == NULL)
562 cmg->config = __s_api_loadrefresh_config_global();
563 if (cmg->config != NULL) {
565 * main nscd get config change notice from ldap_cachemgr
566 * so won't times out and refresh the config
568 if (cmg->is_nscd == B_TRUE)
569 (cmg->config)->paramList[NS_LDAP_EXP_P].ns_tm = 0;
570 cmg->cfg_cookie = cmg->config->config_cookie;
573 return (cmg);
576 static void
577 mark_shutdown_or_reloaded(int op)
579 ns_conn_mgmt_t *cmg = ns_connmgmt;
581 (void) mutex_lock(&cmg->lock);
582 if (op == NS_CONN_MGMT_OP_SHUTDOWN)
583 cmg->shutting_down = B_TRUE;
584 else
585 cmg->cfg_reloaded = B_TRUE;
586 atomic_inc_uint(&cmg->ref_cnt);
587 cmg->state = NS_CONN_MGMT_DETACHED;
589 if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG)
590 __s_api_init_config_global(NULL);
592 (void) mutex_unlock(&cmg->lock);
596 * Return a pointer to the current connection management. If
597 * it has not been created, or is requested to recreate, then
598 * create and return the pointer. It is possible, the current
599 * one is created by the parent before fork, create a new
600 * one too in such a case.
602 static ns_conn_mgmt_t *
603 get_current_conn_mgmt(int op)
605 ns_conn_mgmt_t *cmg = ns_connmgmt;
606 static pid_t checked_pid = (pid_t)-1;
607 pid_t mypid;
609 mypid = getpid();
610 if (cmg == NULL || checked_pid != mypid) {
611 checked_pid = mypid;
614 * if current conn_mgmt not created yet or is from parent
615 * or is requested to recreate, create it
617 if (cmg == NULL || cmg->pid != mypid) {
618 if (cmg != NULL) {
620 * We don't want to free the conn_mgmt
621 * allocated by the parent, since
622 * there may be ldap connections
623 * still being used. So leave it
624 * alone but keep it referenced,
625 * so that it will not be flagged
626 * as a piece of leaked memory.
628 ns_connmgmt_parent = cmg;
630 * avoid lint warning; does not
631 * change the conn_mgmt in parent
633 ns_connmgmt_parent->state =
634 NS_CONN_MGMT_DETACHED;
636 ns_connmgmt = init_conn_mgmt();
637 cmg = ns_connmgmt;
639 * ensure it will not be destroyed until explicitly
640 * shut down or reloaded
642 if (op == NS_CONN_MGMT_OP_REF)
643 atomic_inc_uint(&cmg->ref_cnt);
647 return (cmg);
650 static ns_conn_mgmt_t *
651 access_conn_mgmt(int op)
653 ns_conn_mgmt_t *cmg = NULL;
654 ns_conn_mgmt_t *cmg_prev;
656 (void) mutex_lock(&ns_connmgmt_lock);
659 * connection management is not available when the libsldap is being
660 * unloaded or shut down
662 if (ns_connmgmt_shutting_down == B_TRUE) {
663 (void) mutex_unlock(&ns_connmgmt_lock);
664 return (NULL);
667 if (op == NS_CONN_MGMT_OP_SHUTDOWN) {
668 ns_connmgmt_shutting_down = B_TRUE;
669 if (ns_connmgmt != NULL) {
670 cmg = ns_connmgmt;
671 mark_shutdown_or_reloaded(op);
672 ns_connmgmt = NULL;
674 (void) mutex_unlock(&ns_connmgmt_lock);
675 return (cmg);
678 if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
679 op == NS_CONN_MGMT_OP_NEW_CONFIG) {
680 cmg_prev = ns_connmgmt;
681 mark_shutdown_or_reloaded(op);
683 * the previous cmg (cmg_prev) will be freed later
684 * when its ref count reaches zero
686 ns_connmgmt = NULL;
689 cmg = get_current_conn_mgmt(op);
690 if (cmg == NULL) {
691 (void) mutex_unlock(&ns_connmgmt_lock);
692 return (NULL);
695 atomic_inc_uint(&cmg->ref_cnt);
696 if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
697 op == NS_CONN_MGMT_OP_NEW_CONFIG)
698 cmg = cmg_prev;
699 else { /* op is NS_CONN_MGMT_OP_REF or NS_CONN_MGMT_OP_LIB_INIT */
700 if (cmg->config == NULL)
701 cmg->config = __s_api_get_default_config();
704 (void) mutex_unlock(&ns_connmgmt_lock);
705 return (cmg);
709 * free a connection management control
711 static void
712 free_conn_mgmt(ns_conn_mgmt_t *cmg)
714 union {
715 ldap_data_t s_d;
716 char s_b[1024];
717 } space;
718 ldap_data_t *sptr;
719 int ndata;
720 int adata;
721 int rc;
722 ldap_get_chg_cookie_t cookie;
724 if (cmg == NULL)
725 return;
726 cookie = cmg->cfg_cookie;
728 __s_api_free2dArray(cmg->pservers);
729 /* destroy the previous config or release the current one */
730 if (cmg->config != NULL) {
731 if (cmg->state == NS_CONN_MGMT_DETACHED)
732 __s_api_destroy_config(cmg->config);
733 else
734 __s_api_release_config(cmg->config);
737 /* stop the server status/config-change monitor thread */
738 if (cmg->procchg_started == B_TRUE) {
739 if (cmg->procchg_tid != thr_self()) {
740 if (cmg->procchg_door_call == B_TRUE) {
741 adata = sizeof (ldap_call_t) + 1;
742 ndata = sizeof (space);
743 space.s_d.ldap_call.ldap_callnumber =
744 GETSTATUSCHANGE;
745 space.s_d.ldap_call.ldap_u.get_change.op =
746 NS_STATUS_CHANGE_OP_STOP;
747 space.s_d.ldap_call.ldap_u.get_change.cookie =
748 cookie;
749 sptr = &space.s_d;
750 rc = __ns_ldap_trydoorcall(&sptr, &ndata,
751 &adata);
752 if (rc != NS_CACHE_SUCCESS)
753 syslog(LOG_INFO,
754 gettext("libsldap: "
755 "free_conn_mgmt():"
756 " stopping door call "
757 " GETSTATUSCHANGE failed "
758 " (rc = %d)"), rc);
760 (void) pthread_cancel(cmg->procchg_tid);
761 cmg->procchg_started = B_FALSE;
765 free(cmg);
768 static ns_conn_mgmt_t *
769 release_conn_mgmt(ns_conn_mgmt_t *cmg, boolean_t unlock_cmg)
771 if (cmg == NULL)
772 return (NULL);
773 if (atomic_dec_uint_nv(&cmg->ref_cnt) == 0) {
774 if (cmg->state == NS_CONN_MGMT_DETACHED) {
775 if (unlock_cmg == B_TRUE)
776 (void) mutex_unlock(&cmg->lock);
777 free_conn_mgmt(cmg);
778 __s_api_free_sessionPool();
779 return (NULL);
780 } else {
781 syslog(LOG_WARNING,
782 gettext("libsldap: connection management "
783 " has a refcount of zero but the state "
784 " is not DETACHED (%d)"), cmg->state);
785 cmg = NULL;
788 return (cmg);
792 * exposed function for initializing a connection management control structure
794 ns_conn_mgmt_t *
795 __s_api_conn_mgmt_init()
797 if (thr_keycreate_once(&ns_mtckey, ns_mtckey_cleanup) != 0) {
798 syslog(LOG_WARNING, NS_CONN_MSG_NO_MTC_KEY);
799 return (NULL);
802 if (thr_keycreate_once(&ns_cmgkey, NULL) != 0) {
803 syslog(LOG_WARNING, NS_CONN_MSG_NO_CMG_KEY);
804 return (NULL);
807 return (access_conn_mgmt(NS_CONN_MGMT_OP_LIB_INIT));
810 /* initialize a connection user */
811 ns_conn_user_t *
812 __s_api_conn_user_init(int type, void *userinfo, boolean_t referral)
814 ns_conn_user_t *cu;
815 ns_conn_mgmt_t *cmg;
817 /* delete the reference to the previously used conn_mgmt */
818 (void) thr_setspecific(ns_cmgkey, NULL);
820 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
821 if (cmg == NULL)
822 return (NULL);
824 if (cmg->state != NS_CONN_MGMT_ACTIVE &&
825 cmg->state != NS_CONN_MGMT_INACTIVE) {
826 atomic_dec_uint(&cmg->ref_cnt);
827 return (NULL);
830 cu = (ns_conn_user_t *)calloc(1, sizeof (*cu));
831 if (cu == NULL) {
832 atomic_dec_uint(&cmg->ref_cnt);
833 return (NULL);
836 cu->type = type;
837 cu->state = NS_CONN_USER_ALLOCATED;
838 cu->tid = thr_self();
839 cu->userinfo = userinfo;
840 cu->referral = referral;
841 cu->ns_rc = NS_LDAP_SUCCESS;
842 cu->conn_mgmt = cmg;
844 (void) conn_tsd_setup(cmg);
846 return (cu);
850 * Free the resources used by a connection user.
851 * The caller should ensure this conn_user is
852 * not associated with any conn_mt, i.e.,
853 * not in any conn_mt's linked list of conn_users.
854 * The caller needs to free the userinfo member
855 * as well.
857 void
858 __s_api_conn_user_free(ns_conn_user_t *cu)
860 ns_conn_mgmt_t *cmg;
862 if (cu == NULL)
863 return;
865 cu->state = NS_CONN_USER_FREED;
866 if (cu->ns_error != NULL)
867 (void) __ns_ldap_freeError(&cu->ns_error);
869 cmg = cu->conn_mgmt;
870 conn_tsd_free();
871 (void) release_conn_mgmt(cmg, B_FALSE);
872 (void) free(cu);
876 * Initialize an MT connection control structure
877 * that will be used to represent an ldap connection
878 * to be shared among multiple threads and to hold
879 * and manage all the conn_users using the ldap
880 * connection.
882 static ns_conn_mt_t *
883 init_conn_mt(ns_conn_mgmt_t *cmg, ns_ldap_error_t **ep)
885 ns_conn_mt_t *cm;
886 ns_conn_mgmt_t *cmg_a;
888 cm = (ns_conn_mt_t *)calloc(1, sizeof (*cm));
889 if (cm == NULL) {
890 if (ep != NULL)
891 *ep = __s_api_make_error(NS_LDAP_MEMORY, NULL);
892 return (NULL);
895 cmg_a = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
896 if (cmg_a != cmg) {
897 if (cmg_a != NULL) {
898 (void) release_conn_mgmt(cmg_a, B_FALSE);
899 if (ep != NULL)
900 *ep = __s_api_make_error(NS_LDAP_OP_FAILED,
901 NS_CONN_MSG_SHUTDOWN_RELOADED);
903 return (NULL);
906 (void) mutex_init(&cm->lock, USYNC_THREAD, NULL);
907 cm->state = NS_CONN_MT_CONNECTING;
908 cm->tid = thr_self();
909 cm->pid = getpid();
910 cm->next = NULL;
911 cm->cu_head = NULL;
912 cm->cu_tail = NULL;
913 cm->conn = NULL;
914 cm->conn_mgmt = cmg;
916 return (cm);
920 * Free an MT connection control structure, assume conn_mgmt is locked.
921 * 'unlock_cmg' is passed to release_conn_mgmt() to indicate the
922 * cmg needs to be unlocked or not.
924 static ns_conn_mgmt_t *
925 free_conn_mt(ns_conn_mt_t *cm, int unlock_cmg)
927 ns_conn_mgmt_t *cmg;
929 if (cm == NULL)
930 return (NULL);
931 if (cm->ns_error != NULL)
932 (void) __ns_ldap_freeError(&cm->ns_error);
933 if (cm->conn != NULL) {
934 if (cm->conn->ld != NULL)
935 (void) ldap_unbind(cm->conn->ld);
936 __s_api_freeConnection(cm->conn);
938 cmg = cm->conn_mgmt;
939 free(cm);
940 return (release_conn_mgmt(cmg, unlock_cmg));
943 /* add a connection user to an MT connection */
944 static void
945 add_cu2cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
948 if (cm->cu_head == NULL) {
949 cm->cu_head = cu;
950 cm->cu_tail = cu;
951 } else {
952 cm->cu_tail->next = cu;
953 cm->cu_tail = cu;
955 cm->cu_cnt++;
958 /* add an MT connection to the connection management */
959 static void
960 add_cm2cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
963 * add connection opened for WRITE to top of list
964 * for garbage collection purpose. This is to
965 * ensure the connection will be closed after a
966 * certain amount of time (60 seconds).
968 if (cmg->cm_head == NULL) {
969 cmg->cm_head = cm;
970 cmg->cm_tail = cm;
971 } else {
972 if (cm->opened_for == NS_CONN_USER_WRITE) {
973 cm->next = cmg->cm_head;
974 cmg->cm_head = cm;
975 } else {
976 cmg->cm_tail->next = cm;
977 cmg->cm_tail = cm;
980 cmg->cm_cnt++;
983 /* delete a connection user from an MT connection */
984 static void
985 del_cu4cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
987 ns_conn_user_t *pu, *u;
989 if (cu == NULL || cm->cu_head == NULL || cm->cu_cnt == 0)
990 return;
992 /* only one conn_user on list */
993 if (cm->cu_head == cm->cu_tail) {
994 if (cu == cm->cu_head) {
995 cm->cu_head = cm->cu_tail = NULL;
996 cm->cu_cnt = 0;
997 cu->next = NULL;
999 return;
1002 /* more than one and cu is the first one */
1003 if (cu == cm->cu_head) {
1004 cm->cu_head = cu->next;
1005 cm->cu_cnt--;
1006 cu->next = NULL;
1007 return;
1010 pu = cm->cu_head;
1011 for (u = cm->cu_head->next; u; u = u->next) {
1012 if (cu == u)
1013 break;
1014 pu = u;
1016 if (pu != cm->cu_tail) {
1017 pu->next = cu->next;
1018 if (pu->next == NULL)
1019 cm->cu_tail = pu;
1020 cm->cu_cnt--;
1021 cu->next = NULL;
1022 } else {
1023 syslog(LOG_INFO, gettext(
1024 "libsldap: del_cu4cm(): connection user not found"));
1028 /* delete an MT connection from the connection management control structure */
1029 static void
1030 del_cm4cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
1032 ns_conn_mt_t *pm, *m;
1034 if (cm == NULL || cmg->cm_head == NULL || cmg->cm_cnt == 0)
1035 return;
1037 /* only one conn_mt on list */
1038 if (cmg->cm_head == cmg->cm_tail) {
1039 if (cm == cmg->cm_head) {
1040 cmg->cm_head = cmg->cm_tail = NULL;
1041 cmg->cm_cnt = 0;
1042 cm->next = NULL;
1044 return;
1047 /* more than one and cm is the first one */
1048 if (cm == cmg->cm_head) {
1049 cmg->cm_head = cm->next;
1050 cmg->cm_cnt--;
1051 cm->next = NULL;
1052 return;
1055 pm = cmg->cm_head;
1056 for (m = cmg->cm_head->next; m; m = m->next) {
1057 if (cm == m)
1058 break;
1059 pm = m;
1061 if (pm != cmg->cm_tail) {
1062 pm->next = cm->next;
1063 if (pm->next == NULL)
1064 cmg->cm_tail = pm;
1065 cmg->cm_cnt--;
1066 cm->next = NULL;
1067 } else {
1068 syslog(LOG_INFO, gettext(
1069 "libsldap: del_cm4cmg(): MT connection not found"));
1074 * compare to see if the server and credential for authentication match
1075 * those used by an MT connection
1077 static boolean_t
1078 is_server_cred_matched(const char *server, const ns_cred_t *cred,
1079 ns_conn_mt_t *cm)
1081 Connection *cp = cm->conn;
1083 /* check server first */
1084 if (server != NULL && *server != 0) {
1085 if (strcasecmp(server, cp->serverAddr) != 0)
1086 return (B_FALSE);
1089 if (cred == NULL)
1090 return (B_TRUE);
1092 /* then check cred */
1093 return (__s_api_is_auth_matched(cp->auth, cred));
1097 * Wait until a pending MT connection becomes available.
1098 * Return 1 if so, 0 if error.
1100 * Assume the current conn_mgmt and the input conn_mt
1101 * are locked.
1103 static int
1104 wait_for_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t *cm)
1107 cu->state = NS_CONN_USER_WAITING;
1108 add_cu2cm(cu, cm);
1109 cu->conn_mt = cm;
1111 (void) mutex_unlock(&cm->lock);
1113 * It could take some time so we don't want to hold
1114 * cm->conn_mgmt across the wait
1116 (void) mutex_unlock(&(cm->conn_mgmt)->lock);
1118 (void) mutex_lock(&cm->lock);
1119 /* check one more time see if need to wait */
1120 if (cm->state == NS_CONN_MT_CONNECTING) {
1121 (void) conn_wait(cm, cu);
1123 /* cm->lock is locked again at this point */
1125 cu->state = NS_CONN_USER_WOKEUP;
1128 if (cm->state == NS_CONN_MT_CONNECTED)
1129 return (1);
1130 else {
1131 del_cu4cm(cu, cm);
1132 cu->conn_mt = NULL;
1133 cu->bad_mt_conn = B_FALSE;
1134 return (0);
1139 * Check and see if the input MT connection '*cm' should be closed.
1140 * In two cases, it should be closed. If a preferred server is
1141 * found to be up when ldap_cachemgr is queried and reported back.
1142 * Or when the server being used for the connection is found to
1143 * be down. Return B_FALSE if the connection is not closed (or not marked
1144 * to be closed), otherwise unlock mutex (*cm)->lock and return B_TRUE.
1145 * This function assumes conn_mgmt cmg and conn_mt *cm are locked.
1147 static boolean_t
1148 check_and_close_conn(ns_conn_mgmt_t *cmg, ns_conn_mt_t **cm,
1149 ns_conn_user_t *cu) {
1151 int rc;
1152 int j;
1153 int svridx = -1;
1154 int upidx = -1;
1155 int free_cm;
1156 ns_server_info_t sinfo;
1157 ns_ldap_error_t *errorp = NULL;
1160 * check only if preferred servers are defined
1162 if (cmg->pservers_loaded == B_FALSE)
1163 get_preferred_servers(B_FALSE, B_FALSE, cmg);
1164 if (cmg->pservers == NULL)
1165 return (B_FALSE);
1168 * ask ldap_cachemgr for the first available server
1170 rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
1171 &sinfo, &errorp, NS_CACHE_ADDR_IP);
1172 if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
1173 (void) __ns_ldap_freeError(&errorp);
1174 return (B_FALSE);
1178 * Did ldap_cachemgr return a preferred server ?
1180 for (j = 0; cmg->pservers[j] != NULL; j++) {
1181 if (strcasecmp(sinfo.server, cmg->pservers[j]) != 0)
1182 continue;
1183 upidx = j;
1184 break;
1188 * Is the server being used a preferred one ?
1190 for (j = 0; cmg->pservers[j] != NULL; j++) {
1191 if (strcasecmp(cmg->pservers[j], (*cm)->conn->serverAddr) != 0)
1192 continue;
1193 svridx = j;
1194 break;
1198 * Need to fall back to a down-but-now-up preferred server ?
1199 * A preferred server falls back to a more preferred one.
1200 * A regular one falls back to any preferred ones. So if
1201 * both are preferred ones and same index, or both
1202 * are not preferred ones, then no need to close the
1203 * connection.
1205 if ((upidx == -1 && svridx == -1) ||
1206 (upidx != -1 && svridx != -1 && upidx == svridx)) {
1207 __s_api_free_server_info(&sinfo);
1208 return (B_FALSE);
1212 * otherwise, 4 cases, all may need to close the connection:
1213 * For case 1 and 2, both servers are preferred ones:
1214 * 1. ldap_cachemgr returned a better one to use (upidx < svridx)
1215 * 2. the server being used is down (upidx > svridx)
1216 * 3. ldap_cachemgr returned a preferred one, but the server
1217 * being used is not, so need to fall back to the preferred server
1218 * 4. ldap_cachemgr returned a non-preferred one, but the server
1219 * being used is a preferred one, so it must be down (since
1220 * ldap_cachemgr always returns a preferred one when possible).
1221 * For case 1 & 3, close the READ connection when no user uses it.
1222 * For 2 and 4, close the connection with error rc, LDAP_SERVER_DOWN.
1224 if (upidx != -1 && (svridx == -1 || upidx < svridx)) { /* case 1 & 3 */
1225 /* fallback does not make sense for WRITE/referred connection */
1226 if ((*cm)->opened_for == NS_CONN_USER_WRITE ||
1227 (*cm)->referral == B_TRUE) {
1228 __s_api_free_server_info(&sinfo);
1229 return (B_FALSE);
1231 free_cm = close_conn_mt_when_nouser(*cm);
1232 if (cmg->shutting_down == B_FALSE)
1233 cu->retry = B_TRUE;
1234 } else {
1235 ns_ldap_error_t *ep;
1236 ep = __s_api_make_error(LDAP_SERVER_DOWN,
1237 NS_CONN_MSG_DOWN_FROM_CACHEMGR);
1238 /* cu has not been attached to cm yet, use NULL as cu pointer */
1239 free_cm = close_conn_mt(*cm, LDAP_SERVER_DOWN, &ep, NULL);
1240 if (cmg->shutting_down == B_FALSE)
1241 cu->retry = B_TRUE;
1242 (void) __ns_ldap_freeError(&ep);
1245 (void) mutex_unlock(&(*cm)->lock);
1246 if (free_cm == 1) {
1247 (void) free_conn_mt(*cm, 0);
1248 *cm = NULL;
1251 __s_api_free_server_info(&sinfo);
1253 return (B_TRUE);
1257 * Check to see if a conn_mt matches the connection criteria from
1258 * a conn_user. Return B_TRUE if yes, B_FALSE, otherwise. The input
1259 * conn_mt pointer (*cmt) may be freed and *cmt will be set to NULL
1260 * to indicate so.
1261 * conn_mt *cmt and conn_mgmt cm->conn_mgmt are assumed locked.
1262 * cm->lock is unlocked at exit if rc is B_FALSE.
1264 static boolean_t
1265 match_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t **cmt,
1266 ns_conn_mt_state_t st, const char *server,
1267 const ns_cred_t *cred)
1269 boolean_t matched = B_FALSE;
1270 boolean_t drop_conn;
1271 int free_cm = 0;
1272 ns_conn_mt_t *cm = *cmt;
1273 ns_conn_mgmt_t *cmg = cm->conn_mgmt;
1275 if (cm->state != st || cm->close_when_nouser == B_TRUE ||
1276 cm->detached == B_TRUE || cm->pid != getpid() ||
1277 cm->referral != cu->referral) {
1278 (void) mutex_unlock(&cm->lock);
1279 return (B_FALSE);
1283 * if a conn_mt opened for WRITE is idle
1284 * long enough, then close it. To improve
1285 * the performance of applications, such
1286 * as ldapaddent, a WRITE connection is
1287 * given a short time to live in the
1288 * connection pool, expecting the write
1289 * requests to come in a quick succession.
1290 * To save resource, the connection will
1291 * be closed if idle more than 60 seconds.
1293 if (cm->opened_for == NS_CONN_USER_WRITE &&
1294 cu->type != NS_CONN_USER_WRITE && cm->cu_cnt == 0 &&
1295 ((time(NULL) - cm->access_time) > 60)) {
1297 * NS_LDAP_INTERNAL is irrelevant here. There no
1298 * conn_user to consume the rc
1300 free_cm = close_conn_mt(cm, NS_LDAP_INTERNAL, NULL, NULL);
1301 (void) mutex_unlock(&cm->lock);
1302 if (free_cm == 1) {
1303 (void) free_conn_mt(cm, 0);
1304 *cmt = NULL;
1306 return (B_FALSE);
1309 switch (cu->type) {
1310 case NS_CONN_USER_SEARCH:
1311 case NS_CONN_USER_GETENT:
1312 if (cm->opened_for == NS_CONN_USER_SEARCH ||
1313 cm->opened_for == NS_CONN_USER_GETENT)
1314 matched = B_TRUE;
1315 break;
1317 case NS_CONN_USER_WRITE:
1318 if (cm->opened_for == NS_CONN_USER_WRITE)
1319 matched = B_TRUE;
1320 break;
1322 default:
1323 matched = B_FALSE;
1324 break;
1327 if (matched == B_TRUE && ((server != NULL || cred != NULL) &&
1328 is_server_cred_matched(server, cred, cm) == B_FALSE))
1329 matched = B_FALSE;
1331 if (matched != B_FALSE) {
1333 * Check and drop the 'connected' connection if
1334 * necessary. Main nscd gets status changes from
1335 * the ldap_cachemgr daemon directly via the
1336 * GETSTATUSCHANGE door call, the standalone
1337 * function works in a no ldap_cachemgr environment,
1338 * so no need to check and drop connections.
1340 if (cm->state == NS_CONN_MT_CONNECTED &&
1341 cmg->is_nscd == B_FALSE && !__s_api_isStandalone()) {
1342 drop_conn = check_and_close_conn(cmg, &cm, cu);
1343 if (drop_conn == B_TRUE) {
1344 if (cm == NULL)
1345 *cmt = NULL;
1346 return (B_FALSE);
1350 /* check if max. users using or waiting for the connection */
1351 if ((cm->state == NS_CONN_MT_CONNECTED &&
1352 cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
1353 cm->cu_cnt >= cm->cu_max) ||
1354 (cm->state == NS_CONN_MT_CONNECTING &&
1355 cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
1356 cm->waiter_cnt >= cm->cu_max - 1))
1357 matched = B_FALSE;
1360 if (matched == B_FALSE)
1361 (void) mutex_unlock(&cm->lock);
1363 return (matched);
1367 * obtain an MT connection from the connection management for a conn_user
1369 * Input:
1370 * server : server name or IP address
1371 * flags : libsldap API flags
1372 * cred : pointer to the user credential
1373 * cu : pointer to the conn_user structure
1374 * Output:
1375 * session : hold pointer to the Connection structure
1376 * errorp : hold pointer to error info (ns_ldap_error_t)
1379 __s_api_conn_mt_get(const char *server, const int flags, const ns_cred_t *cred,
1380 Connection **session, ns_ldap_error_t **errorp, ns_conn_user_t *cu)
1382 int rc;
1383 int i;
1384 ns_conn_mt_t *cn;
1385 ns_conn_mt_state_t st;
1386 ns_conn_mgmt_t *cmg;
1388 if (errorp == NULL || cu == NULL || session == NULL)
1389 return (NS_LDAP_INVALID_PARAM);
1391 *session = NULL;
1392 cmg = cu->conn_mgmt;
1395 * for pam_ldap, always try opening a new connection
1397 if (cu->type == NS_CONN_USER_AUTH)
1398 return (NS_LDAP_NOTFOUND);
1400 /* if need a new conn, then don't reuse */
1401 if (flags & NS_LDAP_NEW_CONN)
1402 return (NS_LDAP_NOTFOUND);
1404 if (flags & NS_LDAP_KEEP_CONN)
1405 cu->keep_conn = B_TRUE;
1408 * We want to use MT connection only if keep-connection flag is
1409 * set or if MT was requested (or active)
1411 if (!((cmg->state == NS_CONN_MGMT_INACTIVE &&
1412 cu->keep_conn == B_TRUE) || cmg->state == NS_CONN_MGMT_ACTIVE))
1413 return (NS_LDAP_NOTFOUND);
1415 /* MT connection will be used now (if possible/available) */
1416 cu->use_mt_conn = B_TRUE;
1418 NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errorp);
1420 /* first look for a connection already open */
1421 st = NS_CONN_MT_CONNECTED;
1422 cu->state = NS_CONN_USER_FINDING;
1423 for (i = 0; i < 2; i++) {
1424 for (cn = cmg->cm_head; cn; cn = cn->next) {
1425 (void) mutex_lock(&cn->lock);
1426 rc = match_conn_mt(cu, &cn, st, server, cred);
1427 if (rc == B_FALSE && cn != NULL) /* not found */
1428 continue;
1429 if (cn == NULL) { /* not found and cn freed */
1431 * as the conn_mt list could
1432 * be different due to cn's
1433 * deletion, scan the entire
1434 * conn_mt list again
1436 st = NS_CONN_MT_CONNECTED;
1437 i = -1;
1438 break;
1441 /* return a connected one if found */
1442 if (cn->state == NS_CONN_MT_CONNECTED) {
1443 *session = cn->conn;
1444 add_cu2cm(cu, cn);
1445 cu->conn_mt = cn;
1446 cu->state = NS_CONN_USER_CONNECTED;
1447 (void) mutex_unlock(&cn->lock);
1448 (void) mutex_unlock(&cmg->lock);
1449 return (NS_LDAP_SUCCESS);
1453 * if cn is not connecting, or allow only
1454 * one user, skip it
1456 if (cn->state != NS_CONN_MT_CONNECTING ||
1457 cn->cu_max == 1) {
1458 (void) mutex_unlock(&cn->lock);
1459 continue;
1462 /* wait for the connecting conn_mt */
1463 if (wait_for_conn_mt(cu, cn) != 1) {
1465 * NS_LDAP_NOTFOUND signals that the function
1466 * __s_api_check_libldap_MT_conn_support()
1467 * detected that the lower libldap library
1468 * does not support MT connection, so return
1469 * NS_LDAP_NOTFOUND to let the caller to
1470 * open a non-MT conneciton. Otherwise,
1471 * connect error occurred, return
1472 * NS_CONN_USER_CONNECT_ERROR
1474 if (cn->ns_rc != NS_LDAP_NOTFOUND)
1475 cu->state = NS_CONN_USER_CONNECT_ERROR;
1476 else {
1477 cu->state = NS_CONN_USER_FINDING;
1478 cu->use_mt_conn = B_FALSE;
1480 (void) mutex_unlock(&cn->lock);
1482 /* cmg->lock unlocked by wait_for_conn_mt() */
1484 return (cn->ns_rc);
1487 /* return the newly available conn_mt */
1488 *session = cn->conn;
1489 cu->state = NS_CONN_USER_CONNECTED;
1490 (void) mutex_unlock(&cn->lock);
1492 /* cmg->lock unlocked by wait_for_conn_mt() */
1494 return (NS_LDAP_SUCCESS);
1497 /* next, look for a connecting conn_mt */
1498 if (i == 0)
1499 st = NS_CONN_MT_CONNECTING;
1502 /* no connection found, start opening one */
1503 cn = init_conn_mt(cmg, errorp);
1504 if (cn == NULL) {
1505 (void) mutex_unlock(&cmg->lock);
1506 return ((*errorp)->status);
1508 cu->conn_mt = cn;
1509 cn->opened_for = cu->type;
1510 cn->referral = cu->referral;
1511 if (cmg->ldap_mt == B_TRUE)
1512 cn->cu_max = NS_CONN_MT_USER_MAX;
1513 else
1514 cn->cu_max = 1;
1515 add_cm2cmg(cn, cmg);
1516 (void) mutex_unlock(&cmg->lock);
1518 return (NS_LDAP_NOTFOUND);
1523 * add an MT connection to the connection management
1525 * Input:
1526 * con : pointer to the Connection info
1527 * cu : pointer to the conn_user structure
1528 * Output:
1529 * ep : hold pointer to error info (ns_ldap_error_t)
1532 __s_api_conn_mt_add(Connection *con, ns_conn_user_t *cu, ns_ldap_error_t **ep)
1534 ns_conn_mgmt_t *cmg = cu->conn_mgmt;
1535 ns_conn_mt_t *cm = cu->conn_mt;
1537 /* if the conn_mgmt is being shut down, return error */
1538 NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
1541 * start the change monitor thread only if it
1542 * hasn't been started and the process is the
1543 * main nscd (not peruser nscd)
1545 if (cmg->procchg_started == B_FALSE && cmg->is_nscd == B_TRUE) {
1546 start_thread(cmg);
1547 cmg->procchg_started = B_TRUE;
1549 (void) mutex_lock(&cm->lock);
1550 cm->conn = con;
1551 cm->state = NS_CONN_MT_CONNECTED;
1552 cm->pid = getpid();
1553 cm->create_time = time(NULL);
1554 cm->access_time = cm->create_time;
1555 cm->opened_for = cu->type;
1556 add_cu2cm(cu, cm);
1557 cu->conn_mt = cm;
1558 cu->state = NS_CONN_USER_CONNECTED;
1559 if (cmg->ldap_mt == B_TRUE)
1560 cm->cu_max = NS_CONN_MT_USER_MAX;
1561 else
1562 cm->cu_max = 1;
1564 /* wake up the waiters if any */
1565 (void) conn_signal(cm);
1567 (void) mutex_unlock(&cm->lock);
1568 (void) mutex_unlock(&cmg->lock);
1570 return (NS_LDAP_SUCCESS);
1574 * return an MT connection to the pool when a conn user is done using it
1576 * Input:
1577 * cu : pointer to the conn_user structure
1578 * Output: NONE
1580 void
1581 __s_api_conn_mt_return(ns_conn_user_t *cu)
1583 ns_conn_mt_t *cm;
1584 ns_conn_mgmt_t *cmg;
1586 if (cu == NULL || cu->use_mt_conn == B_FALSE)
1587 return;
1588 cm = cu->conn_mt;
1589 if (cm == NULL)
1590 return;
1591 cmg = cu->conn_mgmt;
1593 (void) mutex_lock(&cm->lock);
1594 del_cu4cm(cu, cm);
1595 cu->state = NS_CONN_USER_DISCONNECTED;
1596 cu->conn_mt = NULL;
1597 cu->bad_mt_conn = B_FALSE;
1600 * if this MT connection is no longer needed, or not usable, and
1601 * no more conn_user uses it, then close it.
1604 if ((cm->close_when_nouser == B_TRUE ||
1605 cm->state != NS_CONN_MT_CONNECTED) && cm->cu_cnt == 0) {
1606 (void) mutex_unlock(&cm->lock);
1607 (void) mutex_lock(&cmg->lock);
1608 (void) mutex_lock(&cm->lock);
1609 del_cm4cmg(cm, cmg);
1610 /* use ns_conn_free (instead of 1) to avoid lint warning */
1611 NS_CONN_UNLOCK_AND_FREE(ns_conn_free, cm, cmg);
1612 } else {
1613 if (cm->state == NS_CONN_MT_CONNECTED && cm->cu_cnt == 0 &&
1614 cm->conn != NULL && cm->conn->ld != NULL) {
1615 struct timeval zerotime;
1616 LDAPMessage *res;
1618 zerotime.tv_sec = zerotime.tv_usec = 0L;
1619 /* clean up remaining results just in case */
1620 while (ldap_result(cm->conn->ld, LDAP_RES_ANY,
1621 LDAP_MSG_ALL, &zerotime, &res) > 0) {
1622 if (res != NULL)
1623 (void) ldap_msgfree(res);
1626 (void) mutex_unlock(&cm->lock);
1630 /* save error info (rc and ns_ldap_error_t) in the conn_mt */
1631 static void
1632 err2cm(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp) {
1633 ns_ldap_error_t *ep;
1635 cm->ns_rc = rc;
1636 cm->ns_error = NULL;
1637 if (errorp != NULL && *errorp != NULL) {
1638 ep = __s_api_copy_error(*errorp);
1639 if (ep == NULL)
1640 cm->ns_rc = NS_LDAP_MEMORY;
1641 else
1642 cm->ns_error = ep;
1646 /* copy error info (rc and ns_ldap_error_t) from conn_mt to conn_user */
1647 static void
1648 err_from_cm(ns_conn_user_t *cu, ns_conn_mt_t *cm) {
1649 ns_ldap_error_t *ep;
1651 cu->ns_rc = cm->ns_rc;
1652 if (cu->ns_error != NULL)
1653 (void) __ns_ldap_freeError(&cu->ns_error);
1654 cu->ns_error = NULL;
1655 if (cm->ns_rc != NS_LDAP_SUCCESS && cm->ns_error != NULL) {
1656 ep = __s_api_copy_error(cm->ns_error);
1657 if (ep == NULL)
1658 cu->ns_rc = NS_LDAP_MEMORY;
1659 else
1660 cu->ns_error = ep;
1664 /* copy error info (rc and ns_ldap_error_t) from caller to conn_user */
1665 static void
1666 err_from_caller(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp) {
1668 cu->ns_rc = rc;
1669 if (errorp != NULL) {
1670 if (cu->ns_error != NULL)
1671 (void) __ns_ldap_freeError(&cu->ns_error);
1672 cu->ns_error = *errorp;
1673 *errorp = NULL;
1674 } else
1675 cu->ns_error = NULL;
1679 * remove an MT connection from the connection management when failed to open
1681 * Input:
1682 * cu : pointer to the conn_user structure
1683 * rc : error code
1684 * errorp : pointer to pointer to error info (ns_ldap_error_t)
1685 * Output:
1686 * errorp : set to NULL, if none NULL cm, callers do not need to free it
1688 void
1689 __s_api_conn_mt_remove(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
1691 ns_conn_mgmt_t *cmg;
1692 ns_conn_mt_t *cm;
1693 int free_cm = 0;
1695 if (cu == NULL || cu->use_mt_conn == B_FALSE)
1696 return;
1697 if ((cm = cu->conn_mt) == NULL)
1698 return;
1699 cmg = cu->conn_mgmt;
1701 (void) mutex_lock(&cmg->lock);
1702 (void) mutex_lock(&cm->lock);
1703 if (cm->state != NS_CONN_MT_CONNECT_ERROR) {
1704 cm->state = NS_CONN_MT_CONNECT_ERROR;
1705 cm->ns_rc = rc;
1706 if (errorp != NULL) {
1707 cm->ns_error = *errorp;
1708 *errorp = NULL;
1712 /* all the conn_users share the same error rc and ns_ldap_error_t */
1713 err_from_cm(cu, cm);
1714 /* wake up the waiters if any */
1715 (void) conn_signal(cm);
1717 del_cu4cm(cu, cm);
1718 cu->conn_mt = NULL;
1719 cu->bad_mt_conn = B_FALSE;
1720 if (cm->cu_cnt == 0) {
1721 del_cm4cmg(cm, cmg);
1722 free_cm = 1;
1725 NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
1729 * check to see if the underlying libldap supports multi-threaded client
1730 * (MT connections)
1733 __s_api_check_libldap_MT_conn_support(ns_conn_user_t *cu, LDAP *ld,
1734 ns_ldap_error_t **ep)
1736 int rc;
1737 ns_conn_mgmt_t *cmg;
1739 /* if no need to check, just return success */
1740 if (cu->conn_mt == NULL || cu->use_mt_conn == B_FALSE)
1741 return (NS_LDAP_SUCCESS);
1743 cmg = cu->conn_mgmt;
1744 rc = setup_mt_ld(ld, cmg);
1746 if (cmg->do_mt_conn == B_FALSE) {
1748 * If the conn_mgmt is being shut down, return error.
1749 * if cmg is usable, cmg->lock will be locked. Otherwise,
1750 * this function will return with rc NS_LDAP_OP_FAILED.
1752 NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
1753 if (cmg->do_mt_conn == B_FALSE) {
1754 if (rc < 0)
1755 cmg->ldap_mt = B_FALSE;
1756 else {
1757 cmg->ldap_mt = B_TRUE;
1758 if (cmg->is_nscd == B_TRUE ||
1759 cmg->is_peruser_nscd == B_TRUE) {
1760 cmg->do_mt_conn = B_TRUE;
1761 cmg->state = NS_CONN_MGMT_ACTIVE;
1765 (void) mutex_unlock(&cmg->lock);
1768 if (rc < 0)
1769 __s_api_conn_mt_remove(cu, NS_LDAP_NOTFOUND, NULL);
1770 return (NS_LDAP_SUCCESS);
1774 * Close an MT connection.
1775 * Assume cm not null and locked, assume conn_mgmt is also locked.
1776 * Return -1 if error, 1 if the cm should be freed, otherwise 0.
1778 static int
1779 close_conn_mt(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp,
1780 ns_conn_user_t *cu)
1782 ns_conn_mgmt_t *cmg = cm->conn_mgmt;
1783 ns_conn_mt_t *m;
1784 ns_conn_user_t *u;
1786 if ((cm->state != NS_CONN_MT_CONNECTED && cm->state !=
1787 NS_CONN_MT_CLOSING) || cmg->cm_head == NULL || cmg->cm_cnt == 0)
1788 return (-1);
1790 /* if the conn_mt is not in the MT connection pool, nothing to do */
1791 for (m = cmg->cm_head; m; m = m->next) {
1792 if (cm == m)
1793 break;
1795 if (m == NULL)
1796 return (-1);
1798 if (cm->state == NS_CONN_MT_CONNECTED) { /* first time in here */
1799 cm->state = NS_CONN_MT_CLOSING;
1801 * If more cu exist to consume the error info, copy
1802 * it to the cm. If the caller calls on behalf of
1803 * a cu, cu won't be NULL. Check to see if there's
1804 * more cu that needs the error info. If caller does
1805 * not have a specific cu attached to it (e.g.,
1806 * shutdown_all_conn_mt()), cu is NULL, check if at
1807 * least one cu exists.
1809 if ((cu != NULL && cm->cu_cnt > 1) ||
1810 (cu == NULL && cm->cu_cnt > 0)) {
1811 err2cm(cm, rc, errorp);
1812 /* wake up waiter (conn_user) if any */
1813 (void) conn_signal(cm);
1816 /* for each conn_user using the conn_mt, set bad_mt_conn flag */
1817 if (cm->cu_head != NULL) {
1818 for (u = cm->cu_head; u; u = u->next) {
1819 u->bad_mt_conn = B_TRUE;
1820 if (cmg->shutting_down == B_FALSE)
1821 u->retry = B_TRUE;
1826 /* detach the conn_mt if no more conn_user left */
1827 if ((cu != NULL && cm->cu_cnt == 1) ||
1828 (cu == NULL && cm->cu_cnt == 0)) {
1829 del_cm4cmg(cm, cmg);
1830 cm->detached = B_TRUE;
1831 return (1);
1834 return (0);
1838 * An MT connection becomes bad, close it and free resources.
1839 * This function is called with a ns_conn_user_t representing
1840 * a user of the MT connection.
1842 * Input:
1843 * cu : pointer to the conn_user structure
1844 * rc : error code
1845 * errorp : pointer to pointer to error info (ns_ldap_error_t)
1846 * Output:
1847 * errorp : set to NULL (if no error), callers do not need to free it
1849 void
1850 __s_api_conn_mt_close(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
1852 ns_conn_mgmt_t *cmg;
1853 ns_conn_mt_t *cm;
1854 int free_cm = 0;
1856 if (cu == NULL || cu->use_mt_conn == B_FALSE)
1857 return;
1859 if (cu->state != NS_CONN_USER_CONNECTED || (cm = cu->conn_mt) == NULL)
1860 return;
1861 cmg = cu->conn_mgmt;
1863 (void) mutex_lock(&cmg->lock);
1864 (void) mutex_lock(&cm->lock);
1866 /* close the MT connection if possible */
1867 free_cm = close_conn_mt(cm, rc, errorp, cu);
1868 if (free_cm == -1) { /* error case */
1869 (void) mutex_unlock(&cm->lock);
1870 (void) mutex_unlock(&cmg->lock);
1871 return;
1874 if (rc != NS_LDAP_SUCCESS) { /* error info passed in, use it */
1875 err_from_caller(cu, rc, errorp);
1876 } else { /* error not passed in, use those saved in the conn_mt */
1877 err_from_cm(cu, cm);
1880 /* detach the conn_user from the conn_mt */
1881 del_cu4cm(cu, cm);
1882 cu->conn_mt = NULL;
1883 cu->bad_mt_conn = B_FALSE;
1884 if (cmg->shutting_down == B_FALSE)
1885 cu->retry = B_TRUE;
1886 NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
1890 * Close an MT connection when the associated server is known to be
1891 * down. This function is called with a ns_conn_mt_t representing
1892 * the MT connection. That is, the caller is not a conn_user
1893 * thread but rather the procchg thread.
1895 static void
1896 close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg)
1898 ns_conn_mgmt_t *cmg;
1899 int free_cm = 0;
1900 ns_ldap_error_t *ep;
1902 if (cm == NULL)
1903 return;
1904 cmg = cm->conn_mgmt;
1906 ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
1907 if (ep != NULL) {
1908 ep->status = rc;
1909 if (errmsg != NULL)
1910 ep->message = strdup(errmsg); /* OK if returns NULL */
1913 (void) mutex_lock(&cmg->lock);
1914 (void) mutex_lock(&cm->lock);
1916 /* close the MT connection if possible */
1917 free_cm = close_conn_mt(cm, LDAP_SERVER_DOWN, &ep, NULL);
1918 if (free_cm == -1) { /* error case */
1919 (void) mutex_unlock(&cm->lock);
1920 (void) mutex_unlock(&cmg->lock);
1921 return;
1923 (void) __ns_ldap_freeError(&ep);
1925 NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
1929 * Close an MT connection when there is a better server to connect to.
1930 * Mark the connection as to-be-closed-when-no-one-using so that
1931 * any outstanding ldap operations can run to completion.
1932 * Assume that both the conn_mt and conn_mgmt are locked.
1933 * Return 1 if the conn_mt should be freed.
1935 static int
1936 close_conn_mt_when_nouser(ns_conn_mt_t *cm)
1938 int free_cm = 0;
1940 if (cm->cu_cnt == 0) {
1941 del_cm4cmg(cm, cm->conn_mgmt);
1942 free_cm = 1;
1943 } else {
1944 cm->close_when_nouser = B_TRUE;
1947 return (free_cm);
1951 * Retrieve the configured preferred server list.
1952 * This function locked the conn_mgmt and does not
1953 * unlock at exit.
1955 static void
1956 get_preferred_servers(boolean_t lock, boolean_t reload, ns_conn_mgmt_t *cmg)
1958 ns_ldap_error_t *errorp = NULL;
1959 void **pservers = NULL;
1961 if (lock == B_TRUE)
1962 (void) mutex_lock(&cmg->lock);
1964 /* if already done, and no reload, then return */
1965 if (cmg->pservers_loaded == B_TRUE && reload == B_FALSE)
1966 return;
1968 if (cmg->pservers != NULL) {
1969 (void) __ns_ldap_freeParam((void ***)&cmg->pservers);
1970 cmg->pservers = NULL;
1973 if (__ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
1974 &pservers, &errorp) == NS_LDAP_SUCCESS) {
1975 cmg->pservers = (char **)pservers;
1976 cmg->pservers_loaded = B_TRUE;
1977 } else {
1978 (void) __ns_ldap_freeError(&errorp);
1979 (void) __ns_ldap_freeParam(&pservers);
1984 * This function handles the config or server status change notification
1985 * from the ldap_cachemgr.
1987 static ns_conn_mgmt_t *
1988 proc_server_change(ns_server_status_change_t *chg, ns_conn_mgmt_t *cmg)
1990 int cnt, i, j, k, n;
1991 boolean_t loop = B_TRUE;
1992 boolean_t cmg_locked = B_FALSE;
1993 char *s;
1994 ns_conn_mt_t *cm;
1995 ns_conn_mgmt_t *ocmg;
1997 /* if config changed, reload the configuration */
1998 if (chg->config_changed == B_TRUE) {
1999 /* reload the conn_mgmt and Native LDAP config */
2000 ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_RELOAD_CONFIG);
2001 shutdown_all_conn_mt(ocmg);
2002 /* release the one obtained from access_conn_mgmt(RELOAD) */
2003 (void) release_conn_mgmt(ocmg, B_FALSE);
2004 /* release the one obtained when ocmg was created */
2005 (void) release_conn_mgmt(ocmg, B_FALSE);
2006 return (ocmg);
2009 if ((cnt = chg->num_server) == 0)
2010 return (cmg);
2012 /* handle down servers first */
2013 for (i = 0; i < cnt; i++) {
2015 if (chg->changes[i] != NS_SERVER_DOWN)
2016 continue;
2017 s = chg->servers[i];
2020 * look for a CONNECTED MT connection using
2021 * the same server s, and close it
2023 while (loop) {
2024 if (cmg_locked == B_FALSE) {
2025 (void) mutex_lock(&cmg->lock);
2026 cmg_locked = B_TRUE;
2028 for (cm = cmg->cm_head; cm; cm = cm->next) {
2029 (void) mutex_lock(&cm->lock);
2031 if (cm->state == NS_CONN_MT_CONNECTED &&
2032 cm->conn != NULL &&
2033 strcasecmp(cm->conn->serverAddr, s) == 0) {
2034 (void) mutex_unlock(&cm->lock);
2035 break;
2038 (void) mutex_unlock(&cm->lock);
2040 if (cm != NULL) {
2041 (void) mutex_unlock(&cmg->lock);
2042 cmg_locked = B_FALSE;
2043 close_conn_mt_by_procchg(cm, LDAP_SERVER_DOWN,
2044 NS_CONN_MSG_DOWN_FROM_CACHEMGR);
2046 * Process the next cm using server s.
2047 * Start from the head of the cm linked
2048 * list again, as the cm list may change
2049 * after close_conn_mt_by_procchg() is done.
2051 continue;
2055 * No (more) MT connection using the down server s.
2056 * Process the next server on the list.
2058 break;
2059 } /* while loop */
2063 * Next handle servers whose status changed to up.
2064 * Get the preferred server list first if not done yet.
2065 * get_preferred_servers() leaves conn_mgmt locked.
2067 get_preferred_servers(cmg_locked == B_FALSE ? B_TRUE : B_FALSE,
2068 B_FALSE, cmg);
2069 cmg_locked = B_TRUE;
2071 * if no preferred server configured, we don't switch MT connection
2072 * to a more preferred server (i.e., fallback), so just return
2074 if (cmg->pservers == NULL) {
2075 (void) mutex_unlock(&cmg->lock);
2076 return (cmg);
2079 /* for each server that is up now */
2080 for (i = 0; i < cnt; i++) {
2081 if (chg->changes[i] != NS_SERVER_UP)
2082 continue;
2083 s = chg->servers[i];
2086 * look for a CONNECTED MT connection which uses
2087 * a server less preferred than s, and treat it
2088 * as 'fallback needed' by calling
2089 * close_conn_mt_when_nouser()
2091 k = -1;
2092 loop = B_TRUE;
2093 while (loop) {
2094 if (cmg_locked == B_FALSE) {
2095 (void) mutex_lock(&cmg->lock);
2096 cmg_locked = B_TRUE;
2099 /* Is s a preferred server ? */
2100 if (k == -1) {
2101 for (j = 0; cmg->pservers[j] != NULL; j++) {
2102 if (strcasecmp(cmg->pservers[j],
2103 s) == 0) {
2104 k = j;
2105 break;
2109 /* skip s if not a preferred server */
2110 if (k == -1) {
2111 break;
2114 /* check each MT connection */
2115 for (cm = cmg->cm_head; cm; cm = cm->next) {
2116 (void) mutex_lock(&cm->lock);
2118 * Find an MT connection that is connected and
2119 * not marked, but leave WRITE or REFERRAL
2120 * connections alone, since fallback does not
2121 * make sense for them.
2123 if (cm->state == NS_CONN_MT_CONNECTED &&
2124 cm->close_when_nouser == B_FALSE &&
2125 cm->conn != NULL && cm->opened_for !=
2126 NS_CONN_USER_WRITE &&
2127 cm->referral == B_FALSE) {
2128 n = -1;
2130 * j < k ??? should we close
2131 * an active MT that is using s ?
2132 * ie could s went down and up
2133 * again, but cm is bound prior to
2134 * the down ? Play safe here,
2135 * and check j <= k.
2137 for (j = 0; j <= k; j++) {
2138 if (strcasecmp(
2139 cm->conn->serverAddr,
2140 cmg->pservers[j]) == 0) {
2141 n = j;
2142 break;
2146 * s is preferred, if its location
2147 * in the preferred server list is
2148 * ahead of that of the server
2149 * used by the cm (i.e., no match
2150 * found before s)
2152 if (n == -1) { /* s is preferred */
2153 int fr = 0;
2154 fr = close_conn_mt_when_nouser(
2155 cm);
2156 NS_CONN_UNLOCK_AND_FREE(fr,
2157 cm, cmg);
2158 cmg_locked = B_FALSE;
2160 * break, not continue,
2161 * because we need to
2162 * check the entire cm
2163 * list again. The call
2164 * above may change the
2165 * cm list.
2167 break;
2170 (void) mutex_unlock(&cm->lock);
2172 /* if no (more) cm using s, check next server */
2173 if (cm == NULL)
2174 loop = B_FALSE;
2175 } /* while loop */
2177 if (cmg_locked == B_TRUE)
2178 (void) mutex_unlock(&cmg->lock);
2179 return (cmg);
2182 /* Shut down all MT connection managed by the connection management */
2183 void
2184 shutdown_all_conn_mt(ns_conn_mgmt_t *cmg)
2186 ns_ldap_error_t *ep;
2187 ns_conn_mt_t *cm;
2188 int free_cm = 0;
2189 boolean_t done = B_FALSE;
2191 ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
2192 if (ep != NULL) { /* if NULL, not a problem */
2193 /* OK if returns NULL */
2194 ep->message = strdup(NS_CONN_MSG_SHUTDOWN_RELOADED);
2197 (void) mutex_lock(&cmg->lock);
2198 while (cmg->cm_head != NULL && done == B_FALSE) {
2199 for (cm = cmg->cm_head; cm; cm = cm->next) {
2200 (void) mutex_lock(&cm->lock);
2201 if (cm->next == NULL)
2202 done = B_TRUE;
2203 /* shut down each conn_mt, ignore errors */
2204 free_cm = close_conn_mt(cm, LDAP_OTHER, &ep, NULL);
2205 (void) mutex_unlock(&cm->lock);
2206 if (free_cm == 1) {
2207 (void) free_conn_mt(cm, 0);
2209 * conn_mt may change, so start from
2210 * top of list again
2212 break;
2216 (void) mutex_unlock(&cmg->lock);
2217 (void) __ns_ldap_freeError(&ep);
2220 /* free all the resources used by the connection management */
2221 void
2222 __s_api_shutdown_conn_mgmt()
2224 ns_conn_mgmt_t *cmg;
2226 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_SHUTDOWN);
2227 if (cmg == NULL) /* already being SHUT done */
2228 return;
2230 (void) shutdown_all_conn_mt(cmg);
2231 (void) release_conn_mgmt(cmg, B_FALSE);
2233 /* then destroy the conn_mgmt */
2234 (void) release_conn_mgmt(cmg, B_FALSE);
2239 * Reinitialize the libsldap connection management after
2240 * a new native LDAP configuration is received.
2242 void
2243 __s_api_reinit_conn_mgmt_new_config(ns_config_t *new_cfg)
2245 ns_conn_mgmt_t *cmg;
2246 ns_conn_mgmt_t *ocmg;
2248 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
2249 if (cmg == NULL)
2250 return;
2251 if (cmg->config == new_cfg || cmg->state == NS_CONN_MGMT_DETACHED) {
2252 (void) release_conn_mgmt(cmg, B_FALSE);
2253 return;
2256 /* reload the conn_mgmt and native LDAP config */
2257 ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_NEW_CONFIG);
2258 if (ocmg == cmg)
2259 shutdown_all_conn_mt(ocmg);
2260 /* release the one obtained from access_conn_mgmt(RELOAD) */
2261 (void) release_conn_mgmt(ocmg, B_FALSE);
2262 /* release the one obtained when ocmg was created */
2263 (void) release_conn_mgmt(ocmg, B_FALSE);
2264 /* release the one obtained when this function is entered */
2265 (void) release_conn_mgmt(cmg, B_FALSE);
2269 * Prepare to retry ldap search operation if needed.
2270 * Return 1 if retry is needed, otherwise 0.
2271 * If first time in, return 1. If not, return 1 if:
2272 * - not a NS_CONN_USER_GETENT conn_user AND
2273 * - have not retried 3 times yet AND
2274 * - previous search failed AND
2275 * - the retry flag is set in the ns_conn_user_t or config was reloaded
2278 __s_api_setup_retry_search(ns_conn_user_t **conn_user,
2279 ns_conn_user_type_t type, int *try_cnt, int *rc,
2280 ns_ldap_error_t **errorp)
2282 boolean_t retry;
2283 ns_conn_user_t *cu = *conn_user;
2284 ns_conn_mgmt_t *cmg;
2286 if (*try_cnt > 0 && cu != NULL) {
2288 * if called from firstEntry(), keep conn_mt for
2289 * the subsequent getnext requests
2291 if (cu->type == NS_CONN_USER_GETENT && *rc == NS_LDAP_SUCCESS)
2292 return (0);
2293 cmg = cu->conn_mgmt;
2294 retry = cu->retry;
2295 if (cu->conn_mt != NULL)
2296 __s_api_conn_mt_return(cu);
2297 if (cmg != NULL && cmg->cfg_reloaded == B_TRUE)
2298 retry = B_TRUE;
2299 __s_api_conn_user_free(cu);
2300 *conn_user = NULL;
2302 if (*rc == NS_LDAP_SUCCESS || retry != B_TRUE)
2303 return (0);
2306 *try_cnt = *try_cnt + 1;
2307 if (*try_cnt > NS_LIST_TRY_MAX)
2308 return (0);
2310 *conn_user = __s_api_conn_user_init(type, NULL, B_FALSE);
2311 if (*conn_user == NULL) {
2312 if (*try_cnt == 1) { /* first call before any retry */
2313 *rc = NS_LDAP_MEMORY;
2314 *errorp = NULL;
2316 /* for 1+ try, use previous rc and errorp */
2317 return (0);
2320 /* free ldap_error_t from previous search */
2321 if (*try_cnt > 1 && rc != NS_LDAP_SUCCESS && *errorp != NULL)
2322 (void) __ns_ldap_freeError(errorp);
2324 return (1);
2327 /* prepare to get the next entry for an enumeration */
2329 __s_api_setup_getnext(ns_conn_user_t *cu, int *ns_err,
2330 ns_ldap_error_t **errorp)
2332 int rc;
2333 ns_conn_mgmt_t *cmg;
2336 * if using an MT connection, ensure the thread-specific data are set,
2337 * but if the MT connection is no longer good, return the error saved.
2339 if (cu->conn_mt != NULL && (cmg = cu->conn_mgmt) != NULL) {
2341 if (cu->bad_mt_conn == B_TRUE) {
2342 __s_api_conn_mt_close(cu, 0, NULL);
2343 *ns_err = cu->ns_rc;
2344 *errorp = cu->ns_error;
2345 cu->ns_error = NULL;
2346 return (*ns_err);
2349 rc = conn_tsd_check(cmg);
2350 if (rc != NS_LDAP_SUCCESS) {
2351 *errorp = NULL;
2352 return (rc);
2356 return (NS_LDAP_SUCCESS);
2359 /* wait for an MT connection to become available */
2360 static int
2361 conn_wait(ns_conn_mt_t *conn_mt, ns_conn_user_t *conn_user)
2363 ns_conn_waiter_t mywait;
2364 ns_conn_waiter_t *head = &conn_mt->waiter;
2366 (void) cond_init(&(mywait.waitcv), USYNC_THREAD, 0);
2367 mywait.key = conn_user;
2368 mywait.signaled = 0;
2369 mywait.next = head->next;
2370 mywait.prev = head;
2371 if (mywait.next)
2372 mywait.next->prev = &mywait;
2373 head->next = &mywait;
2374 atomic_inc_uint(&conn_mt->waiter_cnt);
2376 while (!mywait.signaled)
2377 (void) cond_wait(&(mywait.waitcv), &conn_mt->lock);
2378 if (mywait.prev)
2379 mywait.prev->next = mywait.next;
2380 if (mywait.next)
2381 mywait.next->prev = mywait.prev;
2382 return (0);
2385 /* signal that an MT connection is now available */
2386 static int
2387 conn_signal(ns_conn_mt_t *conn_mt)
2389 int c = 0;
2390 ns_conn_waiter_t *head = &conn_mt->waiter;
2391 ns_conn_waiter_t *tmp = head->next;
2393 while (tmp) {
2394 (void) cond_signal(&(tmp->waitcv));
2395 tmp->signaled = 1;
2396 atomic_dec_uint(&conn_mt->waiter_cnt);
2397 c++;
2398 tmp = tmp->next;
2401 return (c);
2405 * wait and process the server status and/or config change notification
2406 * from ldap_cachemgr
2408 static void *
2409 get_server_change(void *arg)
2411 union {
2412 ldap_data_t s_d;
2413 char s_b[DOORBUFFERSIZE];
2414 } space;
2415 ldap_data_t *sptr = &space.s_d;
2416 int ndata;
2417 int adata;
2418 char *ptr;
2419 int ds_cnt;
2420 int door_rc;
2421 int which;
2422 int retry = 0;
2423 boolean_t loop = B_TRUE;
2424 char *c, *oc;
2425 int dslen = strlen(DOORLINESEP);
2426 char dsep = DOORLINESEP_CHR;
2427 char chg_data[DOORBUFFERSIZE];
2428 char **servers = NULL;
2429 boolean_t getchg_not_supported = B_FALSE;
2430 ns_conn_mgmt_t *ocmg = (ns_conn_mgmt_t *)arg;
2431 ns_conn_mgmt_t *cmg;
2432 ns_server_status_t *status = NULL;
2433 ns_server_status_change_t chg = { 0 };
2434 ldap_get_change_out_t *get_chg;
2435 ldap_get_chg_cookie_t cookie;
2436 ldap_get_chg_cookie_t new_cookie;
2438 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
2439 if (cmg != ocmg)
2440 thr_exit(NULL);
2441 /* cmg is locked before called */
2442 cmg->procchg_tid = thr_self();
2444 /* make sure the thread specific data are set */
2445 (void) conn_tsd_setup(cmg);
2446 cookie = cmg->cfg_cookie;
2448 while (loop) {
2450 free(chg.servers);
2451 free(chg.changes);
2452 if (sptr != &space.s_d)
2453 (void) munmap((char *)sptr, sizeof (space));
2456 * If the attached conn_mgmt has been deleted,
2457 * then exit. The new conn_mgmt will starts it
2458 * own monitor thread later. If libsldap is being
2459 * unloaded or configuration reloaded, OR
2460 * ldap_cachemgr rejected the GETSTATUSCHANGE door
2461 * call, then exit as well.
2463 if (cmg == NULL || cmg->state == NS_CONN_MGMT_DETACHED ||
2464 getchg_not_supported == B_TRUE) {
2466 if (cmg != NULL) {
2467 cmg->procchg_started = B_FALSE;
2468 (void) release_conn_mgmt(cmg, B_FALSE);
2471 conn_tsd_free();
2472 thr_exit(NULL);
2475 (void) memset(space.s_b, 0, DOORBUFFERSIZE);
2476 (void) memset(&chg, 0, sizeof (chg));
2477 adata = sizeof (ldap_call_t) + 1;
2478 ndata = sizeof (space);
2479 space.s_d.ldap_call.ldap_callnumber = GETSTATUSCHANGE;
2480 space.s_d.ldap_call.ldap_u.get_change.op =
2481 NS_STATUS_CHANGE_OP_START;
2482 space.s_d.ldap_call.ldap_u.get_change.cookie = cookie;
2483 sptr = &space.s_d;
2484 door_rc = __ns_ldap_trydoorcall_getfd();
2485 cmg->procchg_door_call = B_TRUE;
2486 if (release_conn_mgmt(cmg, B_FALSE) == NULL) {
2487 conn_tsd_free();
2488 thr_exit(NULL);
2491 if (door_rc == NS_CACHE_SUCCESS)
2492 door_rc = __ns_ldap_trydoorcall_send(&sptr, &ndata,
2493 &adata);
2496 * Check and see if the conn_mgmt is still current.
2497 * If not, no need to continue.
2499 cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
2500 if (cmg != NULL)
2501 cmg->procchg_door_call = B_FALSE;
2502 if (cmg != ocmg) {
2503 if (cmg != NULL) {
2504 cmg->procchg_started = B_FALSE;
2505 (void) release_conn_mgmt(cmg, B_FALSE);
2507 conn_tsd_free();
2508 thr_exit(NULL);
2511 if (door_rc != NS_CACHE_SUCCESS) {
2512 if (door_rc == NS_CACHE_NOSERVER) {
2513 if (retry++ > 10)
2514 getchg_not_supported = B_TRUE;
2515 else {
2517 * ldap_cachemgr may be down, give
2518 * it time to restart
2520 (void) sleep(2);
2522 } else if (door_rc == NS_CACHE_NOTFOUND)
2523 getchg_not_supported = B_TRUE;
2524 continue;
2525 } else
2526 retry = 0;
2528 /* copy info from door call return structure */
2529 get_chg = &sptr->ldap_ret.ldap_u.changes;
2530 ptr = get_chg->data;
2531 /* configuration change ? */
2532 if (get_chg->type == NS_STATUS_CHANGE_TYPE_CONFIG) {
2533 chg.config_changed = B_TRUE;
2534 cmg = proc_server_change(&chg, cmg);
2535 continue;
2538 /* server status changes ? */
2539 if (get_chg->type == NS_STATUS_CHANGE_TYPE_SERVER) {
2541 * first check cookies, if don't match, config
2542 * has changed
2544 new_cookie = get_chg->cookie;
2545 if (new_cookie.mgr_pid != cookie.mgr_pid ||
2546 new_cookie.seq_num != cookie.seq_num) {
2547 chg.config_changed = B_TRUE;
2548 cmg = proc_server_change(&chg, cmg);
2549 continue;
2552 (void) strlcpy(chg_data, ptr, sizeof (chg_data));
2553 chg.num_server = get_chg->server_count;
2555 servers = (char **)calloc(chg.num_server,
2556 sizeof (char *));
2557 if (servers == NULL) {
2558 syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
2559 continue;
2561 status = (ns_server_status_t *)calloc(chg.num_server,
2562 sizeof (int));
2563 if (status == NULL) {
2564 syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
2565 free(servers);
2566 continue;
2568 ds_cnt = 0;
2569 which = 0;
2570 oc = ptr;
2571 for (c = ptr; which != 2; c++) {
2572 /* look for DOORLINESEP or end of string */
2573 if (*c != dsep && *c != '\0')
2574 continue;
2575 if (*c == dsep) { /* DOORLINESEP */
2576 *c = '\0'; /* current value */
2577 c += dslen; /* skip to next value */
2579 if (which == 0) { /* get server info */
2580 servers[ds_cnt] = oc;
2581 oc = c;
2582 which = 1; /* get status next */
2583 continue;
2585 /* which == 1, get up/down status */
2586 if (strcmp(NS_SERVER_CHANGE_UP, oc) == 0) {
2587 status[ds_cnt] = NS_SERVER_UP;
2588 } else if (strcmp(NS_SERVER_CHANGE_DOWN,
2589 oc) == 0)
2590 status[ds_cnt] = NS_SERVER_DOWN;
2591 else {
2592 syslog(LOG_INFO,
2593 NS_CONN_MSG_BAD_CACHEMGR_DATA);
2594 continue;
2596 oc = c;
2597 ds_cnt++;
2598 if (*c == '\0')
2599 which = 2; /* exit the loop */
2600 else
2601 which = 0; /* get server info next */
2603 chg.servers = servers;
2604 chg.changes = status;
2605 cmg = proc_server_change(&chg, cmg);
2606 continue;
2610 return (NULL);
2613 /* start the thread handling the change notification from ldap_cachemgr */
2614 static void
2615 start_thread(ns_conn_mgmt_t *cmg) {
2617 int errnum;
2620 * start a thread to get and process config and server status changes
2622 if (thr_create(NULL, 0, get_server_change,
2623 (void *)cmg, THR_DETACHED, NULL) != 0) {
2624 errnum = errno;
2625 syslog(LOG_WARNING, NS_CONN_MSG_NO_PROCCHG_THREAD,
2626 strerror(errnum));