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