4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
39 #include "solaris-int.h"
40 #include "ns_connmgmt.h"
41 #include "ns_cache_door.h"
42 #include "ns_internal.h"
45 * Access (reference, shutdown, or reload) the current connection
46 * management control structure conn_mgmt_t.
48 #define NS_CONN_MGMT_OP_REF 1
49 #define NS_CONN_MGMT_OP_SHUTDOWN 2
50 #define NS_CONN_MGMT_OP_RELOAD_CONFIG 3
51 #define NS_CONN_MGMT_OP_NEW_CONFIG 4
52 #define NS_CONN_MGMT_OP_LIB_INIT 5
54 static ns_conn_mgmt_t
*access_conn_mgmt(int);
55 static ns_conn_mgmt_t
*release_conn_mgmt(ns_conn_mgmt_t
*, boolean_t
);
56 static int close_conn_mt(ns_conn_mt_t
*, int, ns_ldap_error_t
**,
58 static int close_conn_mt_when_nouser(ns_conn_mt_t
*cm
);
59 void shutdown_all_conn_mt(ns_conn_mgmt_t
*cmg
);
60 static int conn_signal(ns_conn_mt_t
*);
61 static int conn_wait(ns_conn_mt_t
*, ns_conn_user_t
*);
62 static void close_conn_mt_by_procchg(ns_conn_mt_t
*cm
, int rc
, char *errmsg
);
63 static ns_conn_mgmt_t
*proc_server_change(ns_server_status_change_t
*chg
,
65 static void get_preferred_servers(boolean_t
, boolean_t
, ns_conn_mgmt_t
*);
66 static void start_thread();
68 static ns_conn_mgmt_t
*ns_connmgmt
= NULL
;
69 static ns_conn_mgmt_t
*ns_connmgmt_parent
= NULL
;
70 static mutex_t ns_connmgmt_lock
= DEFAULTMUTEX
;
71 static boolean_t ns_connmgmt_shutting_down
= B_FALSE
;
73 #define NS_CONN_MSG_NO_CONN_MGMT gettext( \
74 "libsldap: unable to allocate the connection management control")
75 #define NS_CONN_MSG_NO_MTC_KEY gettext( \
76 "libsldap: unable to allocate the TSD key for per-thread ldap error")
77 #define NS_CONN_MSG_NO_CMG_KEY gettext( \
78 "libsldap: unable to allocate the TSD key for connection management")
79 #define NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded")
80 #define NS_CONN_MSG_RELOADED gettext( \
81 "libsldap: configuration has been reloaded")
82 #define NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \
83 "libsldap: library unloaded or configuration has been reloaded")
84 #define NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \
85 "libsldap: received incorrect data from ldap_cachemgr")
86 #define NS_CONN_MSG_MEMORY_ERROR gettext( \
87 "libsldap: unable to allocate memory")
88 #define NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \
89 "libsldap: unable to start the server monitor thread (%s)")
90 #define NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \
91 "libsldap: server down reported by ldap_cachemgr")
93 static int ns_conn_free
= 1;
94 #define NS_CONN_UNLOCK_AND_FREE(free, cm, cmg) \
96 (void) mutex_unlock(&(cm)->lock); \
98 cmg = free_conn_mt((cm), 1); \
100 (void) mutex_unlock(&(cmg)->lock); \
103 #define NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \
106 (void) mutex_lock(&(cmg)->lock); \
107 if ((cmg)->shutting_down == B_TRUE) \
108 msg = NS_CONN_MSG_SHUTDOWN; \
109 else if ((cmg)->cfg_reloaded == B_TRUE) \
110 msg = NS_CONN_MSG_RELOADED; \
112 (*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \
113 (void) mutex_unlock(&(cmg)->lock); \
114 return (NS_LDAP_OP_FAILED); \
119 * TSD keys ns_mtckey and ns_cmgkey are for sharing ldap connections
120 * and their associated connection management structure among
121 * multiple threads. The pointers to the per-thread ldap error
122 * information and the connection management structure are
123 * saved in ns_mtckey and ns_cmgkey.
125 thread_key_t ns_mtckey
= THR_ONCE_KEY
;
126 thread_key_t ns_cmgkey
= THR_ONCE_KEY
;
128 /* Per thread LDAP error resides in thread-specific data (ns_mtckey) */
135 /* NULL struct ldap_error */
136 static struct ldap_error ldap_error_NULL
= { LDAP_SUCCESS
, NULL
, NULL
};
138 /* destructor: free the ldap error data in the thread specific area */
140 ns_mtckey_cleanup(void *key
) {
141 struct ldap_error
*le
= (struct ldap_error
*)key
;
145 if (le
->le_matched
!= NULL
) {
146 ldap_memfree(le
->le_matched
);
148 if (le
->le_errmsg
!= NULL
) {
149 ldap_memfree(le
->le_errmsg
);
154 /* Free/detach the thread specific data structures */
160 /* free the per-thread ldap error info */
161 rc
= thr_getspecific(ns_mtckey
, &tsd
);
162 if (rc
== 0 && tsd
!= NULL
)
163 ns_mtckey_cleanup(tsd
);
164 (void) thr_setspecific(ns_mtckey
, NULL
);
166 /* detach the connection management control */
167 (void) thr_setspecific(ns_cmgkey
, NULL
);
170 /* per-thread callback function for allocating a mutex */
174 mutex_t
*mutexp
= NULL
;
176 if ((mutexp
= malloc(sizeof (mutex_t
))) != NULL
) {
177 if (mutex_init(mutexp
, USYNC_THREAD
, NULL
) != 0) {
185 /* per-thread callback function for freeing a mutex */
187 ns_mutex_free(void *mutexp
)
189 (void) mutex_destroy((mutex_t
*)mutexp
);
194 * Function for setting up thread-specific data
195 * where per thread LDAP error and the pointer
196 * to the active connection management control
200 conn_tsd_setup(ns_conn_mgmt_t
*cmg
)
205 rc
= thr_setspecific(ns_cmgkey
, cmg
);
206 if (rc
!= 0) /* must be ENOMEM */
209 /* return success if the ns_mtckey TSD is already set */
210 rc
= thr_getspecific(ns_mtckey
, &tsd
);
211 if (rc
== 0 && tsd
!= NULL
)
214 /* allocate and set the ns_mtckey TSD */
215 tsd
= (void *) calloc(1, sizeof (struct ldap_error
));
218 rc
= thr_setspecific(ns_mtckey
, tsd
);
219 if (rc
!= 0) { /* must be ENOMEM */
226 /* Callback function for setting the per thread LDAP error */
229 set_ld_error(int err
, char *matched
, char *errmsg
, void *dummy
)
231 struct ldap_error
*le
;
234 if ((eno
= thr_getspecific(ns_mtckey
, (void **)&le
)) != 0) {
235 syslog(LOG_ERR
, gettext(
236 "libsldap: set_ld_error: thr_getspecific failed (%s)."),
241 /* play safe, do nothing if TSD pointer is NULL */
243 syslog(LOG_INFO
, gettext(
244 "libsldap: set_ld_error: TSD pointer is NULL."));
250 if (le
->le_matched
!= NULL
) {
251 ldap_memfree(le
->le_matched
);
252 le
->le_matched
= NULL
;
254 le
->le_matched
= matched
;
256 if (le
->le_errmsg
!= NULL
) {
257 ldap_memfree(le
->le_errmsg
);
258 le
->le_errmsg
= NULL
;
260 le
->le_errmsg
= errmsg
;
263 /* check and allocate the thread-specific data for using a MT connection */
265 conn_tsd_check(ns_conn_mgmt_t
*cmg
)
267 if (conn_tsd_setup(cmg
) != 0)
268 return (NS_LDAP_MEMORY
);
270 return (NS_LDAP_SUCCESS
);
273 /* Callback function for getting the per thread LDAP error */
276 get_ld_error(char **matched
, char **errmsg
, void *dummy
)
278 struct ldap_error
*le
;
281 if ((eno
= thr_getspecific(ns_mtckey
, (void **)&le
)) != 0) {
282 syslog(LOG_ERR
, gettext(
283 "libsldap: get_ld_error: thr_getspecific failed (%s)"),
288 /* play safe, return NULL error data, if TSD pointer is NULL */
290 le
= &ldap_error_NULL
;
292 if (matched
!= NULL
) {
293 *matched
= le
->le_matched
;
295 if (errmsg
!= NULL
) {
296 *errmsg
= le
->le_errmsg
;
298 return (le
->le_errno
);
301 /* Callback function for setting per thread errno */
308 /* Callback function for getting per thread errno */
315 /* set up an ldap session 'ld' for sharing among multiple threads */
317 setup_mt_conn(LDAP
*ld
)
320 struct ldap_thread_fns tfns
;
321 struct ldap_extra_thread_fns extrafns
;
325 * Set the function pointers for dealing with mutexes
326 * and error information
328 (void) memset(&tfns
, '\0', sizeof (struct ldap_thread_fns
));
329 tfns
.ltf_mutex_alloc
= (void *(*)(void)) ns_mutex_alloc
;
330 tfns
.ltf_mutex_free
= (void (*)(void *)) ns_mutex_free
;
331 tfns
.ltf_mutex_lock
= (int (*)(void *)) mutex_lock
;
332 tfns
.ltf_mutex_unlock
= (int (*)(void *)) mutex_unlock
;
333 tfns
.ltf_get_errno
= get_errno
;
334 tfns
.ltf_set_errno
= set_errno
;
335 tfns
.ltf_get_lderrno
= get_ld_error
;
336 tfns
.ltf_set_lderrno
= set_ld_error
;
337 tfns
.ltf_lderrno_arg
= NULL
;
340 * Set up the ld to use those function pointers
342 rc
= ldap_set_option(ld
, LDAP_OPT_THREAD_FN_PTRS
,
345 syslog(LOG_INFO
, gettext("libsldap: ldap_set_option "
346 "(LDAP_OPT_THREAD_FN_PTRS)"));
351 * Set the function pointers for working with semaphores
353 (void) memset(&extrafns
, '\0',
354 sizeof (struct ldap_extra_thread_fns
));
355 extrafns
.ltf_threadid_fn
= (void * (*)(void))thr_self
;
356 extrafns
.ltf_mutex_trylock
= NULL
;
357 extrafns
.ltf_sema_alloc
= NULL
;
358 extrafns
.ltf_sema_free
= NULL
;
359 extrafns
.ltf_sema_wait
= NULL
;
360 extrafns
.ltf_sema_post
= NULL
;
362 /* Set up the ld to use those function pointers */
363 rc
= ldap_set_option(ld
, LDAP_OPT_EXTRA_THREAD_FN_PTRS
,
366 syslog(LOG_INFO
, gettext("libsldap: ldap_set_option "
367 "(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"));
374 /* set up an MT connection for sharing among multiple threads */
376 setup_mt_ld(LDAP
*ld
, ns_conn_mgmt_t
*cmg
)
378 thread_t t
= thr_self();
380 /* set up the per-thread data for using the MT connection */
381 if (conn_tsd_setup(cmg
) == -1) {
383 gettext("libsldap: tid= %d: unable to set up TSD\n"), t
);
387 if (setup_mt_conn(ld
) == 0) {
388 /* multiple threads per connection not supported */
389 syslog(LOG_WARNING
, gettext("libsldap: tid= %d: multiple "
390 "threads per connection not supported\n"), t
);
398 * Check name and UID of process, if it is nscd.
401 * pid : PID of checked process
402 * check_uid : check if UID == 0
404 * B_TRUE : nscd detected
405 * B_FALSE : nscd not confirmed
408 check_nscd_proc(pid_t pid
, boolean_t check_uid
)
411 char fname
[MAXPATHLEN
];
415 if (snprintf(fname
, MAXPATHLEN
, "/proc/%d/psinfo", pid
) > 0) {
416 if ((fd
= open(fname
, O_RDONLY
)) >= 0) {
417 ret
= read(fd
, &pinfo
, sizeof (psinfo_t
));
419 if ((ret
== sizeof (psinfo_t
)) &&
420 (strcmp(pinfo
.pr_fname
, "nscd") == 0)) {
421 if (check_uid
&& (pinfo
.pr_uid
!= 0))
431 * Check if this process is peruser nscd.
434 __s_api_peruser_proc(void)
437 static mutex_t nscdLock
= DEFAULTMUTEX
;
438 static pid_t checkedPpid
= (pid_t
)-1;
439 static boolean_t isPeruserNscd
= B_FALSE
;
444 * Already checked before for this process? If yes, return cached
447 if (my_ppid
== checkedPpid
) {
448 return (isPeruserNscd
);
451 (void) mutex_lock(&nscdLock
);
453 /* Check once more incase another thread has just complete this. */
454 if (my_ppid
== checkedPpid
) {
455 (void) mutex_unlock(&nscdLock
);
456 return (isPeruserNscd
);
459 /* Reinitialize to be sure there is no residue after fork. */
460 isPeruserNscd
= B_FALSE
;
462 /* Am I the nscd process? */
463 if (check_nscd_proc(getpid(), B_FALSE
)) {
464 /* Is my parent the nscd process with UID == 0. */
465 isPeruserNscd
= check_nscd_proc(my_ppid
, B_TRUE
);
468 /* Remember for whom isPeruserNscd is. */
469 checkedPpid
= my_ppid
;
471 (void) mutex_unlock(&nscdLock
);
472 return (isPeruserNscd
);
476 * Check if this process is main nscd.
479 __s_api_nscd_proc(void)
482 static mutex_t nscdLock
= DEFAULTMUTEX
;
483 static pid_t checkedPid
= (pid_t
)-1;
484 static boolean_t isMainNscd
= B_FALSE
;
487 * Don't bother checking if this process isn't root, this cannot
496 * Already checked before for this process? If yes, return cached
499 if (my_pid
== checkedPid
) {
503 (void) mutex_lock(&nscdLock
);
505 /* Check once more incase another thread has just done this. */
506 if (my_pid
== checkedPid
) {
507 (void) mutex_unlock(&nscdLock
);
512 * Am I the nscd process? UID is already checked, not needed from
515 isMainNscd
= check_nscd_proc(my_pid
, B_FALSE
);
517 /* Remember for whom isMainNscd is. */
520 (void) mutex_unlock(&nscdLock
);
525 * initialize a connection management control structure conn_mgmt_t
532 cmg
= (ns_conn_mgmt_t
*)calloc(1, sizeof (*cmg
));
534 syslog(LOG_ERR
, NS_CONN_MSG_NO_CONN_MGMT
);
538 /* is this process nscd or peruser nscd ? */
539 cmg
->is_nscd
= __s_api_nscd_proc();
540 cmg
->is_peruser_nscd
= __s_api_peruser_proc();
543 * assume the underlying libldap allows multiple threads sharing
544 * the same ldap connection (MT connection)
546 cmg
->ldap_mt
= B_TRUE
;
547 /* state is inactive until MT connection is required/requested */
548 cmg
->state
= NS_CONN_MGMT_INACTIVE
;
550 (void) mutex_init(&cmg
->lock
, USYNC_THREAD
, NULL
);
551 (void) mutex_init(&cmg
->cfg_lock
, USYNC_THREAD
, NULL
);
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
;
559 * reference (or initialize) the current Native LDAP configuration and
560 * if in nscd process, make it never refreshed
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
) {
567 * main nscd get config change notice from ldap_cachemgr
568 * so won't times out and refresh the config
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
;
579 mark_shutdown_or_reloaded(int op
)
581 ns_conn_mgmt_t
*cmg
= ns_connmgmt
;
583 (void) mutex_lock(&cmg
->lock
);
584 if (op
== NS_CONN_MGMT_OP_SHUTDOWN
)
585 cmg
->shutting_down
= B_TRUE
;
587 cmg
->cfg_reloaded
= B_TRUE
;
588 atomic_inc_uint(&cmg
->ref_cnt
);
589 cmg
->state
= NS_CONN_MGMT_DETACHED
;
591 if (op
== NS_CONN_MGMT_OP_RELOAD_CONFIG
)
592 __s_api_init_config_global(NULL
);
594 (void) mutex_unlock(&cmg
->lock
);
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.
604 static ns_conn_mgmt_t
*
605 get_current_conn_mgmt(int op
)
607 ns_conn_mgmt_t
*cmg
= ns_connmgmt
;
608 static pid_t checked_pid
= (pid_t
)-1;
612 if (cmg
== NULL
|| checked_pid
!= mypid
) {
616 * if current conn_mgmt not created yet or is from parent
617 * or is requested to recreate, create it
619 if (cmg
== NULL
|| cmg
->pid
!= mypid
) {
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.
630 ns_connmgmt_parent
= cmg
;
632 * avoid lint warning; does not
633 * change the conn_mgmt in parent
635 ns_connmgmt_parent
->state
=
636 NS_CONN_MGMT_DETACHED
;
638 ns_connmgmt
= init_conn_mgmt();
641 * ensure it will not be destroyed until explicitly
642 * shut down or reloaded
644 if (op
== NS_CONN_MGMT_OP_REF
)
645 atomic_inc_uint(&cmg
->ref_cnt
);
652 static ns_conn_mgmt_t
*
653 access_conn_mgmt(int op
)
655 ns_conn_mgmt_t
*cmg
= NULL
;
656 ns_conn_mgmt_t
*cmg_prev
;
658 (void) mutex_lock(&ns_connmgmt_lock
);
661 * connection management is not available when the libsldap is being
662 * unloaded or shut down
664 if (ns_connmgmt_shutting_down
== B_TRUE
) {
665 (void) mutex_unlock(&ns_connmgmt_lock
);
669 if (op
== NS_CONN_MGMT_OP_SHUTDOWN
) {
670 ns_connmgmt_shutting_down
= B_TRUE
;
671 if (ns_connmgmt
!= NULL
) {
673 mark_shutdown_or_reloaded(op
);
676 (void) mutex_unlock(&ns_connmgmt_lock
);
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
);
685 * the previous cmg (cmg_prev) will be freed later
686 * when its ref count reaches zero
691 cmg
= get_current_conn_mgmt(op
);
693 (void) mutex_unlock(&ns_connmgmt_lock
);
697 atomic_inc_uint(&cmg
->ref_cnt
);
698 if (op
== NS_CONN_MGMT_OP_RELOAD_CONFIG
||
699 op
== NS_CONN_MGMT_OP_NEW_CONFIG
)
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();
706 (void) mutex_unlock(&ns_connmgmt_lock
);
711 * free a connection management control
714 free_conn_mgmt(ns_conn_mgmt_t
*cmg
)
724 ldap_get_chg_cookie_t cookie
;
728 cookie
= cmg
->cfg_cookie
;
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
);
736 __s_api_release_config(cmg
->config
);
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
=
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
=
752 rc
= __ns_ldap_trydoorcall(&sptr
, &ndata
,
754 if (rc
!= NS_CACHE_SUCCESS
)
758 " stopping door call "
759 " GETSTATUSCHANGE failed "
762 (void) pthread_cancel(cmg
->procchg_tid
);
763 cmg
->procchg_started
= B_FALSE
;
770 static ns_conn_mgmt_t
*
771 release_conn_mgmt(ns_conn_mgmt_t
*cmg
, boolean_t unlock_cmg
)
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
);
780 __s_api_free_sessionPool();
784 gettext("libsldap: connection management "
785 " has a refcount of zero but the state "
786 " is not DETACHED (%d)"), cmg
->state
);
794 * exposed function for initializing a connection management control structure
797 __s_api_conn_mgmt_init()
799 if (thr_keycreate_once(&ns_mtckey
, ns_mtckey_cleanup
) != 0) {
800 syslog(LOG_WARNING
, NS_CONN_MSG_NO_MTC_KEY
);
804 if (thr_keycreate_once(&ns_cmgkey
, NULL
) != 0) {
805 syslog(LOG_WARNING
, NS_CONN_MSG_NO_CMG_KEY
);
809 return (access_conn_mgmt(NS_CONN_MGMT_OP_LIB_INIT
));
812 /* initialize a connection user */
814 __s_api_conn_user_init(int type
, void *userinfo
, boolean_t referral
)
819 /* delete the reference to the previously used conn_mgmt */
820 (void) thr_setspecific(ns_cmgkey
, NULL
);
822 cmg
= access_conn_mgmt(NS_CONN_MGMT_OP_REF
);
826 if (cmg
->state
!= NS_CONN_MGMT_ACTIVE
&&
827 cmg
->state
!= NS_CONN_MGMT_INACTIVE
) {
828 atomic_dec_uint(&cmg
->ref_cnt
);
832 cu
= (ns_conn_user_t
*)calloc(1, sizeof (*cu
));
834 atomic_dec_uint(&cmg
->ref_cnt
);
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
;
846 (void) conn_tsd_setup(cmg
);
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
860 __s_api_conn_user_free(ns_conn_user_t
*cu
)
867 cu
->state
= NS_CONN_USER_FREED
;
868 if (cu
->ns_error
!= NULL
)
869 (void) __ns_ldap_freeError(&cu
->ns_error
);
873 (void) release_conn_mgmt(cmg
, B_FALSE
);
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
884 static ns_conn_mt_t
*
885 init_conn_mt(ns_conn_mgmt_t
*cmg
, ns_ldap_error_t
**ep
)
888 ns_conn_mgmt_t
*cmg_a
;
890 cm
= (ns_conn_mt_t
*)calloc(1, sizeof (*cm
));
893 *ep
= __s_api_make_error(NS_LDAP_MEMORY
, NULL
);
897 cmg_a
= access_conn_mgmt(NS_CONN_MGMT_OP_REF
);
900 (void) release_conn_mgmt(cmg_a
, B_FALSE
);
902 *ep
= __s_api_make_error(NS_LDAP_OP_FAILED
,
903 NS_CONN_MSG_SHUTDOWN_RELOADED
);
908 (void) mutex_init(&cm
->lock
, USYNC_THREAD
, NULL
);
909 cm
->state
= NS_CONN_MT_CONNECTING
;
910 cm
->tid
= thr_self();
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.
926 static ns_conn_mgmt_t
*
927 free_conn_mt(ns_conn_mt_t
*cm
, int unlock_cmg
)
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
);
942 return (release_conn_mgmt(cmg
, unlock_cmg
));
945 /* add a connection user to an MT connection */
947 add_cu2cm(ns_conn_user_t
*cu
, ns_conn_mt_t
*cm
)
950 if (cm
->cu_head
== NULL
) {
954 cm
->cu_tail
->next
= cu
;
960 /* add an MT connection to the connection management */
962 add_cm2cmg(ns_conn_mt_t
*cm
, ns_conn_mgmt_t
*cmg
)
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).
970 if (cmg
->cm_head
== NULL
) {
974 if (cm
->opened_for
== NS_CONN_USER_WRITE
) {
975 cm
->next
= cmg
->cm_head
;
978 cmg
->cm_tail
->next
= cm
;
985 /* delete a connection user from an MT connection */
987 del_cu4cm(ns_conn_user_t
*cu
, ns_conn_mt_t
*cm
)
989 ns_conn_user_t
*pu
, *u
;
991 if (cu
== NULL
|| cm
->cu_head
== NULL
|| cm
->cu_cnt
== 0)
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
;
1004 /* more than one and cu is the first one */
1005 if (cu
== cm
->cu_head
) {
1006 cm
->cu_head
= cu
->next
;
1013 for (u
= cm
->cu_head
->next
; u
; u
= u
->next
) {
1018 if (pu
!= cm
->cu_tail
) {
1019 pu
->next
= cu
->next
;
1020 if (pu
->next
== NULL
)
1025 syslog(LOG_INFO
, gettext(
1026 "libsldap: del_cu4cm(): connection user not found"));
1030 /* delete an MT connection from the connection management control structure */
1032 del_cm4cmg(ns_conn_mt_t
*cm
, ns_conn_mgmt_t
*cmg
)
1034 ns_conn_mt_t
*pm
, *m
;
1036 if (cm
== NULL
|| cmg
->cm_head
== NULL
|| cmg
->cm_cnt
== 0)
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
;
1049 /* more than one and cm is the first one */
1050 if (cm
== cmg
->cm_head
) {
1051 cmg
->cm_head
= cm
->next
;
1058 for (m
= cmg
->cm_head
->next
; m
; m
= m
->next
) {
1063 if (pm
!= cmg
->cm_tail
) {
1064 pm
->next
= cm
->next
;
1065 if (pm
->next
== NULL
)
1070 syslog(LOG_INFO
, gettext(
1071 "libsldap: del_cm4cmg(): MT connection not found"));
1076 * compare to see if the server and credential for authentication match
1077 * those used by an MT connection
1080 is_server_cred_matched(const char *server
, const ns_cred_t
*cred
,
1083 Connection
*cp
= cm
->conn
;
1085 /* check server first */
1086 if (server
!= NULL
&& *server
!= 0) {
1087 if (strcasecmp(server
, cp
->serverAddr
) != 0)
1094 /* then check cred */
1095 return (__s_api_is_auth_matched(cp
->auth
, cred
));
1099 * Wait until a pending MT connection becomes available.
1100 * Return 1 if so, 0 if error.
1102 * Assume the current conn_mgmt and the input conn_mt
1106 wait_for_conn_mt(ns_conn_user_t
*cu
, ns_conn_mt_t
*cm
)
1109 cu
->state
= NS_CONN_USER_WAITING
;
1113 (void) mutex_unlock(&cm
->lock
);
1115 * It could take some time so we don't want to hold
1116 * cm->conn_mgmt across the wait
1118 (void) mutex_unlock(&(cm
->conn_mgmt
)->lock
);
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
);
1125 /* cm->lock is locked again at this point */
1127 cu
->state
= NS_CONN_USER_WOKEUP
;
1130 if (cm
->state
== NS_CONN_MT_CONNECTED
)
1135 cu
->bad_mt_conn
= B_FALSE
;
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.
1150 check_and_close_conn(ns_conn_mgmt_t
*cmg
, ns_conn_mt_t
**cm
,
1151 ns_conn_user_t
*cu
) {
1158 ns_server_info_t sinfo
;
1159 ns_ldap_error_t
*errorp
= NULL
;
1162 * check only if preferred servers are defined
1164 if (cmg
->pservers_loaded
== B_FALSE
)
1165 get_preferred_servers(B_FALSE
, B_FALSE
, cmg
);
1166 if (cmg
->pservers
== NULL
)
1170 * ask ldap_cachemgr for the first available server
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
);
1180 * Did ldap_cachemgr return a preferred server ?
1182 for (j
= 0; cmg
->pservers
[j
] != NULL
; j
++) {
1183 if (strcasecmp(sinfo
.server
, cmg
->pservers
[j
]) != 0)
1190 * Is the server being used a preferred one ?
1192 for (j
= 0; cmg
->pservers
[j
] != NULL
; j
++) {
1193 if (strcasecmp(cmg
->pservers
[j
], (*cm
)->conn
->serverAddr
) != 0)
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
1207 if ((upidx
== -1 && svridx
== -1) ||
1208 (upidx
!= -1 && svridx
!= -1 && upidx
== svridx
)) {
1209 __s_api_free_server_info(&sinfo
);
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.
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
);
1233 free_cm
= close_conn_mt_when_nouser(*cm
);
1234 if (cmg
->shutting_down
== B_FALSE
)
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
)
1244 (void) __ns_ldap_freeError(&ep
);
1247 (void) mutex_unlock(&(*cm
)->lock
);
1249 (void) free_conn_mt(*cm
, 0);
1253 __s_api_free_server_info(&sinfo
);
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
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.
1267 match_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
)
1271 boolean_t matched
= B_FALSE
;
1272 boolean_t drop_conn
;
1274 ns_conn_mt_t
*cm
= *cmt
;
1275 ns_conn_mgmt_t
*cmg
= cm
->conn_mgmt
;
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
);
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.
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)) {
1299 * NS_LDAP_INTERNAL is irrelevant here. There no
1300 * conn_user to consume the rc
1302 free_cm
= close_conn_mt(cm
, NS_LDAP_INTERNAL
, NULL
, NULL
);
1303 (void) mutex_unlock(&cm
->lock
);
1305 (void) free_conn_mt(cm
, 0);
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
)
1319 case NS_CONN_USER_WRITE
:
1320 if (cm
->opened_for
== NS_CONN_USER_WRITE
)
1329 if (matched
== B_TRUE
&& ((server
!= NULL
|| cred
!= NULL
) &&
1330 is_server_cred_matched(server
, cred
, cm
) == B_FALSE
))
1333 if (matched
!= B_FALSE
) {
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.
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
) {
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))
1362 if (matched
== B_FALSE
)
1363 (void) mutex_unlock(&cm
->lock
);
1369 * obtain an MT connection from the connection management for a conn_user
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
1377 * session : hold pointer to the Connection structure
1378 * errorp : hold pointer to error info (ns_ldap_error_t)
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
)
1387 ns_conn_mt_state_t st
;
1388 ns_conn_mgmt_t
*cmg
;
1390 if (errorp
== NULL
|| cu
== NULL
|| session
== NULL
)
1391 return (NS_LDAP_INVALID_PARAM
);
1394 cmg
= cu
->conn_mgmt
;
1397 * for pam_ldap, always try opening a new connection
1399 if (cu
->type
== NS_CONN_USER_AUTH
)
1400 return (NS_LDAP_NOTFOUND
);
1402 /* if need a new conn, then don't reuse */
1403 if (flags
& NS_LDAP_NEW_CONN
)
1404 return (NS_LDAP_NOTFOUND
);
1406 if (flags
& NS_LDAP_KEEP_CONN
)
1407 cu
->keep_conn
= B_TRUE
;
1410 * We want to use MT connection only if keep-connection flag is
1411 * set or if MT was requested (or active)
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
);
1417 /* MT connection will be used now (if possible/available) */
1418 cu
->use_mt_conn
= B_TRUE
;
1420 NS_CONN_CHECK_ABORT_AND_LOCK(cmg
, cu
, errorp
);
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 */
1431 if (cn
== NULL
) { /* not found and cn freed */
1433 * as the conn_mt list could
1434 * be different due to cn's
1435 * deletion, scan the entire
1436 * conn_mt list again
1438 st
= NS_CONN_MT_CONNECTED
;
1443 /* return a connected one if found */
1444 if (cn
->state
== NS_CONN_MT_CONNECTED
) {
1445 *session
= cn
->conn
;
1448 cu
->state
= NS_CONN_USER_CONNECTED
;
1449 (void) mutex_unlock(&cn
->lock
);
1450 (void) mutex_unlock(&cmg
->lock
);
1451 return (NS_LDAP_SUCCESS
);
1455 * if cn is not connecting, or allow only
1458 if (cn
->state
!= NS_CONN_MT_CONNECTING
||
1460 (void) mutex_unlock(&cn
->lock
);
1464 /* wait for the connecting conn_mt */
1465 if (wait_for_conn_mt(cu
, cn
) != 1) {
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
1476 if (cn
->ns_rc
!= NS_LDAP_NOTFOUND
)
1477 cu
->state
= NS_CONN_USER_CONNECT_ERROR
;
1479 cu
->state
= NS_CONN_USER_FINDING
;
1480 cu
->use_mt_conn
= B_FALSE
;
1482 (void) mutex_unlock(&cn
->lock
);
1484 /* cmg->lock unlocked by wait_for_conn_mt() */
1489 /* return the newly available conn_mt */
1490 *session
= cn
->conn
;
1491 cu
->state
= NS_CONN_USER_CONNECTED
;
1492 (void) mutex_unlock(&cn
->lock
);
1494 /* cmg->lock unlocked by wait_for_conn_mt() */
1496 return (NS_LDAP_SUCCESS
);
1499 /* next, look for a connecting conn_mt */
1501 st
= NS_CONN_MT_CONNECTING
;
1504 /* no connection found, start opening one */
1505 cn
= init_conn_mt(cmg
, errorp
);
1507 (void) mutex_unlock(&cmg
->lock
);
1508 return ((*errorp
)->status
);
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
;
1517 add_cm2cmg(cn
, cmg
);
1518 (void) mutex_unlock(&cmg
->lock
);
1520 return (NS_LDAP_NOTFOUND
);
1525 * add an MT connection to the connection management
1528 * con : pointer to the Connection info
1529 * cu : pointer to the conn_user structure
1531 * ep : hold pointer to error info (ns_ldap_error_t)
1534 __s_api_conn_mt_add(Connection
*con
, ns_conn_user_t
*cu
, ns_ldap_error_t
**ep
)
1536 ns_conn_mgmt_t
*cmg
= cu
->conn_mgmt
;
1537 ns_conn_mt_t
*cm
= cu
->conn_mt
;
1539 /* if the conn_mgmt is being shut down, return error */
1540 NS_CONN_CHECK_ABORT_AND_LOCK(cmg
, cu
, ep
);
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)
1547 if (cmg
->procchg_started
== B_FALSE
&& cmg
->is_nscd
== B_TRUE
) {
1549 cmg
->procchg_started
= B_TRUE
;
1551 (void) mutex_lock(&cm
->lock
);
1553 cm
->state
= NS_CONN_MT_CONNECTED
;
1555 cm
->create_time
= time(NULL
);
1556 cm
->access_time
= cm
->create_time
;
1557 cm
->opened_for
= cu
->type
;
1560 cu
->state
= NS_CONN_USER_CONNECTED
;
1561 if (cmg
->ldap_mt
== B_TRUE
)
1562 cm
->cu_max
= NS_CONN_MT_USER_MAX
;
1566 /* wake up the waiters if any */
1567 (void) conn_signal(cm
);
1569 (void) mutex_unlock(&cm
->lock
);
1570 (void) mutex_unlock(&cmg
->lock
);
1572 return (NS_LDAP_SUCCESS
);
1576 * return an MT connection to the pool when a conn user is done using it
1579 * cu : pointer to the conn_user structure
1583 __s_api_conn_mt_return(ns_conn_user_t
*cu
)
1586 ns_conn_mgmt_t
*cmg
;
1588 if (cu
== NULL
|| cu
->use_mt_conn
== B_FALSE
)
1593 cmg
= cu
->conn_mgmt
;
1595 (void) mutex_lock(&cm
->lock
);
1597 cu
->state
= NS_CONN_USER_DISCONNECTED
;
1599 cu
->bad_mt_conn
= B_FALSE
;
1602 * if this MT connection is no longer needed, or not usable, and
1603 * no more conn_user uses it, then close it.
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
);
1615 if (cm
->state
== NS_CONN_MT_CONNECTED
&& cm
->cu_cnt
== 0 &&
1616 cm
->conn
!= NULL
&& cm
->conn
->ld
!= NULL
) {
1617 struct timeval zerotime
;
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) {
1625 (void) ldap_msgfree(res
);
1628 (void) mutex_unlock(&cm
->lock
);
1632 /* save error info (rc and ns_ldap_error_t) in the conn_mt */
1634 err2cm(ns_conn_mt_t
*cm
, int rc
, ns_ldap_error_t
**errorp
) {
1635 ns_ldap_error_t
*ep
;
1638 cm
->ns_error
= NULL
;
1639 if (errorp
!= NULL
&& *errorp
!= NULL
) {
1640 ep
= __s_api_copy_error(*errorp
);
1642 cm
->ns_rc
= NS_LDAP_MEMORY
;
1648 /* copy error info (rc and ns_ldap_error_t) from conn_mt to conn_user */
1650 err_from_cm(ns_conn_user_t
*cu
, ns_conn_mt_t
*cm
) {
1651 ns_ldap_error_t
*ep
;
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
);
1660 cu
->ns_rc
= NS_LDAP_MEMORY
;
1666 /* copy error info (rc and ns_ldap_error_t) from caller to conn_user */
1668 err_from_caller(ns_conn_user_t
*cu
, int rc
, ns_ldap_error_t
**errorp
) {
1671 if (errorp
!= NULL
) {
1672 if (cu
->ns_error
!= NULL
)
1673 (void) __ns_ldap_freeError(&cu
->ns_error
);
1674 cu
->ns_error
= *errorp
;
1677 cu
->ns_error
= NULL
;
1681 * remove an MT connection from the connection management when failed to open
1684 * cu : pointer to the conn_user structure
1686 * errorp : pointer to pointer to error info (ns_ldap_error_t)
1688 * errorp : set to NULL, if none NULL cm, callers do not need to free it
1691 __s_api_conn_mt_remove(ns_conn_user_t
*cu
, int rc
, ns_ldap_error_t
**errorp
)
1693 ns_conn_mgmt_t
*cmg
;
1697 if (cu
== NULL
|| cu
->use_mt_conn
== B_FALSE
)
1699 if ((cm
= cu
->conn_mt
) == NULL
)
1701 cmg
= cu
->conn_mgmt
;
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
;
1708 if (errorp
!= NULL
) {
1709 cm
->ns_error
= *errorp
;
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
);
1721 cu
->bad_mt_conn
= B_FALSE
;
1722 if (cm
->cu_cnt
== 0) {
1723 del_cm4cmg(cm
, cmg
);
1727 NS_CONN_UNLOCK_AND_FREE(free_cm
, cm
, cmg
);
1731 * check to see if the underlying libldap supports multi-threaded client
1735 __s_api_check_libldap_MT_conn_support(ns_conn_user_t
*cu
, LDAP
*ld
,
1736 ns_ldap_error_t
**ep
)
1739 ns_conn_mgmt_t
*cmg
;
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
);
1745 cmg
= cu
->conn_mgmt
;
1746 rc
= setup_mt_ld(ld
, cmg
);
1748 if (cmg
->do_mt_conn
== B_FALSE
) {
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.
1754 NS_CONN_CHECK_ABORT_AND_LOCK(cmg
, cu
, ep
);
1755 if (cmg
->do_mt_conn
== B_FALSE
) {
1757 cmg
->ldap_mt
= B_FALSE
;
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
;
1767 (void) mutex_unlock(&cmg
->lock
);
1771 __s_api_conn_mt_remove(cu
, NS_LDAP_NOTFOUND
, NULL
);
1772 return (NS_LDAP_SUCCESS
);
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.
1781 close_conn_mt(ns_conn_mt_t
*cm
, int rc
, ns_ldap_error_t
**errorp
,
1784 ns_conn_mgmt_t
*cmg
= cm
->conn_mgmt
;
1788 if ((cm
->state
!= NS_CONN_MT_CONNECTED
&& cm
->state
!=
1789 NS_CONN_MT_CLOSING
) || cmg
->cm_head
== NULL
|| cmg
->cm_cnt
== 0)
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
) {
1800 if (cm
->state
== NS_CONN_MT_CONNECTED
) { /* first time in here */
1801 cm
->state
= NS_CONN_MT_CLOSING
;
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.
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
);
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
)
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
;
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.
1845 * cu : pointer to the conn_user structure
1847 * errorp : pointer to pointer to error info (ns_ldap_error_t)
1849 * errorp : set to NULL (if no error), callers do not need to free it
1852 __s_api_conn_mt_close(ns_conn_user_t
*cu
, int rc
, ns_ldap_error_t
**errorp
)
1854 ns_conn_mgmt_t
*cmg
;
1858 if (cu
== NULL
|| cu
->use_mt_conn
== B_FALSE
)
1861 if (cu
->state
!= NS_CONN_USER_CONNECTED
|| (cm
= cu
->conn_mt
) == NULL
)
1863 cmg
= cu
->conn_mgmt
;
1865 (void) mutex_lock(&cmg
->lock
);
1866 (void) mutex_lock(&cm
->lock
);
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
);
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
);
1882 /* detach the conn_user from the conn_mt */
1885 cu
->bad_mt_conn
= B_FALSE
;
1886 if (cmg
->shutting_down
== B_FALSE
)
1888 NS_CONN_UNLOCK_AND_FREE(free_cm
, cm
, cmg
);
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.
1898 close_conn_mt_by_procchg(ns_conn_mt_t
*cm
, int rc
, char *errmsg
)
1900 ns_conn_mgmt_t
*cmg
;
1902 ns_ldap_error_t
*ep
;
1906 cmg
= cm
->conn_mgmt
;
1908 ep
= (ns_ldap_error_t
*)calloc(1, sizeof (*ep
));
1912 ep
->message
= strdup(errmsg
); /* OK if returns NULL */
1915 (void) mutex_lock(&cmg
->lock
);
1916 (void) mutex_lock(&cm
->lock
);
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
);
1925 (void) __ns_ldap_freeError(&ep
);
1927 NS_CONN_UNLOCK_AND_FREE(free_cm
, cm
, cmg
);
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.
1938 close_conn_mt_when_nouser(ns_conn_mt_t
*cm
)
1942 if (cm
->cu_cnt
== 0) {
1943 del_cm4cmg(cm
, cm
->conn_mgmt
);
1946 cm
->close_when_nouser
= B_TRUE
;
1953 * Retrieve the configured preferred server list.
1954 * This function locked the conn_mgmt and does not
1958 get_preferred_servers(boolean_t lock
, boolean_t reload
, ns_conn_mgmt_t
*cmg
)
1960 ns_ldap_error_t
*errorp
= NULL
;
1961 void **pservers
= NULL
;
1964 (void) mutex_lock(&cmg
->lock
);
1966 /* if already done, and no reload, then return */
1967 if (cmg
->pservers_loaded
== B_TRUE
&& reload
== B_FALSE
)
1970 if (cmg
->pservers
!= NULL
) {
1971 (void) __ns_ldap_freeParam((void ***)&cmg
->pservers
);
1972 cmg
->pservers
= NULL
;
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
;
1980 (void) __ns_ldap_freeError(&errorp
);
1981 (void) __ns_ldap_freeParam(&pservers
);
1986 * This function handles the config or server status change notification
1987 * from the ldap_cachemgr.
1989 static ns_conn_mgmt_t
*
1990 proc_server_change(ns_server_status_change_t
*chg
, ns_conn_mgmt_t
*cmg
)
1992 int cnt
, i
, j
, k
, n
;
1993 boolean_t loop
= B_TRUE
;
1994 boolean_t cmg_locked
= B_FALSE
;
1997 ns_conn_mgmt_t
*ocmg
;
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
);
2011 if ((cnt
= chg
->num_server
) == 0)
2014 /* handle down servers first */
2015 for (i
= 0; i
< cnt
; i
++) {
2017 if (chg
->changes
[i
] != NS_SERVER_DOWN
)
2019 s
= chg
->servers
[i
];
2022 * look for a CONNECTED MT connection using
2023 * the same server s, and close it
2026 if (cmg_locked
== B_FALSE
) {
2027 (void) mutex_lock(&cmg
->lock
);
2028 cmg_locked
= B_TRUE
;
2030 for (cm
= cmg
->cm_head
; cm
; cm
= cm
->next
) {
2031 (void) mutex_lock(&cm
->lock
);
2033 if (cm
->state
== NS_CONN_MT_CONNECTED
&&
2035 strcasecmp(cm
->conn
->serverAddr
, s
) == 0) {
2036 (void) mutex_unlock(&cm
->lock
);
2040 (void) mutex_unlock(&cm
->lock
);
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
);
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.
2057 * No (more) MT connection using the down server s.
2058 * Process the next server on the list.
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.
2069 get_preferred_servers(cmg_locked
== B_FALSE
? B_TRUE
: B_FALSE
,
2071 cmg_locked
= B_TRUE
;
2073 * if no preferred server configured, we don't switch MT connection
2074 * to a more preferred server (i.e., fallback), so just return
2076 if (cmg
->pservers
== NULL
) {
2077 (void) mutex_unlock(&cmg
->lock
);
2081 /* for each server that is up now */
2082 for (i
= 0; i
< cnt
; i
++) {
2083 if (chg
->changes
[i
] != NS_SERVER_UP
)
2085 s
= chg
->servers
[i
];
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()
2096 if (cmg_locked
== B_FALSE
) {
2097 (void) mutex_lock(&cmg
->lock
);
2098 cmg_locked
= B_TRUE
;
2101 /* Is s a preferred server ? */
2103 for (j
= 0; cmg
->pservers
[j
] != NULL
; j
++) {
2104 if (strcasecmp(cmg
->pservers
[j
],
2111 /* skip s if not a preferred server */
2116 /* check each MT connection */
2117 for (cm
= cmg
->cm_head
; cm
; cm
= cm
->next
) {
2118 (void) mutex_lock(&cm
->lock
);
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.
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
) {
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,
2139 for (j
= 0; j
<= k
; j
++) {
2141 cm
->conn
->serverAddr
,
2142 cmg
->pservers
[j
]) == 0) {
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
2154 if (n
== -1) { /* s is preferred */
2156 fr
= close_conn_mt_when_nouser(
2158 NS_CONN_UNLOCK_AND_FREE(fr
,
2160 cmg_locked
= B_FALSE
;
2162 * break, not continue,
2163 * because we need to
2164 * check the entire cm
2165 * list again. The call
2166 * above may change the
2172 (void) mutex_unlock(&cm
->lock
);
2174 /* if no (more) cm using s, check next server */
2179 if (cmg_locked
== B_TRUE
)
2180 (void) mutex_unlock(&cmg
->lock
);
2184 /* Shut down all MT connection managed by the connection management */
2186 shutdown_all_conn_mt(ns_conn_mgmt_t
*cmg
)
2188 ns_ldap_error_t
*ep
;
2191 boolean_t done
= B_FALSE
;
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
);
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
)
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
);
2209 (void) free_conn_mt(cm
, 0);
2211 * conn_mt may change, so start from
2218 (void) mutex_unlock(&cmg
->lock
);
2219 (void) __ns_ldap_freeError(&ep
);
2222 /* free all the resources used by the connection management */
2224 __s_api_shutdown_conn_mgmt()
2226 ns_conn_mgmt_t
*cmg
;
2228 cmg
= access_conn_mgmt(NS_CONN_MGMT_OP_SHUTDOWN
);
2229 if (cmg
== NULL
) /* already being SHUT done */
2232 (void) shutdown_all_conn_mt(cmg
);
2233 (void) release_conn_mgmt(cmg
, B_FALSE
);
2235 /* then destroy the conn_mgmt */
2236 (void) release_conn_mgmt(cmg
, B_FALSE
);
2241 * Reinitialize the libsldap connection management after
2242 * a new native LDAP configuration is received.
2245 __s_api_reinit_conn_mgmt_new_config(ns_config_t
*new_cfg
)
2247 ns_conn_mgmt_t
*cmg
;
2248 ns_conn_mgmt_t
*ocmg
;
2250 cmg
= access_conn_mgmt(NS_CONN_MGMT_OP_REF
);
2253 if (cmg
->config
== new_cfg
|| cmg
->state
== NS_CONN_MGMT_DETACHED
) {
2254 (void) release_conn_mgmt(cmg
, B_FALSE
);
2258 /* reload the conn_mgmt and native LDAP config */
2259 ocmg
= access_conn_mgmt(NS_CONN_MGMT_OP_NEW_CONFIG
);
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
);
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
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
)
2285 ns_conn_user_t
*cu
= *conn_user
;
2286 ns_conn_mgmt_t
*cmg
;
2288 if (*try_cnt
> 0 && cu
!= NULL
) {
2290 * if called from firstEntry(), keep conn_mt for
2291 * the subsequent getnext requests
2293 if (cu
->type
== NS_CONN_USER_GETENT
&& *rc
== NS_LDAP_SUCCESS
)
2295 cmg
= cu
->conn_mgmt
;
2297 if (cu
->conn_mt
!= NULL
)
2298 __s_api_conn_mt_return(cu
);
2299 if (cmg
!= NULL
&& cmg
->cfg_reloaded
== B_TRUE
)
2301 __s_api_conn_user_free(cu
);
2304 if (*rc
== NS_LDAP_SUCCESS
|| retry
!= B_TRUE
)
2308 *try_cnt
= *try_cnt
+ 1;
2309 if (*try_cnt
> NS_LIST_TRY_MAX
)
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
;
2318 /* for 1+ try, use previous rc and errorp */
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
);
2329 /* prepare to get the next entry for an enumeration */
2331 __s_api_setup_getnext(ns_conn_user_t
*cu
, int *ns_err
,
2332 ns_ldap_error_t
**errorp
)
2335 ns_conn_mgmt_t
*cmg
;
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.
2341 if (cu
->conn_mt
!= NULL
&& (cmg
= cu
->conn_mgmt
) != NULL
) {
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
;
2351 rc
= conn_tsd_check(cmg
);
2352 if (rc
!= NS_LDAP_SUCCESS
) {
2358 return (NS_LDAP_SUCCESS
);
2361 /* wait for an MT connection to become available */
2363 conn_wait(ns_conn_mt_t
*conn_mt
, ns_conn_user_t
*conn_user
)
2365 ns_conn_waiter_t mywait
;
2366 ns_conn_waiter_t
*head
= &conn_mt
->waiter
;
2368 (void) cond_init(&(mywait
.waitcv
), USYNC_THREAD
, 0);
2369 mywait
.key
= conn_user
;
2370 mywait
.signaled
= 0;
2371 mywait
.next
= head
->next
;
2374 mywait
.next
->prev
= &mywait
;
2375 head
->next
= &mywait
;
2376 atomic_inc_uint(&conn_mt
->waiter_cnt
);
2378 while (!mywait
.signaled
)
2379 (void) cond_wait(&(mywait
.waitcv
), &conn_mt
->lock
);
2381 mywait
.prev
->next
= mywait
.next
;
2383 mywait
.next
->prev
= mywait
.prev
;
2387 /* signal that an MT connection is now available */
2389 conn_signal(ns_conn_mt_t
*conn_mt
)
2392 ns_conn_waiter_t
*head
= &conn_mt
->waiter
;
2393 ns_conn_waiter_t
*tmp
= head
->next
;
2396 (void) cond_signal(&(tmp
->waitcv
));
2398 atomic_dec_uint(&conn_mt
->waiter_cnt
);
2407 * wait and process the server status and/or config change notification
2408 * from ldap_cachemgr
2411 get_server_change(void *arg
)
2415 char s_b
[DOORBUFFERSIZE
];
2417 ldap_data_t
*sptr
= &space
.s_d
;
2425 boolean_t loop
= B_TRUE
;
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
;
2440 cmg
= access_conn_mgmt(NS_CONN_MGMT_OP_REF
);
2443 /* cmg is locked before called */
2444 cmg
->procchg_tid
= thr_self();
2446 /* make sure the thread specific data are set */
2447 (void) conn_tsd_setup(cmg
);
2448 cookie
= cmg
->cfg_cookie
;
2452 if (chg
.servers
!= NULL
)
2454 if (chg
.changes
!= NULL
)
2456 if (sptr
!= &space
.s_d
)
2457 (void) munmap((char *)sptr
, sizeof (space
));
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.
2467 if (cmg
== NULL
|| cmg
->state
== NS_CONN_MGMT_DETACHED
||
2468 getchg_not_supported
== B_TRUE
) {
2471 cmg
->procchg_started
= B_FALSE
;
2472 (void) release_conn_mgmt(cmg
, B_FALSE
);
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
;
2488 door_rc
= __ns_ldap_trydoorcall_getfd();
2489 cmg
->procchg_door_call
= B_TRUE
;
2490 if (release_conn_mgmt(cmg
, B_FALSE
) == NULL
) {
2495 if (door_rc
== NS_CACHE_SUCCESS
)
2496 door_rc
= __ns_ldap_trydoorcall_send(&sptr
, &ndata
,
2500 * Check and see if the conn_mgmt is still current.
2501 * If not, no need to continue.
2503 cmg
= access_conn_mgmt(NS_CONN_MGMT_OP_REF
);
2505 cmg
->procchg_door_call
= B_FALSE
;
2508 cmg
->procchg_started
= B_FALSE
;
2509 (void) release_conn_mgmt(cmg
, B_FALSE
);
2515 if (door_rc
!= NS_CACHE_SUCCESS
) {
2516 if (door_rc
== NS_CACHE_NOSERVER
) {
2518 getchg_not_supported
= B_TRUE
;
2521 * ldap_cachemgr may be down, give
2522 * it time to restart
2526 } else if (door_rc
== NS_CACHE_NOTFOUND
)
2527 getchg_not_supported
= B_TRUE
;
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
);
2542 /* server status changes ? */
2543 if (get_chg
->type
== NS_STATUS_CHANGE_TYPE_SERVER
) {
2545 * first check cookies, if don't match, config
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
);
2556 (void) strlcpy(chg_data
, ptr
, sizeof (chg_data
));
2557 chg
.num_server
= get_chg
->server_count
;
2559 servers
= (char **)calloc(chg
.num_server
,
2561 if (servers
== NULL
) {
2562 syslog(LOG_INFO
, NS_CONN_MSG_MEMORY_ERROR
);
2565 status
= (ns_server_status_t
*)calloc(chg
.num_server
,
2567 if (status
== NULL
) {
2568 syslog(LOG_INFO
, NS_CONN_MSG_MEMORY_ERROR
);
2575 for (c
= ptr
; which
!= 2; c
++) {
2576 /* look for DOORLINESEP or end of string */
2577 if (*c
!= dsep
&& *c
!= '\0')
2579 if (*c
== dsep
) { /* DOORLINESEP */
2580 *c
= '\0'; /* current value */
2581 c
+= dslen
; /* skip to next value */
2583 if (which
== 0) { /* get server info */
2584 servers
[ds_cnt
] = oc
;
2586 which
= 1; /* get status next */
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
,
2594 status
[ds_cnt
] = NS_SERVER_DOWN
;
2597 NS_CONN_MSG_BAD_CACHEMGR_DATA
);
2603 which
= 2; /* exit the loop */
2605 which
= 0; /* get server info next */
2607 chg
.servers
= servers
;
2608 chg
.changes
= status
;
2609 cmg
= proc_server_change(&chg
, cmg
);
2617 /* start the thread handling the change notification from ldap_cachemgr */
2619 start_thread(ns_conn_mgmt_t
*cmg
) {
2624 * start a thread to get and process config and server status changes
2626 if (thr_create(NULL
, NULL
, get_server_change
,
2627 (void *)cmg
, THR_DETACHED
, NULL
) != 0) {
2629 syslog(LOG_WARNING
, NS_CONN_MSG_NO_PROCCHG_THREAD
,