xref: /onnv-gate/usr/src/lib/libsldap/common/ns_connmgmt.c (revision 7281:15ede204db00)
16842Sth160488 /*
26842Sth160488  * CDDL HEADER START
36842Sth160488  *
46842Sth160488  * The contents of this file are subject to the terms of the
56842Sth160488  * Common Development and Distribution License (the "License").
66842Sth160488  * You may not use this file except in compliance with the License.
76842Sth160488  *
86842Sth160488  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96842Sth160488  * or http://www.opensolaris.org/os/licensing.
106842Sth160488  * See the License for the specific language governing permissions
116842Sth160488  * and limitations under the License.
126842Sth160488  *
136842Sth160488  * When distributing Covered Code, include this CDDL HEADER in each
146842Sth160488  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156842Sth160488  * If applicable, add the following below this CDDL HEADER, with the
166842Sth160488  * fields enclosed by brackets "[]" replaced with your own identifying
176842Sth160488  * information: Portions Copyright [yyyy] [name of copyright owner]
186842Sth160488  *
196842Sth160488  * CDDL HEADER END
206842Sth160488  */
216842Sth160488 /*
226842Sth160488  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
236842Sth160488  * Use is subject to license terms.
246842Sth160488  */
256842Sth160488 
266842Sth160488 #pragma ident	"%Z%%M%	%I%	%E% SMI"
276842Sth160488 
286842Sth160488 #include <string.h>
296842Sth160488 #include <errno.h>
306842Sth160488 #include <syslog.h>
316842Sth160488 #include <procfs.h>
326842Sth160488 #include <unistd.h>
336842Sth160488 #include <fcntl.h>
346842Sth160488 #include <libintl.h>
356842Sth160488 #include <atomic.h>
366842Sth160488 #include <pthread.h>
376842Sth160488 #include <sys/mman.h>
386842Sth160488 #include <time.h>
396842Sth160488 #include "solaris-int.h"
406842Sth160488 #include "ns_connmgmt.h"
416842Sth160488 #include "ns_cache_door.h"
426842Sth160488 #include "ns_internal.h"
436842Sth160488 
446842Sth160488 /*
456842Sth160488  * Access (reference, shutdown, or reload) the current connection
466842Sth160488  * management control structure conn_mgmt_t.
476842Sth160488  */
486842Sth160488 #define	NS_CONN_MGMT_OP_REF		1
496842Sth160488 #define	NS_CONN_MGMT_OP_SHUTDOWN	2
506842Sth160488 #define	NS_CONN_MGMT_OP_RELOAD_CONFIG	3
516842Sth160488 #define	NS_CONN_MGMT_OP_NEW_CONFIG	4
526842Sth160488 #define	NS_CONN_MGMT_OP_LIB_INIT	5
536842Sth160488 
546842Sth160488 static ns_conn_mgmt_t *access_conn_mgmt(int);
556842Sth160488 static ns_conn_mgmt_t *release_conn_mgmt(ns_conn_mgmt_t *, boolean_t);
566842Sth160488 static int close_conn_mt(ns_conn_mt_t *, int, ns_ldap_error_t **,
576842Sth160488 	ns_conn_user_t *);
586842Sth160488 static int close_conn_mt_when_nouser(ns_conn_mt_t *cm);
596842Sth160488 void shutdown_all_conn_mt(ns_conn_mgmt_t *cmg);
606842Sth160488 static int conn_signal(ns_conn_mt_t *);
616842Sth160488 static int conn_wait(ns_conn_mt_t *, ns_conn_user_t *);
626842Sth160488 static void close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg);
636842Sth160488 static ns_conn_mgmt_t *proc_server_change(ns_server_status_change_t *chg,
646842Sth160488 	ns_conn_mgmt_t  *cmg);
656842Sth160488 static void get_preferred_servers(boolean_t, boolean_t, ns_conn_mgmt_t *);
666842Sth160488 static void start_thread();
676842Sth160488 
686842Sth160488 static ns_conn_mgmt_t	*ns_connmgmt = NULL;
696842Sth160488 static ns_conn_mgmt_t	*ns_connmgmt_parent = NULL;
706842Sth160488 static mutex_t		ns_connmgmt_lock = DEFAULTMUTEX;
716842Sth160488 static boolean_t	ns_connmgmt_shutting_down = B_FALSE;
726842Sth160488 
736842Sth160488 #define	NS_CONN_MSG_NO_CONN_MGMT gettext( \
746842Sth160488 	"libsldap: unable to allocate the connection management control")
756842Sth160488 #define	NS_CONN_MSG_NO_MTC_KEY gettext( \
766842Sth160488 	"libsldap: unable to allocate the TSD key for per-thread ldap error")
776842Sth160488 #define	NS_CONN_MSG_NO_CMG_KEY gettext( \
786842Sth160488 	"libsldap: unable to allocate the TSD key for connection management")
796842Sth160488 #define	NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded")
806842Sth160488 #define	NS_CONN_MSG_RELOADED gettext( \
816842Sth160488 	"libsldap: configuration has been reloaded")
826842Sth160488 #define	NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \
836842Sth160488 	"libsldap: library unloaded or configuration has been reloaded")
846842Sth160488 #define	NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \
856842Sth160488 	"libsldap: received incorrect data from ldap_cachemgr")
866842Sth160488 #define	NS_CONN_MSG_MEMORY_ERROR gettext( \
876842Sth160488 	"libsldap: unable to allocate memory")
886842Sth160488 #define	NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \
896842Sth160488 	"libsldap: unable to start the server monitor thread (%s)")
906842Sth160488 #define	NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \
916842Sth160488 	"libsldap: server down reported by ldap_cachemgr")
926842Sth160488 
936842Sth160488 static int ns_conn_free = 1;
946842Sth160488 #define	NS_CONN_UNLOCK_AND_FREE(free, cm, cmg)  \
956842Sth160488 { \
966842Sth160488 	(void) mutex_unlock(&(cm)->lock);	\
976842Sth160488 	if (free == 1)	\
986842Sth160488 		cmg = free_conn_mt((cm), 1); \
996842Sth160488 	if (cmg != NULL) \
1006842Sth160488 		(void) mutex_unlock(&(cmg)->lock); \
1016842Sth160488 }
1026842Sth160488 
1036842Sth160488 #define	NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \
1046842Sth160488 { \
1056842Sth160488 	char *msg = NULL; \
1066842Sth160488 	(void) mutex_lock(&(cmg)->lock); \
1076842Sth160488 	if ((cmg)->shutting_down == B_TRUE) \
1086842Sth160488 		msg = NS_CONN_MSG_SHUTDOWN; \
1096842Sth160488 	else if ((cmg)->cfg_reloaded == B_TRUE)  \
1106842Sth160488 		msg = NS_CONN_MSG_RELOADED; \
1116842Sth160488 	if (msg != NULL) { \
1126842Sth160488 		(*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \
1136842Sth160488 		(void) mutex_unlock(&(cmg)->lock); \
1146842Sth160488 		return (NS_LDAP_OP_FAILED); \
1156842Sth160488 	} \
1166842Sth160488 }
1176842Sth160488 
1186842Sth160488 /*
1196842Sth160488  * TSD keys ns_mtckey and ns_cmgkey are for sharing ldap connections
1206842Sth160488  * and their associated connection management structure among
1216842Sth160488  * multiple threads. The pointers to the per-thread ldap error
1226842Sth160488  * information and the connection management structure are
1236842Sth160488  * saved in ns_mtckey and ns_cmgkey.
1246842Sth160488  */
1256842Sth160488 thread_key_t ns_mtckey = THR_ONCE_KEY;
1266842Sth160488 thread_key_t ns_cmgkey = THR_ONCE_KEY;
1276842Sth160488 
1286842Sth160488 /* Per thread LDAP error resides in thread-specific data (ns_mtckey) */
1296842Sth160488 struct ldap_error {
1306842Sth160488 	int	le_errno;
1316842Sth160488 	char	*le_matched;
1326842Sth160488 	char	*le_errmsg;
1336842Sth160488 };
1346842Sth160488 
1356842Sth160488 /* NULL struct ldap_error */
1366842Sth160488 static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
1376842Sth160488 
1386842Sth160488 /* destructor: free the ldap error data in the thread specific area */
1396842Sth160488 static void
ns_mtckey_cleanup(void * key)1406842Sth160488 ns_mtckey_cleanup(void *key) {
1416842Sth160488 	struct ldap_error *le = (struct ldap_error *)key;
1426842Sth160488 
1436842Sth160488 	if (le == NULL)
1446842Sth160488 		return;
1456842Sth160488 	if (le->le_matched != NULL) {
1466842Sth160488 		ldap_memfree(le->le_matched);
1476842Sth160488 	}
1486842Sth160488 	if (le->le_errmsg != NULL) {
1496842Sth160488 		ldap_memfree(le->le_errmsg);
1506842Sth160488 	}
1516842Sth160488 	free(le);
1526842Sth160488 }
1536842Sth160488 
1546842Sth160488 /* Free/detach the thread specific data structures */
1556842Sth160488 static void
conn_tsd_free()1566842Sth160488 conn_tsd_free() {
1576842Sth160488 	void	*tsd = NULL;
1586842Sth160488 	int	rc;
1596842Sth160488 
1606842Sth160488 	/* free the per-thread ldap error info */
1616842Sth160488 	rc = thr_getspecific(ns_mtckey, &tsd);
1626842Sth160488 	if (rc == 0 && tsd != NULL)
1636842Sth160488 		ns_mtckey_cleanup(tsd);
1646842Sth160488 	(void) thr_setspecific(ns_mtckey, NULL);
1656842Sth160488 
1666842Sth160488 	/* detach the connection management control */
1676842Sth160488 	(void) thr_setspecific(ns_cmgkey, NULL);
1686842Sth160488 }
1696842Sth160488 
1706842Sth160488 /* per-thread callback function for allocating a mutex */
1716842Sth160488 static void *
ns_mutex_alloc(void)1726842Sth160488 ns_mutex_alloc(void)
1736842Sth160488 {
1746842Sth160488 	mutex_t *mutexp = NULL;
1756842Sth160488 
1766842Sth160488 	if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
1776842Sth160488 		if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
1786842Sth160488 			free(mutexp);
1796842Sth160488 			mutexp = NULL;
1806842Sth160488 		}
1816842Sth160488 	}
1826842Sth160488 	return (mutexp);
1836842Sth160488 }
1846842Sth160488 
1856842Sth160488 /* per-thread callback function for freeing a mutex */
1866842Sth160488 static void
ns_mutex_free(void * mutexp)1876842Sth160488 ns_mutex_free(void *mutexp)
1886842Sth160488 {
1896842Sth160488 	(void) mutex_destroy((mutex_t *)mutexp);
1906842Sth160488 	free(mutexp);
1916842Sth160488 }
1926842Sth160488 
1936842Sth160488 /*
1946842Sth160488  * Function for setting up thread-specific data
1956842Sth160488  * where per thread LDAP error and the pointer
1966842Sth160488  * to the active connection management control
1976842Sth160488  * are stored.
1986842Sth160488  */
1996842Sth160488 static int
conn_tsd_setup(ns_conn_mgmt_t * cmg)2006842Sth160488 conn_tsd_setup(ns_conn_mgmt_t *cmg)
2016842Sth160488 {
2026842Sth160488 	void	*tsd;
2036842Sth160488 	int	rc;
2046842Sth160488 
2056842Sth160488 	rc = thr_setspecific(ns_cmgkey, cmg);
2066842Sth160488 	if (rc != 0) /* must be ENOMEM */
2076842Sth160488 		return (-1);
2086842Sth160488 
2096842Sth160488 	/* return success if the ns_mtckey TSD is already set */
2106842Sth160488 	rc = thr_getspecific(ns_mtckey, &tsd);
2116842Sth160488 	if (rc == 0 && tsd != NULL)
2126842Sth160488 		return (0);
2136842Sth160488 
2146842Sth160488 	/* allocate and set the ns_mtckey TSD */
2156842Sth160488 	tsd = (void *) calloc(1, sizeof (struct ldap_error));
2166842Sth160488 	if (tsd == NULL)
2176842Sth160488 		return (-1);
2186842Sth160488 	rc = thr_setspecific(ns_mtckey, tsd);
2196842Sth160488 	if (rc != 0) { /* must be ENOMEM */
2206842Sth160488 		free(tsd);
2216842Sth160488 		return (-1);
2226842Sth160488 	}
2236842Sth160488 	return (0);
2246842Sth160488 }
2256842Sth160488 
2266842Sth160488 /* Callback function for setting the per thread LDAP error */
2276842Sth160488 /*ARGSUSED*/
2286842Sth160488 static void
set_ld_error(int err,char * matched,char * errmsg,void * dummy)2296842Sth160488 set_ld_error(int err, char *matched, char *errmsg, void *dummy)
2306842Sth160488 {
2316842Sth160488 	struct ldap_error	*le;
2326842Sth160488 	int			eno;
2336842Sth160488 
2346842Sth160488 	if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
2356842Sth160488 		syslog(LOG_ERR, gettext(
2366842Sth160488 		    "libsldap: set_ld_error: thr_getspecific failed (%s)."),
2376842Sth160488 		    strerror(eno));
2386842Sth160488 		return;
2396842Sth160488 	}
2406842Sth160488 
2416842Sth160488 	/* play safe, do nothing if TSD pointer is NULL */
2426842Sth160488 	if (le == NULL) {
2436842Sth160488 		syslog(LOG_INFO, gettext(
2446842Sth160488 		    "libsldap: set_ld_error: TSD pointer is NULL."));
2456842Sth160488 		return;
2466842Sth160488 	}
2476842Sth160488 
2486842Sth160488 	le->le_errno = err;
2496842Sth160488 
2506842Sth160488 	if (le->le_matched != NULL) {
2516842Sth160488 		ldap_memfree(le->le_matched);
2526842Sth160488 		le->le_matched = NULL;
2536842Sth160488 	}
2546842Sth160488 	le->le_matched = matched;
2556842Sth160488 
2566842Sth160488 	if (le->le_errmsg != NULL) {
2576842Sth160488 		ldap_memfree(le->le_errmsg);
2586842Sth160488 		le->le_errmsg = NULL;
2596842Sth160488 	}
2606842Sth160488 	le->le_errmsg = errmsg;
2616842Sth160488 }
2626842Sth160488 
2636842Sth160488 /* check and allocate the thread-specific data for using a MT connection */
2646842Sth160488 static int
conn_tsd_check(ns_conn_mgmt_t * cmg)2656842Sth160488 conn_tsd_check(ns_conn_mgmt_t *cmg)
2666842Sth160488 {
2676842Sth160488 	if (conn_tsd_setup(cmg) != 0)
2686842Sth160488 		return (NS_LDAP_MEMORY);
2696842Sth160488 
2706842Sth160488 	return (NS_LDAP_SUCCESS);
2716842Sth160488 }
2726842Sth160488 
2736842Sth160488 /* Callback function for getting the per thread LDAP error */
2746842Sth160488 /*ARGSUSED*/
2756842Sth160488 static int
get_ld_error(char ** matched,char ** errmsg,void * dummy)2766842Sth160488 get_ld_error(char **matched, char **errmsg, void *dummy)
2776842Sth160488 {
2786842Sth160488 	struct ldap_error	*le;
2796842Sth160488 	int			eno;
2806842Sth160488 
2816842Sth160488 	if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
2826842Sth160488 		syslog(LOG_ERR, gettext(
2836842Sth160488 		    "libsldap: get_ld_error: thr_getspecific failed (%s)"),
2846842Sth160488 		    strerror(eno));
2856842Sth160488 		return (eno);
2866842Sth160488 	}
2876842Sth160488 
2886842Sth160488 	/* play safe, return NULL error data, if TSD pointer is NULL */
2896842Sth160488 	if (le == NULL)
2906842Sth160488 		le = &ldap_error_NULL;
2916842Sth160488 
2926842Sth160488 	if (matched != NULL) {
2936842Sth160488 		*matched = le->le_matched;
2946842Sth160488 	}
2956842Sth160488 	if (errmsg != NULL) {
2966842Sth160488 		*errmsg = le->le_errmsg;
2976842Sth160488 	}
2986842Sth160488 	return (le->le_errno);
2996842Sth160488 }
3006842Sth160488 
3016842Sth160488 /* Callback function for setting per thread errno */
3026842Sth160488 static void
set_errno(int err)3036842Sth160488 set_errno(int err)
3046842Sth160488 {
3056842Sth160488 	errno = err;
3066842Sth160488 }
3076842Sth160488 
3086842Sth160488 /* Callback function for getting per thread errno */
3096842Sth160488 static int
get_errno(void)3106842Sth160488 get_errno(void)
3116842Sth160488 {
3126842Sth160488 	return (errno);
3136842Sth160488 }
3146842Sth160488 
3156842Sth160488 /* set up an ldap session 'ld' for sharing among multiple threads */
3166842Sth160488 static int
setup_mt_conn(LDAP * ld)3176842Sth160488 setup_mt_conn(LDAP *ld)
3186842Sth160488 {
3196842Sth160488 
3206842Sth160488 	struct ldap_thread_fns		tfns;
3216842Sth160488 	struct ldap_extra_thread_fns	extrafns;
3226842Sth160488 	int				rc;
3236842Sth160488 
3246842Sth160488 	/*
3256842Sth160488 	 * Set the function pointers for dealing with mutexes
3266842Sth160488 	 * and error information
3276842Sth160488 	 */
3286842Sth160488 	(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
3296842Sth160488 	tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
3306842Sth160488 	tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
3316842Sth160488 	tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
3326842Sth160488 	tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
3336842Sth160488 	tfns.ltf_get_errno = get_errno;
3346842Sth160488 	tfns.ltf_set_errno = set_errno;
3356842Sth160488 	tfns.ltf_get_lderrno = get_ld_error;
3366842Sth160488 	tfns.ltf_set_lderrno = set_ld_error;
3376842Sth160488 	tfns.ltf_lderrno_arg = NULL;
3386842Sth160488 
3396842Sth160488 	/*
3406842Sth160488 	 * Set up the ld to use those function pointers
3416842Sth160488 	 */
3426842Sth160488 	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
3436842Sth160488 	    (void *) &tfns);
3446842Sth160488 	if (rc < 0) {
3456842Sth160488 		syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
3466842Sth160488 		"(LDAP_OPT_THREAD_FN_PTRS)"));
3476842Sth160488 		return (0);
3486842Sth160488 	}
3496842Sth160488 
3506842Sth160488 	/*
3516842Sth160488 	 * Set the function pointers for working with semaphores
3526842Sth160488 	 */
3536842Sth160488 	(void) memset(&extrafns, '\0',
3546842Sth160488 	    sizeof (struct ldap_extra_thread_fns));
3556842Sth160488 	extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
3566842Sth160488 	extrafns.ltf_mutex_trylock = NULL;
3576842Sth160488 	extrafns.ltf_sema_alloc = NULL;
3586842Sth160488 	extrafns.ltf_sema_free = NULL;
3596842Sth160488 	extrafns.ltf_sema_wait = NULL;
3606842Sth160488 	extrafns.ltf_sema_post = NULL;
3616842Sth160488 
3626842Sth160488 	/* Set up the ld to use those function pointers */
3636842Sth160488 	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
3646842Sth160488 	    (void *) &extrafns);
3656842Sth160488 	if (rc < 0) {
3666842Sth160488 		syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
3676842Sth160488 		"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"));
3686842Sth160488 		return (0);
3696842Sth160488 	}
3706842Sth160488 
3716842Sth160488 	return (1);
3726842Sth160488 }
3736842Sth160488 
3746842Sth160488 /* set up an MT connection for sharing among multiple threads */
3756842Sth160488 static int
setup_mt_ld(LDAP * ld,ns_conn_mgmt_t * cmg)3766842Sth160488 setup_mt_ld(LDAP *ld, ns_conn_mgmt_t *cmg)
3776842Sth160488 {
3786842Sth160488 	thread_t	t = thr_self();
3796842Sth160488 
3806842Sth160488 	/* set up the per-thread data for using the MT connection */
3816842Sth160488 	if (conn_tsd_setup(cmg) == -1) {
3826842Sth160488 		syslog(LOG_WARNING,
3836842Sth160488 		    gettext("libsldap: tid= %d: unable to set up TSD\n"), t);
3846842Sth160488 		return (-1);
3856842Sth160488 	}
3866842Sth160488 
3876842Sth160488 	if (setup_mt_conn(ld) == 0) {
3886842Sth160488 		/* multiple threads per connection not supported */
3896842Sth160488 		syslog(LOG_WARNING, gettext("libsldap: tid= %d: multiple "
3906842Sth160488 		    "threads per connection not supported\n"), t);
3916842Sth160488 		conn_tsd_free();
3926842Sth160488 		return (-1);
3936842Sth160488 	}
3946842Sth160488 	return (0);
3956842Sth160488 }
3966842Sth160488 
3976842Sth160488 /*
3986842Sth160488  * Check name and UID of process, if it is nscd.
3996842Sth160488  *
4006842Sth160488  * Input:
4016842Sth160488  *   pid	: PID of checked process
4026842Sth160488  *   check_uid	: check if UID == 0
4036842Sth160488  * Output:
4046842Sth160488  *   B_TRUE	: nscd detected
4056842Sth160488  *   B_FALSE	: nscd not confirmed
4066842Sth160488  */
4076842Sth160488 static boolean_t
check_nscd_proc(pid_t pid,boolean_t check_uid)4086842Sth160488 check_nscd_proc(pid_t pid, boolean_t check_uid)
4096842Sth160488 {
4106842Sth160488 	psinfo_t	pinfo;
4116842Sth160488 	char		fname[MAXPATHLEN];
4126842Sth160488 	ssize_t		ret;
4136842Sth160488 	int		fd;
4146842Sth160488 
4156842Sth160488 	if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
4166842Sth160488 		if ((fd = open(fname,  O_RDONLY)) >= 0) {
4176842Sth160488 			ret = read(fd, &pinfo, sizeof (psinfo_t));
4186842Sth160488 			(void) close(fd);
4196842Sth160488 			if ((ret == sizeof (psinfo_t)) &&
4206842Sth160488 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
4216842Sth160488 				if (check_uid && (pinfo.pr_uid != 0))
4226842Sth160488 					return (B_FALSE);
4236842Sth160488 				return (B_TRUE);
4246842Sth160488 			}
4256842Sth160488 		}
4266842Sth160488 	}
4276842Sth160488 	return (B_FALSE);
4286842Sth160488 }
4296842Sth160488 
4306842Sth160488 /*
4316842Sth160488  * Check if this process is peruser nscd.
4326842Sth160488  */
4336842Sth160488 boolean_t
__s_api_peruser_proc(void)4346842Sth160488 __s_api_peruser_proc(void)
4356842Sth160488 {
4366842Sth160488 	pid_t		my_ppid;
4376842Sth160488 	static mutex_t	nscdLock = DEFAULTMUTEX;
4386842Sth160488 	static pid_t	checkedPpid = (pid_t)-1;
4396842Sth160488 	static boolean_t isPeruserNscd = B_FALSE;
4406842Sth160488 
4416842Sth160488 	my_ppid = getppid();
4426842Sth160488 
4436842Sth160488 	/*
4446842Sth160488 	 * Already checked before for this process? If yes, return cached
4456842Sth160488 	 * response.
4466842Sth160488 	 */
4476842Sth160488 	if (my_ppid == checkedPpid) {
4486842Sth160488 		return (isPeruserNscd);
4496842Sth160488 	}
4506842Sth160488 
4516842Sth160488 	(void) mutex_lock(&nscdLock);
4526842Sth160488 
4536842Sth160488 	/* Check once more incase another thread has just complete this. */
4546842Sth160488 	if (my_ppid == checkedPpid) {
4556842Sth160488 		(void) mutex_unlock(&nscdLock);
4566842Sth160488 		return (isPeruserNscd);
4576842Sth160488 	}
4586842Sth160488 
4596842Sth160488 	/* Reinitialize to be sure there is no residue after fork. */
4606842Sth160488 	isPeruserNscd = B_FALSE;
4616842Sth160488 
4626842Sth160488 	/* Am I the nscd process? */
4636842Sth160488 	if (check_nscd_proc(getpid(), B_FALSE)) {
4646842Sth160488 		/* Is my parent the nscd process with UID == 0. */
4656842Sth160488 		isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
4666842Sth160488 	}
4676842Sth160488 
4686921Smichen 	/* Remember for whom isPeruserNscd is. */
4696842Sth160488 	checkedPpid = my_ppid;
4706842Sth160488 
4716842Sth160488 	(void) mutex_unlock(&nscdLock);
4726842Sth160488 	return (isPeruserNscd);
4736842Sth160488 }
4746842Sth160488 
4756842Sth160488 /*
4766842Sth160488  * Check if this process is main nscd.
4776842Sth160488  */
4786842Sth160488 boolean_t
__s_api_nscd_proc(void)4796842Sth160488 __s_api_nscd_proc(void)
4806842Sth160488 {
4816842Sth160488 	pid_t		my_pid;
4826842Sth160488 	static mutex_t	nscdLock = DEFAULTMUTEX;
4836842Sth160488 	static pid_t	checkedPid = (pid_t)-1;
4846842Sth160488 	static boolean_t isMainNscd = B_FALSE;
4856842Sth160488 
4866842Sth160488 	/*
4876842Sth160488 	 * Don't bother checking if this process isn't root, this cannot
4886842Sth160488 	 * be main nscd.
4896842Sth160488 	 */
4906842Sth160488 	if (getuid() != 0)
4916842Sth160488 		return (B_FALSE);
4926842Sth160488 
4936842Sth160488 	my_pid = getpid();
4946842Sth160488 
4956842Sth160488 	/*
4966842Sth160488 	 * Already checked before for this process? If yes, return cached
4976842Sth160488 	 * response.
4986842Sth160488 	 */
4996842Sth160488 	if (my_pid == checkedPid) {
5006842Sth160488 		return (isMainNscd);
5016842Sth160488 	}
5026842Sth160488 
5036842Sth160488 	(void) mutex_lock(&nscdLock);
5046842Sth160488 
5056842Sth160488 	/* Check once more incase another thread has just done this. */
5066842Sth160488 	if (my_pid == checkedPid) {
5076842Sth160488 		(void) mutex_unlock(&nscdLock);
5086842Sth160488 		return (isMainNscd);
5096842Sth160488 	}
5106842Sth160488 
5116842Sth160488 	/*
5126842Sth160488 	 * Am I the nscd process? UID is already checked, not needed from
5136842Sth160488 	 * psinfo.
5146842Sth160488 	 */
5156842Sth160488 	isMainNscd = check_nscd_proc(my_pid, B_FALSE);
5166842Sth160488 
5176921Smichen 	/* Remember for whom isMainNscd is. */
5186842Sth160488 	checkedPid = my_pid;
5196842Sth160488 
5206842Sth160488 	(void) mutex_unlock(&nscdLock);
5216842Sth160488 	return (isMainNscd);
5226842Sth160488 }
5236842Sth160488 
5246842Sth160488 /*
5256842Sth160488  * initialize a connection management control structure conn_mgmt_t
5266842Sth160488  */
5276842Sth160488 ns_conn_mgmt_t *
init_conn_mgmt()5286842Sth160488 init_conn_mgmt()
5296842Sth160488 {
5306842Sth160488 	ns_conn_mgmt_t	*cmg;
5316842Sth160488 
5326842Sth160488 	cmg = (ns_conn_mgmt_t *)calloc(1, sizeof (*cmg));
5336842Sth160488 	if (cmg == NULL) {
5346842Sth160488 		syslog(LOG_ERR, NS_CONN_MSG_NO_CONN_MGMT);
5356842Sth160488 		return (NULL);
5366842Sth160488 	}
5376842Sth160488 
5386842Sth160488 	/* is this process nscd or peruser nscd ? */
5396842Sth160488 	cmg->is_nscd = __s_api_nscd_proc();
5406842Sth160488 	cmg->is_peruser_nscd = __s_api_peruser_proc();
5416842Sth160488 
5426842Sth160488 	/*
5436842Sth160488 	 * assume the underlying libldap allows multiple threads sharing
5446842Sth160488 	 * the same ldap connection (MT connection)
5456842Sth160488 	 */
5466842Sth160488 	cmg->ldap_mt = B_TRUE;
5476842Sth160488 	/* state is inactive until MT connection is required/requested */
5486842Sth160488 	cmg->state = NS_CONN_MGMT_INACTIVE;
5496842Sth160488 
5506842Sth160488 	(void) mutex_init(&cmg->lock, USYNC_THREAD, NULL);
5516842Sth160488 	(void) mutex_init(&cmg->cfg_lock, USYNC_THREAD, NULL);
5526842Sth160488 	cmg->pid = getpid();
5536842Sth160488 
5546842Sth160488 	/* for nscd or peruser nscd, MT connection is required */
5556842Sth160488 	if (cmg->is_nscd == B_TRUE || cmg->is_peruser_nscd == B_TRUE)
5566842Sth160488 		cmg->state = NS_CONN_MGMT_ACTIVE;
5576842Sth160488 
5586842Sth160488 	/*
5596842Sth160488 	 * reference (or initialize) the current Native LDAP configuration and
5606842Sth160488 	 * if in nscd process, make it never refreshed
5616842Sth160488 	 */
5626842Sth160488 	cmg->config = __s_api_get_default_config_global();
5636842Sth160488 	if (cmg->config == NULL)
5646842Sth160488 		cmg->config = __s_api_loadrefresh_config_global();
5656842Sth160488 	if (cmg->config != NULL) {
5666842Sth160488 		/*
5676842Sth160488 		 * main nscd get config change notice from ldap_cachemgr
5686842Sth160488 		 * so won't times out and refresh the config
5696842Sth160488 		 */
5706842Sth160488 		if (cmg->is_nscd  == B_TRUE)
5716842Sth160488 			(cmg->config)->paramList[NS_LDAP_EXP_P].ns_tm = 0;
5726842Sth160488 		cmg->cfg_cookie = cmg->config->config_cookie;
5736842Sth160488 	}
5746842Sth160488 
5756842Sth160488 	return (cmg);
5766842Sth160488 }
5776842Sth160488 
5786842Sth160488 static void
mark_shutdown_or_reloaded(int op)5796921Smichen mark_shutdown_or_reloaded(int op)
5806842Sth160488 {
5816842Sth160488 	ns_conn_mgmt_t	*cmg = ns_connmgmt;
5826842Sth160488 
5836842Sth160488 	(void) mutex_lock(&cmg->lock);
5846842Sth160488 	if (op == NS_CONN_MGMT_OP_SHUTDOWN)
5856842Sth160488 		cmg->shutting_down = B_TRUE;
5866842Sth160488 	else
5876842Sth160488 		cmg->cfg_reloaded = B_TRUE;
5886842Sth160488 	atomic_inc_uint(&cmg->ref_cnt);
5896842Sth160488 	cmg->state = NS_CONN_MGMT_DETACHED;
5906842Sth160488 
5916842Sth160488 	if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG)
5926842Sth160488 		__s_api_init_config_global(NULL);
5936842Sth160488 
5946842Sth160488 	(void) mutex_unlock(&cmg->lock);
5956842Sth160488 }
5966842Sth160488 
5976842Sth160488 /*
5986842Sth160488  * Return a pointer to the current connection management. If
5996842Sth160488  * it has not been created, or is requested to recreate, then
6006842Sth160488  * create and return the pointer. It is possible, the current
6016842Sth160488  * one is created by the parent before fork, create a new
6026842Sth160488  * one too in such a case.
6036842Sth160488  */
6046842Sth160488 static ns_conn_mgmt_t *
get_current_conn_mgmt(int op)6056842Sth160488 get_current_conn_mgmt(int op)
6066842Sth160488 {
6076842Sth160488 	ns_conn_mgmt_t	*cmg = ns_connmgmt;
6086842Sth160488 	static pid_t	checked_pid = (pid_t)-1;
6096842Sth160488 	pid_t		mypid;
6106842Sth160488 
6116842Sth160488 	mypid = getpid();
6126842Sth160488 	if (cmg == NULL || checked_pid != mypid) {
6136842Sth160488 		checked_pid = mypid;
6146842Sth160488 
6156842Sth160488 		/*
6166842Sth160488 		 * if current conn_mgmt not created yet or is from parent
6176842Sth160488 		 * or is requested to recreate, create it
6186842Sth160488 		 */
6196842Sth160488 		if (cmg == NULL || cmg->pid != mypid) {
6206842Sth160488 			if (cmg != NULL) {
6216842Sth160488 				/*
6226842Sth160488 				 * We don't want to free the conn_mgmt
6236842Sth160488 				 * allocated by the parent, since
6246842Sth160488 				 * there may be ldap connections
6256842Sth160488 				 * still being used. So leave it
6266842Sth160488 				 * alone but keep it referenced,
6276842Sth160488 				 * so that it will not be flagged
6286842Sth160488 				 * as a piece of leaked memory.
6296842Sth160488 				 */
6306842Sth160488 				ns_connmgmt_parent = cmg;
6316842Sth160488 				/*
6326842Sth160488 				 * avoid lint warning; does not
6336842Sth160488 				 * change the conn_mgmt in parent
6346842Sth160488 				 */
6356842Sth160488 				ns_connmgmt_parent->state =
6366842Sth160488 				    NS_CONN_MGMT_DETACHED;
6376842Sth160488 			}
6386842Sth160488 			ns_connmgmt = init_conn_mgmt();
6396842Sth160488 			cmg = ns_connmgmt;
6406842Sth160488 			/*
6416842Sth160488 			 * ensure it will not be destroyed until explicitly
6426842Sth160488 			 * shut down or reloaded
6436842Sth160488 			 */
6446842Sth160488 			if (op == NS_CONN_MGMT_OP_REF)
6456842Sth160488 				atomic_inc_uint(&cmg->ref_cnt);
6466842Sth160488 		}
6476842Sth160488 	}
6486842Sth160488 
6496842Sth160488 	return (cmg);
6506842Sth160488 }
6516842Sth160488 
6526842Sth160488 static ns_conn_mgmt_t *
access_conn_mgmt(int op)6536842Sth160488 access_conn_mgmt(int op)
6546842Sth160488 {
6556842Sth160488 	ns_conn_mgmt_t	*cmg = NULL;
6566842Sth160488 	ns_conn_mgmt_t	*cmg_prev;
6576842Sth160488 
6586842Sth160488 	(void) mutex_lock(&ns_connmgmt_lock);
6596842Sth160488 
6606842Sth160488 	/*
6616842Sth160488 	 * connection management is not available when the libsldap is being
6626842Sth160488 	 * unloaded or shut down
6636842Sth160488 	 */
6646842Sth160488 	if (ns_connmgmt_shutting_down == B_TRUE) {
6656842Sth160488 		(void) mutex_unlock(&ns_connmgmt_lock);
6666842Sth160488 		return (NULL);
6676842Sth160488 	}
6686842Sth160488 
6696842Sth160488 	if (op == NS_CONN_MGMT_OP_SHUTDOWN) {
6706842Sth160488 		ns_connmgmt_shutting_down = B_TRUE;
6716842Sth160488 		if (ns_connmgmt != NULL) {
6726842Sth160488 			cmg = ns_connmgmt;
6736921Smichen 			mark_shutdown_or_reloaded(op);
6746842Sth160488 			ns_connmgmt = NULL;
6756842Sth160488 		}
6766842Sth160488 		(void) mutex_unlock(&ns_connmgmt_lock);
6776842Sth160488 		return (cmg);
6786842Sth160488 	}
6796842Sth160488 
6806842Sth160488 	if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
6816842Sth160488 	    op == NS_CONN_MGMT_OP_NEW_CONFIG) {
6826842Sth160488 		cmg_prev = ns_connmgmt;
6836921Smichen 		mark_shutdown_or_reloaded(op);
6846842Sth160488 		/*
6856842Sth160488 		 * the previous cmg (cmg_prev) will be freed later
6866842Sth160488 		 * when its ref count reaches zero
6876842Sth160488 		 */
6886842Sth160488 		ns_connmgmt = NULL;
6896842Sth160488 	}
6906842Sth160488 
6916842Sth160488 	cmg = get_current_conn_mgmt(op);
6926842Sth160488 	if (cmg == NULL) {
6936842Sth160488 		(void) mutex_unlock(&ns_connmgmt_lock);
6946842Sth160488 		return (NULL);
6956842Sth160488 	}
6966842Sth160488 
6976842Sth160488 	atomic_inc_uint(&cmg->ref_cnt);
6986842Sth160488 	if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
6996842Sth160488 	    op == NS_CONN_MGMT_OP_NEW_CONFIG)
7006842Sth160488 		cmg = cmg_prev;
7016842Sth160488 	else { /* op is  NS_CONN_MGMT_OP_REF  or NS_CONN_MGMT_OP_LIB_INIT */
7026842Sth160488 		if (cmg->config == NULL)
7036842Sth160488 			cmg->config = __s_api_get_default_config();
7046842Sth160488 	}
7056842Sth160488 
7066842Sth160488 	(void) mutex_unlock(&ns_connmgmt_lock);
7076842Sth160488 	return (cmg);
7086842Sth160488 }
7096842Sth160488 
7106842Sth160488 /*
7116842Sth160488  * free a connection management control
7126842Sth160488  */
7136842Sth160488 static void
free_conn_mgmt(ns_conn_mgmt_t * cmg)7146842Sth160488 free_conn_mgmt(ns_conn_mgmt_t *cmg)
7156842Sth160488 {
7166842Sth160488 	union {
7176842Sth160488 		ldap_data_t	s_d;
7186842Sth160488 		char		s_b[1024];
7196842Sth160488 	} space;
7206842Sth160488 	ldap_data_t	*sptr;
7216842Sth160488 	int		ndata;
7226842Sth160488 	int		adata;
7236842Sth160488 	int		rc;
7246842Sth160488 	ldap_get_chg_cookie_t cookie;
7256842Sth160488 
7266842Sth160488 	if (cmg == NULL)
7276842Sth160488 		return;
7286842Sth160488 	cookie = cmg->cfg_cookie;
7296842Sth160488 
7306842Sth160488 	__s_api_free2dArray(cmg->pservers);
7316842Sth160488 	/* destroy the previous config or release the current one */
7326842Sth160488 	if (cmg->config != NULL) {
7336842Sth160488 		if (cmg->state == NS_CONN_MGMT_DETACHED)
7346842Sth160488 			__s_api_destroy_config(cmg->config);
7356842Sth160488 		else
7366842Sth160488 			__s_api_release_config(cmg->config);
7376842Sth160488 	}
7386842Sth160488 
7396842Sth160488 	/* stop the server status/config-change monitor thread */
7406842Sth160488 	if (cmg->procchg_started == B_TRUE) {
7416842Sth160488 		if (cmg->procchg_tid != thr_self()) {
7426842Sth160488 			if (cmg->procchg_door_call == B_TRUE) {
7436842Sth160488 				adata = sizeof (ldap_call_t) + 1;
7446842Sth160488 				ndata = sizeof (space);
7456842Sth160488 				space.s_d.ldap_call.ldap_callnumber =
7466842Sth160488 				    GETSTATUSCHANGE;
7476842Sth160488 				space.s_d.ldap_call.ldap_u.get_change.op =
7486842Sth160488 				    NS_STATUS_CHANGE_OP_STOP;
7496842Sth160488 				space.s_d.ldap_call.ldap_u.get_change.cookie =
7506842Sth160488 				    cookie;
7516842Sth160488 				sptr = &space.s_d;
7526842Sth160488 				rc = __ns_ldap_trydoorcall(&sptr, &ndata,
7536842Sth160488 				    &adata);
7546842Sth160488 				if (rc != NS_CACHE_SUCCESS)
7556842Sth160488 					syslog(LOG_INFO,
7566842Sth160488 					    gettext("libsldap: "
7576842Sth160488 					    "free_conn_mgmt():"
7586842Sth160488 					    " stopping door call "
7596842Sth160488 					    " GETSTATUSCHANGE failed "
7606842Sth160488 					    " (rc = %d)"), rc);
7616842Sth160488 			}
7626842Sth160488 			(void) pthread_cancel(cmg->procchg_tid);
7636842Sth160488 			cmg->procchg_started = B_FALSE;
7646842Sth160488 		}
7656842Sth160488 	}
7666842Sth160488 
7676842Sth160488 	free(cmg);
7686842Sth160488 }
7696842Sth160488 
7706842Sth160488 static ns_conn_mgmt_t *
release_conn_mgmt(ns_conn_mgmt_t * cmg,boolean_t unlock_cmg)7716842Sth160488 release_conn_mgmt(ns_conn_mgmt_t *cmg, boolean_t unlock_cmg)
7726842Sth160488 {
7736842Sth160488 	if (cmg == NULL)
7746842Sth160488 		return (NULL);
7756842Sth160488 	if (atomic_dec_uint_nv(&cmg->ref_cnt) == 0) {
7766842Sth160488 		if (cmg->state == NS_CONN_MGMT_DETACHED) {
7776842Sth160488 			if (unlock_cmg == B_TRUE)
7786842Sth160488 				(void) mutex_unlock(&cmg->lock);
7796842Sth160488 			free_conn_mgmt(cmg);
780*7281Smichen 			__s_api_free_sessionPool();
7816842Sth160488 			return (NULL);
7826842Sth160488 		} else {
7836842Sth160488 			syslog(LOG_WARNING,
7846842Sth160488 			    gettext("libsldap: connection management "
7856842Sth160488 			    " has a refcount of zero but the state "
7866842Sth160488 			    " is not DETACHED (%d)"), cmg->state);
7876842Sth160488 			cmg = NULL;
7886842Sth160488 		}
7896842Sth160488 	}
7906842Sth160488 	return (cmg);
7916842Sth160488 }
7926842Sth160488 
7936842Sth160488 /*
7946842Sth160488  * exposed function for initializing a connection management control structure
7956842Sth160488  */
7966842Sth160488 ns_conn_mgmt_t *
__s_api_conn_mgmt_init()7976842Sth160488 __s_api_conn_mgmt_init()
7986842Sth160488 {
7996842Sth160488 	if (thr_keycreate_once(&ns_mtckey, ns_mtckey_cleanup) != 0) {
8006842Sth160488 		syslog(LOG_WARNING, NS_CONN_MSG_NO_MTC_KEY);
8016842Sth160488 		return (NULL);
8026842Sth160488 	}
8036842Sth160488 
8046842Sth160488 	if (thr_keycreate_once(&ns_cmgkey, NULL) != 0) {
8056842Sth160488 		syslog(LOG_WARNING, NS_CONN_MSG_NO_CMG_KEY);
8066842Sth160488 		return (NULL);
8076842Sth160488 	}
8086842Sth160488 
8096842Sth160488 	return (access_conn_mgmt(NS_CONN_MGMT_OP_LIB_INIT));
8106842Sth160488 }
8116842Sth160488 
8126842Sth160488 /* initialize a connection user */
8136842Sth160488 ns_conn_user_t *
__s_api_conn_user_init(int type,void * userinfo,boolean_t referral)8146842Sth160488 __s_api_conn_user_init(int type, void *userinfo, boolean_t referral)
8156842Sth160488 {
8166842Sth160488 	ns_conn_user_t	*cu;
8176842Sth160488 	ns_conn_mgmt_t	*cmg;
8186842Sth160488 
8196842Sth160488 	/* delete the reference to the previously used conn_mgmt */
8206842Sth160488 	(void) thr_setspecific(ns_cmgkey, NULL);
8216842Sth160488 
8226842Sth160488 	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
8236842Sth160488 	if (cmg == NULL)
8246842Sth160488 		return (NULL);
8256842Sth160488 
8266842Sth160488 	if (cmg->state != NS_CONN_MGMT_ACTIVE &&
8276842Sth160488 	    cmg->state != NS_CONN_MGMT_INACTIVE) {
8286842Sth160488 		atomic_dec_uint(&cmg->ref_cnt);
8296842Sth160488 		return (NULL);
8306842Sth160488 	}
8316842Sth160488 
8326842Sth160488 	cu = (ns_conn_user_t *)calloc(1, sizeof (*cu));
8336842Sth160488 	if (cu == NULL) {
8346842Sth160488 		atomic_dec_uint(&cmg->ref_cnt);
8356842Sth160488 		return (NULL);
8366842Sth160488 	}
8376842Sth160488 
8386842Sth160488 	cu->type = type;
8396842Sth160488 	cu->state = NS_CONN_USER_ALLOCATED;
8406842Sth160488 	cu->tid = thr_self();
8416842Sth160488 	cu->userinfo = userinfo;
8426842Sth160488 	cu->referral = referral;
8436842Sth160488 	cu->ns_rc = NS_LDAP_SUCCESS;
8446842Sth160488 	cu->conn_mgmt = cmg;
8456842Sth160488 
8466842Sth160488 	(void) conn_tsd_setup(cmg);
8476842Sth160488 
8486842Sth160488 	return (cu);
8496842Sth160488 }
8506842Sth160488 
8516842Sth160488 /*
8526842Sth160488  * Free the resources used by a connection user.
8536842Sth160488  * The caller should ensure this conn_user is
8546842Sth160488  * not associated with any conn_mt, i.e.,
8556842Sth160488  * not in any conn_mt's linked list of conn_users.
8566842Sth160488  * The caller needs to free the userinfo member
8576842Sth160488  * as well.
8586842Sth160488  */
8596842Sth160488 void
__s_api_conn_user_free(ns_conn_user_t * cu)8606842Sth160488 __s_api_conn_user_free(ns_conn_user_t *cu)
8616842Sth160488 {
8626842Sth160488 	ns_conn_mgmt_t	*cmg;
8636842Sth160488 
8646842Sth160488 	if (cu == NULL)
8656842Sth160488 		return;
8666842Sth160488 
8676842Sth160488 	cu->state = NS_CONN_USER_FREED;
8686842Sth160488 	if (cu->ns_error != NULL)
8696842Sth160488 		(void) __ns_ldap_freeError(&cu->ns_error);
8706842Sth160488 
8716842Sth160488 	cmg = cu->conn_mgmt;
8726842Sth160488 	conn_tsd_free();
8736842Sth160488 	(void) release_conn_mgmt(cmg, B_FALSE);
8746842Sth160488 	(void) free(cu);
8756842Sth160488 }
8766842Sth160488 
8776842Sth160488 /*
8786842Sth160488  * Initialize an MT connection control structure
8796842Sth160488  * that will be used to represent an ldap connection
8806842Sth160488  * to be shared among multiple threads and to hold
8816842Sth160488  * and manage all the conn_users using the ldap
8826842Sth160488  * connection.
8836842Sth160488  */
8846842Sth160488 static ns_conn_mt_t *
init_conn_mt(ns_conn_mgmt_t * cmg,ns_ldap_error_t ** ep)8856842Sth160488 init_conn_mt(ns_conn_mgmt_t *cmg, ns_ldap_error_t **ep)
8866842Sth160488 {
8876842Sth160488 	ns_conn_mt_t	*cm;
8886842Sth160488 	ns_conn_mgmt_t	*cmg_a;
8896842Sth160488 
8906842Sth160488 	cm = (ns_conn_mt_t *)calloc(1, sizeof (*cm));
8916842Sth160488 	if (cm == NULL) {
8926842Sth160488 		if (ep != NULL)
8936842Sth160488 			*ep = __s_api_make_error(NS_LDAP_MEMORY, NULL);
8946842Sth160488 		return (NULL);
8956842Sth160488 	}
8966842Sth160488 
8976842Sth160488 	cmg_a = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
8986842Sth160488 	if (cmg_a != cmg) {
8996842Sth160488 		if (cmg_a != NULL) {
9006842Sth160488 			(void) release_conn_mgmt(cmg_a, B_FALSE);
9016842Sth160488 			if (ep != NULL)
9026842Sth160488 				*ep = __s_api_make_error(NS_LDAP_OP_FAILED,
9036842Sth160488 				    NS_CONN_MSG_SHUTDOWN_RELOADED);
9046842Sth160488 		}
9056842Sth160488 		return (NULL);
9066842Sth160488 	}
9076842Sth160488 
9086842Sth160488 	(void) mutex_init(&cm->lock, USYNC_THREAD, NULL);
9096842Sth160488 	cm->state = NS_CONN_MT_CONNECTING;
9106842Sth160488 	cm->tid = thr_self();
9116842Sth160488 	cm->pid = getpid();
9126842Sth160488 	cm->next = NULL;
9136842Sth160488 	cm->cu_head = NULL;
9146842Sth160488 	cm->cu_tail = NULL;
9156842Sth160488 	cm->conn = NULL;
9166842Sth160488 	cm->conn_mgmt = cmg;
9176842Sth160488 
9186842Sth160488 	return (cm);
9196842Sth160488 }
9206842Sth160488 
9216842Sth160488 /*
9226842Sth160488  * Free an MT connection control structure, assume conn_mgmt is locked.
9236842Sth160488  * 'unlock_cmg' is passed to release_conn_mgmt() to indicate the
9246842Sth160488  * cmg needs to be unlocked or not.
9256842Sth160488  */
9266842Sth160488 static ns_conn_mgmt_t *
free_conn_mt(ns_conn_mt_t * cm,int unlock_cmg)9276842Sth160488 free_conn_mt(ns_conn_mt_t *cm, int unlock_cmg)
9286842Sth160488 {
9296842Sth160488 	ns_conn_mgmt_t	*cmg;
9306842Sth160488 
9316842Sth160488 	if (cm == NULL)
9326842Sth160488 		return (NULL);
9336842Sth160488 	if (cm->ns_error != NULL)
9346842Sth160488 		(void) __ns_ldap_freeError(&cm->ns_error);
9356842Sth160488 	if (cm->conn != NULL) {
9366842Sth160488 		if (cm->conn->ld != NULL)
9376842Sth160488 			(void) ldap_unbind(cm->conn->ld);
9386842Sth160488 		__s_api_freeConnection(cm->conn);
9396842Sth160488 	}
9406842Sth160488 	cmg = cm->conn_mgmt;
9416842Sth160488 	free(cm);
9426842Sth160488 	return (release_conn_mgmt(cmg, unlock_cmg));
9436842Sth160488 }
9446842Sth160488 
9456842Sth160488 /* add a connection user to an MT connection */
9466842Sth160488 static void
add_cu2cm(ns_conn_user_t * cu,ns_conn_mt_t * cm)9476842Sth160488 add_cu2cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
9486842Sth160488 {
9496842Sth160488 
9506842Sth160488 	if (cm->cu_head == NULL) {
9516842Sth160488 		cm->cu_head = cu;
9526842Sth160488 		cm->cu_tail = cu;
9536842Sth160488 	} else {
9546842Sth160488 		cm->cu_tail->next = cu;
9556842Sth160488 		cm->cu_tail = cu;
9566842Sth160488 	}
9576842Sth160488 	cm->cu_cnt++;
9586842Sth160488 }
9596842Sth160488 
9606842Sth160488 /* add an MT connection to the connection management */
9616842Sth160488 static void
add_cm2cmg(ns_conn_mt_t * cm,ns_conn_mgmt_t * cmg)9626842Sth160488 add_cm2cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
9636842Sth160488 {
9646842Sth160488 	/*
9656842Sth160488 	 * add connection opened for WRITE to top of list
9666842Sth160488 	 * for garbage collection purpose. This is to
9676842Sth160488 	 * ensure the connection will be closed after a
9686842Sth160488 	 * certain amount of time (60 seconds).
9696842Sth160488 	 */
9706842Sth160488 	if (cmg->cm_head == NULL) {
9716842Sth160488 		cmg->cm_head = cm;
9726842Sth160488 		cmg->cm_tail = cm;
9736842Sth160488 	} else {
9746842Sth160488 		if (cm->opened_for == NS_CONN_USER_WRITE) {
9756842Sth160488 			cm->next = cmg->cm_head;
9766842Sth160488 			cmg->cm_head = cm;
9776842Sth160488 		} else {
9786842Sth160488 			cmg->cm_tail->next = cm;
9796842Sth160488 			cmg->cm_tail = cm;
9806842Sth160488 		}
9816842Sth160488 	}
9826842Sth160488 	cmg->cm_cnt++;
9836842Sth160488 }
9846842Sth160488 
9856842Sth160488 /* delete a connection user from an MT connection */
9866842Sth160488 static void
del_cu4cm(ns_conn_user_t * cu,ns_conn_mt_t * cm)9876842Sth160488 del_cu4cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
9886842Sth160488 {
9896842Sth160488 	ns_conn_user_t *pu, *u;
9906842Sth160488 
9916842Sth160488 	if (cu == NULL || cm->cu_head == NULL || cm->cu_cnt == 0)
9926842Sth160488 		return;
9936842Sth160488 
9946842Sth160488 	/* only one conn_user on list */
9956842Sth160488 	if (cm->cu_head == cm->cu_tail) {
9966842Sth160488 		if (cu == cm->cu_head) {
9976842Sth160488 			cm->cu_head = cm->cu_tail = NULL;
9986842Sth160488 			cm->cu_cnt = 0;
9996842Sth160488 			cu->next = NULL;
10006842Sth160488 		}
10016842Sth160488 		return;
10026842Sth160488 	}
10036842Sth160488 
10046842Sth160488 	/* more than one and cu is the first one */
10056842Sth160488 	if (cu == cm->cu_head) {
10066842Sth160488 		cm->cu_head = cu->next;
10076842Sth160488 		cm->cu_cnt--;
10086842Sth160488 		cu->next = NULL;
10096842Sth160488 		return;
10106842Sth160488 	}
10116842Sth160488 
10126842Sth160488 	pu = cm->cu_head;
10136842Sth160488 	for (u = cm->cu_head->next; u; u = u->next) {
10146842Sth160488 		if (cu == u)
10156842Sth160488 			break;
10166842Sth160488 		pu = u;
10176842Sth160488 	}
10186842Sth160488 	if (pu != cm->cu_tail) {
10196842Sth160488 		pu->next = cu->next;
10206842Sth160488 		if (pu->next == NULL)
10216842Sth160488 			cm->cu_tail = pu;
10226842Sth160488 		cm->cu_cnt--;
10236842Sth160488 		cu->next = NULL;
10246842Sth160488 	} else {
10256842Sth160488 		syslog(LOG_INFO, gettext(
10266842Sth160488 		    "libsldap: del_cu4cm(): connection user not found"));
10276842Sth160488 	}
10286842Sth160488 }
10296842Sth160488 
10306842Sth160488 /* delete an MT connection from the connection management control structure */
10316842Sth160488 static void
del_cm4cmg(ns_conn_mt_t * cm,ns_conn_mgmt_t * cmg)10326842Sth160488 del_cm4cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
10336842Sth160488 {
10346842Sth160488 	ns_conn_mt_t *pm, *m;
10356842Sth160488 
10366842Sth160488 	if (cm == NULL || cmg->cm_head == NULL || cmg->cm_cnt == 0)
10376842Sth160488 		return;
10386842Sth160488 
10396842Sth160488 	/* only one conn_mt on list */
10406842Sth160488 	if (cmg->cm_head == cmg->cm_tail) {
10416842Sth160488 		if (cm == cmg->cm_head) {
10426842Sth160488 			cmg->cm_head = cmg->cm_tail = NULL;
10436842Sth160488 			cmg->cm_cnt = 0;
10446842Sth160488 			cm->next = NULL;
10456842Sth160488 		}
10466842Sth160488 		return;
10476842Sth160488 	}
10486842Sth160488 
10496842Sth160488 	/* more than one and cm is the first one */
10506842Sth160488 	if (cm == cmg->cm_head) {
10516842Sth160488 		cmg->cm_head = cm->next;
10526842Sth160488 		cmg->cm_cnt--;
10536842Sth160488 		cm->next = NULL;
10546842Sth160488 		return;
10556842Sth160488 	}
10566842Sth160488 
10576842Sth160488 	pm = cmg->cm_head;
10586842Sth160488 	for (m = cmg->cm_head->next; m; m = m->next) {
10596842Sth160488 		if (cm == m)
10606842Sth160488 			break;
10616842Sth160488 		pm = m;
10626842Sth160488 	}
10636842Sth160488 	if (pm != cmg->cm_tail) {
10646842Sth160488 		pm->next = cm->next;
10656842Sth160488 		if (pm->next == NULL)
10666842Sth160488 			cmg->cm_tail = pm;
10676842Sth160488 		cmg->cm_cnt--;
10686842Sth160488 		cm->next = NULL;
10696842Sth160488 	} else {
10706842Sth160488 		syslog(LOG_INFO, gettext(
10716842Sth160488 		    "libsldap: del_cm4cmg(): MT connection not found"));
10726842Sth160488 	}
10736842Sth160488 }
10746842Sth160488 
10756842Sth160488 /*
10766842Sth160488  * compare to see if the server and credential for authentication match
10776842Sth160488  * those used by an MT connection
10786842Sth160488  */
10796842Sth160488 static boolean_t
is_server_cred_matched(const char * server,const ns_cred_t * cred,ns_conn_mt_t * cm)10806842Sth160488 is_server_cred_matched(const char *server, const ns_cred_t *cred,
10816842Sth160488 	ns_conn_mt_t *cm)
10826842Sth160488 {
10836842Sth160488 	Connection	*cp = cm->conn;
10846842Sth160488 
10856842Sth160488 	/* check server first */
10866842Sth160488 	if (server != NULL && *server != 0) {
10876842Sth160488 		if (strcasecmp(server, cp->serverAddr) != 0)
10886842Sth160488 			return (B_FALSE);
10896842Sth160488 	}
10906842Sth160488 
10916842Sth160488 	if (cred == NULL)
10926842Sth160488 		return (B_TRUE);
10936842Sth160488 
10946842Sth160488 	/* then check cred */
10956842Sth160488 	return (__s_api_is_auth_matched(cp->auth, cred));
10966842Sth160488 }
10976842Sth160488 
10986842Sth160488 /*
10996842Sth160488  * Wait until a pending MT connection becomes available.
11006842Sth160488  * Return 1 if so, 0 if error.
11016842Sth160488  *
11026842Sth160488  * Assume the current conn_mgmt and the input conn_mt
11036842Sth160488  * are locked.
11046842Sth160488  */
11056842Sth160488 static int
wait_for_conn_mt(ns_conn_user_t * cu,ns_conn_mt_t * cm)11066842Sth160488 wait_for_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t *cm)
11076842Sth160488 {
11086842Sth160488 
11096842Sth160488 	cu->state = NS_CONN_USER_WAITING;
11106842Sth160488 	add_cu2cm(cu, cm);
11116842Sth160488 	cu->conn_mt = cm;
11126842Sth160488 
11136842Sth160488 	(void) mutex_unlock(&cm->lock);
11146842Sth160488 	/*
11156842Sth160488 	 * It could take some time so we don't want to hold
11166842Sth160488 	 * cm->conn_mgmt across the wait
11176842Sth160488 	 */
11186842Sth160488 	(void) mutex_unlock(&(cm->conn_mgmt)->lock);
11196842Sth160488 
11206842Sth160488 	(void) mutex_lock(&cm->lock);
11216842Sth160488 	/* check one more time see if need to wait */
11226842Sth160488 	if (cm->state == NS_CONN_MT_CONNECTING) {
11236842Sth160488 		(void) conn_wait(cm, cu);
11246842Sth160488 
11256842Sth160488 		/* cm->lock is locked again at this point */
11266842Sth160488 
11276842Sth160488 		cu->state = NS_CONN_USER_WOKEUP;
11286842Sth160488 	}
11296842Sth160488 
11306842Sth160488 	if (cm->state == NS_CONN_MT_CONNECTED)
11316842Sth160488 		return (1);
11326842Sth160488 	else {
11336842Sth160488 		del_cu4cm(cu, cm);
11346842Sth160488 		cu->conn_mt = NULL;
11356842Sth160488 		cu->bad_mt_conn = B_FALSE;
11366842Sth160488 		return (0);
11376842Sth160488 	}
11386842Sth160488 }
11396842Sth160488 
11406842Sth160488 /*
11416842Sth160488  * Check and see if the input MT connection '*cm' should be closed.
11426842Sth160488  * In two cases, it should be closed. If a preferred server is
11436842Sth160488  * found to be up when ldap_cachemgr is queried and reported back.
11446842Sth160488  * Or when the server being used for the connection is found to
11456842Sth160488  * be down. Return B_FALSE if the connection is not closed (or not marked
11466842Sth160488  * to be closed), otherwise unlock mutex (*cm)->lock and return B_TRUE.
11476842Sth160488  * This function assumes conn_mgmt cmg and conn_mt *cm are locked.
11486842Sth160488  */
11496842Sth160488 static boolean_t
check_and_close_conn(ns_conn_mgmt_t * cmg,ns_conn_mt_t ** cm,ns_conn_user_t * cu)11506842Sth160488 check_and_close_conn(ns_conn_mgmt_t *cmg, ns_conn_mt_t **cm,
11516842Sth160488 	ns_conn_user_t *cu) {
11526842Sth160488 
11536842Sth160488 	int rc;
11546842Sth160488 	int j;
11556842Sth160488 	int svridx = -1;
11566842Sth160488 	int upidx = -1;
11576842Sth160488 	int free_cm;
11586842Sth160488 	ns_server_info_t sinfo;
11596842Sth160488 	ns_ldap_error_t *errorp = NULL;
11606842Sth160488 
11616842Sth160488 	/*
11626842Sth160488 	 * check only if preferred servers are defined
11636842Sth160488 	 */
11646842Sth160488 	if (cmg->pservers_loaded == B_FALSE)
11656842Sth160488 		get_preferred_servers(B_FALSE, B_FALSE, cmg);
11666842Sth160488 	if (cmg->pservers == NULL)
11676842Sth160488 		return (B_FALSE);
11686842Sth160488 
11696842Sth160488 	/*
11706842Sth160488 	 * ask ldap_cachemgr for the first available server
11716842Sth160488 	 */
11726842Sth160488 	rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
11736842Sth160488 	    &sinfo, &errorp, NS_CACHE_ADDR_IP);
11746842Sth160488 	if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
11756842Sth160488 		(void) __ns_ldap_freeError(&errorp);
11766842Sth160488 		return (B_FALSE);
11776842Sth160488 	}
11786842Sth160488 
11796842Sth160488 	/*
11806842Sth160488 	 * Did ldap_cachemgr return a preferred server ?
11816842Sth160488 	 */
11826842Sth160488 	for (j = 0; cmg->pservers[j] != NULL; j++) {
11836842Sth160488 		if (strcasecmp(sinfo.server, cmg->pservers[j]) != 0)
11846842Sth160488 			continue;
11856842Sth160488 		upidx = j;
11866842Sth160488 		break;
11876842Sth160488 	}
11886842Sth160488 
11896842Sth160488 	/*
11906842Sth160488 	 * Is the server being used a preferred one ?
11916842Sth160488 	 */
11926842Sth160488 	for (j = 0; cmg->pservers[j] != NULL; j++) {
11936842Sth160488 		if (strcasecmp(cmg->pservers[j], (*cm)->conn->serverAddr) != 0)
11946842Sth160488 			continue;
11956842Sth160488 		svridx = j;
11966842Sth160488 		break;
11976842Sth160488 	}
11986842Sth160488 
11996842Sth160488 	/*
12006842Sth160488 	 * Need to fall back to a down-but-now-up preferred server ?
12016842Sth160488 	 * A preferred server falls back to a more preferred one.
12026842Sth160488 	 * A regular one falls back to any preferred ones. So if
12036842Sth160488 	 * both are preferred ones and same index, or both
12046842Sth160488 	 * are not preferred ones, then no need to close the
12056842Sth160488 	 * connection.
12066842Sth160488 	 */
12076842Sth160488 	if ((upidx == -1 && svridx == -1) ||
12086842Sth160488 	    (upidx != -1 && svridx != -1 && upidx == svridx)) {
12096842Sth160488 		__s_api_free_server_info(&sinfo);
12106842Sth160488 		return (B_FALSE);
12116842Sth160488 	}
12126842Sth160488 
12136842Sth160488 	/*
12146842Sth160488 	 * otherwise, 4 cases, all may need to close the connection:
12156842Sth160488 	 * For case 1 and 2, both servers are preferred ones:
12166842Sth160488 	 * 1. ldap_cachemgr returned a better one to use (upidx < svridx)
12176842Sth160488 	 * 2. the server being used is down (upidx > svridx)
12186842Sth160488 	 * 3. ldap_cachemgr returned a preferred one, but the server
12196842Sth160488 	 *    being used is not, so need to fall back to the preferred server
12206842Sth160488 	 * 4. ldap_cachemgr returned a non-preferred one, but the server
12216842Sth160488 	 *    being used is a preferred one, so it must be down (since
12226842Sth160488 	 *    ldap_cachemgr always returns a preferred one when possible).
12236842Sth160488 	 * For case 1 & 3, close the READ connection when no user uses it.
12246842Sth160488 	 * For 2 and 4, close the connection with error rc, LDAP_SERVER_DOWN.
12256842Sth160488 	 */
12266842Sth160488 	if (upidx != -1 && (svridx == -1 || upidx < svridx)) { /* case 1 & 3 */
12276842Sth160488 		/* fallback does not make sense for WRITE/referred connection */
12286842Sth160488 		if ((*cm)->opened_for == NS_CONN_USER_WRITE ||
12296842Sth160488 		    (*cm)->referral == B_TRUE) {
12306842Sth160488 			__s_api_free_server_info(&sinfo);
12316842Sth160488 			return (B_FALSE);
12326842Sth160488 		}
12336842Sth160488 		free_cm = close_conn_mt_when_nouser(*cm);
12346842Sth160488 		if (cmg->shutting_down == B_FALSE)
12356842Sth160488 			cu->retry = B_TRUE;
12366842Sth160488 	} else {
12376842Sth160488 		ns_ldap_error_t *ep;
12386842Sth160488 		ep = __s_api_make_error(LDAP_SERVER_DOWN,
12396842Sth160488 		    NS_CONN_MSG_DOWN_FROM_CACHEMGR);
12406842Sth160488 		/* cu has not been attached to cm yet, use NULL as cu pointer */
12416842Sth160488 		free_cm = close_conn_mt(*cm, LDAP_SERVER_DOWN, &ep, NULL);
12426842Sth160488 		if (cmg->shutting_down == B_FALSE)
12436842Sth160488 			cu->retry = B_TRUE;
12446842Sth160488 		(void) __ns_ldap_freeError(&ep);
12456842Sth160488 	}
12466842Sth160488 
12476842Sth160488 	(void) mutex_unlock(&(*cm)->lock);
12486842Sth160488 	if (free_cm == 1) {
12496842Sth160488 		(void) free_conn_mt(*cm, 0);
12506842Sth160488 		*cm = NULL;
12516842Sth160488 	}
12526842Sth160488 
12536842Sth160488 	__s_api_free_server_info(&sinfo);
12546842Sth160488 
12556842Sth160488 	return (B_TRUE);
12566842Sth160488 }
12576842Sth160488 
12586842Sth160488 /*
12596842Sth160488  * Check to see if a conn_mt matches the connection criteria from
12606842Sth160488  * a conn_user. Return B_TRUE if yes, B_FALSE, otherwise. The input
12616842Sth160488  * conn_mt pointer (*cmt) may be freed and *cmt will be set to NULL
12626842Sth160488  * to indicate so.
12636842Sth160488  * conn_mt *cmt and conn_mgmt cm->conn_mgmt are assumed locked.
12646842Sth160488  * cm->lock is unlocked at exit if rc is B_FALSE.
12656842Sth160488  */
12666842Sth160488 static boolean_t
match_conn_mt(ns_conn_user_t * cu,ns_conn_mt_t ** cmt,ns_conn_mt_state_t st,const char * server,const ns_cred_t * cred)12676842Sth160488 match_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t **cmt,
12686842Sth160488 	ns_conn_mt_state_t st, const char *server,
12696842Sth160488 	const ns_cred_t *cred)
12706842Sth160488 {
12716842Sth160488 	boolean_t	matched = B_FALSE;
12726842Sth160488 	boolean_t	drop_conn;
12736842Sth160488 	int		free_cm = 0;
12746842Sth160488 	ns_conn_mt_t	*cm = *cmt;
12756842Sth160488 	ns_conn_mgmt_t	*cmg = cm->conn_mgmt;
12766842Sth160488 
12776842Sth160488 	if (cm->state != st || cm->close_when_nouser  == B_TRUE ||
12786842Sth160488 	    cm->detached == B_TRUE || cm->pid != getpid() ||
12796842Sth160488 	    cm->referral != cu->referral) {
12806842Sth160488 		(void) mutex_unlock(&cm->lock);
12816842Sth160488 		return (B_FALSE);
12826842Sth160488 	}
12836842Sth160488 
12846842Sth160488 	/*
12856842Sth160488 	 * if a conn_mt opened for WRITE is idle
12866842Sth160488 	 * long enough, then close it. To improve
12876842Sth160488 	 * the performance of applications, such
12886842Sth160488 	 * as ldapaddent, a WRITE connection is
12896842Sth160488 	 * given a short time to live in the
12906842Sth160488 	 * connection pool, expecting the write
12916842Sth160488 	 * requests to come in a quick succession.
12926842Sth160488 	 * To save resource, the connection will
12936842Sth160488 	 * be closed if idle more than 60 seconds.
12946842Sth160488 	 */
12956842Sth160488 	if (cm->opened_for == NS_CONN_USER_WRITE &&
12966842Sth160488 	    cu->type != NS_CONN_USER_WRITE && cm->cu_cnt == 0 &&
12976842Sth160488 	    ((time(NULL) - cm->access_time) > 60)) {
12986842Sth160488 		/*
12996842Sth160488 		 * NS_LDAP_INTERNAL is irrelevant here. There no
13006842Sth160488 		 * conn_user to consume the rc
13016842Sth160488 		 */
13026842Sth160488 		free_cm = close_conn_mt(cm, NS_LDAP_INTERNAL, NULL, NULL);
13036842Sth160488 		(void) mutex_unlock(&cm->lock);
13046842Sth160488 		if (free_cm == 1) {
13056842Sth160488 			(void) free_conn_mt(cm, 0);
13066842Sth160488 			*cmt = NULL;
13076842Sth160488 		}
13086842Sth160488 		return (B_FALSE);
13096842Sth160488 	}
13106842Sth160488 
13116842Sth160488 	switch (cu->type) {
13126842Sth160488 	case NS_CONN_USER_SEARCH:
13136842Sth160488 	case NS_CONN_USER_GETENT:
13146842Sth160488 		if (cm->opened_for == NS_CONN_USER_SEARCH ||
13156842Sth160488 		    cm->opened_for == NS_CONN_USER_GETENT)
13166842Sth160488 			matched = B_TRUE;
13176842Sth160488 		break;
13186842Sth160488 
13196842Sth160488 	case NS_CONN_USER_WRITE:
13206842Sth160488 		if (cm->opened_for == NS_CONN_USER_WRITE)
13216842Sth160488 			matched = B_TRUE;
13226842Sth160488 		break;
13236842Sth160488 
13246842Sth160488 	default:
13256842Sth160488 		matched = B_FALSE;
13266842Sth160488 		break;
13276842Sth160488 	}
13286842Sth160488 
13296842Sth160488 	if (matched == B_TRUE && ((server != NULL || cred != NULL) &&
13306842Sth160488 	    is_server_cred_matched(server, cred, cm) == B_FALSE))
13316842Sth160488 		matched = B_FALSE;
13326842Sth160488 
13336842Sth160488 	if (matched != B_FALSE) {
13346842Sth160488 		/*
13356842Sth160488 		 * Check and drop the 'connected' connection if
13366842Sth160488 		 * necessary. Main nscd gets status changes from
13376842Sth160488 		 * the ldap_cachemgr daemon directly via the
13386842Sth160488 		 * GETSTATUSCHANGE door call, the standalone
13396842Sth160488 		 * function works in a no ldap_cachemgr environment,
13406842Sth160488 		 * so no need to check and drop connections.
13416842Sth160488 		 */
13426842Sth160488 		if (cm->state == NS_CONN_MT_CONNECTED &&
13436842Sth160488 		    cmg->is_nscd == B_FALSE && !__s_api_isStandalone()) {
13446842Sth160488 			drop_conn = check_and_close_conn(cmg, &cm, cu);
13456842Sth160488 			if (drop_conn == B_TRUE) {
13466842Sth160488 				if (cm == NULL)
13476842Sth160488 					*cmt = NULL;
13486842Sth160488 				return (B_FALSE);
13496842Sth160488 			}
13506842Sth160488 		}
13516842Sth160488 
13526842Sth160488 		/* check if max. users using or waiting for the connection */
13536842Sth160488 		if ((cm->state == NS_CONN_MT_CONNECTED &&
13546842Sth160488 		    cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
13556842Sth160488 		    cm->cu_cnt >= cm->cu_max) ||
13566842Sth160488 		    (cm->state == NS_CONN_MT_CONNECTING &&
13576842Sth160488 		    cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
13586842Sth160488 		    cm->waiter_cnt >= cm->cu_max - 1))
13596842Sth160488 			matched = B_FALSE;
13606842Sth160488 	}
13616842Sth160488 
13626842Sth160488 	if (matched == B_FALSE)
13636842Sth160488 		(void) mutex_unlock(&cm->lock);
13646842Sth160488 
13656842Sth160488 	return (matched);
13666842Sth160488 }
13676842Sth160488 
13686842Sth160488 /*
13696842Sth160488  * obtain an MT connection from the connection management for a conn_user
13706842Sth160488  *
13716842Sth160488  * Input:
13726842Sth160488  *   server	: server name or IP address
13736842Sth160488  *   flags	: libsldap API flags
13746842Sth160488  *   cred	: pointer to the user credential
13756842Sth160488  *   cu		: pointer to the conn_user structure
13766842Sth160488  * Output:
13776842Sth160488  *   session	: hold pointer to the Connection structure
13786842Sth160488  *   errorp	: hold pointer to error info (ns_ldap_error_t)
13796842Sth160488  */
13806842Sth160488 int
__s_api_conn_mt_get(const char * server,const int flags,const ns_cred_t * cred,Connection ** session,ns_ldap_error_t ** errorp,ns_conn_user_t * cu)13816842Sth160488 __s_api_conn_mt_get(const char *server, const int flags, const ns_cred_t *cred,
13826842Sth160488 	Connection **session, ns_ldap_error_t **errorp, ns_conn_user_t *cu)
13836842Sth160488 {
13846842Sth160488 	int		rc;
13856842Sth160488 	int		i;
13866842Sth160488 	ns_conn_mt_t	*cn;
13876842Sth160488 	ns_conn_mt_state_t st;
13886842Sth160488 	ns_conn_mgmt_t	*cmg;
13896842Sth160488 
13906842Sth160488 	if (errorp == NULL || cu == NULL || session == NULL)
13916842Sth160488 		return (NS_LDAP_INVALID_PARAM);
13926842Sth160488 
13936842Sth160488 	*session = NULL;
13946842Sth160488 	cmg = cu->conn_mgmt;
13956842Sth160488 
13966842Sth160488 	/*
13976842Sth160488 	 * for pam_ldap, always try opening a new connection
13986842Sth160488 	 */
13996842Sth160488 	if (cu->type == NS_CONN_USER_AUTH)
14006842Sth160488 		return (NS_LDAP_NOTFOUND);
14016842Sth160488 
14026842Sth160488 	/* if need a new conn, then don't reuse */
14036842Sth160488 	if (flags & NS_LDAP_NEW_CONN)
14046842Sth160488 		return (NS_LDAP_NOTFOUND);
14056842Sth160488 
14066842Sth160488 	if (flags & NS_LDAP_KEEP_CONN)
14076842Sth160488 		cu->keep_conn = B_TRUE;
14086842Sth160488 
14096842Sth160488 	/*
14106842Sth160488 	 * We want to use MT connection only if keep-connection flag is
14116842Sth160488 	 * set or if MT was requested (or active)
14126842Sth160488 	 */
14136842Sth160488 	if (!((cmg->state == NS_CONN_MGMT_INACTIVE &&
14146842Sth160488 	    cu->keep_conn == B_TRUE) || cmg->state == NS_CONN_MGMT_ACTIVE))
14156842Sth160488 		return (NS_LDAP_NOTFOUND);
14166842Sth160488 
14176842Sth160488 	/* MT connection will be used now (if possible/available) */
14186842Sth160488 	cu->use_mt_conn = B_TRUE;
14196842Sth160488 
14206842Sth160488 	NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errorp);
14216842Sth160488 
14226842Sth160488 	/* first look for a connection already open */
14236842Sth160488 	st = NS_CONN_MT_CONNECTED;
14246842Sth160488 	cu->state = NS_CONN_USER_FINDING;
14256842Sth160488 	for (i = 0; i < 2; i++) {
14266842Sth160488 		for (cn = cmg->cm_head; cn; cn = cn->next) {
14276842Sth160488 			(void) mutex_lock(&cn->lock);
14286842Sth160488 			rc = match_conn_mt(cu, &cn, st, server, cred);
14296842Sth160488 			if (rc == B_FALSE && cn != NULL) /* not found */
14306842Sth160488 				continue;
14316842Sth160488 			if (cn == NULL) { /* not found and cn freed */
14326842Sth160488 				/*
14336842Sth160488 				 * as the conn_mt list could
14346842Sth160488 				 * be different due to cn's
14356842Sth160488 				 * deletion, scan the entire
14366842Sth160488 				 * conn_mt list again
14376842Sth160488 				 */
14386842Sth160488 				st = NS_CONN_MT_CONNECTED;
14396842Sth160488 				i = -1;
14406842Sth160488 				break;
14416842Sth160488 			}
14426842Sth160488 
14436842Sth160488 			/* return a connected one if found */
14446842Sth160488 			if (cn->state == NS_CONN_MT_CONNECTED) {
14456842Sth160488 				*session = cn->conn;
14466842Sth160488 				add_cu2cm(cu, cn);
14476842Sth160488 				cu->conn_mt = cn;
14486842Sth160488 				cu->state = NS_CONN_USER_CONNECTED;
14496842Sth160488 				(void) mutex_unlock(&cn->lock);
14506842Sth160488 				(void) mutex_unlock(&cmg->lock);
14516842Sth160488 				return (NS_LDAP_SUCCESS);
14526842Sth160488 			}
14536842Sth160488 
14546842Sth160488 			/*
14556842Sth160488 			 * if cn is not connecting, or allow only
14566842Sth160488 			 * one user, skip it
14576842Sth160488 			 */
14586842Sth160488 			if (cn->state != NS_CONN_MT_CONNECTING ||
14596842Sth160488 			    cn->cu_max == 1) {
14606842Sth160488 				(void) mutex_unlock(&cn->lock);
14616842Sth160488 				continue;
14626842Sth160488 			}
14636842Sth160488 
14646842Sth160488 			/* wait for the connecting conn_mt */
14656842Sth160488 			if (wait_for_conn_mt(cu, cn) != 1) {
14666842Sth160488 				/*
14676842Sth160488 				 * NS_LDAP_NOTFOUND signals that the function
14686842Sth160488 				 * __s_api_check_libldap_MT_conn_support()
14696842Sth160488 				 * detected that the lower libldap library
14706842Sth160488 				 * does not support MT connection, so return
14716842Sth160488 				 * NS_LDAP_NOTFOUND to let the caller to
14726842Sth160488 				 * open a non-MT conneciton. Otherwise,
14736842Sth160488 				 * connect error occurred, return
14746842Sth160488 				 * NS_CONN_USER_CONNECT_ERROR
14756842Sth160488 				 */
14766842Sth160488 				if (cn->ns_rc != NS_LDAP_NOTFOUND)
14776842Sth160488 					cu->state = NS_CONN_USER_CONNECT_ERROR;
14786842Sth160488 				else {
14796842Sth160488 					cu->state = NS_CONN_USER_FINDING;
14806842Sth160488 					cu->use_mt_conn = B_FALSE;
14816842Sth160488 				}
14826842Sth160488 				(void) mutex_unlock(&cn->lock);
14836842Sth160488 
14846842Sth160488 				/* cmg->lock unlocked by wait_for_conn_mt() */
14856842Sth160488 
14866842Sth160488 				return (cn->ns_rc);
14876842Sth160488 			}
14886842Sth160488 
14896842Sth160488 			/* return the newly available conn_mt */
14906842Sth160488 			*session = cn->conn;
14916842Sth160488 			cu->state = NS_CONN_USER_CONNECTED;
14926842Sth160488 			(void) mutex_unlock(&cn->lock);
14936842Sth160488 
14946842Sth160488 			/* cmg->lock unlocked by wait_for_conn_mt() */
14956842Sth160488 
14966842Sth160488 			return (NS_LDAP_SUCCESS);
14976842Sth160488 		}
14986842Sth160488 
14996842Sth160488 		/* next, look for a connecting conn_mt */
15006842Sth160488 		if (i == 0)
15016842Sth160488 			st = NS_CONN_MT_CONNECTING;
15026842Sth160488 	}
15036842Sth160488 
15046842Sth160488 	/* no connection found, start opening one */
15056842Sth160488 	cn = init_conn_mt(cmg, errorp);
15066842Sth160488 	if (cn == NULL) {
15076842Sth160488 		(void) mutex_unlock(&cmg->lock);
15086842Sth160488 		return ((*errorp)->status);
15096842Sth160488 	}
15106842Sth160488 	cu->conn_mt = cn;
15116842Sth160488 	cn->opened_for = cu->type;
15126842Sth160488 	cn->referral = cu->referral;
15136842Sth160488 	if (cmg->ldap_mt == B_TRUE)
15146842Sth160488 		cn->cu_max = NS_CONN_MT_USER_MAX;
15156842Sth160488 	else
15166842Sth160488 		cn->cu_max = 1;
15176842Sth160488 	add_cm2cmg(cn, cmg);
15186842Sth160488 	(void) mutex_unlock(&cmg->lock);
15196842Sth160488 
15206842Sth160488 	return (NS_LDAP_NOTFOUND);
15216842Sth160488 }
15226842Sth160488 
15236842Sth160488 
15246842Sth160488 /*
15256842Sth160488  * add an MT connection to the connection management
15266842Sth160488  *
15276842Sth160488  * Input:
15286842Sth160488  *   con	: pointer to the Connection info
15296842Sth160488  *   cu		: pointer to the conn_user structure
15306842Sth160488  * Output:
15316842Sth160488  *   ep		: hold pointer to error info (ns_ldap_error_t)
15326842Sth160488  */
15336842Sth160488 int
__s_api_conn_mt_add(Connection * con,ns_conn_user_t * cu,ns_ldap_error_t ** ep)15346842Sth160488 __s_api_conn_mt_add(Connection *con, ns_conn_user_t *cu, ns_ldap_error_t **ep)
15356842Sth160488 {
15366842Sth160488 	ns_conn_mgmt_t	*cmg = cu->conn_mgmt;
15376842Sth160488 	ns_conn_mt_t	*cm = cu->conn_mt;
15386842Sth160488 
15396842Sth160488 	/* if the conn_mgmt is being shut down, return error */
15406842Sth160488 	NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
15416842Sth160488 
15426842Sth160488 	/*
15436842Sth160488 	 * start the change monitor thread only if it
15446842Sth160488 	 * hasn't been started and the process is the
15456842Sth160488 	 * main nscd (not peruser nscd)
15466842Sth160488 	 */
15476842Sth160488 	if (cmg->procchg_started == B_FALSE && cmg->is_nscd == B_TRUE) {
15486842Sth160488 		start_thread(cmg);
15496842Sth160488 		cmg->procchg_started = B_TRUE;
15506842Sth160488 	}
15516842Sth160488 	(void) mutex_lock(&cm->lock);
15526842Sth160488 	cm->conn = con;
15536842Sth160488 	cm->state = NS_CONN_MT_CONNECTED;
15546842Sth160488 	cm->pid = getpid();
15556842Sth160488 	cm->create_time = time(NULL);
15566842Sth160488 	cm->access_time = cm->create_time;
15576842Sth160488 	cm->opened_for = cu->type;
15586842Sth160488 	add_cu2cm(cu, cm);
15596842Sth160488 	cu->conn_mt = cm;
15606842Sth160488 	cu->state = NS_CONN_USER_CONNECTED;
15616842Sth160488 	if (cmg->ldap_mt == B_TRUE)
15626842Sth160488 		cm->cu_max = NS_CONN_MT_USER_MAX;
15636842Sth160488 	else
15646842Sth160488 		cm->cu_max = 1;
15656842Sth160488 
15666842Sth160488 	/* wake up the waiters if any */
15676842Sth160488 	(void) conn_signal(cm);
15686842Sth160488 
15696842Sth160488 	(void) mutex_unlock(&cm->lock);
15706842Sth160488 	(void) mutex_unlock(&cmg->lock);
15716842Sth160488 
15726842Sth160488 	return (NS_LDAP_SUCCESS);
15736842Sth160488 }
15746842Sth160488 
15756842Sth160488 /*
15766921Smichen  * return an MT connection to the pool when a conn user is done using it
15776842Sth160488  *
15786842Sth160488  * Input:
15796842Sth160488  *   cu		: pointer to the conn_user structure
15806842Sth160488  * Output:	NONE
15816842Sth160488  */
15826842Sth160488 void
__s_api_conn_mt_return(ns_conn_user_t * cu)15836842Sth160488 __s_api_conn_mt_return(ns_conn_user_t *cu)
15846842Sth160488 {
15856842Sth160488 	ns_conn_mt_t	*cm;
15866842Sth160488 	ns_conn_mgmt_t	*cmg;
15876842Sth160488 
15886842Sth160488 	if (cu == NULL || cu->use_mt_conn == B_FALSE)
15896842Sth160488 		return;
15906842Sth160488 	cm = cu->conn_mt;
15916842Sth160488 	if (cm == NULL)
15926842Sth160488 		return;
15936842Sth160488 	cmg = cu->conn_mgmt;
15946842Sth160488 
15956842Sth160488 	(void) mutex_lock(&cm->lock);
15966842Sth160488 	del_cu4cm(cu, cm);
15976842Sth160488 	cu->state = NS_CONN_USER_DISCONNECTED;
15986842Sth160488 	cu->conn_mt = NULL;
15996842Sth160488 	cu->bad_mt_conn = B_FALSE;
16006842Sth160488 
16016842Sth160488 	/*
16026842Sth160488 	 *  if this MT connection is no longer needed, or not usable, and
16036842Sth160488 	 * no more conn_user uses it, then close it.
16046842Sth160488 	 */
16056842Sth160488 
16066842Sth160488 	if ((cm->close_when_nouser == B_TRUE ||
16076842Sth160488 	    cm->state != NS_CONN_MT_CONNECTED) && cm->cu_cnt == 0) {
16086842Sth160488 		(void) mutex_unlock(&cm->lock);
16096842Sth160488 		(void) mutex_lock(&cmg->lock);
16106842Sth160488 		(void) mutex_lock(&cm->lock);
16116842Sth160488 		del_cm4cmg(cm, cmg);
16126842Sth160488 		/* use ns_conn_free (instead of 1) to avoid lint warning */
16136842Sth160488 		NS_CONN_UNLOCK_AND_FREE(ns_conn_free, cm, cmg);
16146842Sth160488 	} else {
16156842Sth160488 		if (cm->state == NS_CONN_MT_CONNECTED && cm->cu_cnt == 0 &&
16166842Sth160488 		    cm->conn != NULL && cm->conn->ld != NULL) {
16176842Sth160488 			struct timeval	zerotime;
16186842Sth160488 			LDAPMessage	*res;
16196842Sth160488 
16206842Sth160488 			zerotime.tv_sec = zerotime.tv_usec = 0L;
16216842Sth160488 			/* clean up remaining results just in case */
16226842Sth160488 			while (ldap_result(cm->conn->ld, LDAP_RES_ANY,
16236842Sth160488 			    LDAP_MSG_ALL, &zerotime, &res) > 0) {
16246842Sth160488 				if (res != NULL)
16256842Sth160488 					(void) ldap_msgfree(res);
16266842Sth160488 			}
16276842Sth160488 		}
16286842Sth160488 		(void) mutex_unlock(&cm->lock);
16296842Sth160488 	}
16306842Sth160488 }
16316842Sth160488 
16326842Sth160488 /* save error info (rc and ns_ldap_error_t) in the conn_mt */
16336842Sth160488 static void
err2cm(ns_conn_mt_t * cm,int rc,ns_ldap_error_t ** errorp)16346842Sth160488 err2cm(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp) {
16356842Sth160488 	ns_ldap_error_t	*ep;
16366842Sth160488 
16376842Sth160488 	cm->ns_rc = rc;
16386842Sth160488 	cm->ns_error = NULL;
16396842Sth160488 	if (errorp != NULL && *errorp != NULL) {
16406842Sth160488 		ep = __s_api_copy_error(*errorp);
16416842Sth160488 		if (ep == NULL)
16426842Sth160488 			cm->ns_rc = NS_LDAP_MEMORY;
16436842Sth160488 		else
16446842Sth160488 			cm->ns_error = ep;
16456842Sth160488 	}
16466842Sth160488 }
16476842Sth160488 
16486842Sth160488 /* copy error info (rc and ns_ldap_error_t) from conn_mt to conn_user */
16496842Sth160488 static void
err_from_cm(ns_conn_user_t * cu,ns_conn_mt_t * cm)16506842Sth160488 err_from_cm(ns_conn_user_t *cu, ns_conn_mt_t *cm) {
16516842Sth160488 	ns_ldap_error_t	*ep;
16526842Sth160488 
16536842Sth160488 	cu->ns_rc = cm->ns_rc;
16546842Sth160488 	if (cu->ns_error != NULL)
16556842Sth160488 		(void) __ns_ldap_freeError(&cu->ns_error);
16566842Sth160488 	cu->ns_error = NULL;
16576842Sth160488 	if (cm->ns_rc != NS_LDAP_SUCCESS && cm->ns_error != NULL) {
16586842Sth160488 		ep = __s_api_copy_error(cm->ns_error);
16596842Sth160488 		if (ep == NULL)
16606842Sth160488 			cu->ns_rc = NS_LDAP_MEMORY;
16616842Sth160488 		else
16626842Sth160488 			cu->ns_error = ep;
16636842Sth160488 	}
16646842Sth160488 }
16656842Sth160488 
16666842Sth160488 /* copy error info (rc and ns_ldap_error_t) from caller to conn_user */
16676842Sth160488 static void
err_from_caller(ns_conn_user_t * cu,int rc,ns_ldap_error_t ** errorp)16686842Sth160488 err_from_caller(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp) {
16696842Sth160488 
16706842Sth160488 	cu->ns_rc = rc;
16716842Sth160488 	if (errorp != NULL) {
16726842Sth160488 		if (cu->ns_error != NULL)
16736842Sth160488 			(void) __ns_ldap_freeError(&cu->ns_error);
16746842Sth160488 		cu->ns_error = *errorp;
16756842Sth160488 		*errorp = NULL;
16766842Sth160488 	} else
16776842Sth160488 		cu->ns_error = NULL;
16786842Sth160488 }
16796842Sth160488 
16806842Sth160488 /*
16816842Sth160488  * remove an MT connection from the connection management when failed to open
16826842Sth160488  *
16836842Sth160488  * Input:
16846842Sth160488  *   cu		: pointer to the conn_user structure
16856842Sth160488  *   rc		: error code
16866842Sth160488  *   errorp	: pointer to pointer to error info (ns_ldap_error_t)
16876842Sth160488  * Output:
16886842Sth160488  *   errorp	: set to NULL, if none NULL cm, callers do not need to free it
16896842Sth160488  */
16906842Sth160488 void
__s_api_conn_mt_remove(ns_conn_user_t * cu,int rc,ns_ldap_error_t ** errorp)16916842Sth160488 __s_api_conn_mt_remove(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
16926842Sth160488 {
16936842Sth160488 	ns_conn_mgmt_t	*cmg;
16946842Sth160488 	ns_conn_mt_t	*cm;
16956842Sth160488 	int		free_cm = 0;
16966842Sth160488 
16976842Sth160488 	if (cu == NULL || cu->use_mt_conn == B_FALSE)
16986842Sth160488 		return;
16996842Sth160488 	if ((cm = cu->conn_mt) == NULL)
17006842Sth160488 		return;
17016842Sth160488 	cmg = cu->conn_mgmt;
17026842Sth160488 
17036842Sth160488 	(void) mutex_lock(&cmg->lock);
17046842Sth160488 	(void) mutex_lock(&cm->lock);
17056842Sth160488 	if (cm->state != NS_CONN_MT_CONNECT_ERROR) {
17066842Sth160488 		cm->state = NS_CONN_MT_CONNECT_ERROR;
17076842Sth160488 		cm->ns_rc = rc;
17086842Sth160488 		if (errorp != NULL) {
17096842Sth160488 			cm->ns_error = *errorp;
17106842Sth160488 			*errorp = NULL;
17116842Sth160488 		}
17126842Sth160488 	}
17136842Sth160488 
17146842Sth160488 	/* all the conn_users share the same error rc and ns_ldap_error_t */
17156842Sth160488 	err_from_cm(cu, cm);
17166842Sth160488 	/* wake up the waiters if any */
17176842Sth160488 	(void) conn_signal(cm);
17186842Sth160488 
17196842Sth160488 	del_cu4cm(cu, cm);
17206842Sth160488 	cu->conn_mt = NULL;
17216842Sth160488 	cu->bad_mt_conn = B_FALSE;
17226842Sth160488 	if (cm->cu_cnt == 0) {
17236842Sth160488 		del_cm4cmg(cm, cmg);
17246842Sth160488 		free_cm = 1;
17256842Sth160488 	}
17266842Sth160488 
17276842Sth160488 	NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
17286842Sth160488 }
17296842Sth160488 
17306842Sth160488 /*
17316842Sth160488  * check to see if the underlying libldap supports multi-threaded client
17326842Sth160488  * (MT connections)
17336842Sth160488  */
17346842Sth160488 int
__s_api_check_libldap_MT_conn_support(ns_conn_user_t * cu,LDAP * ld,ns_ldap_error_t ** ep)17356842Sth160488 __s_api_check_libldap_MT_conn_support(ns_conn_user_t *cu, LDAP *ld,
17366842Sth160488 	ns_ldap_error_t **ep)
17376842Sth160488 {
17386842Sth160488 	int		rc;
17396842Sth160488 	ns_conn_mgmt_t	*cmg;
17406842Sth160488 
17416842Sth160488 	/* if no need to check, just return success */
17426842Sth160488 	if (cu->conn_mt == NULL || cu->use_mt_conn == B_FALSE)
17436842Sth160488 		return (NS_LDAP_SUCCESS);
17446842Sth160488 
17456842Sth160488 	cmg = cu->conn_mgmt;
17466842Sth160488 	rc = setup_mt_ld(ld, cmg);
17476842Sth160488 
17486842Sth160488 	if (cmg->do_mt_conn == B_FALSE) {
17496842Sth160488 		/*
17506842Sth160488 		 * If the conn_mgmt is being shut down, return error.
17516842Sth160488 		 * if cmg is usable, cmg->lock will be locked. Otherwise,
17526842Sth160488 		 * this function will return with rc NS_LDAP_OP_FAILED.
17536842Sth160488 		 */
17546842Sth160488 		NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
17556842Sth160488 		if (cmg->do_mt_conn == B_FALSE) {
17566842Sth160488 			if (rc < 0)
17576842Sth160488 				cmg->ldap_mt = B_FALSE;
17586842Sth160488 			else {
17596842Sth160488 				cmg->ldap_mt = B_TRUE;
17606842Sth160488 				if (cmg->is_nscd  == B_TRUE ||
17616842Sth160488 				    cmg->is_peruser_nscd == B_TRUE) {
17626842Sth160488 					cmg->do_mt_conn = B_TRUE;
17636842Sth160488 					cmg->state = NS_CONN_MGMT_ACTIVE;
17646842Sth160488 				}
17656842Sth160488 			}
17666842Sth160488 		}
17676842Sth160488 		(void) mutex_unlock(&cmg->lock);
17686842Sth160488 	}
17696842Sth160488 
17706842Sth160488 	if (rc < 0)
17716842Sth160488 		__s_api_conn_mt_remove(cu, NS_LDAP_NOTFOUND, NULL);
17726842Sth160488 	return (NS_LDAP_SUCCESS);
17736842Sth160488 }
17746842Sth160488 
17756842Sth160488 /*
17766842Sth160488  * Close an MT connection.
17776842Sth160488  * Assume cm not null and locked, assume conn_mgmt is also locked.
17786842Sth160488  * Return -1 if error, 1 if the cm should be freed, otherwise 0.
17796842Sth160488  */
17806842Sth160488 static int
close_conn_mt(ns_conn_mt_t * cm,int rc,ns_ldap_error_t ** errorp,ns_conn_user_t * cu)17816842Sth160488 close_conn_mt(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp,
17826842Sth160488 	ns_conn_user_t *cu)
17836842Sth160488 {
17846842Sth160488 	ns_conn_mgmt_t	*cmg = cm->conn_mgmt;
17856842Sth160488 	ns_conn_mt_t	*m;
17866842Sth160488 	ns_conn_user_t	*u;
17876842Sth160488 
17886842Sth160488 	if ((cm->state != NS_CONN_MT_CONNECTED && cm->state !=
17896842Sth160488 	    NS_CONN_MT_CLOSING) || cmg->cm_head == NULL || cmg->cm_cnt == 0)
17906842Sth160488 		return (-1);
17916842Sth160488 
17926842Sth160488 	/* if the conn_mt is not in the MT connection pool, nothing to do */
17936842Sth160488 	for (m = cmg->cm_head; m; m = m->next) {
17946842Sth160488 		if (cm == m)
17956842Sth160488 			break;
17966842Sth160488 	}
17976842Sth160488 	if (m == NULL)
17986842Sth160488 		return (-1);
17996842Sth160488 
18006842Sth160488 	if (cm->state == NS_CONN_MT_CONNECTED) { /* first time in here */
18016842Sth160488 		cm->state = NS_CONN_MT_CLOSING;
18026842Sth160488 		/*
18036842Sth160488 		 * If more cu exist to consume the error info, copy
18046842Sth160488 		 * it to the cm. If the caller calls on behalf of
18056842Sth160488 		 * a cu, cu won't be NULL. Check to see if there's
18066842Sth160488 		 * more cu that needs the error info. If caller does
18076842Sth160488 		 * not have a specific cu attached to it (e.g.,
18086842Sth160488 		 * shutdown_all_conn_mt()), cu is NULL, check if at
18096842Sth160488 		 * least one cu exists.
18106842Sth160488 		 */
18116842Sth160488 		if ((cu != NULL && cm->cu_cnt > 1) ||
18126842Sth160488 		    (cu == NULL && cm->cu_cnt > 0)) {
18136842Sth160488 			err2cm(cm, rc, errorp);
18146842Sth160488 			/* wake up waiter (conn_user) if any */
18156842Sth160488 			(void) conn_signal(cm);
18166842Sth160488 		}
18176842Sth160488 
18186842Sth160488 		/* for each conn_user using the conn_mt, set bad_mt_conn flag */
18196842Sth160488 		if (cm->cu_head != NULL) {
18206842Sth160488 			for (u = cm->cu_head; u; u = u->next) {
18216842Sth160488 				u->bad_mt_conn = B_TRUE;
18226842Sth160488 				if (cmg->shutting_down == B_FALSE)
18236842Sth160488 					u->retry = B_TRUE;
18246842Sth160488 			}
18256842Sth160488 		}
18266842Sth160488 	}
18276842Sth160488 
18286842Sth160488 	/* detach the conn_mt if no more conn_user left */
18296842Sth160488 	if ((cu != NULL && cm->cu_cnt == 1) ||
18306842Sth160488 	    (cu == NULL && cm->cu_cnt ==  0)) {
18316842Sth160488 		del_cm4cmg(cm, cmg);
18326842Sth160488 		cm->detached = B_TRUE;
18336842Sth160488 		return (1);
18346842Sth160488 	}
18356842Sth160488 
18366842Sth160488 	return (0);
18376842Sth160488 }
18386842Sth160488 
18396842Sth160488 /*
18406842Sth160488  * An MT connection becomes bad, close it and free resources.
18416842Sth160488  * This function is called with a ns_conn_user_t representing
18426842Sth160488  * a user of the MT connection.
18436842Sth160488  *
18446842Sth160488  * Input:
18456842Sth160488  *   cu		: pointer to the conn_user structure
18466842Sth160488  *   rc		: error code
18476842Sth160488  *   errorp	: pointer to pointer to error info (ns_ldap_error_t)
18486842Sth160488  * Output:
18496842Sth160488  *   errorp	: set to NULL (if no error), callers do not need to free it
18506842Sth160488  */
18516842Sth160488 void
__s_api_conn_mt_close(ns_conn_user_t * cu,int rc,ns_ldap_error_t ** errorp)18526842Sth160488 __s_api_conn_mt_close(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
18536842Sth160488 {
18546842Sth160488 	ns_conn_mgmt_t	*cmg;
18556842Sth160488 	ns_conn_mt_t	*cm;
18566842Sth160488 	int		free_cm = 0;
18576842Sth160488 
18586842Sth160488 	if (cu == NULL || cu->use_mt_conn == B_FALSE)
18596842Sth160488 		return;
18606842Sth160488 
18616842Sth160488 	if (cu->state != NS_CONN_USER_CONNECTED || (cm = cu->conn_mt) == NULL)
18626842Sth160488 		return;
18636842Sth160488 	cmg = cu->conn_mgmt;
18646842Sth160488 
18656842Sth160488 	(void) mutex_lock(&cmg->lock);
18666842Sth160488 	(void) mutex_lock(&cm->lock);
18676842Sth160488 
18686842Sth160488 	/* close the MT connection if possible */
18696842Sth160488 	free_cm = close_conn_mt(cm, rc, errorp, cu);
18706842Sth160488 	if (free_cm == -1) { /* error case */
18716842Sth160488 		(void) mutex_unlock(&cm->lock);
18726842Sth160488 		(void) mutex_unlock(&cmg->lock);
18736842Sth160488 		return;
18746842Sth160488 	}
18756842Sth160488 
18766842Sth160488 	if (rc != NS_LDAP_SUCCESS) { /* error info passed in, use it */
18776842Sth160488 		err_from_caller(cu, rc, errorp);
18786842Sth160488 	} else { /* error not passed in, use those saved in the conn_mt */
18796842Sth160488 		err_from_cm(cu, cm);
18806842Sth160488 	}
18816842Sth160488 
18826842Sth160488 	/* detach the conn_user from the conn_mt */
18836842Sth160488 	del_cu4cm(cu, cm);
18846842Sth160488 	cu->conn_mt = NULL;
18856842Sth160488 	cu->bad_mt_conn = B_FALSE;
18866842Sth160488 	if (cmg->shutting_down == B_FALSE)
18876842Sth160488 		cu->retry = B_TRUE;
18886842Sth160488 	NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
18896842Sth160488 }
18906842Sth160488 
18916842Sth160488 /*
18926842Sth160488  * Close an MT connection when the associated server is known to be
18936842Sth160488  * down. This function is called with a ns_conn_mt_t representing
18946842Sth160488  * the MT connection. That is, the caller is not a conn_user
18956842Sth160488  * thread but rather the procchg thread.
18966842Sth160488  */
18976842Sth160488 static void
close_conn_mt_by_procchg(ns_conn_mt_t * cm,int rc,char * errmsg)18986842Sth160488 close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg)
18996842Sth160488 {
19006842Sth160488 	ns_conn_mgmt_t	*cmg;
19016842Sth160488 	int		free_cm = 0;
19026842Sth160488 	ns_ldap_error_t	*ep;
19036842Sth160488 
19046842Sth160488 	if (cm == NULL)
19056842Sth160488 		return;
19066842Sth160488 	cmg = cm->conn_mgmt;
19076842Sth160488 
19086842Sth160488 	ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
19096842Sth160488 	if (ep != NULL) {
19106842Sth160488 		ep->status = rc;
19116842Sth160488 		if (errmsg != NULL)
19126842Sth160488 			ep->message =  strdup(errmsg); /* OK if returns NULL */
19136842Sth160488 	}
19146842Sth160488 
19156842Sth160488 	(void) mutex_lock(&cmg->lock);
19166842Sth160488 	(void) mutex_lock(&cm->lock);
19176842Sth160488 
19186842Sth160488 	/* close the MT connection if possible */
19196842Sth160488 	free_cm = close_conn_mt(cm, LDAP_SERVER_DOWN, &ep, NULL);
19206842Sth160488 	if (free_cm == -1) { /* error case */
19216842Sth160488 		(void) mutex_unlock(&cm->lock);
19226842Sth160488 		(void) mutex_unlock(&cmg->lock);
19236842Sth160488 		return;
19246842Sth160488 	}
19256842Sth160488 	(void) __ns_ldap_freeError(&ep);
19266842Sth160488 
19276842Sth160488 	NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
19286842Sth160488 }
19296842Sth160488 
19306842Sth160488 /*
19316842Sth160488  * Close an MT connection when there is a better server to connect to.
19326842Sth160488  * Mark the connection as to-be-closed-when-no-one-using so that
19336842Sth160488  * any outstanding ldap operations can run to completion.
19346842Sth160488  * Assume that both the conn_mt and conn_mgmt are locked.
19356842Sth160488  * Return 1 if the conn_mt should be freed.
19366842Sth160488  */
19376842Sth160488 static int
close_conn_mt_when_nouser(ns_conn_mt_t * cm)19386842Sth160488 close_conn_mt_when_nouser(ns_conn_mt_t *cm)
19396842Sth160488 {
19406842Sth160488 	int		free_cm = 0;
19416842Sth160488 
19426842Sth160488 	if (cm->cu_cnt == 0) {
19436842Sth160488 		del_cm4cmg(cm, cm->conn_mgmt);
19446842Sth160488 		free_cm = 1;
19456842Sth160488 	} else {
19466842Sth160488 		cm->close_when_nouser = B_TRUE;
19476842Sth160488 	}
19486842Sth160488 
19496842Sth160488 	return (free_cm);
19506842Sth160488 }
19516842Sth160488 
19526842Sth160488 /*
19536842Sth160488  * Retrieve the configured preferred server list.
19546842Sth160488  * This function locked the conn_mgmt and does not
19556842Sth160488  * unlock at exit.
19566842Sth160488  */
19576842Sth160488 static void
get_preferred_servers(boolean_t lock,boolean_t reload,ns_conn_mgmt_t * cmg)19586842Sth160488 get_preferred_servers(boolean_t lock, boolean_t reload, ns_conn_mgmt_t *cmg)
19596842Sth160488 {
19606842Sth160488 	ns_ldap_error_t *errorp = NULL;
19616842Sth160488 	void		**pservers = NULL;
19626842Sth160488 
19636842Sth160488 	if (lock == B_TRUE)
19646842Sth160488 		(void) mutex_lock(&cmg->lock);
19656842Sth160488 
19666842Sth160488 	/* if already done, and no reload, then return */
19676842Sth160488 	if (cmg->pservers_loaded == B_TRUE && reload == B_FALSE)
19686842Sth160488 		return;
19696842Sth160488 
19706842Sth160488 	if (cmg->pservers != NULL) {
19716842Sth160488 		(void) __ns_ldap_freeParam((void ***)&cmg->pservers);
19726842Sth160488 		cmg->pservers = NULL;
19736842Sth160488 	}
19746842Sth160488 
19756842Sth160488 	if (__ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
19766842Sth160488 	    &pservers, &errorp) == NS_LDAP_SUCCESS) {
19776842Sth160488 		cmg->pservers = (char **)pservers;
19786842Sth160488 		cmg->pservers_loaded = B_TRUE;
19796842Sth160488 	} else {
19806842Sth160488 		(void) __ns_ldap_freeError(&errorp);
19816842Sth160488 		(void) __ns_ldap_freeParam(&pservers);
19826842Sth160488 	}
19836842Sth160488 }
19846842Sth160488 
19856842Sth160488 /*
19866842Sth160488  * This function handles the config or server status change notification
19876842Sth160488  * from the ldap_cachemgr.
19886842Sth160488  */
19896842Sth160488 static ns_conn_mgmt_t *
proc_server_change(ns_server_status_change_t * chg,ns_conn_mgmt_t * cmg)19906842Sth160488 proc_server_change(ns_server_status_change_t *chg, ns_conn_mgmt_t  *cmg)
19916842Sth160488 {
19926842Sth160488 	int		cnt, i, j, k, n;
19936842Sth160488 	boolean_t	loop = B_TRUE;
19946842Sth160488 	boolean_t	cmg_locked = B_FALSE;
19956842Sth160488 	char 		*s;
19966842Sth160488 	ns_conn_mt_t	*cm;
19976842Sth160488 	ns_conn_mgmt_t	*ocmg;
19986842Sth160488 
19996842Sth160488 	/* if config changed, reload the configuration */
20006842Sth160488 	if (chg->config_changed == B_TRUE) {
20016842Sth160488 		/* reload the conn_mgmt and Native LDAP config */
20026842Sth160488 		ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_RELOAD_CONFIG);
20036842Sth160488 		shutdown_all_conn_mt(ocmg);
20046842Sth160488 		/* release the one obtained from access_conn_mgmt(RELOAD) */
20056842Sth160488 		(void) release_conn_mgmt(ocmg, B_FALSE);
20066842Sth160488 		/* release the one obtained when ocmg was created */
20076842Sth160488 		(void) release_conn_mgmt(ocmg, B_FALSE);
20086842Sth160488 		return (ocmg);
20096842Sth160488 	}
20106842Sth160488 
20116842Sth160488 	if ((cnt = chg->num_server) == 0)
20126842Sth160488 		return (cmg);
20136842Sth160488 
20146842Sth160488 	/* handle down servers first */
20156842Sth160488 	for (i = 0; i < cnt; i++) {
20166842Sth160488 
20176842Sth160488 		if (chg->changes[i] != NS_SERVER_DOWN)
20186842Sth160488 			continue;
20196842Sth160488 		s = chg->servers[i];
20206842Sth160488 
20216842Sth160488 		/*
20226842Sth160488 		 * look for a CONNECTED MT connection using
20236842Sth160488 		 * the same server s, and close it
20246842Sth160488 		 */
20256842Sth160488 		while (loop) {
20266842Sth160488 			if (cmg_locked == B_FALSE) {
20276842Sth160488 				(void) mutex_lock(&cmg->lock);
20286842Sth160488 				cmg_locked = B_TRUE;
20296842Sth160488 			}
20306842Sth160488 			for (cm = cmg->cm_head; cm; cm = cm->next) {
20316842Sth160488 				(void) mutex_lock(&cm->lock);
20326842Sth160488 
20336842Sth160488 				if (cm->state == NS_CONN_MT_CONNECTED &&
20346842Sth160488 				    cm->conn != NULL &&
20356842Sth160488 				    strcasecmp(cm->conn->serverAddr, s) == 0) {
20366842Sth160488 					(void) mutex_unlock(&cm->lock);
20376842Sth160488 					break;
20386842Sth160488 				}
20396842Sth160488 
20406842Sth160488 				(void) mutex_unlock(&cm->lock);
20416842Sth160488 			}
20426842Sth160488 			if (cm != NULL) {
20436842Sth160488 				(void) mutex_unlock(&cmg->lock);
20446842Sth160488 				cmg_locked = B_FALSE;
20456842Sth160488 				close_conn_mt_by_procchg(cm, LDAP_SERVER_DOWN,
20466842Sth160488 				    NS_CONN_MSG_DOWN_FROM_CACHEMGR);
20476842Sth160488 				/*
20486842Sth160488 				 * Process the next cm using server s.
20496842Sth160488 				 * Start from the head of the cm linked
20506842Sth160488 				 * list again, as the cm list may change
20516842Sth160488 				 * after close_conn_mt_by_procchg() is done.
20526842Sth160488 				 */
20536842Sth160488 				continue;
20546842Sth160488 			}
20556842Sth160488 
20566842Sth160488 			/*
20576842Sth160488 			 * No (more) MT connection using the down server s.
20586842Sth160488 			 * Process the next server on the list.
20596842Sth160488 			 */
20606842Sth160488 			break;
20616842Sth160488 		} /* while loop */
20626842Sth160488 	}
20636842Sth160488 
20646842Sth160488 	/*
20656842Sth160488 	 * Next handle servers whose status changed to up.
20666842Sth160488 	 * Get the preferred server list first if not done yet.
20676842Sth160488 	 * get_preferred_servers() leaves conn_mgmt locked.
20686842Sth160488 	 */
20696842Sth160488 	get_preferred_servers(cmg_locked == B_FALSE ? B_TRUE : B_FALSE,
20706842Sth160488 	    B_FALSE, cmg);
20716842Sth160488 	cmg_locked = B_TRUE;
20726842Sth160488 	/*
20736842Sth160488 	 * if no preferred server configured, we don't switch MT connection
20746842Sth160488 	 * to a more preferred server (i.e., fallback), so just return
20756842Sth160488 	 */
20766842Sth160488 	if (cmg->pservers == NULL) {
20776842Sth160488 		(void) mutex_unlock(&cmg->lock);
20786842Sth160488 		return (cmg);
20796842Sth160488 	}
20806842Sth160488 
20816842Sth160488 	/* for each server that is up now */
20826842Sth160488 	for (i = 0; i < cnt; i++) {
20836842Sth160488 		if (chg->changes[i] != NS_SERVER_UP)
20846842Sth160488 			continue;
20856842Sth160488 		s = chg->servers[i];
20866842Sth160488 
20876842Sth160488 		/*
20886842Sth160488 		 * look for a CONNECTED MT connection which uses
20896842Sth160488 		 * a server less preferred than s, and treat it
20906842Sth160488 		 * as 'fallback needed' by calling
20916842Sth160488 		 * close_conn_mt_when_nouser()
20926842Sth160488 		 */
20936842Sth160488 		k = -1;
20946842Sth160488 		loop = B_TRUE;
20956842Sth160488 		while (loop) {
20966842Sth160488 			if (cmg_locked == B_FALSE) {
20976842Sth160488 				(void) mutex_lock(&cmg->lock);
20986842Sth160488 				cmg_locked = B_TRUE;
20996842Sth160488 			}
21006842Sth160488 
21016842Sth160488 			/* Is s a preferred server ? */
21026842Sth160488 			if (k == -1) {
21036842Sth160488 				for (j = 0; cmg->pservers[j] != NULL; j++) {
21046842Sth160488 					if (strcasecmp(cmg->pservers[j],
21056842Sth160488 					    s) == 0) {
21066842Sth160488 						k = j;
21076842Sth160488 						break;
21086842Sth160488 					}
21096842Sth160488 				}
21106842Sth160488 			}
21116842Sth160488 			/* skip s if not a preferred server */
21126842Sth160488 			if (k == -1) {
21136842Sth160488 				break;
21146842Sth160488 			}
21156842Sth160488 
21166842Sth160488 			/* check each MT connection */
21176842Sth160488 			for (cm = cmg->cm_head; cm; cm = cm->next) {
21186842Sth160488 				(void) mutex_lock(&cm->lock);
21196842Sth160488 				/*
21206842Sth160488 				 * Find an MT connection that is connected and
21216842Sth160488 				 * not marked, but leave WRITE or REFERRAL
21226842Sth160488 				 * connections alone, since fallback does not
21236842Sth160488 				 * make sense for them.
21246842Sth160488 				 */
21256842Sth160488 				if (cm->state == NS_CONN_MT_CONNECTED &&
21266842Sth160488 				    cm->close_when_nouser == B_FALSE &&
21276842Sth160488 				    cm->conn != NULL && cm->opened_for !=
21286842Sth160488 				    NS_CONN_USER_WRITE &&
21296842Sth160488 				    cm->referral == B_FALSE) {
21306842Sth160488 					n = -1;
21316842Sth160488 					/*
21326842Sth160488 					 * j < k ??? should we close
21336842Sth160488 					 * an active MT that is using s ?
21346842Sth160488 					 * ie could s went down and up
21356842Sth160488 					 * again, but cm is bound prior to
21366842Sth160488 					 * the down ? Play safe here,
21376842Sth160488 					 * and check j <= k.
21386842Sth160488 					 */
21396842Sth160488 					for (j = 0; j <= k; j++) {
21406842Sth160488 						if (strcasecmp(
21416842Sth160488 						    cm->conn->serverAddr,
21426842Sth160488 						    cmg->pservers[j]) == 0) {
21436842Sth160488 							n = j;
21446842Sth160488 							break;
21456842Sth160488 						}
21466842Sth160488 					}
21476842Sth160488 					/*
21486842Sth160488 					 * s is preferred, if its location
21496842Sth160488 					 * in the preferred server list is
21506842Sth160488 					 * ahead of that of the server
21516842Sth160488 					 * used by the cm (i.e., no match
21526842Sth160488 					 * found before s)
21536842Sth160488 					 */
21546842Sth160488 					if (n == -1) { /* s is preferred */
21556842Sth160488 						int fr = 0;
21566842Sth160488 						fr = close_conn_mt_when_nouser(
21576842Sth160488 						    cm);
21586842Sth160488 						NS_CONN_UNLOCK_AND_FREE(fr,
21596842Sth160488 						    cm, cmg);
21606842Sth160488 						cmg_locked = B_FALSE;
21616842Sth160488 						/*
21626842Sth160488 						 * break, not continue,
21636842Sth160488 						 * because we need to
21646842Sth160488 						 * check the entire cm
21656842Sth160488 						 * list again. The call
21666842Sth160488 						 * above may change the
21676842Sth160488 						 * cm list.
21686842Sth160488 						 */
21696842Sth160488 						break;
21706842Sth160488 					}
21716842Sth160488 				}
21726842Sth160488 				(void) mutex_unlock(&cm->lock);
21736842Sth160488 			}
21746842Sth160488 			/* if no (more) cm using s, check next server */
21756842Sth160488 			if (cm == NULL)
21766842Sth160488 				loop = B_FALSE;
21776842Sth160488 		} /* while loop */
21786842Sth160488 	}
21796842Sth160488 	if (cmg_locked == B_TRUE)
21806842Sth160488 		(void) mutex_unlock(&cmg->lock);
21816842Sth160488 	return (cmg);
21826842Sth160488 }
21836842Sth160488 
21846842Sth160488 /* Shut down all MT connection managed by the connection management */
21856842Sth160488 void
shutdown_all_conn_mt(ns_conn_mgmt_t * cmg)21866842Sth160488 shutdown_all_conn_mt(ns_conn_mgmt_t  *cmg)
21876842Sth160488 {
21886842Sth160488 	ns_ldap_error_t	*ep;
21896842Sth160488 	ns_conn_mt_t	*cm;
21906842Sth160488 	int		free_cm = 0;
21916842Sth160488 	boolean_t	done = B_FALSE;
21926842Sth160488 
21936842Sth160488 	ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
21946842Sth160488 	if (ep != NULL) { /* if NULL, not a problem */
21956842Sth160488 		/* OK if returns NULL */
21966842Sth160488 		ep->message = strdup(NS_CONN_MSG_SHUTDOWN_RELOADED);
21976842Sth160488 	}
21986842Sth160488 
21996842Sth160488 	(void) mutex_lock(&cmg->lock);
22006842Sth160488 	while (cmg->cm_head != NULL && done == B_FALSE) {
22016842Sth160488 		for (cm = cmg->cm_head; cm; cm = cm->next) {
22026842Sth160488 			(void) mutex_lock(&cm->lock);
22036842Sth160488 			if (cm->next == NULL)
22046842Sth160488 				done = B_TRUE;
22056842Sth160488 			/* shut down each conn_mt, ignore errors */
22066842Sth160488 			free_cm = close_conn_mt(cm, LDAP_OTHER, &ep, NULL);
22076842Sth160488 			(void) mutex_unlock(&cm->lock);
22086842Sth160488 			if (free_cm == 1) {
22096842Sth160488 				(void) free_conn_mt(cm, 0);
22106842Sth160488 				/*
22116842Sth160488 				 * conn_mt may change, so start from
22126842Sth160488 				 * top of list again
22136842Sth160488 				 */
22146842Sth160488 				break;
22156842Sth160488 			}
22166842Sth160488 		}
22176842Sth160488 	}
22186842Sth160488 	(void) mutex_unlock(&cmg->lock);
22196842Sth160488 	(void) __ns_ldap_freeError(&ep);
22206842Sth160488 }
22216842Sth160488 
22226842Sth160488 /* free all the resources used by the connection management */
22236842Sth160488 void
__s_api_shutdown_conn_mgmt()22246842Sth160488 __s_api_shutdown_conn_mgmt()
22256842Sth160488 {
22266842Sth160488 	ns_conn_mgmt_t	*cmg;
22276842Sth160488 
22286842Sth160488 	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_SHUTDOWN);
22296842Sth160488 	if (cmg == NULL) /* already being SHUT done */
22306842Sth160488 		return;
22316842Sth160488 
22326842Sth160488 	(void) shutdown_all_conn_mt(cmg);
22336842Sth160488 	(void) release_conn_mgmt(cmg, B_FALSE);
22346842Sth160488 
22356842Sth160488 	/* then destroy the conn_mgmt */
22366842Sth160488 	(void) release_conn_mgmt(cmg, B_FALSE);
22376842Sth160488 }
22386842Sth160488 
22396842Sth160488 
22406842Sth160488 /*
22416921Smichen  * Reinitialize the libsldap connection management after
22426921Smichen  * a new native LDAP configuration is received.
22436842Sth160488  */
22446842Sth160488 void
__s_api_reinit_conn_mgmt_new_config(ns_config_t * new_cfg)22456842Sth160488 __s_api_reinit_conn_mgmt_new_config(ns_config_t *new_cfg)
22466842Sth160488 {
22476842Sth160488 	ns_conn_mgmt_t	*cmg;
22486842Sth160488 	ns_conn_mgmt_t	*ocmg;
22496842Sth160488 
22506842Sth160488 	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
22516842Sth160488 	if (cmg == NULL)
22526842Sth160488 		return;
22536842Sth160488 	if (cmg->config == new_cfg || cmg->state == NS_CONN_MGMT_DETACHED) {
22546842Sth160488 		(void) release_conn_mgmt(cmg, B_FALSE);
22556842Sth160488 		return;
22566842Sth160488 	}
22576842Sth160488 
22586842Sth160488 	/* reload the conn_mgmt and native LDAP config */
22596842Sth160488 	ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_NEW_CONFIG);
22606842Sth160488 	if (ocmg == cmg)
22616842Sth160488 		shutdown_all_conn_mt(ocmg);
22626842Sth160488 	/* release the one obtained from access_conn_mgmt(RELOAD) */
22636842Sth160488 	(void) release_conn_mgmt(ocmg, B_FALSE);
22646842Sth160488 	/* release the one obtained when ocmg was created */
22656842Sth160488 	(void) release_conn_mgmt(ocmg, B_FALSE);
22666842Sth160488 	/* release the one obtained when this function is entered */
22676842Sth160488 	(void) release_conn_mgmt(cmg, B_FALSE);
22686842Sth160488 }
22696842Sth160488 
22706842Sth160488 /*
22716842Sth160488  * Prepare to retry ldap search operation if needed.
22726842Sth160488  * Return 1 if retry is needed, otherwise 0.
22736842Sth160488  * If first time in, return 1. If not, return 1 if:
22746842Sth160488  * - not a NS_CONN_USER_GETENT conn_user AND
22756842Sth160488  * - have not retried 3 times yet AND
22766842Sth160488  * - previous search failed AND
22776842Sth160488  * - the retry flag is set in the ns_conn_user_t or config was reloaded
22786842Sth160488  */
22796842Sth160488 int
__s_api_setup_retry_search(ns_conn_user_t ** conn_user,ns_conn_user_type_t type,int * try_cnt,int * rc,ns_ldap_error_t ** errorp)22806842Sth160488 __s_api_setup_retry_search(ns_conn_user_t **conn_user,
22816842Sth160488 	ns_conn_user_type_t type, int *try_cnt, int *rc,
22826842Sth160488 	ns_ldap_error_t **errorp)
22836842Sth160488 {
22846842Sth160488 	boolean_t	retry;
22856842Sth160488 	ns_conn_user_t	*cu = *conn_user;
22866842Sth160488 	ns_conn_mgmt_t	*cmg;
22876842Sth160488 
22886842Sth160488 	if (*try_cnt > 0 && cu != NULL) {
22896842Sth160488 		/*
22906842Sth160488 		 * if called from firstEntry(), keep conn_mt for
22916842Sth160488 		 * the subsequent getnext requests
22926842Sth160488 		 */
22936842Sth160488 		if (cu->type == NS_CONN_USER_GETENT && *rc == NS_LDAP_SUCCESS)
22946842Sth160488 			return (0);
22956842Sth160488 		cmg = cu->conn_mgmt;
22966842Sth160488 		retry = cu->retry;
22976842Sth160488 		if (cu->conn_mt != NULL)
22986842Sth160488 			__s_api_conn_mt_return(cu);
22996842Sth160488 		if (cmg != NULL && cmg->cfg_reloaded == B_TRUE)
23006842Sth160488 			retry = B_TRUE;
23016842Sth160488 		__s_api_conn_user_free(cu);
23026842Sth160488 		*conn_user = NULL;
23036842Sth160488 
23046842Sth160488 		if (*rc == NS_LDAP_SUCCESS || retry != B_TRUE)
23056842Sth160488 			return (0);
23066842Sth160488 	}
23076842Sth160488 
23086842Sth160488 	*try_cnt = *try_cnt + 1;
23096842Sth160488 	if (*try_cnt > NS_LIST_TRY_MAX)
23106842Sth160488 		return (0);
23116842Sth160488 
23126842Sth160488 	*conn_user = __s_api_conn_user_init(type, NULL, B_FALSE);
23136842Sth160488 	if (*conn_user == NULL) {
23146842Sth160488 		if (*try_cnt == 1) { /* first call before any retry */
23156842Sth160488 			*rc = NS_LDAP_MEMORY;
23166842Sth160488 			*errorp = NULL;
23176842Sth160488 		}
23186842Sth160488 		/* for 1+ try, use previous rc and errorp */
23196842Sth160488 		return (0);
23206842Sth160488 	}
23216842Sth160488 
23226842Sth160488 	/* free ldap_error_t from previous search */
23236842Sth160488 	if (*try_cnt > 1 && rc != NS_LDAP_SUCCESS && *errorp != NULL)
23246842Sth160488 		(void) __ns_ldap_freeError(errorp);
23256842Sth160488 
23266842Sth160488 	return (1);
23276842Sth160488 }
23286842Sth160488 
23296842Sth160488 /* prepare to get the next entry for an enumeration */
23306842Sth160488 int
__s_api_setup_getnext(ns_conn_user_t * cu,int * ns_err,ns_ldap_error_t ** errorp)23316842Sth160488 __s_api_setup_getnext(ns_conn_user_t *cu, int *ns_err,
23326842Sth160488 	ns_ldap_error_t **errorp)
23336842Sth160488 {
23346842Sth160488 	int rc;
23356842Sth160488 	ns_conn_mgmt_t	*cmg;
23366842Sth160488 
23376842Sth160488 	/*
23386842Sth160488 	 * if using an MT connection, ensure the thread-specific data are set,
23396842Sth160488 	 * but if the MT connection is no longer good, return the error saved.
23406842Sth160488 	 */
23416842Sth160488 	if (cu->conn_mt != NULL && (cmg = cu->conn_mgmt) != NULL) {
23426842Sth160488 
23436842Sth160488 		if (cu->bad_mt_conn ==  B_TRUE) {
23446842Sth160488 			__s_api_conn_mt_close(cu, 0, NULL);
23456842Sth160488 			*ns_err = cu->ns_rc;
23466842Sth160488 			*errorp = cu->ns_error;
23476842Sth160488 			cu->ns_error = NULL;
23486842Sth160488 			return (*ns_err);
23496842Sth160488 		}
23506842Sth160488 
23516842Sth160488 		rc = conn_tsd_check(cmg);
23526842Sth160488 		if (rc != NS_LDAP_SUCCESS) {
23536842Sth160488 			*errorp = NULL;
23546842Sth160488 			return (rc);
23556842Sth160488 		}
23566842Sth160488 	}
23576842Sth160488 
23586842Sth160488 	return (NS_LDAP_SUCCESS);
23596842Sth160488 }
23606842Sth160488 
23616842Sth160488 /* wait for an MT connection to become available */
23626842Sth160488 static int
conn_wait(ns_conn_mt_t * conn_mt,ns_conn_user_t * conn_user)23636842Sth160488 conn_wait(ns_conn_mt_t *conn_mt, ns_conn_user_t *conn_user)
23646842Sth160488 {
23656842Sth160488 	ns_conn_waiter_t	mywait;
23666842Sth160488 	ns_conn_waiter_t	*head = &conn_mt->waiter;
23676842Sth160488 
23686842Sth160488 	(void) cond_init(&(mywait.waitcv), USYNC_THREAD, 0);
23696842Sth160488 	mywait.key = conn_user;
23706842Sth160488 	mywait.signaled = 0;
23716842Sth160488 	mywait.next = head->next;
23726842Sth160488 	mywait.prev = head;
23736842Sth160488 	if (mywait.next)
23746842Sth160488 		mywait.next->prev = &mywait;
23756842Sth160488 	head->next = &mywait;
23766842Sth160488 	atomic_inc_uint(&conn_mt->waiter_cnt);
23776842Sth160488 
23786842Sth160488 	while (!mywait.signaled)
23796842Sth160488 		(void) cond_wait(&(mywait.waitcv), &conn_mt->lock);
23806842Sth160488 	if (mywait.prev)
23816842Sth160488 		mywait.prev->next = mywait.next;
23826842Sth160488 	if (mywait.next)
23836842Sth160488 		mywait.next->prev = mywait.prev;
23846842Sth160488 	return (0);
23856842Sth160488 }
23866842Sth160488 
23876842Sth160488 /* signal that an MT connection is now available */
23886842Sth160488 static int
conn_signal(ns_conn_mt_t * conn_mt)23896842Sth160488 conn_signal(ns_conn_mt_t *conn_mt)
23906842Sth160488 {
23916842Sth160488 	int			c = 0;
23926842Sth160488 	ns_conn_waiter_t	*head = &conn_mt->waiter;
23936842Sth160488 	ns_conn_waiter_t	*tmp = head->next;
23946842Sth160488 
23956842Sth160488 	while (tmp) {
23966842Sth160488 		(void) cond_signal(&(tmp->waitcv));
23976842Sth160488 		tmp->signaled = 1;
23986842Sth160488 		atomic_dec_uint(&conn_mt->waiter_cnt);
23996842Sth160488 		c++;
24006842Sth160488 		tmp = tmp->next;
24016842Sth160488 	}
24026842Sth160488 
24036842Sth160488 	return (c);
24046842Sth160488 }
24056842Sth160488 
24066842Sth160488 /*
24076842Sth160488  * wait and process the server status and/or config change notification
24086842Sth160488  * from ldap_cachemgr
24096842Sth160488  */
24106842Sth160488 static void *
get_server_change(void * arg)24116842Sth160488 get_server_change(void *arg)
24126842Sth160488 {
24136842Sth160488 	union {
24146842Sth160488 		ldap_data_t	s_d;
24156842Sth160488 		char		s_b[DOORBUFFERSIZE];
24166842Sth160488 	} space;
24176842Sth160488 	ldap_data_t	*sptr = &space.s_d;
24186842Sth160488 	int		ndata;
24196842Sth160488 	int		adata;
24206842Sth160488 	char		*ptr;
24216842Sth160488 	int		ds_cnt;
24226842Sth160488 	int		door_rc;
24236842Sth160488 	int		which;
24246842Sth160488 	int		retry = 0;
24256842Sth160488 	boolean_t	loop = B_TRUE;
24266842Sth160488 	char		*c, *oc;
24276842Sth160488 	int		dslen = strlen(DOORLINESEP);
24286842Sth160488 	char		dsep = DOORLINESEP_CHR;
24296842Sth160488 	char		chg_data[DOORBUFFERSIZE];
24306842Sth160488 	char		**servers = NULL;
24316842Sth160488 	boolean_t	getchg_not_supported = B_FALSE;
24326842Sth160488 	ns_conn_mgmt_t	*ocmg = (ns_conn_mgmt_t *)arg;
24336842Sth160488 	ns_conn_mgmt_t	*cmg;
24346842Sth160488 	ns_server_status_t *status = NULL;
24356842Sth160488 	ns_server_status_change_t chg = { 0 };
24366842Sth160488 	ldap_get_change_out_t *get_chg;
24376842Sth160488 	ldap_get_chg_cookie_t cookie;
24386842Sth160488 	ldap_get_chg_cookie_t new_cookie;
24396842Sth160488 
24406842Sth160488 	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
24416842Sth160488 	if (cmg != ocmg)
24426842Sth160488 		thr_exit(NULL);
24436842Sth160488 	/* cmg is locked before called */
24446842Sth160488 	cmg->procchg_tid = thr_self();
24456842Sth160488 
24466842Sth160488 	/* make sure the thread specific data are set */
24476842Sth160488 	(void) conn_tsd_setup(cmg);
24486842Sth160488 	cookie = cmg->cfg_cookie;
24496842Sth160488 
24506842Sth160488 	while (loop) {
24516842Sth160488 
24526842Sth160488 		if (chg.servers != NULL)
24536842Sth160488 			free(chg.servers);
24546842Sth160488 		if (chg.changes != NULL)
24556842Sth160488 			free(chg.changes);
24566842Sth160488 		if (sptr != &space.s_d)
24576842Sth160488 			(void) munmap((char *)sptr, sizeof (space));
24586842Sth160488 
24596842Sth160488 		/*
24606842Sth160488 		 * If the attached conn_mgmt has been deleted,
24616842Sth160488 		 * then exit. The new conn_mgmt will starts it
24626842Sth160488 		 * own monitor thread later. If libsldap is being
24636842Sth160488 		 * unloaded or configuration reloaded, OR
24646842Sth160488 		 * ldap_cachemgr rejected the GETSTATUSCHANGE door
24656842Sth160488 		 * call, then exit as well.
24666842Sth160488 		 */
24676842Sth160488 		if (cmg == NULL || cmg->state == NS_CONN_MGMT_DETACHED ||
24686842Sth160488 		    getchg_not_supported == B_TRUE) {
24696842Sth160488 
24706842Sth160488 			if (cmg != NULL) {
24716842Sth160488 				cmg->procchg_started = B_FALSE;
24726842Sth160488 				(void) release_conn_mgmt(cmg, B_FALSE);
24736842Sth160488 			}
24746842Sth160488 
24756842Sth160488 			conn_tsd_free();
24766842Sth160488 			thr_exit(NULL);
24776842Sth160488 		}
24786842Sth160488 
24796842Sth160488 		(void) memset(space.s_b, 0, DOORBUFFERSIZE);
24806842Sth160488 		(void) memset(&chg, 0, sizeof (chg));
24816842Sth160488 		adata = sizeof (ldap_call_t) + 1;
24826842Sth160488 		ndata = sizeof (space);
24836842Sth160488 		space.s_d.ldap_call.ldap_callnumber = GETSTATUSCHANGE;
24846842Sth160488 		space.s_d.ldap_call.ldap_u.get_change.op =
24856842Sth160488 		    NS_STATUS_CHANGE_OP_START;
24866842Sth160488 		space.s_d.ldap_call.ldap_u.get_change.cookie = cookie;
24876842Sth160488 		sptr = &space.s_d;
24886842Sth160488 		door_rc = __ns_ldap_trydoorcall_getfd();
24896842Sth160488 		cmg->procchg_door_call = B_TRUE;
24906842Sth160488 		if (release_conn_mgmt(cmg, B_FALSE) == NULL) {
24916842Sth160488 			conn_tsd_free();
24926842Sth160488 			thr_exit(NULL);
24936842Sth160488 		}
24946842Sth160488 
24956842Sth160488 		if (door_rc == NS_CACHE_SUCCESS)
24966842Sth160488 			door_rc = __ns_ldap_trydoorcall_send(&sptr, &ndata,
24976842Sth160488 			    &adata);
24986842Sth160488 
24996842Sth160488 		/*
25006842Sth160488 		 * Check and see if the conn_mgmt is still current.
25016842Sth160488 		 * If not, no need to continue.
25026842Sth160488 		 */
25036842Sth160488 		cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
25046842Sth160488 		if (cmg != NULL)
25056842Sth160488 			cmg->procchg_door_call = B_FALSE;
25066842Sth160488 		if (cmg != ocmg) {
25076842Sth160488 			if (cmg != NULL) {
25086842Sth160488 				cmg->procchg_started = B_FALSE;
25096842Sth160488 				(void) release_conn_mgmt(cmg, B_FALSE);
25106842Sth160488 			}
25116842Sth160488 			conn_tsd_free();
25126842Sth160488 			thr_exit(NULL);
25136842Sth160488 		}
25146842Sth160488 
25156842Sth160488 		if (door_rc != NS_CACHE_SUCCESS) {
25166842Sth160488 			if (door_rc == NS_CACHE_NOSERVER) {
25176842Sth160488 				if (retry++ > 10)
25186842Sth160488 					getchg_not_supported = B_TRUE;
25196842Sth160488 				else {
25206842Sth160488 					/*
25216842Sth160488 					 * ldap_cachemgr may be down, give
25226842Sth160488 					 * it time to restart
25236842Sth160488 					 */
25246842Sth160488 					(void) sleep(2);
25256842Sth160488 				}
25266842Sth160488 			} else if (door_rc == NS_CACHE_NOTFOUND)
25276842Sth160488 				getchg_not_supported = B_TRUE;
25286842Sth160488 			continue;
25296842Sth160488 		} else
25306842Sth160488 			retry = 0;
25316842Sth160488 
25326842Sth160488 		/* copy info from door call return structure */
25336842Sth160488 		get_chg =  &sptr->ldap_ret.ldap_u.changes;
25346842Sth160488 		ptr = get_chg->data;
25356842Sth160488 		/* configuration change ? */
25366842Sth160488 		if (get_chg->type == NS_STATUS_CHANGE_TYPE_CONFIG) {
25376842Sth160488 			chg.config_changed = B_TRUE;
25386842Sth160488 			cmg = proc_server_change(&chg, cmg);
25396842Sth160488 			continue;
25406842Sth160488 		}
25416842Sth160488 
25426842Sth160488 		/* server status changes ? */
25436842Sth160488 		if (get_chg->type == NS_STATUS_CHANGE_TYPE_SERVER) {
25446842Sth160488 			/*
25456842Sth160488 			 * first check cookies, if don't match, config
25466842Sth160488 			 * has changed
25476842Sth160488 			 */
25486842Sth160488 			new_cookie = get_chg->cookie;
25496842Sth160488 			if (new_cookie.mgr_pid != cookie.mgr_pid ||
25506842Sth160488 			    new_cookie.seq_num != cookie.seq_num) {
25516842Sth160488 				chg.config_changed = B_TRUE;
25526842Sth160488 				cmg = proc_server_change(&chg, cmg);
25536842Sth160488 				continue;
25546842Sth160488 			}
25556842Sth160488 
25566842Sth160488 			(void) strlcpy(chg_data, ptr, sizeof (chg_data));
25576842Sth160488 			chg.num_server = get_chg->server_count;
25586842Sth160488 
25596842Sth160488 			servers = (char **)calloc(chg.num_server,
25606842Sth160488 			    sizeof (char *));
25616842Sth160488 			if (servers == NULL) {
25626842Sth160488 				syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
25636842Sth160488 				continue;
25646842Sth160488 			}
25656842Sth160488 			status = (ns_server_status_t *)calloc(chg.num_server,
25666842Sth160488 			    sizeof (int));
25676842Sth160488 			if (status == NULL) {
25686842Sth160488 				syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
25696842Sth160488 				free(servers);
25706842Sth160488 				continue;
25716842Sth160488 			}
25726842Sth160488 			ds_cnt = 0;
25736842Sth160488 			which = 0;
25746842Sth160488 			oc = ptr;
25756842Sth160488 			for (c = ptr; which != 2; c++) {
25766842Sth160488 				/* look for DOORLINESEP or end of string */
25776842Sth160488 				if (*c != dsep && *c != '\0')
25786842Sth160488 					continue;
25796842Sth160488 				if (*c == dsep) { /* DOORLINESEP */
25806842Sth160488 					*c = '\0'; /* current value */
25816842Sth160488 					c += dslen; /* skip to next value */
25826842Sth160488 				}
25836842Sth160488 				if (which == 0) { /* get server info */
25846842Sth160488 					servers[ds_cnt] = oc;
25856842Sth160488 					oc = c;
25866842Sth160488 					which = 1; /* get status next */
25876842Sth160488 					continue;
25886842Sth160488 				}
25896842Sth160488 				/* which == 1, get up/down status */
25906842Sth160488 				if (strcmp(NS_SERVER_CHANGE_UP, oc) == 0) {
25916842Sth160488 					status[ds_cnt] = NS_SERVER_UP;
25926842Sth160488 				} else if (strcmp(NS_SERVER_CHANGE_DOWN,
25936842Sth160488 				    oc) == 0)
25946842Sth160488 					status[ds_cnt] = NS_SERVER_DOWN;
25956842Sth160488 				else {
25966842Sth160488 					syslog(LOG_INFO,
25976842Sth160488 					    NS_CONN_MSG_BAD_CACHEMGR_DATA);
25986842Sth160488 					continue;
25996842Sth160488 				}
26006842Sth160488 				oc = c;
26016842Sth160488 				ds_cnt++;
26026842Sth160488 				if (*c == '\0')
26036842Sth160488 					which = 2; /* exit the loop */
26046842Sth160488 				else
26056842Sth160488 					which = 0; /* get server info next */
26066842Sth160488 			}
26076842Sth160488 			chg.servers = servers;
26086842Sth160488 			chg.changes = status;
26096842Sth160488 			cmg = proc_server_change(&chg, cmg);
26106842Sth160488 			continue;
26116842Sth160488 		}
26126842Sth160488 	}
26136842Sth160488 
26146842Sth160488 	return (NULL);
26156842Sth160488 }
26166842Sth160488 
26176842Sth160488 /* start the thread handling the change notification from ldap_cachemgr */
26186842Sth160488 static void
start_thread(ns_conn_mgmt_t * cmg)26196842Sth160488 start_thread(ns_conn_mgmt_t *cmg) {
26206842Sth160488 
26216842Sth160488 	int		errnum;
26226842Sth160488 
26236842Sth160488 	/*
26246842Sth160488 	 * start a thread to get and process config and server status changes
26256842Sth160488 	 */
26266842Sth160488 	if (thr_create(NULL, NULL, get_server_change,
26276842Sth160488 	    (void *)cmg, THR_DETACHED, NULL) != 0) {
26286842Sth160488 		errnum = errno;
26296842Sth160488 		syslog(LOG_WARNING, NS_CONN_MSG_NO_PROCCHG_THREAD,
26306842Sth160488 		    strerror(errnum));
26316842Sth160488 	}
26326842Sth160488 }
2633