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