#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <procfs.h>
#include <unistd.h>
#include <fcntl.h>
#include <libintl.h>
#include <atomic.h>
#include <pthread.h>
#include <sys/mman.h>
#include <time.h>
#include "solaris-int.h"
#include "ns_connmgmt.h"
#include "ns_cache_door.h"
#include "ns_internal.h"
#define NS_CONN_MGMT_OP_REF 1
#define NS_CONN_MGMT_OP_SHUTDOWN 2
#define NS_CONN_MGMT_OP_RELOAD_CONFIG 3
#define NS_CONN_MGMT_OP_NEW_CONFIG 4
#define NS_CONN_MGMT_OP_LIB_INIT 5
static ns_conn_mgmt_t *access_conn_mgmt(int);
static ns_conn_mgmt_t *release_conn_mgmt(ns_conn_mgmt_t *, boolean_t);
static int close_conn_mt(ns_conn_mt_t *, int, ns_ldap_error_t **,
ns_conn_user_t *);
static int close_conn_mt_when_nouser(ns_conn_mt_t *cm);
void shutdown_all_conn_mt(ns_conn_mgmt_t *cmg);
static int conn_signal(ns_conn_mt_t *);
static int conn_wait(ns_conn_mt_t *, ns_conn_user_t *);
static void close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg);
static ns_conn_mgmt_t *proc_server_change(ns_server_status_change_t *chg,
ns_conn_mgmt_t *cmg);
static void get_preferred_servers(boolean_t, boolean_t, ns_conn_mgmt_t *);
static void start_thread();
static ns_conn_mgmt_t *ns_connmgmt = NULL;
static ns_conn_mgmt_t *ns_connmgmt_parent = NULL;
static mutex_t ns_connmgmt_lock = DEFAULTMUTEX;
static boolean_t ns_connmgmt_shutting_down = B_FALSE;
#define NS_CONN_MSG_NO_CONN_MGMT gettext( \
"libsldap: unable to allocate the connection management control")
#define NS_CONN_MSG_NO_MTC_KEY gettext( \
"libsldap: unable to allocate the TSD key for per-thread ldap error")
#define NS_CONN_MSG_NO_CMG_KEY gettext( \
"libsldap: unable to allocate the TSD key for connection management")
#define NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded")
#define NS_CONN_MSG_RELOADED gettext( \
"libsldap: configuration has been reloaded")
#define NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \
"libsldap: library unloaded or configuration has been reloaded")
#define NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \
"libsldap: received incorrect data from ldap_cachemgr")
#define NS_CONN_MSG_MEMORY_ERROR gettext( \
"libsldap: unable to allocate memory")
#define NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \
"libsldap: unable to start the server monitor thread (%s)")
#define NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \
"libsldap: server down reported by ldap_cachemgr")
static int ns_conn_free = 1;
#define NS_CONN_UNLOCK_AND_FREE(free, cm, cmg) \
{ \
(void) mutex_unlock(&(cm)->lock); \
if (free == 1) \
cmg = free_conn_mt((cm), 1); \
if (cmg != NULL) \
(void) mutex_unlock(&(cmg)->lock); \
}
#define NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \
{ \
char *msg = NULL; \
(void) mutex_lock(&(cmg)->lock); \
if ((cmg)->shutting_down == B_TRUE) \
msg = NS_CONN_MSG_SHUTDOWN; \
else if ((cmg)->cfg_reloaded == B_TRUE) \
msg = NS_CONN_MSG_RELOADED; \
if (msg != NULL) { \
(*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \
(void) mutex_unlock(&(cmg)->lock); \
return (NS_LDAP_OP_FAILED); \
} \
}
thread_key_t ns_mtckey = THR_ONCE_KEY;
thread_key_t ns_cmgkey = THR_ONCE_KEY;
struct ldap_error {
int le_errno;
char *le_matched;
char *le_errmsg;
};
static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
static void
ns_mtckey_cleanup(void *key)
{
struct ldap_error *le = (struct ldap_error *)key;
if (le == NULL)
return;
if (le->le_matched != NULL) {
ldap_memfree(le->le_matched);
}
if (le->le_errmsg != NULL) {
ldap_memfree(le->le_errmsg);
}
free(le);
}
static void
conn_tsd_free(void)
{
void *tsd = NULL;
int rc;
rc = thr_getspecific(ns_mtckey, &tsd);
if (rc == 0 && tsd != NULL)
ns_mtckey_cleanup(tsd);
(void) thr_setspecific(ns_mtckey, NULL);
(void) thr_setspecific(ns_cmgkey, NULL);
}
static void *
ns_mutex_alloc(void)
{
mutex_t *mutexp = NULL;
if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
free(mutexp);
mutexp = NULL;
}
}
return (mutexp);
}
static void
ns_mutex_free(void *mutexp)
{
(void) mutex_destroy((mutex_t *)mutexp);
free(mutexp);
}
static int
conn_tsd_setup(ns_conn_mgmt_t *cmg)
{
void *tsd;
int rc;
rc = thr_setspecific(ns_cmgkey, cmg);
if (rc != 0)
return (-1);
rc = thr_getspecific(ns_mtckey, &tsd);
if (rc == 0 && tsd != NULL)
return (0);
tsd = (void *) calloc(1, sizeof (struct ldap_error));
if (tsd == NULL)
return (-1);
rc = thr_setspecific(ns_mtckey, tsd);
if (rc != 0) {
free(tsd);
return (-1);
}
return (0);
}
static void
set_ld_error(int err, char *matched, char *errmsg, void *dummy)
{
struct ldap_error *le;
int eno;
if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
syslog(LOG_ERR, gettext(
"libsldap: set_ld_error: thr_getspecific failed (%s)."),
strerror(eno));
return;
}
if (le == NULL) {
syslog(LOG_INFO, gettext(
"libsldap: set_ld_error: TSD pointer is NULL."));
return;
}
le->le_errno = err;
if (le->le_matched != NULL) {
ldap_memfree(le->le_matched);
le->le_matched = NULL;
}
le->le_matched = matched;
if (le->le_errmsg != NULL) {
ldap_memfree(le->le_errmsg);
le->le_errmsg = NULL;
}
le->le_errmsg = errmsg;
}
static int
conn_tsd_check(ns_conn_mgmt_t *cmg)
{
if (conn_tsd_setup(cmg) != 0)
return (NS_LDAP_MEMORY);
return (NS_LDAP_SUCCESS);
}
static int
get_ld_error(char **matched, char **errmsg, void *dummy)
{
struct ldap_error *le;
int eno;
if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
syslog(LOG_ERR, gettext(
"libsldap: get_ld_error: thr_getspecific failed (%s)"),
strerror(eno));
return (eno);
}
if (le == NULL)
le = &ldap_error_NULL;
if (matched != NULL) {
*matched = le->le_matched;
}
if (errmsg != NULL) {
*errmsg = le->le_errmsg;
}
return (le->le_errno);
}
static void
set_errno(int err)
{
errno = err;
}
static int
get_errno(void)
{
return (errno);
}
static void *
ltf_threadid(void)
{
return ((void *)(uintptr_t)thr_self());
}
static int
setup_mt_conn(LDAP *ld)
{
struct ldap_thread_fns tfns;
struct ldap_extra_thread_fns extrafns;
int rc;
(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
tfns.ltf_get_errno = get_errno;
tfns.ltf_set_errno = set_errno;
tfns.ltf_get_lderrno = get_ld_error;
tfns.ltf_set_lderrno = set_ld_error;
tfns.ltf_lderrno_arg = NULL;
rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
(void *) &tfns);
if (rc < 0) {
syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
"(LDAP_OPT_THREAD_FN_PTRS)"));
return (0);
}
(void) memset(&extrafns, '\0',
sizeof (struct ldap_extra_thread_fns));
extrafns.ltf_threadid_fn = ltf_threadid;
extrafns.ltf_mutex_trylock = NULL;
extrafns.ltf_sema_alloc = NULL;
extrafns.ltf_sema_free = NULL;
extrafns.ltf_sema_wait = NULL;
extrafns.ltf_sema_post = NULL;
rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
(void *) &extrafns);
if (rc < 0) {
syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"));
return (0);
}
return (1);
}
static int
setup_mt_ld(LDAP *ld, ns_conn_mgmt_t *cmg)
{
thread_t t = thr_self();
if (conn_tsd_setup(cmg) == -1) {
syslog(LOG_WARNING,
gettext("libsldap: tid= %d: unable to set up TSD\n"), t);
return (-1);
}
if (setup_mt_conn(ld) == 0) {
syslog(LOG_WARNING, gettext("libsldap: tid= %d: multiple "
"threads per connection not supported\n"), t);
conn_tsd_free();
return (-1);
}
return (0);
}
static boolean_t
check_nscd_proc(pid_t pid, boolean_t check_uid)
{
psinfo_t pinfo;
char fname[MAXPATHLEN];
ssize_t ret;
int fd;
if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
if ((fd = open(fname, O_RDONLY)) >= 0) {
ret = read(fd, &pinfo, sizeof (psinfo_t));
(void) close(fd);
if ((ret == sizeof (psinfo_t)) &&
(strcmp(pinfo.pr_fname, "nscd") == 0)) {
if (check_uid && (pinfo.pr_uid != 0))
return (B_FALSE);
return (B_TRUE);
}
}
}
return (B_FALSE);
}
boolean_t
__s_api_peruser_proc(void)
{
pid_t my_ppid;
static mutex_t nscdLock = DEFAULTMUTEX;
static pid_t checkedPpid = (pid_t)-1;
static boolean_t isPeruserNscd = B_FALSE;
my_ppid = getppid();
if (my_ppid == checkedPpid) {
return (isPeruserNscd);
}
(void) mutex_lock(&nscdLock);
if (my_ppid == checkedPpid) {
(void) mutex_unlock(&nscdLock);
return (isPeruserNscd);
}
isPeruserNscd = B_FALSE;
if (check_nscd_proc(getpid(), B_FALSE)) {
isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
}
checkedPpid = my_ppid;
(void) mutex_unlock(&nscdLock);
return (isPeruserNscd);
}
boolean_t
__s_api_nscd_proc(void)
{
pid_t my_pid;
static mutex_t nscdLock = DEFAULTMUTEX;
static pid_t checkedPid = (pid_t)-1;
static boolean_t isMainNscd = B_FALSE;
if (getuid() != 0)
return (B_FALSE);
my_pid = getpid();
if (my_pid == checkedPid) {
return (isMainNscd);
}
(void) mutex_lock(&nscdLock);
if (my_pid == checkedPid) {
(void) mutex_unlock(&nscdLock);
return (isMainNscd);
}
isMainNscd = check_nscd_proc(my_pid, B_FALSE);
checkedPid = my_pid;
(void) mutex_unlock(&nscdLock);
return (isMainNscd);
}
ns_conn_mgmt_t *
init_conn_mgmt()
{
ns_conn_mgmt_t *cmg;
cmg = (ns_conn_mgmt_t *)calloc(1, sizeof (*cmg));
if (cmg == NULL) {
syslog(LOG_ERR, NS_CONN_MSG_NO_CONN_MGMT);
return (NULL);
}
cmg->is_nscd = __s_api_nscd_proc();
cmg->is_peruser_nscd = __s_api_peruser_proc();
cmg->ldap_mt = B_TRUE;
cmg->state = NS_CONN_MGMT_INACTIVE;
(void) mutex_init(&cmg->lock, USYNC_THREAD, NULL);
(void) mutex_init(&cmg->cfg_lock, USYNC_THREAD, NULL);
cmg->pid = getpid();
if (cmg->is_nscd == B_TRUE || cmg->is_peruser_nscd == B_TRUE)
cmg->state = NS_CONN_MGMT_ACTIVE;
cmg->config = __s_api_get_default_config_global();
if (cmg->config == NULL)
cmg->config = __s_api_loadrefresh_config_global();
if (cmg->config != NULL) {
if (cmg->is_nscd == B_TRUE)
(cmg->config)->paramList[NS_LDAP_EXP_P].ns_tm = 0;
cmg->cfg_cookie = cmg->config->config_cookie;
}
return (cmg);
}
static void
mark_shutdown_or_reloaded(int op)
{
ns_conn_mgmt_t *cmg = ns_connmgmt;
(void) mutex_lock(&cmg->lock);
if (op == NS_CONN_MGMT_OP_SHUTDOWN)
cmg->shutting_down = B_TRUE;
else
cmg->cfg_reloaded = B_TRUE;
atomic_inc_uint(&cmg->ref_cnt);
cmg->state = NS_CONN_MGMT_DETACHED;
if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG)
__s_api_init_config_global(NULL);
(void) mutex_unlock(&cmg->lock);
}
static ns_conn_mgmt_t *
get_current_conn_mgmt(int op)
{
ns_conn_mgmt_t *cmg = ns_connmgmt;
static pid_t checked_pid = (pid_t)-1;
pid_t mypid;
mypid = getpid();
if (cmg == NULL || checked_pid != mypid) {
checked_pid = mypid;
if (cmg == NULL || cmg->pid != mypid) {
if (cmg != NULL) {
ns_connmgmt_parent = cmg;
ns_connmgmt_parent->state =
NS_CONN_MGMT_DETACHED;
}
ns_connmgmt = init_conn_mgmt();
cmg = ns_connmgmt;
if (op == NS_CONN_MGMT_OP_REF)
atomic_inc_uint(&cmg->ref_cnt);
}
}
return (cmg);
}
static ns_conn_mgmt_t *
access_conn_mgmt(int op)
{
ns_conn_mgmt_t *cmg = NULL;
ns_conn_mgmt_t *cmg_prev;
(void) mutex_lock(&ns_connmgmt_lock);
if (ns_connmgmt_shutting_down == B_TRUE) {
(void) mutex_unlock(&ns_connmgmt_lock);
return (NULL);
}
if (op == NS_CONN_MGMT_OP_SHUTDOWN) {
ns_connmgmt_shutting_down = B_TRUE;
if (ns_connmgmt != NULL) {
cmg = ns_connmgmt;
mark_shutdown_or_reloaded(op);
ns_connmgmt = NULL;
}
(void) mutex_unlock(&ns_connmgmt_lock);
return (cmg);
}
if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
op == NS_CONN_MGMT_OP_NEW_CONFIG) {
cmg_prev = ns_connmgmt;
mark_shutdown_or_reloaded(op);
ns_connmgmt = NULL;
}
cmg = get_current_conn_mgmt(op);
if (cmg == NULL) {
(void) mutex_unlock(&ns_connmgmt_lock);
return (NULL);
}
atomic_inc_uint(&cmg->ref_cnt);
if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
op == NS_CONN_MGMT_OP_NEW_CONFIG)
cmg = cmg_prev;
else {
if (cmg->config == NULL)
cmg->config = __s_api_get_default_config();
}
(void) mutex_unlock(&ns_connmgmt_lock);
return (cmg);
}
static void
free_conn_mgmt(ns_conn_mgmt_t *cmg)
{
union {
ldap_data_t s_d;
char s_b[1024];
} space;
ldap_data_t *sptr;
int ndata;
int adata;
int rc;
ldap_get_chg_cookie_t cookie;
if (cmg == NULL)
return;
cookie = cmg->cfg_cookie;
__s_api_free2dArray(cmg->pservers);
if (cmg->config != NULL) {
if (cmg->state == NS_CONN_MGMT_DETACHED)
__s_api_destroy_config(cmg->config);
else
__s_api_release_config(cmg->config);
}
if (cmg->procchg_started == B_TRUE) {
if (cmg->procchg_tid != thr_self()) {
if (cmg->procchg_door_call == B_TRUE) {
adata = sizeof (ldap_call_t) + 1;
ndata = sizeof (space);
space.s_d.ldap_call.ldap_callnumber =
GETSTATUSCHANGE;
space.s_d.ldap_call.ldap_u.get_change.op =
NS_STATUS_CHANGE_OP_STOP;
space.s_d.ldap_call.ldap_u.get_change.cookie =
cookie;
sptr = &space.s_d;
rc = __ns_ldap_trydoorcall(&sptr, &ndata,
&adata);
if (rc != NS_CACHE_SUCCESS)
syslog(LOG_INFO,
gettext("libsldap: "
"free_conn_mgmt():"
" stopping door call "
" GETSTATUSCHANGE failed "
" (rc = %d)"), rc);
}
(void) pthread_cancel(cmg->procchg_tid);
cmg->procchg_started = B_FALSE;
}
}
free(cmg);
}
static ns_conn_mgmt_t *
release_conn_mgmt(ns_conn_mgmt_t *cmg, boolean_t unlock_cmg)
{
if (cmg == NULL)
return (NULL);
if (atomic_dec_uint_nv(&cmg->ref_cnt) == 0) {
if (cmg->state == NS_CONN_MGMT_DETACHED) {
if (unlock_cmg == B_TRUE)
(void) mutex_unlock(&cmg->lock);
free_conn_mgmt(cmg);
__s_api_free_sessionPool();
return (NULL);
} else {
syslog(LOG_WARNING,
gettext("libsldap: connection management "
" has a refcount of zero but the state "
" is not DETACHED (%d)"), cmg->state);
cmg = NULL;
}
}
return (cmg);
}
ns_conn_mgmt_t *
__s_api_conn_mgmt_init()
{
if (thr_keycreate_once(&ns_mtckey, ns_mtckey_cleanup) != 0) {
syslog(LOG_WARNING, NS_CONN_MSG_NO_MTC_KEY);
return (NULL);
}
if (thr_keycreate_once(&ns_cmgkey, NULL) != 0) {
syslog(LOG_WARNING, NS_CONN_MSG_NO_CMG_KEY);
return (NULL);
}
return (access_conn_mgmt(NS_CONN_MGMT_OP_LIB_INIT));
}
ns_conn_user_t *
__s_api_conn_user_init(int type, void *userinfo, boolean_t referral)
{
ns_conn_user_t *cu;
ns_conn_mgmt_t *cmg;
(void) thr_setspecific(ns_cmgkey, NULL);
cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
if (cmg == NULL)
return (NULL);
if (cmg->state != NS_CONN_MGMT_ACTIVE &&
cmg->state != NS_CONN_MGMT_INACTIVE) {
atomic_dec_uint(&cmg->ref_cnt);
return (NULL);
}
cu = (ns_conn_user_t *)calloc(1, sizeof (*cu));
if (cu == NULL) {
atomic_dec_uint(&cmg->ref_cnt);
return (NULL);
}
cu->type = type;
cu->state = NS_CONN_USER_ALLOCATED;
cu->tid = thr_self();
cu->userinfo = userinfo;
cu->referral = referral;
cu->ns_rc = NS_LDAP_SUCCESS;
cu->conn_mgmt = cmg;
(void) conn_tsd_setup(cmg);
return (cu);
}
void
__s_api_conn_user_free(ns_conn_user_t *cu)
{
ns_conn_mgmt_t *cmg;
if (cu == NULL)
return;
cu->state = NS_CONN_USER_FREED;
if (cu->ns_error != NULL)
(void) __ns_ldap_freeError(&cu->ns_error);
cmg = cu->conn_mgmt;
conn_tsd_free();
(void) release_conn_mgmt(cmg, B_FALSE);
(void) free(cu);
}
static ns_conn_mt_t *
init_conn_mt(ns_conn_mgmt_t *cmg, ns_ldap_error_t **ep)
{
ns_conn_mt_t *cm;
ns_conn_mgmt_t *cmg_a;
cm = (ns_conn_mt_t *)calloc(1, sizeof (*cm));
if (cm == NULL) {
if (ep != NULL)
*ep = __s_api_make_error(NS_LDAP_MEMORY, NULL);
return (NULL);
}
cmg_a = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
if (cmg_a != cmg) {
if (cmg_a != NULL) {
(void) release_conn_mgmt(cmg_a, B_FALSE);
if (ep != NULL)
*ep = __s_api_make_error(NS_LDAP_OP_FAILED,
NS_CONN_MSG_SHUTDOWN_RELOADED);
}
return (NULL);
}
(void) mutex_init(&cm->lock, USYNC_THREAD, NULL);
cm->state = NS_CONN_MT_CONNECTING;
cm->tid = thr_self();
cm->pid = getpid();
cm->next = NULL;
cm->cu_head = NULL;
cm->cu_tail = NULL;
cm->conn = NULL;
cm->conn_mgmt = cmg;
return (cm);
}
static ns_conn_mgmt_t *
free_conn_mt(ns_conn_mt_t *cm, int unlock_cmg)
{
ns_conn_mgmt_t *cmg;
if (cm == NULL)
return (NULL);
if (cm->ns_error != NULL)
(void) __ns_ldap_freeError(&cm->ns_error);
if (cm->conn != NULL) {
if (cm->conn->ld != NULL)
(void) ldap_unbind(cm->conn->ld);
__s_api_freeConnection(cm->conn);
}
cmg = cm->conn_mgmt;
free(cm);
return (release_conn_mgmt(cmg, unlock_cmg));
}
static void
add_cu2cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
{
if (cm->cu_head == NULL) {
cm->cu_head = cu;
cm->cu_tail = cu;
} else {
cm->cu_tail->next = cu;
cm->cu_tail = cu;
}
cm->cu_cnt++;
}
static void
add_cm2cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
{
if (cmg->cm_head == NULL) {
cmg->cm_head = cm;
cmg->cm_tail = cm;
} else {
if (cm->opened_for == NS_CONN_USER_WRITE) {
cm->next = cmg->cm_head;
cmg->cm_head = cm;
} else {
cmg->cm_tail->next = cm;
cmg->cm_tail = cm;
}
}
cmg->cm_cnt++;
}
static void
del_cu4cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
{
ns_conn_user_t *pu, *u;
if (cu == NULL || cm->cu_head == NULL || cm->cu_cnt == 0)
return;
if (cm->cu_head == cm->cu_tail) {
if (cu == cm->cu_head) {
cm->cu_head = cm->cu_tail = NULL;
cm->cu_cnt = 0;
cu->next = NULL;
}
return;
}
if (cu == cm->cu_head) {
cm->cu_head = cu->next;
cm->cu_cnt--;
cu->next = NULL;
return;
}
pu = cm->cu_head;
for (u = cm->cu_head->next; u; u = u->next) {
if (cu == u)
break;
pu = u;
}
if (pu != cm->cu_tail) {
pu->next = cu->next;
if (pu->next == NULL)
cm->cu_tail = pu;
cm->cu_cnt--;
cu->next = NULL;
} else {
syslog(LOG_INFO, gettext(
"libsldap: del_cu4cm(): connection user not found"));
}
}
static void
del_cm4cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
{
ns_conn_mt_t *pm, *m;
if (cm == NULL || cmg->cm_head == NULL || cmg->cm_cnt == 0)
return;
if (cmg->cm_head == cmg->cm_tail) {
if (cm == cmg->cm_head) {
cmg->cm_head = cmg->cm_tail = NULL;
cmg->cm_cnt = 0;
cm->next = NULL;
}
return;
}
if (cm == cmg->cm_head) {
cmg->cm_head = cm->next;
cmg->cm_cnt--;
cm->next = NULL;
return;
}
pm = cmg->cm_head;
for (m = cmg->cm_head->next; m; m = m->next) {
if (cm == m)
break;
pm = m;
}
if (pm != cmg->cm_tail) {
pm->next = cm->next;
if (pm->next == NULL)
cmg->cm_tail = pm;
cmg->cm_cnt--;
cm->next = NULL;
} else {
syslog(LOG_INFO, gettext(
"libsldap: del_cm4cmg(): MT connection not found"));
}
}
static boolean_t
is_server_cred_matched(const char *server, const ns_cred_t *cred,
ns_conn_mt_t *cm)
{
Connection *cp = cm->conn;
if (server != NULL && *server != 0) {
if (strcasecmp(server, cp->serverAddr) != 0)
return (B_FALSE);
}
if (cred == NULL)
return (B_TRUE);
return (__s_api_is_auth_matched(cp->auth, cred));
}
static int
wait_for_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t *cm)
{
cu->state = NS_CONN_USER_WAITING;
add_cu2cm(cu, cm);
cu->conn_mt = cm;
(void) mutex_unlock(&cm->lock);
(void) mutex_unlock(&(cm->conn_mgmt)->lock);
(void) mutex_lock(&cm->lock);
if (cm->state == NS_CONN_MT_CONNECTING) {
(void) conn_wait(cm, cu);
cu->state = NS_CONN_USER_WOKEUP;
}
if (cm->state == NS_CONN_MT_CONNECTED)
return (1);
else {
del_cu4cm(cu, cm);
cu->conn_mt = NULL;
cu->bad_mt_conn = B_FALSE;
return (0);
}
}
static boolean_t
check_and_close_conn(ns_conn_mgmt_t *cmg, ns_conn_mt_t **cm,
ns_conn_user_t *cu)
{
int rc;
int j;
int svridx = -1;
int upidx = -1;
int free_cm;
ns_server_info_t sinfo;
ns_ldap_error_t *errorp = NULL;
if (cmg->pservers_loaded == B_FALSE)
get_preferred_servers(B_FALSE, B_FALSE, cmg);
if (cmg->pservers == NULL)
return (B_FALSE);
rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
&sinfo, &errorp, NS_CACHE_ADDR_IP);
if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
(void) __ns_ldap_freeError(&errorp);
return (B_FALSE);
}
for (j = 0; cmg->pservers[j] != NULL; j++) {
if (strcasecmp(sinfo.server, cmg->pservers[j]) != 0)
continue;
upidx = j;
break;
}
for (j = 0; cmg->pservers[j] != NULL; j++) {
if (strcasecmp(cmg->pservers[j], (*cm)->conn->serverAddr) != 0)
continue;
svridx = j;
break;
}
if ((upidx == -1 && svridx == -1) ||
(upidx != -1 && svridx != -1 && upidx == svridx)) {
__s_api_free_server_info(&sinfo);
return (B_FALSE);
}
if (upidx != -1 && (svridx == -1 || upidx < svridx)) {
if ((*cm)->opened_for == NS_CONN_USER_WRITE ||
(*cm)->referral == B_TRUE) {
__s_api_free_server_info(&sinfo);
return (B_FALSE);
}
free_cm = close_conn_mt_when_nouser(*cm);
if (cmg->shutting_down == B_FALSE)
cu->retry = B_TRUE;
} else {
ns_ldap_error_t *ep;
ep = __s_api_make_error(LDAP_SERVER_DOWN,
NS_CONN_MSG_DOWN_FROM_CACHEMGR);
free_cm = close_conn_mt(*cm, LDAP_SERVER_DOWN, &ep, NULL);
if (cmg->shutting_down == B_FALSE)
cu->retry = B_TRUE;
(void) __ns_ldap_freeError(&ep);
}
(void) mutex_unlock(&(*cm)->lock);
if (free_cm == 1) {
(void) free_conn_mt(*cm, 0);
*cm = NULL;
}
__s_api_free_server_info(&sinfo);
return (B_TRUE);
}
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)
{
boolean_t matched = B_FALSE;
boolean_t drop_conn;
int free_cm = 0;
ns_conn_mt_t *cm = *cmt;
ns_conn_mgmt_t *cmg = cm->conn_mgmt;
if (cm->state != st || cm->close_when_nouser == B_TRUE ||
cm->detached == B_TRUE || cm->pid != getpid() ||
cm->referral != cu->referral) {
(void) mutex_unlock(&cm->lock);
return (B_FALSE);
}
if (cm->opened_for == NS_CONN_USER_WRITE &&
cu->type != NS_CONN_USER_WRITE && cm->cu_cnt == 0 &&
((time(NULL) - cm->access_time) > 60)) {
free_cm = close_conn_mt(cm, NS_LDAP_INTERNAL, NULL, NULL);
(void) mutex_unlock(&cm->lock);
if (free_cm == 1) {
(void) free_conn_mt(cm, 0);
*cmt = NULL;
}
return (B_FALSE);
}
switch (cu->type) {
case NS_CONN_USER_SEARCH:
case NS_CONN_USER_GETENT:
if (cm->opened_for == NS_CONN_USER_SEARCH ||
cm->opened_for == NS_CONN_USER_GETENT)
matched = B_TRUE;
break;
case NS_CONN_USER_WRITE:
if (cm->opened_for == NS_CONN_USER_WRITE)
matched = B_TRUE;
break;
default:
matched = B_FALSE;
break;
}
if (matched == B_TRUE && ((server != NULL || cred != NULL) &&
cm->conn != NULL &&
is_server_cred_matched(server, cred, cm) == B_FALSE))
matched = B_FALSE;
if (matched != B_FALSE) {
if (cm->state == NS_CONN_MT_CONNECTED &&
cmg->is_nscd == B_FALSE && !__s_api_isStandalone()) {
drop_conn = check_and_close_conn(cmg, &cm, cu);
if (drop_conn == B_TRUE) {
if (cm == NULL)
*cmt = NULL;
return (B_FALSE);
}
}
if ((cm->state == NS_CONN_MT_CONNECTED &&
cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
cm->cu_cnt >= cm->cu_max) ||
(cm->state == NS_CONN_MT_CONNECTING &&
cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
cm->waiter_cnt >= cm->cu_max - 1))
matched = B_FALSE;
}
if (matched == B_FALSE)
(void) mutex_unlock(&cm->lock);
return (matched);
}
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)
{
int rc;
int i;
ns_conn_mt_t *cn;
ns_conn_mt_state_t st;
ns_conn_mgmt_t *cmg;
if (errorp == NULL || cu == NULL || session == NULL)
return (NS_LDAP_INVALID_PARAM);
*session = NULL;
cmg = cu->conn_mgmt;
if (cu->type == NS_CONN_USER_AUTH)
return (NS_LDAP_NOTFOUND);
if (flags & NS_LDAP_NEW_CONN)
return (NS_LDAP_NOTFOUND);
if (flags & NS_LDAP_KEEP_CONN)
cu->keep_conn = B_TRUE;
if (!((cmg->state == NS_CONN_MGMT_INACTIVE &&
cu->keep_conn == B_TRUE) || cmg->state == NS_CONN_MGMT_ACTIVE))
return (NS_LDAP_NOTFOUND);
cu->use_mt_conn = B_TRUE;
NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errorp);
st = NS_CONN_MT_CONNECTED;
cu->state = NS_CONN_USER_FINDING;
for (i = 0; i < 2; i++) {
for (cn = cmg->cm_head; cn; cn = cn->next) {
(void) mutex_lock(&cn->lock);
rc = match_conn_mt(cu, &cn, st, server, cred);
if (rc == B_FALSE && cn != NULL)
continue;
if (cn == NULL) {
st = NS_CONN_MT_CONNECTED;
i = -1;
break;
}
if (cn->state == NS_CONN_MT_CONNECTED) {
*session = cn->conn;
add_cu2cm(cu, cn);
cu->conn_mt = cn;
cu->state = NS_CONN_USER_CONNECTED;
(void) mutex_unlock(&cn->lock);
(void) mutex_unlock(&cmg->lock);
return (NS_LDAP_SUCCESS);
}
if (cn->state != NS_CONN_MT_CONNECTING ||
cn->cu_max == 1) {
(void) mutex_unlock(&cn->lock);
continue;
}
if (wait_for_conn_mt(cu, cn) != 1) {
if (cn->ns_rc != NS_LDAP_NOTFOUND)
cu->state = NS_CONN_USER_CONNECT_ERROR;
else {
cu->state = NS_CONN_USER_FINDING;
cu->use_mt_conn = B_FALSE;
}
(void) mutex_unlock(&cn->lock);
return (cn->ns_rc);
}
*session = cn->conn;
cu->state = NS_CONN_USER_CONNECTED;
(void) mutex_unlock(&cn->lock);
return (NS_LDAP_SUCCESS);
}
if (i == 0)
st = NS_CONN_MT_CONNECTING;
}
cn = init_conn_mt(cmg, errorp);
if (cn == NULL) {
(void) mutex_unlock(&cmg->lock);
return ((*errorp)->status);
}
cu->conn_mt = cn;
cn->opened_for = cu->type;
cn->referral = cu->referral;
if (cmg->ldap_mt == B_TRUE)
cn->cu_max = NS_CONN_MT_USER_MAX;
else
cn->cu_max = 1;
add_cm2cmg(cn, cmg);
(void) mutex_unlock(&cmg->lock);
return (NS_LDAP_NOTFOUND);
}
int
__s_api_conn_mt_add(Connection *con, ns_conn_user_t *cu, ns_ldap_error_t **ep)
{
ns_conn_mgmt_t *cmg = cu->conn_mgmt;
ns_conn_mt_t *cm = cu->conn_mt;
NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
if (cmg->procchg_started == B_FALSE && cmg->is_nscd == B_TRUE) {
start_thread(cmg);
cmg->procchg_started = B_TRUE;
}
(void) mutex_lock(&cm->lock);
cm->conn = con;
cm->state = NS_CONN_MT_CONNECTED;
cm->pid = getpid();
cm->create_time = time(NULL);
cm->access_time = cm->create_time;
cm->opened_for = cu->type;
add_cu2cm(cu, cm);
cu->conn_mt = cm;
cu->state = NS_CONN_USER_CONNECTED;
if (cmg->ldap_mt == B_TRUE)
cm->cu_max = NS_CONN_MT_USER_MAX;
else
cm->cu_max = 1;
(void) conn_signal(cm);
(void) mutex_unlock(&cm->lock);
(void) mutex_unlock(&cmg->lock);
return (NS_LDAP_SUCCESS);
}
void
__s_api_conn_mt_return(ns_conn_user_t *cu)
{
ns_conn_mt_t *cm;
ns_conn_mgmt_t *cmg;
if (cu == NULL || cu->use_mt_conn == B_FALSE)
return;
cm = cu->conn_mt;
if (cm == NULL)
return;
cmg = cu->conn_mgmt;
(void) mutex_lock(&cm->lock);
del_cu4cm(cu, cm);
cu->state = NS_CONN_USER_DISCONNECTED;
cu->conn_mt = NULL;
cu->bad_mt_conn = B_FALSE;
if ((cm->close_when_nouser == B_TRUE ||
cm->state != NS_CONN_MT_CONNECTED) && cm->cu_cnt == 0) {
(void) mutex_unlock(&cm->lock);
(void) mutex_lock(&cmg->lock);
(void) mutex_lock(&cm->lock);
del_cm4cmg(cm, cmg);
NS_CONN_UNLOCK_AND_FREE(ns_conn_free, cm, cmg);
} else {
if (cm->state == NS_CONN_MT_CONNECTED && cm->cu_cnt == 0 &&
cm->conn != NULL && cm->conn->ld != NULL) {
struct timeval zerotime;
LDAPMessage *res;
zerotime.tv_sec = zerotime.tv_usec = 0L;
while (ldap_result(cm->conn->ld, LDAP_RES_ANY,
LDAP_MSG_ALL, &zerotime, &res) > 0) {
if (res != NULL)
(void) ldap_msgfree(res);
}
}
(void) mutex_unlock(&cm->lock);
}
}
static void
err2cm(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp)
{
ns_ldap_error_t *ep;
cm->ns_rc = rc;
cm->ns_error = NULL;
if (errorp != NULL && *errorp != NULL) {
ep = __s_api_copy_error(*errorp);
if (ep == NULL)
cm->ns_rc = NS_LDAP_MEMORY;
else
cm->ns_error = ep;
}
}
static void
err_from_cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
{
ns_ldap_error_t *ep;
cu->ns_rc = cm->ns_rc;
if (cu->ns_error != NULL)
(void) __ns_ldap_freeError(&cu->ns_error);
cu->ns_error = NULL;
if (cm->ns_rc != NS_LDAP_SUCCESS && cm->ns_error != NULL) {
ep = __s_api_copy_error(cm->ns_error);
if (ep == NULL)
cu->ns_rc = NS_LDAP_MEMORY;
else
cu->ns_error = ep;
}
}
static void
err_from_caller(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
{
cu->ns_rc = rc;
if (errorp != NULL) {
if (cu->ns_error != NULL)
(void) __ns_ldap_freeError(&cu->ns_error);
cu->ns_error = *errorp;
*errorp = NULL;
} else
cu->ns_error = NULL;
}
void
__s_api_conn_mt_remove(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
{
ns_conn_mgmt_t *cmg;
ns_conn_mt_t *cm;
int free_cm = 0;
if (cu == NULL || cu->use_mt_conn == B_FALSE)
return;
if ((cm = cu->conn_mt) == NULL)
return;
cmg = cu->conn_mgmt;
(void) mutex_lock(&cmg->lock);
(void) mutex_lock(&cm->lock);
if (cm->state != NS_CONN_MT_CONNECT_ERROR) {
cm->state = NS_CONN_MT_CONNECT_ERROR;
cm->ns_rc = rc;
if (errorp != NULL) {
cm->ns_error = *errorp;
*errorp = NULL;
}
}
err_from_cm(cu, cm);
(void) conn_signal(cm);
del_cu4cm(cu, cm);
cu->conn_mt = NULL;
cu->bad_mt_conn = B_FALSE;
if (cm->cu_cnt == 0) {
del_cm4cmg(cm, cmg);
free_cm = 1;
}
NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
}
int
__s_api_check_libldap_MT_conn_support(ns_conn_user_t *cu, LDAP *ld,
ns_ldap_error_t **ep)
{
int rc;
ns_conn_mgmt_t *cmg;
if (cu->conn_mt == NULL || cu->use_mt_conn == B_FALSE)
return (NS_LDAP_SUCCESS);
cmg = cu->conn_mgmt;
rc = setup_mt_ld(ld, cmg);
if (cmg->do_mt_conn == B_FALSE) {
NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
if (cmg->do_mt_conn == B_FALSE) {
if (rc < 0)
cmg->ldap_mt = B_FALSE;
else {
cmg->ldap_mt = B_TRUE;
if (cmg->is_nscd == B_TRUE ||
cmg->is_peruser_nscd == B_TRUE) {
cmg->do_mt_conn = B_TRUE;
cmg->state = NS_CONN_MGMT_ACTIVE;
}
}
}
(void) mutex_unlock(&cmg->lock);
}
if (rc < 0)
__s_api_conn_mt_remove(cu, NS_LDAP_NOTFOUND, NULL);
return (NS_LDAP_SUCCESS);
}
static int
close_conn_mt(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp,
ns_conn_user_t *cu)
{
ns_conn_mgmt_t *cmg = cm->conn_mgmt;
ns_conn_mt_t *m;
ns_conn_user_t *u;
if ((cm->state != NS_CONN_MT_CONNECTED && cm->state !=
NS_CONN_MT_CLOSING) || cmg->cm_head == NULL || cmg->cm_cnt == 0)
return (-1);
for (m = cmg->cm_head; m; m = m->next) {
if (cm == m)
break;
}
if (m == NULL)
return (-1);
if (cm->state == NS_CONN_MT_CONNECTED) {
cm->state = NS_CONN_MT_CLOSING;
if ((cu != NULL && cm->cu_cnt > 1) ||
(cu == NULL && cm->cu_cnt > 0)) {
err2cm(cm, rc, errorp);
(void) conn_signal(cm);
}
if (cm->cu_head != NULL) {
for (u = cm->cu_head; u; u = u->next) {
u->bad_mt_conn = B_TRUE;
if (cmg->shutting_down == B_FALSE)
u->retry = B_TRUE;
}
}
}
if ((cu != NULL && cm->cu_cnt == 1) ||
(cu == NULL && cm->cu_cnt == 0)) {
del_cm4cmg(cm, cmg);
cm->detached = B_TRUE;
return (1);
}
return (0);
}
void
__s_api_conn_mt_close(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
{
ns_conn_mgmt_t *cmg;
ns_conn_mt_t *cm;
int free_cm = 0;
if (cu == NULL || cu->use_mt_conn == B_FALSE)
return;
if (cu->state != NS_CONN_USER_CONNECTED || (cm = cu->conn_mt) == NULL)
return;
cmg = cu->conn_mgmt;
(void) mutex_lock(&cmg->lock);
(void) mutex_lock(&cm->lock);
free_cm = close_conn_mt(cm, rc, errorp, cu);
if (free_cm == -1) {
(void) mutex_unlock(&cm->lock);
(void) mutex_unlock(&cmg->lock);
return;
}
if (rc != NS_LDAP_SUCCESS) {
err_from_caller(cu, rc, errorp);
} else {
err_from_cm(cu, cm);
}
del_cu4cm(cu, cm);
cu->conn_mt = NULL;
cu->bad_mt_conn = B_FALSE;
if (cmg->shutting_down == B_FALSE)
cu->retry = B_TRUE;
NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
}
static void
close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg)
{
ns_conn_mgmt_t *cmg;
int free_cm = 0;
ns_ldap_error_t *ep;
if (cm == NULL)
return;
cmg = cm->conn_mgmt;
ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
if (ep != NULL) {
ep->status = rc;
if (errmsg != NULL)
ep->message = strdup(errmsg);
}
(void) mutex_lock(&cmg->lock);
(void) mutex_lock(&cm->lock);
free_cm = close_conn_mt(cm, LDAP_SERVER_DOWN, &ep, NULL);
if (free_cm == -1) {
(void) mutex_unlock(&cm->lock);
(void) mutex_unlock(&cmg->lock);
return;
}
(void) __ns_ldap_freeError(&ep);
NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
}
static int
close_conn_mt_when_nouser(ns_conn_mt_t *cm)
{
int free_cm = 0;
if (cm->cu_cnt == 0) {
del_cm4cmg(cm, cm->conn_mgmt);
free_cm = 1;
} else {
cm->close_when_nouser = B_TRUE;
}
return (free_cm);
}
static void
get_preferred_servers(boolean_t lock, boolean_t reload, ns_conn_mgmt_t *cmg)
{
ns_ldap_error_t *errorp = NULL;
void **pservers = NULL;
if (lock == B_TRUE)
(void) mutex_lock(&cmg->lock);
if (cmg->pservers_loaded == B_TRUE && reload == B_FALSE)
return;
if (cmg->pservers != NULL) {
(void) __ns_ldap_freeParam((void ***)&cmg->pservers);
cmg->pservers = NULL;
}
if (__ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
&pservers, &errorp) == NS_LDAP_SUCCESS) {
cmg->pservers = (char **)pservers;
cmg->pservers_loaded = B_TRUE;
} else {
(void) __ns_ldap_freeError(&errorp);
(void) __ns_ldap_freeParam(&pservers);
}
}
static ns_conn_mgmt_t *
proc_server_change(ns_server_status_change_t *chg, ns_conn_mgmt_t *cmg)
{
int cnt, i, j, k, n;
boolean_t loop = B_TRUE;
boolean_t cmg_locked = B_FALSE;
char *s;
ns_conn_mt_t *cm;
ns_conn_mgmt_t *ocmg;
if (chg->config_changed == B_TRUE) {
ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_RELOAD_CONFIG);
shutdown_all_conn_mt(ocmg);
(void) release_conn_mgmt(ocmg, B_FALSE);
(void) release_conn_mgmt(ocmg, B_FALSE);
return (ocmg);
}
if ((cnt = chg->num_server) == 0)
return (cmg);
for (i = 0; i < cnt; i++) {
if (chg->changes[i] != NS_SERVER_DOWN)
continue;
s = chg->servers[i];
while (loop) {
if (cmg_locked == B_FALSE) {
(void) mutex_lock(&cmg->lock);
cmg_locked = B_TRUE;
}
for (cm = cmg->cm_head; cm; cm = cm->next) {
(void) mutex_lock(&cm->lock);
if (cm->state == NS_CONN_MT_CONNECTED &&
cm->conn != NULL &&
strcasecmp(cm->conn->serverAddr, s) == 0) {
(void) mutex_unlock(&cm->lock);
break;
}
(void) mutex_unlock(&cm->lock);
}
if (cm != NULL) {
(void) mutex_unlock(&cmg->lock);
cmg_locked = B_FALSE;
close_conn_mt_by_procchg(cm, LDAP_SERVER_DOWN,
NS_CONN_MSG_DOWN_FROM_CACHEMGR);
continue;
}
break;
}
}
get_preferred_servers(cmg_locked == B_FALSE ? B_TRUE : B_FALSE,
B_FALSE, cmg);
cmg_locked = B_TRUE;
if (cmg->pservers == NULL) {
(void) mutex_unlock(&cmg->lock);
return (cmg);
}
for (i = 0; i < cnt; i++) {
if (chg->changes[i] != NS_SERVER_UP)
continue;
s = chg->servers[i];
k = -1;
loop = B_TRUE;
while (loop) {
if (cmg_locked == B_FALSE) {
(void) mutex_lock(&cmg->lock);
cmg_locked = B_TRUE;
}
if (k == -1) {
for (j = 0; cmg->pservers[j] != NULL; j++) {
if (strcasecmp(cmg->pservers[j],
s) == 0) {
k = j;
break;
}
}
}
if (k == -1) {
break;
}
for (cm = cmg->cm_head; cm; cm = cm->next) {
(void) mutex_lock(&cm->lock);
if (cm->state == NS_CONN_MT_CONNECTED &&
cm->close_when_nouser == B_FALSE &&
cm->conn != NULL && cm->opened_for !=
NS_CONN_USER_WRITE &&
cm->referral == B_FALSE) {
n = -1;
for (j = 0; j <= k; j++) {
if (strcasecmp(
cm->conn->serverAddr,
cmg->pservers[j]) == 0) {
n = j;
break;
}
}
if (n == -1) {
int fr = 0;
fr = close_conn_mt_when_nouser(
cm);
NS_CONN_UNLOCK_AND_FREE(fr,
cm, cmg);
cmg_locked = B_FALSE;
break;
}
}
(void) mutex_unlock(&cm->lock);
}
if (cm == NULL)
loop = B_FALSE;
}
}
if (cmg_locked == B_TRUE)
(void) mutex_unlock(&cmg->lock);
return (cmg);
}
void
shutdown_all_conn_mt(ns_conn_mgmt_t *cmg)
{
ns_ldap_error_t *ep;
ns_conn_mt_t *cm;
int free_cm = 0;
boolean_t done = B_FALSE;
ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
if (ep != NULL) {
ep->message = strdup(NS_CONN_MSG_SHUTDOWN_RELOADED);
}
(void) mutex_lock(&cmg->lock);
while (cmg->cm_head != NULL && done == B_FALSE) {
for (cm = cmg->cm_head; cm; cm = cm->next) {
(void) mutex_lock(&cm->lock);
if (cm->next == NULL)
done = B_TRUE;
free_cm = close_conn_mt(cm, LDAP_OTHER, &ep, NULL);
(void) mutex_unlock(&cm->lock);
if (free_cm == 1) {
(void) free_conn_mt(cm, 0);
break;
}
}
}
(void) mutex_unlock(&cmg->lock);
(void) __ns_ldap_freeError(&ep);
}
void
__s_api_shutdown_conn_mgmt()
{
ns_conn_mgmt_t *cmg;
cmg = access_conn_mgmt(NS_CONN_MGMT_OP_SHUTDOWN);
if (cmg == NULL)
return;
(void) shutdown_all_conn_mt(cmg);
(void) release_conn_mgmt(cmg, B_FALSE);
(void) release_conn_mgmt(cmg, B_FALSE);
}
void
__s_api_reinit_conn_mgmt_new_config(ns_config_t *new_cfg)
{
ns_conn_mgmt_t *cmg;
ns_conn_mgmt_t *ocmg;
cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
if (cmg == NULL)
return;
if (cmg->config == new_cfg || cmg->state == NS_CONN_MGMT_DETACHED) {
(void) release_conn_mgmt(cmg, B_FALSE);
return;
}
ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_NEW_CONFIG);
if (ocmg == cmg)
shutdown_all_conn_mt(ocmg);
(void) release_conn_mgmt(ocmg, B_FALSE);
(void) release_conn_mgmt(ocmg, B_FALSE);
(void) release_conn_mgmt(cmg, B_FALSE);
}
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)
{
boolean_t retry;
ns_conn_user_t *cu = *conn_user;
ns_conn_mgmt_t *cmg;
if (*try_cnt > 0 && cu != NULL) {
if (cu->type == NS_CONN_USER_GETENT && *rc == NS_LDAP_SUCCESS)
return (0);
cmg = cu->conn_mgmt;
retry = cu->retry;
if (cu->conn_mt != NULL)
__s_api_conn_mt_return(cu);
if (cmg != NULL && cmg->cfg_reloaded == B_TRUE)
retry = B_TRUE;
__s_api_conn_user_free(cu);
*conn_user = NULL;
if (*rc == NS_LDAP_SUCCESS || retry != B_TRUE)
return (0);
}
*try_cnt = *try_cnt + 1;
if (*try_cnt > NS_LIST_TRY_MAX)
return (0);
*conn_user = __s_api_conn_user_init(type, NULL, B_FALSE);
if (*conn_user == NULL) {
if (*try_cnt == 1) {
*rc = NS_LDAP_MEMORY;
*errorp = NULL;
}
return (0);
}
if (*try_cnt > 1 && rc != NS_LDAP_SUCCESS && *errorp != NULL)
(void) __ns_ldap_freeError(errorp);
return (1);
}
int
__s_api_setup_getnext(ns_conn_user_t *cu, int *ns_err,
ns_ldap_error_t **errorp)
{
int rc;
ns_conn_mgmt_t *cmg;
if (cu->conn_mt != NULL && (cmg = cu->conn_mgmt) != NULL) {
if (cu->bad_mt_conn == B_TRUE) {
__s_api_conn_mt_close(cu, 0, NULL);
*ns_err = cu->ns_rc;
*errorp = cu->ns_error;
cu->ns_error = NULL;
return (*ns_err);
}
rc = conn_tsd_check(cmg);
if (rc != NS_LDAP_SUCCESS) {
*errorp = NULL;
return (rc);
}
}
return (NS_LDAP_SUCCESS);
}
static int
conn_wait(ns_conn_mt_t *conn_mt, ns_conn_user_t *conn_user)
{
ns_conn_waiter_t mywait;
ns_conn_waiter_t *head = &conn_mt->waiter;
(void) cond_init(&(mywait.waitcv), USYNC_THREAD, 0);
mywait.key = conn_user;
mywait.signaled = 0;
mywait.next = head->next;
mywait.prev = head;
if (mywait.next)
mywait.next->prev = &mywait;
head->next = &mywait;
atomic_inc_uint(&conn_mt->waiter_cnt);
while (!mywait.signaled)
(void) cond_wait(&(mywait.waitcv), &conn_mt->lock);
if (mywait.prev)
mywait.prev->next = mywait.next;
if (mywait.next)
mywait.next->prev = mywait.prev;
return (0);
}
static int
conn_signal(ns_conn_mt_t *conn_mt)
{
int c = 0;
ns_conn_waiter_t *head = &conn_mt->waiter;
ns_conn_waiter_t *tmp = head->next;
while (tmp) {
(void) cond_signal(&(tmp->waitcv));
tmp->signaled = 1;
atomic_dec_uint(&conn_mt->waiter_cnt);
c++;
tmp = tmp->next;
}
return (c);
}
static void *
get_server_change(void *arg)
{
union {
ldap_data_t s_d;
char s_b[DOORBUFFERSIZE];
} space;
ldap_data_t *sptr = &space.s_d;
int ndata;
int adata;
char *ptr;
int ds_cnt;
int door_rc;
int which;
int retry = 0;
boolean_t loop = B_TRUE;
char *c, *oc;
int dslen = strlen(DOORLINESEP);
char dsep = DOORLINESEP_CHR;
char chg_data[DOORBUFFERSIZE];
char **servers = NULL;
boolean_t getchg_not_supported = B_FALSE;
ns_conn_mgmt_t *ocmg = (ns_conn_mgmt_t *)arg;
ns_conn_mgmt_t *cmg;
ns_server_status_t *status = NULL;
ns_server_status_change_t chg = { 0 };
ldap_get_change_out_t *get_chg;
ldap_get_chg_cookie_t cookie;
ldap_get_chg_cookie_t new_cookie;
cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
if (cmg != ocmg)
thr_exit(NULL);
cmg->procchg_tid = thr_self();
(void) conn_tsd_setup(cmg);
cookie = cmg->cfg_cookie;
while (loop) {
if (chg.servers != NULL)
free(chg.servers);
if (chg.changes != NULL)
free(chg.changes);
if (sptr != &space.s_d)
(void) munmap((char *)sptr, sizeof (space));
if (cmg == NULL || cmg->state == NS_CONN_MGMT_DETACHED ||
getchg_not_supported == B_TRUE) {
if (cmg != NULL) {
cmg->procchg_started = B_FALSE;
(void) release_conn_mgmt(cmg, B_FALSE);
}
conn_tsd_free();
thr_exit(NULL);
}
(void) memset(space.s_b, 0, DOORBUFFERSIZE);
(void) memset(&chg, 0, sizeof (chg));
adata = sizeof (ldap_call_t) + 1;
ndata = sizeof (space);
space.s_d.ldap_call.ldap_callnumber = GETSTATUSCHANGE;
space.s_d.ldap_call.ldap_u.get_change.op =
NS_STATUS_CHANGE_OP_START;
space.s_d.ldap_call.ldap_u.get_change.cookie = cookie;
sptr = &space.s_d;
door_rc = __ns_ldap_trydoorcall_getfd();
cmg->procchg_door_call = B_TRUE;
if (release_conn_mgmt(cmg, B_FALSE) == NULL) {
conn_tsd_free();
thr_exit(NULL);
}
if (door_rc == NS_CACHE_SUCCESS)
door_rc = __ns_ldap_trydoorcall_send(&sptr, &ndata,
&adata);
cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
if (cmg != NULL)
cmg->procchg_door_call = B_FALSE;
if (cmg != ocmg) {
if (cmg != NULL) {
cmg->procchg_started = B_FALSE;
(void) release_conn_mgmt(cmg, B_FALSE);
}
conn_tsd_free();
thr_exit(NULL);
}
if (door_rc != NS_CACHE_SUCCESS) {
if (door_rc == NS_CACHE_NOSERVER) {
if (retry++ > 10)
getchg_not_supported = B_TRUE;
else {
(void) sleep(2);
}
} else if (door_rc == NS_CACHE_NOTFOUND)
getchg_not_supported = B_TRUE;
continue;
} else
retry = 0;
get_chg = &sptr->ldap_ret.ldap_u.changes;
ptr = get_chg->data;
if (get_chg->type == NS_STATUS_CHANGE_TYPE_CONFIG) {
chg.config_changed = B_TRUE;
cmg = proc_server_change(&chg, cmg);
continue;
}
if (get_chg->type == NS_STATUS_CHANGE_TYPE_SERVER) {
new_cookie = get_chg->cookie;
if (new_cookie.mgr_pid != cookie.mgr_pid ||
new_cookie.seq_num != cookie.seq_num) {
chg.config_changed = B_TRUE;
cmg = proc_server_change(&chg, cmg);
continue;
}
(void) strlcpy(chg_data, ptr, sizeof (chg_data));
chg.num_server = get_chg->server_count;
servers = (char **)calloc(chg.num_server,
sizeof (char *));
if (servers == NULL) {
syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
continue;
}
status = (ns_server_status_t *)calloc(chg.num_server,
sizeof (int));
if (status == NULL) {
syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
free(servers);
continue;
}
ds_cnt = 0;
which = 0;
oc = ptr;
for (c = ptr; which != 2; c++) {
if (*c != dsep && *c != '\0')
continue;
if (*c == dsep) {
*c = '\0';
c += dslen;
}
if (which == 0) {
servers[ds_cnt] = oc;
oc = c;
which = 1;
continue;
}
if (strcmp(NS_SERVER_CHANGE_UP, oc) == 0) {
status[ds_cnt] = NS_SERVER_UP;
} else if (strcmp(NS_SERVER_CHANGE_DOWN,
oc) == 0)
status[ds_cnt] = NS_SERVER_DOWN;
else {
syslog(LOG_INFO,
NS_CONN_MSG_BAD_CACHEMGR_DATA);
continue;
}
oc = c;
ds_cnt++;
if (*c == '\0')
which = 2;
else
which = 0;
}
chg.servers = servers;
chg.changes = status;
cmg = proc_server_change(&chg, cmg);
continue;
}
}
return (NULL);
}
static void
start_thread(ns_conn_mgmt_t *cmg)
{
int errnum;
if (thr_create(NULL, 0, get_server_change,
(void *)cmg, THR_DETACHED, NULL) != 0) {
errnum = errno;
syslog(LOG_WARNING, NS_CONN_MSG_NO_PROCCHG_THREAD,
strerror(errnum));
}
}