ns_connmgmt.c revision 7281:15ede204db00
121308Sache/*
221308Sache * CDDL HEADER START
321308Sache *
421308Sache * The contents of this file are subject to the terms of the
521308Sache * Common Development and Distribution License (the "License").
621308Sache * You may not use this file except in compliance with the License.
721308Sache *
821308Sache * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
921308Sache * or http://www.opensolaris.org/os/licensing.
1021308Sache * See the License for the specific language governing permissions
1121308Sache * and limitations under the License.
1221308Sache *
1321308Sache * When distributing Covered Code, include this CDDL HEADER in each
1421308Sache * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1521308Sache * If applicable, add the following below this CDDL HEADER, with the
1621308Sache * fields enclosed by brackets "[]" replaced with your own identifying
1721308Sache * information: Portions Copyright [yyyy] [name of copyright owner]
1821308Sache *
1921308Sache * CDDL HEADER END
2021308Sache */
2121308Sache/*
2221308Sache * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2321308Sache * Use is subject to license terms.
2421308Sache */
2521308Sache
2621308Sache#pragma ident	"%Z%%M%	%I%	%E% SMI"
2721308Sache
2821308Sache#include <string.h>
2921308Sache#include <errno.h>
3021308Sache#include <syslog.h>
3121308Sache#include <procfs.h>
3221308Sache#include <unistd.h>
3321308Sache#include <fcntl.h>
3421308Sache#include <libintl.h>
3521308Sache#include <atomic.h>
3621308Sache#include <pthread.h>
3721308Sache#include <sys/mman.h>
3821308Sache#include <time.h>
3921308Sache#include "solaris-int.h"
4021308Sache#include "ns_connmgmt.h"
4121308Sache#include "ns_cache_door.h"
4221308Sache#include "ns_internal.h"
4321308Sache
4421308Sache/*
4521308Sache * Access (reference, shutdown, or reload) the current connection
4621308Sache * management control structure conn_mgmt_t.
4721308Sache */
4821308Sache#define	NS_CONN_MGMT_OP_REF		1
4921308Sache#define	NS_CONN_MGMT_OP_SHUTDOWN	2
5021308Sache#define	NS_CONN_MGMT_OP_RELOAD_CONFIG	3
5121308Sache#define	NS_CONN_MGMT_OP_NEW_CONFIG	4
5221308Sache#define	NS_CONN_MGMT_OP_LIB_INIT	5
5321308Sache
5421308Sachestatic ns_conn_mgmt_t *access_conn_mgmt(int);
5521308Sachestatic ns_conn_mgmt_t *release_conn_mgmt(ns_conn_mgmt_t *, boolean_t);
5621308Sachestatic int close_conn_mt(ns_conn_mt_t *, int, ns_ldap_error_t **,
5721308Sache	ns_conn_user_t *);
5821308Sachestatic int close_conn_mt_when_nouser(ns_conn_mt_t *cm);
5921308Sachevoid shutdown_all_conn_mt(ns_conn_mgmt_t *cmg);
6021308Sachestatic int conn_signal(ns_conn_mt_t *);
6121308Sachestatic int conn_wait(ns_conn_mt_t *, ns_conn_user_t *);
6221308Sachestatic void close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg);
6321308Sachestatic ns_conn_mgmt_t *proc_server_change(ns_server_status_change_t *chg,
6421308Sache	ns_conn_mgmt_t  *cmg);
6521308Sachestatic void get_preferred_servers(boolean_t, boolean_t, ns_conn_mgmt_t *);
6621308Sachestatic void start_thread();
6721308Sache
6821308Sachestatic ns_conn_mgmt_t	*ns_connmgmt = NULL;
6921308Sachestatic ns_conn_mgmt_t	*ns_connmgmt_parent = NULL;
7021308Sachestatic mutex_t		ns_connmgmt_lock = DEFAULTMUTEX;
7121308Sachestatic boolean_t	ns_connmgmt_shutting_down = B_FALSE;
7221308Sache
7321308Sache#define	NS_CONN_MSG_NO_CONN_MGMT gettext( \
7421308Sache	"libsldap: unable to allocate the connection management control")
7521308Sache#define	NS_CONN_MSG_NO_MTC_KEY gettext( \
7621308Sache	"libsldap: unable to allocate the TSD key for per-thread ldap error")
7721308Sache#define	NS_CONN_MSG_NO_CMG_KEY gettext( \
7821308Sache	"libsldap: unable to allocate the TSD key for connection management")
7921308Sache#define	NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded")
8021308Sache#define	NS_CONN_MSG_RELOADED gettext( \
8121308Sache	"libsldap: configuration has been reloaded")
8221308Sache#define	NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \
8321308Sache	"libsldap: library unloaded or configuration has been reloaded")
8421308Sache#define	NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \
8521308Sache	"libsldap: received incorrect data from ldap_cachemgr")
8621308Sache#define	NS_CONN_MSG_MEMORY_ERROR gettext( \
8721308Sache	"libsldap: unable to allocate memory")
8821308Sache#define	NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \
8921308Sache	"libsldap: unable to start the server monitor thread (%s)")
9021308Sache#define	NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \
9121308Sache	"libsldap: server down reported by ldap_cachemgr")
9221308Sache
9321308Sachestatic int ns_conn_free = 1;
9421308Sache#define	NS_CONN_UNLOCK_AND_FREE(free, cm, cmg)  \
9521308Sache{ \
9621308Sache	(void) mutex_unlock(&(cm)->lock);	\
9721308Sache	if (free == 1)	\
9821308Sache		cmg = free_conn_mt((cm), 1); \
9921308Sache	if (cmg != NULL) \
10021308Sache		(void) mutex_unlock(&(cmg)->lock); \
10121308Sache}
10221308Sache
10321308Sache#define	NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \
10421308Sache{ \
10521308Sache	char *msg = NULL; \
10621308Sache	(void) mutex_lock(&(cmg)->lock); \
10721308Sache	if ((cmg)->shutting_down == B_TRUE) \
10821308Sache		msg = NS_CONN_MSG_SHUTDOWN; \
10921308Sache	else if ((cmg)->cfg_reloaded == B_TRUE)  \
11021308Sache		msg = NS_CONN_MSG_RELOADED; \
11121308Sache	if (msg != NULL) { \
11221308Sache		(*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \
11321308Sache		(void) mutex_unlock(&(cmg)->lock); \
11421308Sache		return (NS_LDAP_OP_FAILED); \
11521308Sache	} \
11621308Sache}
11721308Sache
11821308Sache/*
11921308Sache * TSD keys ns_mtckey and ns_cmgkey are for sharing ldap connections
12021308Sache * and their associated connection management structure among
12121308Sache * multiple threads. The pointers to the per-thread ldap error
12221308Sache * information and the connection management structure are
12321308Sache * saved in ns_mtckey and ns_cmgkey.
12421308Sache */
12521308Sachethread_key_t ns_mtckey = THR_ONCE_KEY;
12621308Sachethread_key_t ns_cmgkey = THR_ONCE_KEY;
12721308Sache
12821308Sache/* Per thread LDAP error resides in thread-specific data (ns_mtckey) */
12921308Sachestruct ldap_error {
13021308Sache	int	le_errno;
13121308Sache	char	*le_matched;
13221308Sache	char	*le_errmsg;
13321308Sache};
13421308Sache
13521308Sache/* NULL struct ldap_error */
13621308Sachestatic struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
13721308Sache
13821308Sache/* destructor: free the ldap error data in the thread specific area */
13921308Sachestatic void
14021308Sachens_mtckey_cleanup(void *key) {
14121308Sache	struct ldap_error *le = (struct ldap_error *)key;
14221308Sache
14321308Sache	if (le == NULL)
14421308Sache		return;
14521308Sache	if (le->le_matched != NULL) {
14621308Sache		ldap_memfree(le->le_matched);
14721308Sache	}
14821308Sache	if (le->le_errmsg != NULL) {
14921308Sache		ldap_memfree(le->le_errmsg);
15021308Sache	}
15121308Sache	free(le);
15221308Sache}
15321308Sache
15421308Sache/* Free/detach the thread specific data structures */
15521308Sachestatic void
15621308Sacheconn_tsd_free() {
15721308Sache	void	*tsd = NULL;
15821308Sache	int	rc;
15921308Sache
16021308Sache	/* free the per-thread ldap error info */
16121308Sache	rc = thr_getspecific(ns_mtckey, &tsd);
16221308Sache	if (rc == 0 && tsd != NULL)
16321308Sache		ns_mtckey_cleanup(tsd);
16421308Sache	(void) thr_setspecific(ns_mtckey, NULL);
16521308Sache
16621308Sache	/* detach the connection management control */
16721308Sache	(void) thr_setspecific(ns_cmgkey, NULL);
16821308Sache}
16921308Sache
17021308Sache/* per-thread callback function for allocating a mutex */
17121308Sachestatic void *
17221308Sachens_mutex_alloc(void)
17321308Sache{
17421308Sache	mutex_t *mutexp = NULL;
17521308Sache
17621308Sache	if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
17721308Sache		if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
17821308Sache			free(mutexp);
17921308Sache			mutexp = NULL;
18021308Sache		}
18121308Sache	}
18221308Sache	return (mutexp);
18321308Sache}
18421308Sache
18521308Sache/* per-thread callback function for freeing a mutex */
18621308Sachestatic void
18721308Sachens_mutex_free(void *mutexp)
18821308Sache{
18921308Sache	(void) mutex_destroy((mutex_t *)mutexp);
19021308Sache	free(mutexp);
19121308Sache}
19221308Sache
19321308Sache/*
19421308Sache * Function for setting up thread-specific data
19521308Sache * where per thread LDAP error and the pointer
19621308Sache * to the active connection management control
19721308Sache * are stored.
19821308Sache */
19921308Sachestatic int
20021308Sacheconn_tsd_setup(ns_conn_mgmt_t *cmg)
20121308Sache{
20221308Sache	void	*tsd;
20321308Sache	int	rc;
20421308Sache
20521308Sache	rc = thr_setspecific(ns_cmgkey, cmg);
20621308Sache	if (rc != 0) /* must be ENOMEM */
20721308Sache		return (-1);
20821308Sache
20921308Sache	/* return success if the ns_mtckey TSD is already set */
21021308Sache	rc = thr_getspecific(ns_mtckey, &tsd);
21121308Sache	if (rc == 0 && tsd != NULL)
21221308Sache		return (0);
21321308Sache
21421308Sache	/* allocate and set the ns_mtckey TSD */
21521308Sache	tsd = (void *) calloc(1, sizeof (struct ldap_error));
21621308Sache	if (tsd == NULL)
21721308Sache		return (-1);
21821308Sache	rc = thr_setspecific(ns_mtckey, tsd);
21921308Sache	if (rc != 0) { /* must be ENOMEM */
22021308Sache		free(tsd);
22121308Sache		return (-1);
22221308Sache	}
22321308Sache	return (0);
22421308Sache}
22521308Sache
22621308Sache/* Callback function for setting the per thread LDAP error */
22721308Sache/*ARGSUSED*/
22821308Sachestatic void
22921308Sacheset_ld_error(int err, char *matched, char *errmsg, void *dummy)
23021308Sache{
23121308Sache	struct ldap_error	*le;
23221308Sache	int			eno;
23321308Sache
23421308Sache	if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
23521308Sache		syslog(LOG_ERR, gettext(
23621308Sache		    "libsldap: set_ld_error: thr_getspecific failed (%s)."),
23721308Sache		    strerror(eno));
23821308Sache		return;
23921308Sache	}
24021308Sache
24121308Sache	/* play safe, do nothing if TSD pointer is NULL */
24221308Sache	if (le == NULL) {
24321308Sache		syslog(LOG_INFO, gettext(
24421308Sache		    "libsldap: set_ld_error: TSD pointer is NULL."));
24521308Sache		return;
24621308Sache	}
24721308Sache
24821308Sache	le->le_errno = err;
24921308Sache
25021308Sache	if (le->le_matched != NULL) {
25121308Sache		ldap_memfree(le->le_matched);
25221308Sache		le->le_matched = NULL;
25321308Sache	}
25421308Sache	le->le_matched = matched;
25521308Sache
25621308Sache	if (le->le_errmsg != NULL) {
25721308Sache		ldap_memfree(le->le_errmsg);
25821308Sache		le->le_errmsg = NULL;
25921308Sache	}
26021308Sache	le->le_errmsg = errmsg;
26121308Sache}
26221308Sache
26321308Sache/* check and allocate the thread-specific data for using a MT connection */
26421308Sachestatic int
26521308Sacheconn_tsd_check(ns_conn_mgmt_t *cmg)
26621308Sache{
26721308Sache	if (conn_tsd_setup(cmg) != 0)
26821308Sache		return (NS_LDAP_MEMORY);
26921308Sache
27021308Sache	return (NS_LDAP_SUCCESS);
27121308Sache}
27221308Sache
27321308Sache/* Callback function for getting the per thread LDAP error */
27421308Sache/*ARGSUSED*/
27521308Sachestatic int
27621308Sacheget_ld_error(char **matched, char **errmsg, void *dummy)
27721308Sache{
27821308Sache	struct ldap_error	*le;
27921308Sache	int			eno;
28021308Sache
28121308Sache	if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
28221308Sache		syslog(LOG_ERR, gettext(
28321308Sache		    "libsldap: get_ld_error: thr_getspecific failed (%s)"),
28421308Sache		    strerror(eno));
28521308Sache		return (eno);
28621308Sache	}
28721308Sache
28821308Sache	/* play safe, return NULL error data, if TSD pointer is NULL */
28921308Sache	if (le == NULL)
29021308Sache		le = &ldap_error_NULL;
29121308Sache
29221308Sache	if (matched != NULL) {
29321308Sache		*matched = le->le_matched;
29421308Sache	}
29521308Sache	if (errmsg != NULL) {
29621308Sache		*errmsg = le->le_errmsg;
29721308Sache	}
29821308Sache	return (le->le_errno);
29921308Sache}
30021308Sache
30121308Sache/* Callback function for setting per thread errno */
30221308Sachestatic void
30321308Sacheset_errno(int err)
30421308Sache{
30521308Sache	errno = err;
30621308Sache}
30721308Sache
30821308Sache/* Callback function for getting per thread errno */
30921308Sachestatic int
31021308Sacheget_errno(void)
31121308Sache{
31221308Sache	return (errno);
31321308Sache}
31421308Sache
31521308Sache/* set up an ldap session 'ld' for sharing among multiple threads */
31621308Sachestatic int
31721308Sachesetup_mt_conn(LDAP *ld)
31821308Sache{
31921308Sache
32021308Sache	struct ldap_thread_fns		tfns;
32121308Sache	struct ldap_extra_thread_fns	extrafns;
32221308Sache	int				rc;
32321308Sache
32421308Sache	/*
32521308Sache	 * Set the function pointers for dealing with mutexes
32621308Sache	 * and error information
32721308Sache	 */
32821308Sache	(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
32921308Sache	tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
33021308Sache	tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
33121308Sache	tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
33221308Sache	tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
33321308Sache	tfns.ltf_get_errno = get_errno;
33421308Sache	tfns.ltf_set_errno = set_errno;
33521308Sache	tfns.ltf_get_lderrno = get_ld_error;
33621308Sache	tfns.ltf_set_lderrno = set_ld_error;
33721308Sache	tfns.ltf_lderrno_arg = NULL;
33821308Sache
33921308Sache	/*
34021308Sache	 * Set up the ld to use those function pointers
34121308Sache	 */
34221308Sache	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
34321308Sache	    (void *) &tfns);
34421308Sache	if (rc < 0) {
34521308Sache		syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
34621308Sache		"(LDAP_OPT_THREAD_FN_PTRS)"));
34721308Sache		return (0);
34821308Sache	}
34921308Sache
35021308Sache	/*
35121308Sache	 * Set the function pointers for working with semaphores
35221308Sache	 */
35321308Sache	(void) memset(&extrafns, '\0',
35421308Sache	    sizeof (struct ldap_extra_thread_fns));
35521308Sache	extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
35621308Sache	extrafns.ltf_mutex_trylock = NULL;
35721308Sache	extrafns.ltf_sema_alloc = NULL;
35821308Sache	extrafns.ltf_sema_free = NULL;
35921308Sache	extrafns.ltf_sema_wait = NULL;
36021308Sache	extrafns.ltf_sema_post = NULL;
36121308Sache
36221308Sache	/* Set up the ld to use those function pointers */
36321308Sache	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
36421308Sache	    (void *) &extrafns);
36521308Sache	if (rc < 0) {
36621308Sache		syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
36721308Sache		"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"));
36821308Sache		return (0);
36921308Sache	}
37021308Sache
37121308Sache	return (1);
37221308Sache}
37321308Sache
37421308Sache/* set up an MT connection for sharing among multiple threads */
37521308Sachestatic int
37621308Sachesetup_mt_ld(LDAP *ld, ns_conn_mgmt_t *cmg)
37721308Sache{
37821308Sache	thread_t	t = thr_self();
37921308Sache
38021308Sache	/* set up the per-thread data for using the MT connection */
38121308Sache	if (conn_tsd_setup(cmg) == -1) {
38221308Sache		syslog(LOG_WARNING,
38321308Sache		    gettext("libsldap: tid= %d: unable to set up TSD\n"), t);
38421308Sache		return (-1);
38521308Sache	}
38621308Sache
38721308Sache	if (setup_mt_conn(ld) == 0) {
38821308Sache		/* multiple threads per connection not supported */
38921308Sache		syslog(LOG_WARNING, gettext("libsldap: tid= %d: multiple "
39021308Sache		    "threads per connection not supported\n"), t);
39121308Sache		conn_tsd_free();
39221308Sache		return (-1);
39321308Sache	}
39421308Sache	return (0);
39521308Sache}
39621308Sache
39721308Sache/*
39821308Sache * Check name and UID of process, if it is nscd.
39921308Sache *
40021308Sache * Input:
40121308Sache *   pid	: PID of checked process
40221308Sache *   check_uid	: check if UID == 0
40321308Sache * Output:
40421308Sache *   B_TRUE	: nscd detected
40521308Sache *   B_FALSE	: nscd not confirmed
40621308Sache */
40721308Sachestatic boolean_t
40821308Sachecheck_nscd_proc(pid_t pid, boolean_t check_uid)
40921308Sache{
41021308Sache	psinfo_t	pinfo;
41121308Sache	char		fname[MAXPATHLEN];
41221308Sache	ssize_t		ret;
41321308Sache	int		fd;
41421308Sache
41521308Sache	if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
41621308Sache		if ((fd = open(fname,  O_RDONLY)) >= 0) {
41721308Sache			ret = read(fd, &pinfo, sizeof (psinfo_t));
41821308Sache			(void) close(fd);
41921308Sache			if ((ret == sizeof (psinfo_t)) &&
42021308Sache			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
42121308Sache				if (check_uid && (pinfo.pr_uid != 0))
42221308Sache					return (B_FALSE);
42321308Sache				return (B_TRUE);
42421308Sache			}
42521308Sache		}
42621308Sache	}
42721308Sache	return (B_FALSE);
42821308Sache}
42921308Sache
43021308Sache/*
43121308Sache * Check if this process is peruser nscd.
43221308Sache */
43321308Sacheboolean_t
43421308Sache__s_api_peruser_proc(void)
43521308Sache{
43621308Sache	pid_t		my_ppid;
43721308Sache	static mutex_t	nscdLock = DEFAULTMUTEX;
43821308Sache	static pid_t	checkedPpid = (pid_t)-1;
43921308Sache	static boolean_t isPeruserNscd = B_FALSE;
44021308Sache
44121308Sache	my_ppid = getppid();
44221308Sache
44321308Sache	/*
44421308Sache	 * Already checked before for this process? If yes, return cached
44521308Sache	 * response.
44621308Sache	 */
44721308Sache	if (my_ppid == checkedPpid) {
44821308Sache		return (isPeruserNscd);
44921308Sache	}
45021308Sache
45121308Sache	(void) mutex_lock(&nscdLock);
45221308Sache
45321308Sache	/* Check once more incase another thread has just complete this. */
45421308Sache	if (my_ppid == checkedPpid) {
45521308Sache		(void) mutex_unlock(&nscdLock);
45621308Sache		return (isPeruserNscd);
45721308Sache	}
45821308Sache
45921308Sache	/* Reinitialize to be sure there is no residue after fork. */
46021308Sache	isPeruserNscd = B_FALSE;
46121308Sache
46221308Sache	/* Am I the nscd process? */
46321308Sache	if (check_nscd_proc(getpid(), B_FALSE)) {
46421308Sache		/* Is my parent the nscd process with UID == 0. */
46521308Sache		isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
46621308Sache	}
46721308Sache
46821308Sache	/* Remember for whom isPeruserNscd is. */
46921308Sache	checkedPpid = my_ppid;
47021308Sache
47121308Sache	(void) mutex_unlock(&nscdLock);
47221308Sache	return (isPeruserNscd);
47321308Sache}
47421308Sache
47521308Sache/*
47621308Sache * Check if this process is main nscd.
47721308Sache */
47821308Sacheboolean_t
47921308Sache__s_api_nscd_proc(void)
48021308Sache{
48121308Sache	pid_t		my_pid;
48221308Sache	static mutex_t	nscdLock = DEFAULTMUTEX;
48321308Sache	static pid_t	checkedPid = (pid_t)-1;
48421308Sache	static boolean_t isMainNscd = B_FALSE;
48521308Sache
48621308Sache	/*
48721308Sache	 * Don't bother checking if this process isn't root, this cannot
48821308Sache	 * be main nscd.
48921308Sache	 */
49021308Sache	if (getuid() != 0)
49121308Sache		return (B_FALSE);
49221308Sache
49321308Sache	my_pid = getpid();
49421308Sache
49521308Sache	/*
49621308Sache	 * Already checked before for this process? If yes, return cached
49721308Sache	 * response.
49821308Sache	 */
49921308Sache	if (my_pid == checkedPid) {
50021308Sache		return (isMainNscd);
50121308Sache	}
50221308Sache
50321308Sache	(void) mutex_lock(&nscdLock);
50421308Sache
50521308Sache	/* Check once more incase another thread has just done this. */
50621308Sache	if (my_pid == checkedPid) {
50721308Sache		(void) mutex_unlock(&nscdLock);
50821308Sache		return (isMainNscd);
50921308Sache	}
51021308Sache
51121308Sache	/*
51221308Sache	 * Am I the nscd process? UID is already checked, not needed from
51321308Sache	 * psinfo.
51421308Sache	 */
51521308Sache	isMainNscd = check_nscd_proc(my_pid, B_FALSE);
51621308Sache
51721308Sache	/* Remember for whom isMainNscd is. */
51821308Sache	checkedPid = my_pid;
51921308Sache
52021308Sache	(void) mutex_unlock(&nscdLock);
52121308Sache	return (isMainNscd);
52221308Sache}
52321308Sache
52421308Sache/*
52521308Sache * initialize a connection management control structure conn_mgmt_t
52621308Sache */
52721308Sachens_conn_mgmt_t *
52821308Sacheinit_conn_mgmt()
52921308Sache{
53021308Sache	ns_conn_mgmt_t	*cmg;
53121308Sache
53221308Sache	cmg = (ns_conn_mgmt_t *)calloc(1, sizeof (*cmg));
53321308Sache	if (cmg == NULL) {
53421308Sache		syslog(LOG_ERR, NS_CONN_MSG_NO_CONN_MGMT);
53521308Sache		return (NULL);
53621308Sache	}
53721308Sache
53821308Sache	/* is this process nscd or peruser nscd ? */
53921308Sache	cmg->is_nscd = __s_api_nscd_proc();
54021308Sache	cmg->is_peruser_nscd = __s_api_peruser_proc();
54121308Sache
54221308Sache	/*
54321308Sache	 * assume the underlying libldap allows multiple threads sharing
54421308Sache	 * the same ldap connection (MT connection)
54521308Sache	 */
54621308Sache	cmg->ldap_mt = B_TRUE;
54721308Sache	/* state is inactive until MT connection is required/requested */
548	cmg->state = NS_CONN_MGMT_INACTIVE;
549
550	(void) mutex_init(&cmg->lock, USYNC_THREAD, NULL);
551	(void) mutex_init(&cmg->cfg_lock, USYNC_THREAD, NULL);
552	cmg->pid = getpid();
553
554	/* for nscd or peruser nscd, MT connection is required */
555	if (cmg->is_nscd == B_TRUE || cmg->is_peruser_nscd == B_TRUE)
556		cmg->state = NS_CONN_MGMT_ACTIVE;
557
558	/*
559	 * reference (or initialize) the current Native LDAP configuration and
560	 * if in nscd process, make it never refreshed
561	 */
562	cmg->config = __s_api_get_default_config_global();
563	if (cmg->config == NULL)
564		cmg->config = __s_api_loadrefresh_config_global();
565	if (cmg->config != NULL) {
566		/*
567		 * main nscd get config change notice from ldap_cachemgr
568		 * so won't times out and refresh the config
569		 */
570		if (cmg->is_nscd  == B_TRUE)
571			(cmg->config)->paramList[NS_LDAP_EXP_P].ns_tm = 0;
572		cmg->cfg_cookie = cmg->config->config_cookie;
573	}
574
575	return (cmg);
576}
577
578static void
579mark_shutdown_or_reloaded(int op)
580{
581	ns_conn_mgmt_t	*cmg = ns_connmgmt;
582
583	(void) mutex_lock(&cmg->lock);
584	if (op == NS_CONN_MGMT_OP_SHUTDOWN)
585		cmg->shutting_down = B_TRUE;
586	else
587		cmg->cfg_reloaded = B_TRUE;
588	atomic_inc_uint(&cmg->ref_cnt);
589	cmg->state = NS_CONN_MGMT_DETACHED;
590
591	if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG)
592		__s_api_init_config_global(NULL);
593
594	(void) mutex_unlock(&cmg->lock);
595}
596
597/*
598 * Return a pointer to the current connection management. If
599 * it has not been created, or is requested to recreate, then
600 * create and return the pointer. It is possible, the current
601 * one is created by the parent before fork, create a new
602 * one too in such a case.
603 */
604static ns_conn_mgmt_t *
605get_current_conn_mgmt(int op)
606{
607	ns_conn_mgmt_t	*cmg = ns_connmgmt;
608	static pid_t	checked_pid = (pid_t)-1;
609	pid_t		mypid;
610
611	mypid = getpid();
612	if (cmg == NULL || checked_pid != mypid) {
613		checked_pid = mypid;
614
615		/*
616		 * if current conn_mgmt not created yet or is from parent
617		 * or is requested to recreate, create it
618		 */
619		if (cmg == NULL || cmg->pid != mypid) {
620			if (cmg != NULL) {
621				/*
622				 * We don't want to free the conn_mgmt
623				 * allocated by the parent, since
624				 * there may be ldap connections
625				 * still being used. So leave it
626				 * alone but keep it referenced,
627				 * so that it will not be flagged
628				 * as a piece of leaked memory.
629				 */
630				ns_connmgmt_parent = cmg;
631				/*
632				 * avoid lint warning; does not
633				 * change the conn_mgmt in parent
634				 */
635				ns_connmgmt_parent->state =
636				    NS_CONN_MGMT_DETACHED;
637			}
638			ns_connmgmt = init_conn_mgmt();
639			cmg = ns_connmgmt;
640			/*
641			 * ensure it will not be destroyed until explicitly
642			 * shut down or reloaded
643			 */
644			if (op == NS_CONN_MGMT_OP_REF)
645				atomic_inc_uint(&cmg->ref_cnt);
646		}
647	}
648
649	return (cmg);
650}
651
652static ns_conn_mgmt_t *
653access_conn_mgmt(int op)
654{
655	ns_conn_mgmt_t	*cmg = NULL;
656	ns_conn_mgmt_t	*cmg_prev;
657
658	(void) mutex_lock(&ns_connmgmt_lock);
659
660	/*
661	 * connection management is not available when the libsldap is being
662	 * unloaded or shut down
663	 */
664	if (ns_connmgmt_shutting_down == B_TRUE) {
665		(void) mutex_unlock(&ns_connmgmt_lock);
666		return (NULL);
667	}
668
669	if (op == NS_CONN_MGMT_OP_SHUTDOWN) {
670		ns_connmgmt_shutting_down = B_TRUE;
671		if (ns_connmgmt != NULL) {
672			cmg = ns_connmgmt;
673			mark_shutdown_or_reloaded(op);
674			ns_connmgmt = NULL;
675		}
676		(void) mutex_unlock(&ns_connmgmt_lock);
677		return (cmg);
678	}
679
680	if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
681	    op == NS_CONN_MGMT_OP_NEW_CONFIG) {
682		cmg_prev = ns_connmgmt;
683		mark_shutdown_or_reloaded(op);
684		/*
685		 * the previous cmg (cmg_prev) will be freed later
686		 * when its ref count reaches zero
687		 */
688		ns_connmgmt = NULL;
689	}
690
691	cmg = get_current_conn_mgmt(op);
692	if (cmg == NULL) {
693		(void) mutex_unlock(&ns_connmgmt_lock);
694		return (NULL);
695	}
696
697	atomic_inc_uint(&cmg->ref_cnt);
698	if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
699	    op == NS_CONN_MGMT_OP_NEW_CONFIG)
700		cmg = cmg_prev;
701	else { /* op is  NS_CONN_MGMT_OP_REF  or NS_CONN_MGMT_OP_LIB_INIT */
702		if (cmg->config == NULL)
703			cmg->config = __s_api_get_default_config();
704	}
705
706	(void) mutex_unlock(&ns_connmgmt_lock);
707	return (cmg);
708}
709
710/*
711 * free a connection management control
712 */
713static void
714free_conn_mgmt(ns_conn_mgmt_t *cmg)
715{
716	union {
717		ldap_data_t	s_d;
718		char		s_b[1024];
719	} space;
720	ldap_data_t	*sptr;
721	int		ndata;
722	int		adata;
723	int		rc;
724	ldap_get_chg_cookie_t cookie;
725
726	if (cmg == NULL)
727		return;
728	cookie = cmg->cfg_cookie;
729
730	__s_api_free2dArray(cmg->pservers);
731	/* destroy the previous config or release the current one */
732	if (cmg->config != NULL) {
733		if (cmg->state == NS_CONN_MGMT_DETACHED)
734			__s_api_destroy_config(cmg->config);
735		else
736			__s_api_release_config(cmg->config);
737	}
738
739	/* stop the server status/config-change monitor thread */
740	if (cmg->procchg_started == B_TRUE) {
741		if (cmg->procchg_tid != thr_self()) {
742			if (cmg->procchg_door_call == B_TRUE) {
743				adata = sizeof (ldap_call_t) + 1;
744				ndata = sizeof (space);
745				space.s_d.ldap_call.ldap_callnumber =
746				    GETSTATUSCHANGE;
747				space.s_d.ldap_call.ldap_u.get_change.op =
748				    NS_STATUS_CHANGE_OP_STOP;
749				space.s_d.ldap_call.ldap_u.get_change.cookie =
750				    cookie;
751				sptr = &space.s_d;
752				rc = __ns_ldap_trydoorcall(&sptr, &ndata,
753				    &adata);
754				if (rc != NS_CACHE_SUCCESS)
755					syslog(LOG_INFO,
756					    gettext("libsldap: "
757					    "free_conn_mgmt():"
758					    " stopping door call "
759					    " GETSTATUSCHANGE failed "
760					    " (rc = %d)"), rc);
761			}
762			(void) pthread_cancel(cmg->procchg_tid);
763			cmg->procchg_started = B_FALSE;
764		}
765	}
766
767	free(cmg);
768}
769
770static ns_conn_mgmt_t *
771release_conn_mgmt(ns_conn_mgmt_t *cmg, boolean_t unlock_cmg)
772{
773	if (cmg == NULL)
774		return (NULL);
775	if (atomic_dec_uint_nv(&cmg->ref_cnt) == 0) {
776		if (cmg->state == NS_CONN_MGMT_DETACHED) {
777			if (unlock_cmg == B_TRUE)
778				(void) mutex_unlock(&cmg->lock);
779			free_conn_mgmt(cmg);
780			__s_api_free_sessionPool();
781			return (NULL);
782		} else {
783			syslog(LOG_WARNING,
784			    gettext("libsldap: connection management "
785			    " has a refcount of zero but the state "
786			    " is not DETACHED (%d)"), cmg->state);
787			cmg = NULL;
788		}
789	}
790	return (cmg);
791}
792
793/*
794 * exposed function for initializing a connection management control structure
795 */
796ns_conn_mgmt_t *
797__s_api_conn_mgmt_init()
798{
799	if (thr_keycreate_once(&ns_mtckey, ns_mtckey_cleanup) != 0) {
800		syslog(LOG_WARNING, NS_CONN_MSG_NO_MTC_KEY);
801		return (NULL);
802	}
803
804	if (thr_keycreate_once(&ns_cmgkey, NULL) != 0) {
805		syslog(LOG_WARNING, NS_CONN_MSG_NO_CMG_KEY);
806		return (NULL);
807	}
808
809	return (access_conn_mgmt(NS_CONN_MGMT_OP_LIB_INIT));
810}
811
812/* initialize a connection user */
813ns_conn_user_t *
814__s_api_conn_user_init(int type, void *userinfo, boolean_t referral)
815{
816	ns_conn_user_t	*cu;
817	ns_conn_mgmt_t	*cmg;
818
819	/* delete the reference to the previously used conn_mgmt */
820	(void) thr_setspecific(ns_cmgkey, NULL);
821
822	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
823	if (cmg == NULL)
824		return (NULL);
825
826	if (cmg->state != NS_CONN_MGMT_ACTIVE &&
827	    cmg->state != NS_CONN_MGMT_INACTIVE) {
828		atomic_dec_uint(&cmg->ref_cnt);
829		return (NULL);
830	}
831
832	cu = (ns_conn_user_t *)calloc(1, sizeof (*cu));
833	if (cu == NULL) {
834		atomic_dec_uint(&cmg->ref_cnt);
835		return (NULL);
836	}
837
838	cu->type = type;
839	cu->state = NS_CONN_USER_ALLOCATED;
840	cu->tid = thr_self();
841	cu->userinfo = userinfo;
842	cu->referral = referral;
843	cu->ns_rc = NS_LDAP_SUCCESS;
844	cu->conn_mgmt = cmg;
845
846	(void) conn_tsd_setup(cmg);
847
848	return (cu);
849}
850
851/*
852 * Free the resources used by a connection user.
853 * The caller should ensure this conn_user is
854 * not associated with any conn_mt, i.e.,
855 * not in any conn_mt's linked list of conn_users.
856 * The caller needs to free the userinfo member
857 * as well.
858 */
859void
860__s_api_conn_user_free(ns_conn_user_t *cu)
861{
862	ns_conn_mgmt_t	*cmg;
863
864	if (cu == NULL)
865		return;
866
867	cu->state = NS_CONN_USER_FREED;
868	if (cu->ns_error != NULL)
869		(void) __ns_ldap_freeError(&cu->ns_error);
870
871	cmg = cu->conn_mgmt;
872	conn_tsd_free();
873	(void) release_conn_mgmt(cmg, B_FALSE);
874	(void) free(cu);
875}
876
877/*
878 * Initialize an MT connection control structure
879 * that will be used to represent an ldap connection
880 * to be shared among multiple threads and to hold
881 * and manage all the conn_users using the ldap
882 * connection.
883 */
884static ns_conn_mt_t *
885init_conn_mt(ns_conn_mgmt_t *cmg, ns_ldap_error_t **ep)
886{
887	ns_conn_mt_t	*cm;
888	ns_conn_mgmt_t	*cmg_a;
889
890	cm = (ns_conn_mt_t *)calloc(1, sizeof (*cm));
891	if (cm == NULL) {
892		if (ep != NULL)
893			*ep = __s_api_make_error(NS_LDAP_MEMORY, NULL);
894		return (NULL);
895	}
896
897	cmg_a = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
898	if (cmg_a != cmg) {
899		if (cmg_a != NULL) {
900			(void) release_conn_mgmt(cmg_a, B_FALSE);
901			if (ep != NULL)
902				*ep = __s_api_make_error(NS_LDAP_OP_FAILED,
903				    NS_CONN_MSG_SHUTDOWN_RELOADED);
904		}
905		return (NULL);
906	}
907
908	(void) mutex_init(&cm->lock, USYNC_THREAD, NULL);
909	cm->state = NS_CONN_MT_CONNECTING;
910	cm->tid = thr_self();
911	cm->pid = getpid();
912	cm->next = NULL;
913	cm->cu_head = NULL;
914	cm->cu_tail = NULL;
915	cm->conn = NULL;
916	cm->conn_mgmt = cmg;
917
918	return (cm);
919}
920
921/*
922 * Free an MT connection control structure, assume conn_mgmt is locked.
923 * 'unlock_cmg' is passed to release_conn_mgmt() to indicate the
924 * cmg needs to be unlocked or not.
925 */
926static ns_conn_mgmt_t *
927free_conn_mt(ns_conn_mt_t *cm, int unlock_cmg)
928{
929	ns_conn_mgmt_t	*cmg;
930
931	if (cm == NULL)
932		return (NULL);
933	if (cm->ns_error != NULL)
934		(void) __ns_ldap_freeError(&cm->ns_error);
935	if (cm->conn != NULL) {
936		if (cm->conn->ld != NULL)
937			(void) ldap_unbind(cm->conn->ld);
938		__s_api_freeConnection(cm->conn);
939	}
940	cmg = cm->conn_mgmt;
941	free(cm);
942	return (release_conn_mgmt(cmg, unlock_cmg));
943}
944
945/* add a connection user to an MT connection */
946static void
947add_cu2cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
948{
949
950	if (cm->cu_head == NULL) {
951		cm->cu_head = cu;
952		cm->cu_tail = cu;
953	} else {
954		cm->cu_tail->next = cu;
955		cm->cu_tail = cu;
956	}
957	cm->cu_cnt++;
958}
959
960/* add an MT connection to the connection management */
961static void
962add_cm2cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
963{
964	/*
965	 * add connection opened for WRITE to top of list
966	 * for garbage collection purpose. This is to
967	 * ensure the connection will be closed after a
968	 * certain amount of time (60 seconds).
969	 */
970	if (cmg->cm_head == NULL) {
971		cmg->cm_head = cm;
972		cmg->cm_tail = cm;
973	} else {
974		if (cm->opened_for == NS_CONN_USER_WRITE) {
975			cm->next = cmg->cm_head;
976			cmg->cm_head = cm;
977		} else {
978			cmg->cm_tail->next = cm;
979			cmg->cm_tail = cm;
980		}
981	}
982	cmg->cm_cnt++;
983}
984
985/* delete a connection user from an MT connection */
986static void
987del_cu4cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
988{
989	ns_conn_user_t *pu, *u;
990
991	if (cu == NULL || cm->cu_head == NULL || cm->cu_cnt == 0)
992		return;
993
994	/* only one conn_user on list */
995	if (cm->cu_head == cm->cu_tail) {
996		if (cu == cm->cu_head) {
997			cm->cu_head = cm->cu_tail = NULL;
998			cm->cu_cnt = 0;
999			cu->next = NULL;
1000		}
1001		return;
1002	}
1003
1004	/* more than one and cu is the first one */
1005	if (cu == cm->cu_head) {
1006		cm->cu_head = cu->next;
1007		cm->cu_cnt--;
1008		cu->next = NULL;
1009		return;
1010	}
1011
1012	pu = cm->cu_head;
1013	for (u = cm->cu_head->next; u; u = u->next) {
1014		if (cu == u)
1015			break;
1016		pu = u;
1017	}
1018	if (pu != cm->cu_tail) {
1019		pu->next = cu->next;
1020		if (pu->next == NULL)
1021			cm->cu_tail = pu;
1022		cm->cu_cnt--;
1023		cu->next = NULL;
1024	} else {
1025		syslog(LOG_INFO, gettext(
1026		    "libsldap: del_cu4cm(): connection user not found"));
1027	}
1028}
1029
1030/* delete an MT connection from the connection management control structure */
1031static void
1032del_cm4cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
1033{
1034	ns_conn_mt_t *pm, *m;
1035
1036	if (cm == NULL || cmg->cm_head == NULL || cmg->cm_cnt == 0)
1037		return;
1038
1039	/* only one conn_mt on list */
1040	if (cmg->cm_head == cmg->cm_tail) {
1041		if (cm == cmg->cm_head) {
1042			cmg->cm_head = cmg->cm_tail = NULL;
1043			cmg->cm_cnt = 0;
1044			cm->next = NULL;
1045		}
1046		return;
1047	}
1048
1049	/* more than one and cm is the first one */
1050	if (cm == cmg->cm_head) {
1051		cmg->cm_head = cm->next;
1052		cmg->cm_cnt--;
1053		cm->next = NULL;
1054		return;
1055	}
1056
1057	pm = cmg->cm_head;
1058	for (m = cmg->cm_head->next; m; m = m->next) {
1059		if (cm == m)
1060			break;
1061		pm = m;
1062	}
1063	if (pm != cmg->cm_tail) {
1064		pm->next = cm->next;
1065		if (pm->next == NULL)
1066			cmg->cm_tail = pm;
1067		cmg->cm_cnt--;
1068		cm->next = NULL;
1069	} else {
1070		syslog(LOG_INFO, gettext(
1071		    "libsldap: del_cm4cmg(): MT connection not found"));
1072	}
1073}
1074
1075/*
1076 * compare to see if the server and credential for authentication match
1077 * those used by an MT connection
1078 */
1079static boolean_t
1080is_server_cred_matched(const char *server, const ns_cred_t *cred,
1081	ns_conn_mt_t *cm)
1082{
1083	Connection	*cp = cm->conn;
1084
1085	/* check server first */
1086	if (server != NULL && *server != 0) {
1087		if (strcasecmp(server, cp->serverAddr) != 0)
1088			return (B_FALSE);
1089	}
1090
1091	if (cred == NULL)
1092		return (B_TRUE);
1093
1094	/* then check cred */
1095	return (__s_api_is_auth_matched(cp->auth, cred));
1096}
1097
1098/*
1099 * Wait until a pending MT connection becomes available.
1100 * Return 1 if so, 0 if error.
1101 *
1102 * Assume the current conn_mgmt and the input conn_mt
1103 * are locked.
1104 */
1105static int
1106wait_for_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t *cm)
1107{
1108
1109	cu->state = NS_CONN_USER_WAITING;
1110	add_cu2cm(cu, cm);
1111	cu->conn_mt = cm;
1112
1113	(void) mutex_unlock(&cm->lock);
1114	/*
1115	 * It could take some time so we don't want to hold
1116	 * cm->conn_mgmt across the wait
1117	 */
1118	(void) mutex_unlock(&(cm->conn_mgmt)->lock);
1119
1120	(void) mutex_lock(&cm->lock);
1121	/* check one more time see if need to wait */
1122	if (cm->state == NS_CONN_MT_CONNECTING) {
1123		(void) conn_wait(cm, cu);
1124
1125		/* cm->lock is locked again at this point */
1126
1127		cu->state = NS_CONN_USER_WOKEUP;
1128	}
1129
1130	if (cm->state == NS_CONN_MT_CONNECTED)
1131		return (1);
1132	else {
1133		del_cu4cm(cu, cm);
1134		cu->conn_mt = NULL;
1135		cu->bad_mt_conn = B_FALSE;
1136		return (0);
1137	}
1138}
1139
1140/*
1141 * Check and see if the input MT connection '*cm' should be closed.
1142 * In two cases, it should be closed. If a preferred server is
1143 * found to be up when ldap_cachemgr is queried and reported back.
1144 * Or when the server being used for the connection is found to
1145 * be down. Return B_FALSE if the connection is not closed (or not marked
1146 * to be closed), otherwise unlock mutex (*cm)->lock and return B_TRUE.
1147 * This function assumes conn_mgmt cmg and conn_mt *cm are locked.
1148 */
1149static boolean_t
1150check_and_close_conn(ns_conn_mgmt_t *cmg, ns_conn_mt_t **cm,
1151	ns_conn_user_t *cu) {
1152
1153	int rc;
1154	int j;
1155	int svridx = -1;
1156	int upidx = -1;
1157	int free_cm;
1158	ns_server_info_t sinfo;
1159	ns_ldap_error_t *errorp = NULL;
1160
1161	/*
1162	 * check only if preferred servers are defined
1163	 */
1164	if (cmg->pservers_loaded == B_FALSE)
1165		get_preferred_servers(B_FALSE, B_FALSE, cmg);
1166	if (cmg->pservers == NULL)
1167		return (B_FALSE);
1168
1169	/*
1170	 * ask ldap_cachemgr for the first available server
1171	 */
1172	rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
1173	    &sinfo, &errorp, NS_CACHE_ADDR_IP);
1174	if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
1175		(void) __ns_ldap_freeError(&errorp);
1176		return (B_FALSE);
1177	}
1178
1179	/*
1180	 * Did ldap_cachemgr return a preferred server ?
1181	 */
1182	for (j = 0; cmg->pservers[j] != NULL; j++) {
1183		if (strcasecmp(sinfo.server, cmg->pservers[j]) != 0)
1184			continue;
1185		upidx = j;
1186		break;
1187	}
1188
1189	/*
1190	 * Is the server being used a preferred one ?
1191	 */
1192	for (j = 0; cmg->pservers[j] != NULL; j++) {
1193		if (strcasecmp(cmg->pservers[j], (*cm)->conn->serverAddr) != 0)
1194			continue;
1195		svridx = j;
1196		break;
1197	}
1198
1199	/*
1200	 * Need to fall back to a down-but-now-up preferred server ?
1201	 * A preferred server falls back to a more preferred one.
1202	 * A regular one falls back to any preferred ones. So if
1203	 * both are preferred ones and same index, or both
1204	 * are not preferred ones, then no need to close the
1205	 * connection.
1206	 */
1207	if ((upidx == -1 && svridx == -1) ||
1208	    (upidx != -1 && svridx != -1 && upidx == svridx)) {
1209		__s_api_free_server_info(&sinfo);
1210		return (B_FALSE);
1211	}
1212
1213	/*
1214	 * otherwise, 4 cases, all may need to close the connection:
1215	 * For case 1 and 2, both servers are preferred ones:
1216	 * 1. ldap_cachemgr returned a better one to use (upidx < svridx)
1217	 * 2. the server being used is down (upidx > svridx)
1218	 * 3. ldap_cachemgr returned a preferred one, but the server
1219	 *    being used is not, so need to fall back to the preferred server
1220	 * 4. ldap_cachemgr returned a non-preferred one, but the server
1221	 *    being used is a preferred one, so it must be down (since
1222	 *    ldap_cachemgr always returns a preferred one when possible).
1223	 * For case 1 & 3, close the READ connection when no user uses it.
1224	 * For 2 and 4, close the connection with error rc, LDAP_SERVER_DOWN.
1225	 */
1226	if (upidx != -1 && (svridx == -1 || upidx < svridx)) { /* case 1 & 3 */
1227		/* fallback does not make sense for WRITE/referred connection */
1228		if ((*cm)->opened_for == NS_CONN_USER_WRITE ||
1229		    (*cm)->referral == B_TRUE) {
1230			__s_api_free_server_info(&sinfo);
1231			return (B_FALSE);
1232		}
1233		free_cm = close_conn_mt_when_nouser(*cm);
1234		if (cmg->shutting_down == B_FALSE)
1235			cu->retry = B_TRUE;
1236	} else {
1237		ns_ldap_error_t *ep;
1238		ep = __s_api_make_error(LDAP_SERVER_DOWN,
1239		    NS_CONN_MSG_DOWN_FROM_CACHEMGR);
1240		/* cu has not been attached to cm yet, use NULL as cu pointer */
1241		free_cm = close_conn_mt(*cm, LDAP_SERVER_DOWN, &ep, NULL);
1242		if (cmg->shutting_down == B_FALSE)
1243			cu->retry = B_TRUE;
1244		(void) __ns_ldap_freeError(&ep);
1245	}
1246
1247	(void) mutex_unlock(&(*cm)->lock);
1248	if (free_cm == 1) {
1249		(void) free_conn_mt(*cm, 0);
1250		*cm = NULL;
1251	}
1252
1253	__s_api_free_server_info(&sinfo);
1254
1255	return (B_TRUE);
1256}
1257
1258/*
1259 * Check to see if a conn_mt matches the connection criteria from
1260 * a conn_user. Return B_TRUE if yes, B_FALSE, otherwise. The input
1261 * conn_mt pointer (*cmt) may be freed and *cmt will be set to NULL
1262 * to indicate so.
1263 * conn_mt *cmt and conn_mgmt cm->conn_mgmt are assumed locked.
1264 * cm->lock is unlocked at exit if rc is B_FALSE.
1265 */
1266static boolean_t
1267match_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t **cmt,
1268	ns_conn_mt_state_t st, const char *server,
1269	const ns_cred_t *cred)
1270{
1271	boolean_t	matched = B_FALSE;
1272	boolean_t	drop_conn;
1273	int		free_cm = 0;
1274	ns_conn_mt_t	*cm = *cmt;
1275	ns_conn_mgmt_t	*cmg = cm->conn_mgmt;
1276
1277	if (cm->state != st || cm->close_when_nouser  == B_TRUE ||
1278	    cm->detached == B_TRUE || cm->pid != getpid() ||
1279	    cm->referral != cu->referral) {
1280		(void) mutex_unlock(&cm->lock);
1281		return (B_FALSE);
1282	}
1283
1284	/*
1285	 * if a conn_mt opened for WRITE is idle
1286	 * long enough, then close it. To improve
1287	 * the performance of applications, such
1288	 * as ldapaddent, a WRITE connection is
1289	 * given a short time to live in the
1290	 * connection pool, expecting the write
1291	 * requests to come in a quick succession.
1292	 * To save resource, the connection will
1293	 * be closed if idle more than 60 seconds.
1294	 */
1295	if (cm->opened_for == NS_CONN_USER_WRITE &&
1296	    cu->type != NS_CONN_USER_WRITE && cm->cu_cnt == 0 &&
1297	    ((time(NULL) - cm->access_time) > 60)) {
1298		/*
1299		 * NS_LDAP_INTERNAL is irrelevant here. There no
1300		 * conn_user to consume the rc
1301		 */
1302		free_cm = close_conn_mt(cm, NS_LDAP_INTERNAL, NULL, NULL);
1303		(void) mutex_unlock(&cm->lock);
1304		if (free_cm == 1) {
1305			(void) free_conn_mt(cm, 0);
1306			*cmt = NULL;
1307		}
1308		return (B_FALSE);
1309	}
1310
1311	switch (cu->type) {
1312	case NS_CONN_USER_SEARCH:
1313	case NS_CONN_USER_GETENT:
1314		if (cm->opened_for == NS_CONN_USER_SEARCH ||
1315		    cm->opened_for == NS_CONN_USER_GETENT)
1316			matched = B_TRUE;
1317		break;
1318
1319	case NS_CONN_USER_WRITE:
1320		if (cm->opened_for == NS_CONN_USER_WRITE)
1321			matched = B_TRUE;
1322		break;
1323
1324	default:
1325		matched = B_FALSE;
1326		break;
1327	}
1328
1329	if (matched == B_TRUE && ((server != NULL || cred != NULL) &&
1330	    is_server_cred_matched(server, cred, cm) == B_FALSE))
1331		matched = B_FALSE;
1332
1333	if (matched != B_FALSE) {
1334		/*
1335		 * Check and drop the 'connected' connection if
1336		 * necessary. Main nscd gets status changes from
1337		 * the ldap_cachemgr daemon directly via the
1338		 * GETSTATUSCHANGE door call, the standalone
1339		 * function works in a no ldap_cachemgr environment,
1340		 * so no need to check and drop connections.
1341		 */
1342		if (cm->state == NS_CONN_MT_CONNECTED &&
1343		    cmg->is_nscd == B_FALSE && !__s_api_isStandalone()) {
1344			drop_conn = check_and_close_conn(cmg, &cm, cu);
1345			if (drop_conn == B_TRUE) {
1346				if (cm == NULL)
1347					*cmt = NULL;
1348				return (B_FALSE);
1349			}
1350		}
1351
1352		/* check if max. users using or waiting for the connection */
1353		if ((cm->state == NS_CONN_MT_CONNECTED &&
1354		    cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
1355		    cm->cu_cnt >= cm->cu_max) ||
1356		    (cm->state == NS_CONN_MT_CONNECTING &&
1357		    cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
1358		    cm->waiter_cnt >= cm->cu_max - 1))
1359			matched = B_FALSE;
1360	}
1361
1362	if (matched == B_FALSE)
1363		(void) mutex_unlock(&cm->lock);
1364
1365	return (matched);
1366}
1367
1368/*
1369 * obtain an MT connection from the connection management for a conn_user
1370 *
1371 * Input:
1372 *   server	: server name or IP address
1373 *   flags	: libsldap API flags
1374 *   cred	: pointer to the user credential
1375 *   cu		: pointer to the conn_user structure
1376 * Output:
1377 *   session	: hold pointer to the Connection structure
1378 *   errorp	: hold pointer to error info (ns_ldap_error_t)
1379 */
1380int
1381__s_api_conn_mt_get(const char *server, const int flags, const ns_cred_t *cred,
1382	Connection **session, ns_ldap_error_t **errorp, ns_conn_user_t *cu)
1383{
1384	int		rc;
1385	int		i;
1386	ns_conn_mt_t	*cn;
1387	ns_conn_mt_state_t st;
1388	ns_conn_mgmt_t	*cmg;
1389
1390	if (errorp == NULL || cu == NULL || session == NULL)
1391		return (NS_LDAP_INVALID_PARAM);
1392
1393	*session = NULL;
1394	cmg = cu->conn_mgmt;
1395
1396	/*
1397	 * for pam_ldap, always try opening a new connection
1398	 */
1399	if (cu->type == NS_CONN_USER_AUTH)
1400		return (NS_LDAP_NOTFOUND);
1401
1402	/* if need a new conn, then don't reuse */
1403	if (flags & NS_LDAP_NEW_CONN)
1404		return (NS_LDAP_NOTFOUND);
1405
1406	if (flags & NS_LDAP_KEEP_CONN)
1407		cu->keep_conn = B_TRUE;
1408
1409	/*
1410	 * We want to use MT connection only if keep-connection flag is
1411	 * set or if MT was requested (or active)
1412	 */
1413	if (!((cmg->state == NS_CONN_MGMT_INACTIVE &&
1414	    cu->keep_conn == B_TRUE) || cmg->state == NS_CONN_MGMT_ACTIVE))
1415		return (NS_LDAP_NOTFOUND);
1416
1417	/* MT connection will be used now (if possible/available) */
1418	cu->use_mt_conn = B_TRUE;
1419
1420	NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errorp);
1421
1422	/* first look for a connection already open */
1423	st = NS_CONN_MT_CONNECTED;
1424	cu->state = NS_CONN_USER_FINDING;
1425	for (i = 0; i < 2; i++) {
1426		for (cn = cmg->cm_head; cn; cn = cn->next) {
1427			(void) mutex_lock(&cn->lock);
1428			rc = match_conn_mt(cu, &cn, st, server, cred);
1429			if (rc == B_FALSE && cn != NULL) /* not found */
1430				continue;
1431			if (cn == NULL) { /* not found and cn freed */
1432				/*
1433				 * as the conn_mt list could
1434				 * be different due to cn's
1435				 * deletion, scan the entire
1436				 * conn_mt list again
1437				 */
1438				st = NS_CONN_MT_CONNECTED;
1439				i = -1;
1440				break;
1441			}
1442
1443			/* return a connected one if found */
1444			if (cn->state == NS_CONN_MT_CONNECTED) {
1445				*session = cn->conn;
1446				add_cu2cm(cu, cn);
1447				cu->conn_mt = cn;
1448				cu->state = NS_CONN_USER_CONNECTED;
1449				(void) mutex_unlock(&cn->lock);
1450				(void) mutex_unlock(&cmg->lock);
1451				return (NS_LDAP_SUCCESS);
1452			}
1453
1454			/*
1455			 * if cn is not connecting, or allow only
1456			 * one user, skip it
1457			 */
1458			if (cn->state != NS_CONN_MT_CONNECTING ||
1459			    cn->cu_max == 1) {
1460				(void) mutex_unlock(&cn->lock);
1461				continue;
1462			}
1463
1464			/* wait for the connecting conn_mt */
1465			if (wait_for_conn_mt(cu, cn) != 1) {
1466				/*
1467				 * NS_LDAP_NOTFOUND signals that the function
1468				 * __s_api_check_libldap_MT_conn_support()
1469				 * detected that the lower libldap library
1470				 * does not support MT connection, so return
1471				 * NS_LDAP_NOTFOUND to let the caller to
1472				 * open a non-MT conneciton. Otherwise,
1473				 * connect error occurred, return
1474				 * NS_CONN_USER_CONNECT_ERROR
1475				 */
1476				if (cn->ns_rc != NS_LDAP_NOTFOUND)
1477					cu->state = NS_CONN_USER_CONNECT_ERROR;
1478				else {
1479					cu->state = NS_CONN_USER_FINDING;
1480					cu->use_mt_conn = B_FALSE;
1481				}
1482				(void) mutex_unlock(&cn->lock);
1483
1484				/* cmg->lock unlocked by wait_for_conn_mt() */
1485
1486				return (cn->ns_rc);
1487			}
1488
1489			/* return the newly available conn_mt */
1490			*session = cn->conn;
1491			cu->state = NS_CONN_USER_CONNECTED;
1492			(void) mutex_unlock(&cn->lock);
1493
1494			/* cmg->lock unlocked by wait_for_conn_mt() */
1495
1496			return (NS_LDAP_SUCCESS);
1497		}
1498
1499		/* next, look for a connecting conn_mt */
1500		if (i == 0)
1501			st = NS_CONN_MT_CONNECTING;
1502	}
1503
1504	/* no connection found, start opening one */
1505	cn = init_conn_mt(cmg, errorp);
1506	if (cn == NULL) {
1507		(void) mutex_unlock(&cmg->lock);
1508		return ((*errorp)->status);
1509	}
1510	cu->conn_mt = cn;
1511	cn->opened_for = cu->type;
1512	cn->referral = cu->referral;
1513	if (cmg->ldap_mt == B_TRUE)
1514		cn->cu_max = NS_CONN_MT_USER_MAX;
1515	else
1516		cn->cu_max = 1;
1517	add_cm2cmg(cn, cmg);
1518	(void) mutex_unlock(&cmg->lock);
1519
1520	return (NS_LDAP_NOTFOUND);
1521}
1522
1523
1524/*
1525 * add an MT connection to the connection management
1526 *
1527 * Input:
1528 *   con	: pointer to the Connection info
1529 *   cu		: pointer to the conn_user structure
1530 * Output:
1531 *   ep		: hold pointer to error info (ns_ldap_error_t)
1532 */
1533int
1534__s_api_conn_mt_add(Connection *con, ns_conn_user_t *cu, ns_ldap_error_t **ep)
1535{
1536	ns_conn_mgmt_t	*cmg = cu->conn_mgmt;
1537	ns_conn_mt_t	*cm = cu->conn_mt;
1538
1539	/* if the conn_mgmt is being shut down, return error */
1540	NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
1541
1542	/*
1543	 * start the change monitor thread only if it
1544	 * hasn't been started and the process is the
1545	 * main nscd (not peruser nscd)
1546	 */
1547	if (cmg->procchg_started == B_FALSE && cmg->is_nscd == B_TRUE) {
1548		start_thread(cmg);
1549		cmg->procchg_started = B_TRUE;
1550	}
1551	(void) mutex_lock(&cm->lock);
1552	cm->conn = con;
1553	cm->state = NS_CONN_MT_CONNECTED;
1554	cm->pid = getpid();
1555	cm->create_time = time(NULL);
1556	cm->access_time = cm->create_time;
1557	cm->opened_for = cu->type;
1558	add_cu2cm(cu, cm);
1559	cu->conn_mt = cm;
1560	cu->state = NS_CONN_USER_CONNECTED;
1561	if (cmg->ldap_mt == B_TRUE)
1562		cm->cu_max = NS_CONN_MT_USER_MAX;
1563	else
1564		cm->cu_max = 1;
1565
1566	/* wake up the waiters if any */
1567	(void) conn_signal(cm);
1568
1569	(void) mutex_unlock(&cm->lock);
1570	(void) mutex_unlock(&cmg->lock);
1571
1572	return (NS_LDAP_SUCCESS);
1573}
1574
1575/*
1576 * return an MT connection to the pool when a conn user is done using it
1577 *
1578 * Input:
1579 *   cu		: pointer to the conn_user structure
1580 * Output:	NONE
1581 */
1582void
1583__s_api_conn_mt_return(ns_conn_user_t *cu)
1584{
1585	ns_conn_mt_t	*cm;
1586	ns_conn_mgmt_t	*cmg;
1587
1588	if (cu == NULL || cu->use_mt_conn == B_FALSE)
1589		return;
1590	cm = cu->conn_mt;
1591	if (cm == NULL)
1592		return;
1593	cmg = cu->conn_mgmt;
1594
1595	(void) mutex_lock(&cm->lock);
1596	del_cu4cm(cu, cm);
1597	cu->state = NS_CONN_USER_DISCONNECTED;
1598	cu->conn_mt = NULL;
1599	cu->bad_mt_conn = B_FALSE;
1600
1601	/*
1602	 *  if this MT connection is no longer needed, or not usable, and
1603	 * no more conn_user uses it, then close it.
1604	 */
1605
1606	if ((cm->close_when_nouser == B_TRUE ||
1607	    cm->state != NS_CONN_MT_CONNECTED) && cm->cu_cnt == 0) {
1608		(void) mutex_unlock(&cm->lock);
1609		(void) mutex_lock(&cmg->lock);
1610		(void) mutex_lock(&cm->lock);
1611		del_cm4cmg(cm, cmg);
1612		/* use ns_conn_free (instead of 1) to avoid lint warning */
1613		NS_CONN_UNLOCK_AND_FREE(ns_conn_free, cm, cmg);
1614	} else {
1615		if (cm->state == NS_CONN_MT_CONNECTED && cm->cu_cnt == 0 &&
1616		    cm->conn != NULL && cm->conn->ld != NULL) {
1617			struct timeval	zerotime;
1618			LDAPMessage	*res;
1619
1620			zerotime.tv_sec = zerotime.tv_usec = 0L;
1621			/* clean up remaining results just in case */
1622			while (ldap_result(cm->conn->ld, LDAP_RES_ANY,
1623			    LDAP_MSG_ALL, &zerotime, &res) > 0) {
1624				if (res != NULL)
1625					(void) ldap_msgfree(res);
1626			}
1627		}
1628		(void) mutex_unlock(&cm->lock);
1629	}
1630}
1631
1632/* save error info (rc and ns_ldap_error_t) in the conn_mt */
1633static void
1634err2cm(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp) {
1635	ns_ldap_error_t	*ep;
1636
1637	cm->ns_rc = rc;
1638	cm->ns_error = NULL;
1639	if (errorp != NULL && *errorp != NULL) {
1640		ep = __s_api_copy_error(*errorp);
1641		if (ep == NULL)
1642			cm->ns_rc = NS_LDAP_MEMORY;
1643		else
1644			cm->ns_error = ep;
1645	}
1646}
1647
1648/* copy error info (rc and ns_ldap_error_t) from conn_mt to conn_user */
1649static void
1650err_from_cm(ns_conn_user_t *cu, ns_conn_mt_t *cm) {
1651	ns_ldap_error_t	*ep;
1652
1653	cu->ns_rc = cm->ns_rc;
1654	if (cu->ns_error != NULL)
1655		(void) __ns_ldap_freeError(&cu->ns_error);
1656	cu->ns_error = NULL;
1657	if (cm->ns_rc != NS_LDAP_SUCCESS && cm->ns_error != NULL) {
1658		ep = __s_api_copy_error(cm->ns_error);
1659		if (ep == NULL)
1660			cu->ns_rc = NS_LDAP_MEMORY;
1661		else
1662			cu->ns_error = ep;
1663	}
1664}
1665
1666/* copy error info (rc and ns_ldap_error_t) from caller to conn_user */
1667static void
1668err_from_caller(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp) {
1669
1670	cu->ns_rc = rc;
1671	if (errorp != NULL) {
1672		if (cu->ns_error != NULL)
1673			(void) __ns_ldap_freeError(&cu->ns_error);
1674		cu->ns_error = *errorp;
1675		*errorp = NULL;
1676	} else
1677		cu->ns_error = NULL;
1678}
1679
1680/*
1681 * remove an MT connection from the connection management when failed to open
1682 *
1683 * Input:
1684 *   cu		: pointer to the conn_user structure
1685 *   rc		: error code
1686 *   errorp	: pointer to pointer to error info (ns_ldap_error_t)
1687 * Output:
1688 *   errorp	: set to NULL, if none NULL cm, callers do not need to free it
1689 */
1690void
1691__s_api_conn_mt_remove(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
1692{
1693	ns_conn_mgmt_t	*cmg;
1694	ns_conn_mt_t	*cm;
1695	int		free_cm = 0;
1696
1697	if (cu == NULL || cu->use_mt_conn == B_FALSE)
1698		return;
1699	if ((cm = cu->conn_mt) == NULL)
1700		return;
1701	cmg = cu->conn_mgmt;
1702
1703	(void) mutex_lock(&cmg->lock);
1704	(void) mutex_lock(&cm->lock);
1705	if (cm->state != NS_CONN_MT_CONNECT_ERROR) {
1706		cm->state = NS_CONN_MT_CONNECT_ERROR;
1707		cm->ns_rc = rc;
1708		if (errorp != NULL) {
1709			cm->ns_error = *errorp;
1710			*errorp = NULL;
1711		}
1712	}
1713
1714	/* all the conn_users share the same error rc and ns_ldap_error_t */
1715	err_from_cm(cu, cm);
1716	/* wake up the waiters if any */
1717	(void) conn_signal(cm);
1718
1719	del_cu4cm(cu, cm);
1720	cu->conn_mt = NULL;
1721	cu->bad_mt_conn = B_FALSE;
1722	if (cm->cu_cnt == 0) {
1723		del_cm4cmg(cm, cmg);
1724		free_cm = 1;
1725	}
1726
1727	NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
1728}
1729
1730/*
1731 * check to see if the underlying libldap supports multi-threaded client
1732 * (MT connections)
1733 */
1734int
1735__s_api_check_libldap_MT_conn_support(ns_conn_user_t *cu, LDAP *ld,
1736	ns_ldap_error_t **ep)
1737{
1738	int		rc;
1739	ns_conn_mgmt_t	*cmg;
1740
1741	/* if no need to check, just return success */
1742	if (cu->conn_mt == NULL || cu->use_mt_conn == B_FALSE)
1743		return (NS_LDAP_SUCCESS);
1744
1745	cmg = cu->conn_mgmt;
1746	rc = setup_mt_ld(ld, cmg);
1747
1748	if (cmg->do_mt_conn == B_FALSE) {
1749		/*
1750		 * If the conn_mgmt is being shut down, return error.
1751		 * if cmg is usable, cmg->lock will be locked. Otherwise,
1752		 * this function will return with rc NS_LDAP_OP_FAILED.
1753		 */
1754		NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
1755		if (cmg->do_mt_conn == B_FALSE) {
1756			if (rc < 0)
1757				cmg->ldap_mt = B_FALSE;
1758			else {
1759				cmg->ldap_mt = B_TRUE;
1760				if (cmg->is_nscd  == B_TRUE ||
1761				    cmg->is_peruser_nscd == B_TRUE) {
1762					cmg->do_mt_conn = B_TRUE;
1763					cmg->state = NS_CONN_MGMT_ACTIVE;
1764				}
1765			}
1766		}
1767		(void) mutex_unlock(&cmg->lock);
1768	}
1769
1770	if (rc < 0)
1771		__s_api_conn_mt_remove(cu, NS_LDAP_NOTFOUND, NULL);
1772	return (NS_LDAP_SUCCESS);
1773}
1774
1775/*
1776 * Close an MT connection.
1777 * Assume cm not null and locked, assume conn_mgmt is also locked.
1778 * Return -1 if error, 1 if the cm should be freed, otherwise 0.
1779 */
1780static int
1781close_conn_mt(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp,
1782	ns_conn_user_t *cu)
1783{
1784	ns_conn_mgmt_t	*cmg = cm->conn_mgmt;
1785	ns_conn_mt_t	*m;
1786	ns_conn_user_t	*u;
1787
1788	if ((cm->state != NS_CONN_MT_CONNECTED && cm->state !=
1789	    NS_CONN_MT_CLOSING) || cmg->cm_head == NULL || cmg->cm_cnt == 0)
1790		return (-1);
1791
1792	/* if the conn_mt is not in the MT connection pool, nothing to do */
1793	for (m = cmg->cm_head; m; m = m->next) {
1794		if (cm == m)
1795			break;
1796	}
1797	if (m == NULL)
1798		return (-1);
1799
1800	if (cm->state == NS_CONN_MT_CONNECTED) { /* first time in here */
1801		cm->state = NS_CONN_MT_CLOSING;
1802		/*
1803		 * If more cu exist to consume the error info, copy
1804		 * it to the cm. If the caller calls on behalf of
1805		 * a cu, cu won't be NULL. Check to see if there's
1806		 * more cu that needs the error info. If caller does
1807		 * not have a specific cu attached to it (e.g.,
1808		 * shutdown_all_conn_mt()), cu is NULL, check if at
1809		 * least one cu exists.
1810		 */
1811		if ((cu != NULL && cm->cu_cnt > 1) ||
1812		    (cu == NULL && cm->cu_cnt > 0)) {
1813			err2cm(cm, rc, errorp);
1814			/* wake up waiter (conn_user) if any */
1815			(void) conn_signal(cm);
1816		}
1817
1818		/* for each conn_user using the conn_mt, set bad_mt_conn flag */
1819		if (cm->cu_head != NULL) {
1820			for (u = cm->cu_head; u; u = u->next) {
1821				u->bad_mt_conn = B_TRUE;
1822				if (cmg->shutting_down == B_FALSE)
1823					u->retry = B_TRUE;
1824			}
1825		}
1826	}
1827
1828	/* detach the conn_mt if no more conn_user left */
1829	if ((cu != NULL && cm->cu_cnt == 1) ||
1830	    (cu == NULL && cm->cu_cnt ==  0)) {
1831		del_cm4cmg(cm, cmg);
1832		cm->detached = B_TRUE;
1833		return (1);
1834	}
1835
1836	return (0);
1837}
1838
1839/*
1840 * An MT connection becomes bad, close it and free resources.
1841 * This function is called with a ns_conn_user_t representing
1842 * a user of the MT connection.
1843 *
1844 * Input:
1845 *   cu		: pointer to the conn_user structure
1846 *   rc		: error code
1847 *   errorp	: pointer to pointer to error info (ns_ldap_error_t)
1848 * Output:
1849 *   errorp	: set to NULL (if no error), callers do not need to free it
1850 */
1851void
1852__s_api_conn_mt_close(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
1853{
1854	ns_conn_mgmt_t	*cmg;
1855	ns_conn_mt_t	*cm;
1856	int		free_cm = 0;
1857
1858	if (cu == NULL || cu->use_mt_conn == B_FALSE)
1859		return;
1860
1861	if (cu->state != NS_CONN_USER_CONNECTED || (cm = cu->conn_mt) == NULL)
1862		return;
1863	cmg = cu->conn_mgmt;
1864
1865	(void) mutex_lock(&cmg->lock);
1866	(void) mutex_lock(&cm->lock);
1867
1868	/* close the MT connection if possible */
1869	free_cm = close_conn_mt(cm, rc, errorp, cu);
1870	if (free_cm == -1) { /* error case */
1871		(void) mutex_unlock(&cm->lock);
1872		(void) mutex_unlock(&cmg->lock);
1873		return;
1874	}
1875
1876	if (rc != NS_LDAP_SUCCESS) { /* error info passed in, use it */
1877		err_from_caller(cu, rc, errorp);
1878	} else { /* error not passed in, use those saved in the conn_mt */
1879		err_from_cm(cu, cm);
1880	}
1881
1882	/* detach the conn_user from the conn_mt */
1883	del_cu4cm(cu, cm);
1884	cu->conn_mt = NULL;
1885	cu->bad_mt_conn = B_FALSE;
1886	if (cmg->shutting_down == B_FALSE)
1887		cu->retry = B_TRUE;
1888	NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
1889}
1890
1891/*
1892 * Close an MT connection when the associated server is known to be
1893 * down. This function is called with a ns_conn_mt_t representing
1894 * the MT connection. That is, the caller is not a conn_user
1895 * thread but rather the procchg thread.
1896 */
1897static void
1898close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg)
1899{
1900	ns_conn_mgmt_t	*cmg;
1901	int		free_cm = 0;
1902	ns_ldap_error_t	*ep;
1903
1904	if (cm == NULL)
1905		return;
1906	cmg = cm->conn_mgmt;
1907
1908	ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
1909	if (ep != NULL) {
1910		ep->status = rc;
1911		if (errmsg != NULL)
1912			ep->message =  strdup(errmsg); /* OK if returns NULL */
1913	}
1914
1915	(void) mutex_lock(&cmg->lock);
1916	(void) mutex_lock(&cm->lock);
1917
1918	/* close the MT connection if possible */
1919	free_cm = close_conn_mt(cm, LDAP_SERVER_DOWN, &ep, NULL);
1920	if (free_cm == -1) { /* error case */
1921		(void) mutex_unlock(&cm->lock);
1922		(void) mutex_unlock(&cmg->lock);
1923		return;
1924	}
1925	(void) __ns_ldap_freeError(&ep);
1926
1927	NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
1928}
1929
1930/*
1931 * Close an MT connection when there is a better server to connect to.
1932 * Mark the connection as to-be-closed-when-no-one-using so that
1933 * any outstanding ldap operations can run to completion.
1934 * Assume that both the conn_mt and conn_mgmt are locked.
1935 * Return 1 if the conn_mt should be freed.
1936 */
1937static int
1938close_conn_mt_when_nouser(ns_conn_mt_t *cm)
1939{
1940	int		free_cm = 0;
1941
1942	if (cm->cu_cnt == 0) {
1943		del_cm4cmg(cm, cm->conn_mgmt);
1944		free_cm = 1;
1945	} else {
1946		cm->close_when_nouser = B_TRUE;
1947	}
1948
1949	return (free_cm);
1950}
1951
1952/*
1953 * Retrieve the configured preferred server list.
1954 * This function locked the conn_mgmt and does not
1955 * unlock at exit.
1956 */
1957static void
1958get_preferred_servers(boolean_t lock, boolean_t reload, ns_conn_mgmt_t *cmg)
1959{
1960	ns_ldap_error_t *errorp = NULL;
1961	void		**pservers = NULL;
1962
1963	if (lock == B_TRUE)
1964		(void) mutex_lock(&cmg->lock);
1965
1966	/* if already done, and no reload, then return */
1967	if (cmg->pservers_loaded == B_TRUE && reload == B_FALSE)
1968		return;
1969
1970	if (cmg->pservers != NULL) {
1971		(void) __ns_ldap_freeParam((void ***)&cmg->pservers);
1972		cmg->pservers = NULL;
1973	}
1974
1975	if (__ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
1976	    &pservers, &errorp) == NS_LDAP_SUCCESS) {
1977		cmg->pservers = (char **)pservers;
1978		cmg->pservers_loaded = B_TRUE;
1979	} else {
1980		(void) __ns_ldap_freeError(&errorp);
1981		(void) __ns_ldap_freeParam(&pservers);
1982	}
1983}
1984
1985/*
1986 * This function handles the config or server status change notification
1987 * from the ldap_cachemgr.
1988 */
1989static ns_conn_mgmt_t *
1990proc_server_change(ns_server_status_change_t *chg, ns_conn_mgmt_t  *cmg)
1991{
1992	int		cnt, i, j, k, n;
1993	boolean_t	loop = B_TRUE;
1994	boolean_t	cmg_locked = B_FALSE;
1995	char 		*s;
1996	ns_conn_mt_t	*cm;
1997	ns_conn_mgmt_t	*ocmg;
1998
1999	/* if config changed, reload the configuration */
2000	if (chg->config_changed == B_TRUE) {
2001		/* reload the conn_mgmt and Native LDAP config */
2002		ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_RELOAD_CONFIG);
2003		shutdown_all_conn_mt(ocmg);
2004		/* release the one obtained from access_conn_mgmt(RELOAD) */
2005		(void) release_conn_mgmt(ocmg, B_FALSE);
2006		/* release the one obtained when ocmg was created */
2007		(void) release_conn_mgmt(ocmg, B_FALSE);
2008		return (ocmg);
2009	}
2010
2011	if ((cnt = chg->num_server) == 0)
2012		return (cmg);
2013
2014	/* handle down servers first */
2015	for (i = 0; i < cnt; i++) {
2016
2017		if (chg->changes[i] != NS_SERVER_DOWN)
2018			continue;
2019		s = chg->servers[i];
2020
2021		/*
2022		 * look for a CONNECTED MT connection using
2023		 * the same server s, and close it
2024		 */
2025		while (loop) {
2026			if (cmg_locked == B_FALSE) {
2027				(void) mutex_lock(&cmg->lock);
2028				cmg_locked = B_TRUE;
2029			}
2030			for (cm = cmg->cm_head; cm; cm = cm->next) {
2031				(void) mutex_lock(&cm->lock);
2032
2033				if (cm->state == NS_CONN_MT_CONNECTED &&
2034				    cm->conn != NULL &&
2035				    strcasecmp(cm->conn->serverAddr, s) == 0) {
2036					(void) mutex_unlock(&cm->lock);
2037					break;
2038				}
2039
2040				(void) mutex_unlock(&cm->lock);
2041			}
2042			if (cm != NULL) {
2043				(void) mutex_unlock(&cmg->lock);
2044				cmg_locked = B_FALSE;
2045				close_conn_mt_by_procchg(cm, LDAP_SERVER_DOWN,
2046				    NS_CONN_MSG_DOWN_FROM_CACHEMGR);
2047				/*
2048				 * Process the next cm using server s.
2049				 * Start from the head of the cm linked
2050				 * list again, as the cm list may change
2051				 * after close_conn_mt_by_procchg() is done.
2052				 */
2053				continue;
2054			}
2055
2056			/*
2057			 * No (more) MT connection using the down server s.
2058			 * Process the next server on the list.
2059			 */
2060			break;
2061		} /* while loop */
2062	}
2063
2064	/*
2065	 * Next handle servers whose status changed to up.
2066	 * Get the preferred server list first if not done yet.
2067	 * get_preferred_servers() leaves conn_mgmt locked.
2068	 */
2069	get_preferred_servers(cmg_locked == B_FALSE ? B_TRUE : B_FALSE,
2070	    B_FALSE, cmg);
2071	cmg_locked = B_TRUE;
2072	/*
2073	 * if no preferred server configured, we don't switch MT connection
2074	 * to a more preferred server (i.e., fallback), so just return
2075	 */
2076	if (cmg->pservers == NULL) {
2077		(void) mutex_unlock(&cmg->lock);
2078		return (cmg);
2079	}
2080
2081	/* for each server that is up now */
2082	for (i = 0; i < cnt; i++) {
2083		if (chg->changes[i] != NS_SERVER_UP)
2084			continue;
2085		s = chg->servers[i];
2086
2087		/*
2088		 * look for a CONNECTED MT connection which uses
2089		 * a server less preferred than s, and treat it
2090		 * as 'fallback needed' by calling
2091		 * close_conn_mt_when_nouser()
2092		 */
2093		k = -1;
2094		loop = B_TRUE;
2095		while (loop) {
2096			if (cmg_locked == B_FALSE) {
2097				(void) mutex_lock(&cmg->lock);
2098				cmg_locked = B_TRUE;
2099			}
2100
2101			/* Is s a preferred server ? */
2102			if (k == -1) {
2103				for (j = 0; cmg->pservers[j] != NULL; j++) {
2104					if (strcasecmp(cmg->pservers[j],
2105					    s) == 0) {
2106						k = j;
2107						break;
2108					}
2109				}
2110			}
2111			/* skip s if not a preferred server */
2112			if (k == -1) {
2113				break;
2114			}
2115
2116			/* check each MT connection */
2117			for (cm = cmg->cm_head; cm; cm = cm->next) {
2118				(void) mutex_lock(&cm->lock);
2119				/*
2120				 * Find an MT connection that is connected and
2121				 * not marked, but leave WRITE or REFERRAL
2122				 * connections alone, since fallback does not
2123				 * make sense for them.
2124				 */
2125				if (cm->state == NS_CONN_MT_CONNECTED &&
2126				    cm->close_when_nouser == B_FALSE &&
2127				    cm->conn != NULL && cm->opened_for !=
2128				    NS_CONN_USER_WRITE &&
2129				    cm->referral == B_FALSE) {
2130					n = -1;
2131					/*
2132					 * j < k ??? should we close
2133					 * an active MT that is using s ?
2134					 * ie could s went down and up
2135					 * again, but cm is bound prior to
2136					 * the down ? Play safe here,
2137					 * and check j <= k.
2138					 */
2139					for (j = 0; j <= k; j++) {
2140						if (strcasecmp(
2141						    cm->conn->serverAddr,
2142						    cmg->pservers[j]) == 0) {
2143							n = j;
2144							break;
2145						}
2146					}
2147					/*
2148					 * s is preferred, if its location
2149					 * in the preferred server list is
2150					 * ahead of that of the server
2151					 * used by the cm (i.e., no match
2152					 * found before s)
2153					 */
2154					if (n == -1) { /* s is preferred */
2155						int fr = 0;
2156						fr = close_conn_mt_when_nouser(
2157						    cm);
2158						NS_CONN_UNLOCK_AND_FREE(fr,
2159						    cm, cmg);
2160						cmg_locked = B_FALSE;
2161						/*
2162						 * break, not continue,
2163						 * because we need to
2164						 * check the entire cm
2165						 * list again. The call
2166						 * above may change the
2167						 * cm list.
2168						 */
2169						break;
2170					}
2171				}
2172				(void) mutex_unlock(&cm->lock);
2173			}
2174			/* if no (more) cm using s, check next server */
2175			if (cm == NULL)
2176				loop = B_FALSE;
2177		} /* while loop */
2178	}
2179	if (cmg_locked == B_TRUE)
2180		(void) mutex_unlock(&cmg->lock);
2181	return (cmg);
2182}
2183
2184/* Shut down all MT connection managed by the connection management */
2185void
2186shutdown_all_conn_mt(ns_conn_mgmt_t  *cmg)
2187{
2188	ns_ldap_error_t	*ep;
2189	ns_conn_mt_t	*cm;
2190	int		free_cm = 0;
2191	boolean_t	done = B_FALSE;
2192
2193	ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
2194	if (ep != NULL) { /* if NULL, not a problem */
2195		/* OK if returns NULL */
2196		ep->message = strdup(NS_CONN_MSG_SHUTDOWN_RELOADED);
2197	}
2198
2199	(void) mutex_lock(&cmg->lock);
2200	while (cmg->cm_head != NULL && done == B_FALSE) {
2201		for (cm = cmg->cm_head; cm; cm = cm->next) {
2202			(void) mutex_lock(&cm->lock);
2203			if (cm->next == NULL)
2204				done = B_TRUE;
2205			/* shut down each conn_mt, ignore errors */
2206			free_cm = close_conn_mt(cm, LDAP_OTHER, &ep, NULL);
2207			(void) mutex_unlock(&cm->lock);
2208			if (free_cm == 1) {
2209				(void) free_conn_mt(cm, 0);
2210				/*
2211				 * conn_mt may change, so start from
2212				 * top of list again
2213				 */
2214				break;
2215			}
2216		}
2217	}
2218	(void) mutex_unlock(&cmg->lock);
2219	(void) __ns_ldap_freeError(&ep);
2220}
2221
2222/* free all the resources used by the connection management */
2223void
2224__s_api_shutdown_conn_mgmt()
2225{
2226	ns_conn_mgmt_t	*cmg;
2227
2228	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_SHUTDOWN);
2229	if (cmg == NULL) /* already being SHUT done */
2230		return;
2231
2232	(void) shutdown_all_conn_mt(cmg);
2233	(void) release_conn_mgmt(cmg, B_FALSE);
2234
2235	/* then destroy the conn_mgmt */
2236	(void) release_conn_mgmt(cmg, B_FALSE);
2237}
2238
2239
2240/*
2241 * Reinitialize the libsldap connection management after
2242 * a new native LDAP configuration is received.
2243 */
2244void
2245__s_api_reinit_conn_mgmt_new_config(ns_config_t *new_cfg)
2246{
2247	ns_conn_mgmt_t	*cmg;
2248	ns_conn_mgmt_t	*ocmg;
2249
2250	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
2251	if (cmg == NULL)
2252		return;
2253	if (cmg->config == new_cfg || cmg->state == NS_CONN_MGMT_DETACHED) {
2254		(void) release_conn_mgmt(cmg, B_FALSE);
2255		return;
2256	}
2257
2258	/* reload the conn_mgmt and native LDAP config */
2259	ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_NEW_CONFIG);
2260	if (ocmg == cmg)
2261		shutdown_all_conn_mt(ocmg);
2262	/* release the one obtained from access_conn_mgmt(RELOAD) */
2263	(void) release_conn_mgmt(ocmg, B_FALSE);
2264	/* release the one obtained when ocmg was created */
2265	(void) release_conn_mgmt(ocmg, B_FALSE);
2266	/* release the one obtained when this function is entered */
2267	(void) release_conn_mgmt(cmg, B_FALSE);
2268}
2269
2270/*
2271 * Prepare to retry ldap search operation if needed.
2272 * Return 1 if retry is needed, otherwise 0.
2273 * If first time in, return 1. If not, return 1 if:
2274 * - not a NS_CONN_USER_GETENT conn_user AND
2275 * - have not retried 3 times yet AND
2276 * - previous search failed AND
2277 * - the retry flag is set in the ns_conn_user_t or config was reloaded
2278 */
2279int
2280__s_api_setup_retry_search(ns_conn_user_t **conn_user,
2281	ns_conn_user_type_t type, int *try_cnt, int *rc,
2282	ns_ldap_error_t **errorp)
2283{
2284	boolean_t	retry;
2285	ns_conn_user_t	*cu = *conn_user;
2286	ns_conn_mgmt_t	*cmg;
2287
2288	if (*try_cnt > 0 && cu != NULL) {
2289		/*
2290		 * if called from firstEntry(), keep conn_mt for
2291		 * the subsequent getnext requests
2292		 */
2293		if (cu->type == NS_CONN_USER_GETENT && *rc == NS_LDAP_SUCCESS)
2294			return (0);
2295		cmg = cu->conn_mgmt;
2296		retry = cu->retry;
2297		if (cu->conn_mt != NULL)
2298			__s_api_conn_mt_return(cu);
2299		if (cmg != NULL && cmg->cfg_reloaded == B_TRUE)
2300			retry = B_TRUE;
2301		__s_api_conn_user_free(cu);
2302		*conn_user = NULL;
2303
2304		if (*rc == NS_LDAP_SUCCESS || retry != B_TRUE)
2305			return (0);
2306	}
2307
2308	*try_cnt = *try_cnt + 1;
2309	if (*try_cnt > NS_LIST_TRY_MAX)
2310		return (0);
2311
2312	*conn_user = __s_api_conn_user_init(type, NULL, B_FALSE);
2313	if (*conn_user == NULL) {
2314		if (*try_cnt == 1) { /* first call before any retry */
2315			*rc = NS_LDAP_MEMORY;
2316			*errorp = NULL;
2317		}
2318		/* for 1+ try, use previous rc and errorp */
2319		return (0);
2320	}
2321
2322	/* free ldap_error_t from previous search */
2323	if (*try_cnt > 1 && rc != NS_LDAP_SUCCESS && *errorp != NULL)
2324		(void) __ns_ldap_freeError(errorp);
2325
2326	return (1);
2327}
2328
2329/* prepare to get the next entry for an enumeration */
2330int
2331__s_api_setup_getnext(ns_conn_user_t *cu, int *ns_err,
2332	ns_ldap_error_t **errorp)
2333{
2334	int rc;
2335	ns_conn_mgmt_t	*cmg;
2336
2337	/*
2338	 * if using an MT connection, ensure the thread-specific data are set,
2339	 * but if the MT connection is no longer good, return the error saved.
2340	 */
2341	if (cu->conn_mt != NULL && (cmg = cu->conn_mgmt) != NULL) {
2342
2343		if (cu->bad_mt_conn ==  B_TRUE) {
2344			__s_api_conn_mt_close(cu, 0, NULL);
2345			*ns_err = cu->ns_rc;
2346			*errorp = cu->ns_error;
2347			cu->ns_error = NULL;
2348			return (*ns_err);
2349		}
2350
2351		rc = conn_tsd_check(cmg);
2352		if (rc != NS_LDAP_SUCCESS) {
2353			*errorp = NULL;
2354			return (rc);
2355		}
2356	}
2357
2358	return (NS_LDAP_SUCCESS);
2359}
2360
2361/* wait for an MT connection to become available */
2362static int
2363conn_wait(ns_conn_mt_t *conn_mt, ns_conn_user_t *conn_user)
2364{
2365	ns_conn_waiter_t	mywait;
2366	ns_conn_waiter_t	*head = &conn_mt->waiter;
2367
2368	(void) cond_init(&(mywait.waitcv), USYNC_THREAD, 0);
2369	mywait.key = conn_user;
2370	mywait.signaled = 0;
2371	mywait.next = head->next;
2372	mywait.prev = head;
2373	if (mywait.next)
2374		mywait.next->prev = &mywait;
2375	head->next = &mywait;
2376	atomic_inc_uint(&conn_mt->waiter_cnt);
2377
2378	while (!mywait.signaled)
2379		(void) cond_wait(&(mywait.waitcv), &conn_mt->lock);
2380	if (mywait.prev)
2381		mywait.prev->next = mywait.next;
2382	if (mywait.next)
2383		mywait.next->prev = mywait.prev;
2384	return (0);
2385}
2386
2387/* signal that an MT connection is now available */
2388static int
2389conn_signal(ns_conn_mt_t *conn_mt)
2390{
2391	int			c = 0;
2392	ns_conn_waiter_t	*head = &conn_mt->waiter;
2393	ns_conn_waiter_t	*tmp = head->next;
2394
2395	while (tmp) {
2396		(void) cond_signal(&(tmp->waitcv));
2397		tmp->signaled = 1;
2398		atomic_dec_uint(&conn_mt->waiter_cnt);
2399		c++;
2400		tmp = tmp->next;
2401	}
2402
2403	return (c);
2404}
2405
2406/*
2407 * wait and process the server status and/or config change notification
2408 * from ldap_cachemgr
2409 */
2410static void *
2411get_server_change(void *arg)
2412{
2413	union {
2414		ldap_data_t	s_d;
2415		char		s_b[DOORBUFFERSIZE];
2416	} space;
2417	ldap_data_t	*sptr = &space.s_d;
2418	int		ndata;
2419	int		adata;
2420	char		*ptr;
2421	int		ds_cnt;
2422	int		door_rc;
2423	int		which;
2424	int		retry = 0;
2425	boolean_t	loop = B_TRUE;
2426	char		*c, *oc;
2427	int		dslen = strlen(DOORLINESEP);
2428	char		dsep = DOORLINESEP_CHR;
2429	char		chg_data[DOORBUFFERSIZE];
2430	char		**servers = NULL;
2431	boolean_t	getchg_not_supported = B_FALSE;
2432	ns_conn_mgmt_t	*ocmg = (ns_conn_mgmt_t *)arg;
2433	ns_conn_mgmt_t	*cmg;
2434	ns_server_status_t *status = NULL;
2435	ns_server_status_change_t chg = { 0 };
2436	ldap_get_change_out_t *get_chg;
2437	ldap_get_chg_cookie_t cookie;
2438	ldap_get_chg_cookie_t new_cookie;
2439
2440	cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
2441	if (cmg != ocmg)
2442		thr_exit(NULL);
2443	/* cmg is locked before called */
2444	cmg->procchg_tid = thr_self();
2445
2446	/* make sure the thread specific data are set */
2447	(void) conn_tsd_setup(cmg);
2448	cookie = cmg->cfg_cookie;
2449
2450	while (loop) {
2451
2452		if (chg.servers != NULL)
2453			free(chg.servers);
2454		if (chg.changes != NULL)
2455			free(chg.changes);
2456		if (sptr != &space.s_d)
2457			(void) munmap((char *)sptr, sizeof (space));
2458
2459		/*
2460		 * If the attached conn_mgmt has been deleted,
2461		 * then exit. The new conn_mgmt will starts it
2462		 * own monitor thread later. If libsldap is being
2463		 * unloaded or configuration reloaded, OR
2464		 * ldap_cachemgr rejected the GETSTATUSCHANGE door
2465		 * call, then exit as well.
2466		 */
2467		if (cmg == NULL || cmg->state == NS_CONN_MGMT_DETACHED ||
2468		    getchg_not_supported == B_TRUE) {
2469
2470			if (cmg != NULL) {
2471				cmg->procchg_started = B_FALSE;
2472				(void) release_conn_mgmt(cmg, B_FALSE);
2473			}
2474
2475			conn_tsd_free();
2476			thr_exit(NULL);
2477		}
2478
2479		(void) memset(space.s_b, 0, DOORBUFFERSIZE);
2480		(void) memset(&chg, 0, sizeof (chg));
2481		adata = sizeof (ldap_call_t) + 1;
2482		ndata = sizeof (space);
2483		space.s_d.ldap_call.ldap_callnumber = GETSTATUSCHANGE;
2484		space.s_d.ldap_call.ldap_u.get_change.op =
2485		    NS_STATUS_CHANGE_OP_START;
2486		space.s_d.ldap_call.ldap_u.get_change.cookie = cookie;
2487		sptr = &space.s_d;
2488		door_rc = __ns_ldap_trydoorcall_getfd();
2489		cmg->procchg_door_call = B_TRUE;
2490		if (release_conn_mgmt(cmg, B_FALSE) == NULL) {
2491			conn_tsd_free();
2492			thr_exit(NULL);
2493		}
2494
2495		if (door_rc == NS_CACHE_SUCCESS)
2496			door_rc = __ns_ldap_trydoorcall_send(&sptr, &ndata,
2497			    &adata);
2498
2499		/*
2500		 * Check and see if the conn_mgmt is still current.
2501		 * If not, no need to continue.
2502		 */
2503		cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
2504		if (cmg != NULL)
2505			cmg->procchg_door_call = B_FALSE;
2506		if (cmg != ocmg) {
2507			if (cmg != NULL) {
2508				cmg->procchg_started = B_FALSE;
2509				(void) release_conn_mgmt(cmg, B_FALSE);
2510			}
2511			conn_tsd_free();
2512			thr_exit(NULL);
2513		}
2514
2515		if (door_rc != NS_CACHE_SUCCESS) {
2516			if (door_rc == NS_CACHE_NOSERVER) {
2517				if (retry++ > 10)
2518					getchg_not_supported = B_TRUE;
2519				else {
2520					/*
2521					 * ldap_cachemgr may be down, give
2522					 * it time to restart
2523					 */
2524					(void) sleep(2);
2525				}
2526			} else if (door_rc == NS_CACHE_NOTFOUND)
2527				getchg_not_supported = B_TRUE;
2528			continue;
2529		} else
2530			retry = 0;
2531
2532		/* copy info from door call return structure */
2533		get_chg =  &sptr->ldap_ret.ldap_u.changes;
2534		ptr = get_chg->data;
2535		/* configuration change ? */
2536		if (get_chg->type == NS_STATUS_CHANGE_TYPE_CONFIG) {
2537			chg.config_changed = B_TRUE;
2538			cmg = proc_server_change(&chg, cmg);
2539			continue;
2540		}
2541
2542		/* server status changes ? */
2543		if (get_chg->type == NS_STATUS_CHANGE_TYPE_SERVER) {
2544			/*
2545			 * first check cookies, if don't match, config
2546			 * has changed
2547			 */
2548			new_cookie = get_chg->cookie;
2549			if (new_cookie.mgr_pid != cookie.mgr_pid ||
2550			    new_cookie.seq_num != cookie.seq_num) {
2551				chg.config_changed = B_TRUE;
2552				cmg = proc_server_change(&chg, cmg);
2553				continue;
2554			}
2555
2556			(void) strlcpy(chg_data, ptr, sizeof (chg_data));
2557			chg.num_server = get_chg->server_count;
2558
2559			servers = (char **)calloc(chg.num_server,
2560			    sizeof (char *));
2561			if (servers == NULL) {
2562				syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
2563				continue;
2564			}
2565			status = (ns_server_status_t *)calloc(chg.num_server,
2566			    sizeof (int));
2567			if (status == NULL) {
2568				syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
2569				free(servers);
2570				continue;
2571			}
2572			ds_cnt = 0;
2573			which = 0;
2574			oc = ptr;
2575			for (c = ptr; which != 2; c++) {
2576				/* look for DOORLINESEP or end of string */
2577				if (*c != dsep && *c != '\0')
2578					continue;
2579				if (*c == dsep) { /* DOORLINESEP */
2580					*c = '\0'; /* current value */
2581					c += dslen; /* skip to next value */
2582				}
2583				if (which == 0) { /* get server info */
2584					servers[ds_cnt] = oc;
2585					oc = c;
2586					which = 1; /* get status next */
2587					continue;
2588				}
2589				/* which == 1, get up/down status */
2590				if (strcmp(NS_SERVER_CHANGE_UP, oc) == 0) {
2591					status[ds_cnt] = NS_SERVER_UP;
2592				} else if (strcmp(NS_SERVER_CHANGE_DOWN,
2593				    oc) == 0)
2594					status[ds_cnt] = NS_SERVER_DOWN;
2595				else {
2596					syslog(LOG_INFO,
2597					    NS_CONN_MSG_BAD_CACHEMGR_DATA);
2598					continue;
2599				}
2600				oc = c;
2601				ds_cnt++;
2602				if (*c == '\0')
2603					which = 2; /* exit the loop */
2604				else
2605					which = 0; /* get server info next */
2606			}
2607			chg.servers = servers;
2608			chg.changes = status;
2609			cmg = proc_server_change(&chg, cmg);
2610			continue;
2611		}
2612	}
2613
2614	return (NULL);
2615}
2616
2617/* start the thread handling the change notification from ldap_cachemgr */
2618static void
2619start_thread(ns_conn_mgmt_t *cmg) {
2620
2621	int		errnum;
2622
2623	/*
2624	 * start a thread to get and process config and server status changes
2625	 */
2626	if (thr_create(NULL, NULL, get_server_change,
2627	    (void *)cmg, THR_DETACHED, NULL) != 0) {
2628		errnum = errno;
2629		syslog(LOG_WARNING, NS_CONN_MSG_NO_PROCCHG_THREAD,
2630		    strerror(errnum));
2631	}
2632}
2633