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