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