xref: /titanic_52/usr/src/cmd/ldapcachemgr/cachemgr_getldap.c (revision a46da47f55b9dd38acf0b680392eaf03c18a8025)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <assert.h>
27 #include <errno.h>
28 #include <memory.h>
29 #include <signal.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <libintl.h>
34 #include <syslog.h>
35 #include <sys/door.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <synch.h>
41 #include <pthread.h>
42 #include <unistd.h>
43 #include <lber.h>
44 #include <ldap.h>
45 #include <ctype.h>	/* tolower */
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <ucred.h>
50 #include "cachemgr.h"
51 #include "solaris-priv.h"
52 #include "ns_connmgmt.h"
53 
54 static rwlock_t	ldap_lock = DEFAULTRWLOCK;
55 static int	sighup_update = FALSE;
56 extern admin_t	current_admin;
57 
58 extern int is_root_or_all_privs(char *dc_str, ucred_t **ucp);
59 
60 /* variables used for SIGHUP wakeup on sleep */
61 static mutex_t			sighuplock;
62 static cond_t			cond;
63 
64 /* refresh time statistics */
65 static time_t	prev_refresh_time = 0;
66 
67 /* variables used for signaling parent process */
68 static mutex_t	sig_mutex;
69 static int	signal_done = FALSE;
70 
71 /* TCP connection timeout (in milliseconds) */
72 static int tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
73 
74 #ifdef SLP
75 extern int	use_slp;
76 #endif /* SLP */
77 
78 /* nis domain information */
79 #define	_NIS_FILTER		"objectclass=nisDomainObject"
80 #define	_NIS_DOMAIN		"nisdomain"
81 
82 #define	CACHESLEEPTIME		600
83 /*
84  * server list refresh delay when in "no server" mode
85  * (1 second)
86  */
87 #define	REFRESH_DELAY_WHEN_NO_SERVER	1
88 
89 typedef enum {
90 	INFO_OP_CREATE		= 0,
91 	INFO_OP_DELETE		= 1,
92 	INFO_OP_REFRESH		= 2,
93 	INFO_OP_REFRESH_WAIT	= 3,
94 	INFO_OP_GETSERVER	= 4,
95 	INFO_OP_GETSTAT		= 5,
96 	INFO_OP_REMOVESERVER	= 6
97 } info_op_t;
98 
99 typedef enum {
100 	INFO_RW_UNKNOWN		= 0,
101 	INFO_RW_READONLY	= 1,
102 	INFO_RW_WRITEABLE	= 2
103 } info_rw_t;
104 
105 typedef enum {
106 	INFO_SERVER_JUST_INITED	= -1,
107 	INFO_SERVER_UNKNOWN	= 0,
108 	INFO_SERVER_CONNECTING	= 1,
109 	INFO_SERVER_UP		= 2,
110 	INFO_SERVER_ERROR 	= 3,
111 	INFO_SERVER_REMOVED	= 4
112 } info_server_t;
113 
114 typedef enum {
115 	INFO_STATUS_UNKNOWN	= 0,
116 	INFO_STATUS_ERROR 	= 1,
117 	INFO_STATUS_NEW   	= 2,
118 	INFO_STATUS_OLD		= 3
119 } info_status_t;
120 
121 typedef enum {
122 	CACHE_OP_CREATE		= 0,
123 	CACHE_OP_DELETE		= 1,
124 	CACHE_OP_FIND		= 2,
125 	CACHE_OP_ADD		= 3,
126 	CACHE_OP_GETSTAT	= 4
127 } cache_op_t;
128 
129 typedef enum {
130 	CACHE_MAP_UNKNOWN	= 0,
131 	CACHE_MAP_DN2DOMAIN	= 1
132 } cache_type_t;
133 
134 typedef struct server_info_ext {
135 	char			*addr;
136 	char			*hostname;
137 	char			*rootDSE_data;
138 	char			*errormsg;
139 	info_rw_t		type;
140 	info_server_t		server_status;
141 	info_server_t		prev_server_status;
142 	info_status_t 		info_status;
143 	ns_server_status_t	change;
144 } server_info_ext_t;
145 
146 typedef struct server_info {
147 	struct server_info 	*next;
148 	mutex_t			mutex[2];	/* 0: current copy lock */
149 						/* 1: update copy lock */
150 	server_info_ext_t	sinfo[2]; /* 0: current, 1:  update copy */
151 } server_info_t;
152 
153 typedef struct cache_hash {
154 	cache_type_t		type;
155 	char			*from;
156 	char			*to;
157 	struct cache_hash	*next;
158 } cache_hash_t;
159 
160 /*
161  * The status of a server to be removed. It can be up or down.
162  */
163 typedef struct rm_svr {
164 	char	*addr;
165 	int	up; /* 1: up, 0: down */
166 } rm_svr_t;
167 
168 static int getldap_destroy_serverInfo(server_info_t *head);
169 static void test_server_change(server_info_t *head);
170 static void remove_server(char *addr);
171 static ns_server_status_t set_server_status(char *input, server_info_t *head);
172 static void create_buf_and_notify(char *input, ns_server_status_t st);
173 
174 /*
175  * Load configuration
176  * The code was in signal handler getldap_revalidate
177  * It's moved out of the handler because it could cause deadlock
178  * return: 1 SUCCESS
179  *         0 FAIL
180  */
181 static int
182 load_config() {
183 	ns_ldap_error_t *error;
184 	int		rc = 1;
185 
186 	(void) __ns_ldap_setServer(TRUE);
187 
188 	(void) rw_wrlock(&ldap_lock);
189 	if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
190 		logit("Error: Unable to read '%s': %s\n",
191 			NSCONFIGFILE, error->message);
192 		__ns_ldap_freeError(&error);
193 		rc = 0; /* FAIL */
194 	} else
195 		sighup_update = TRUE;
196 
197 	(void) rw_unlock(&ldap_lock);
198 
199 	return (rc);
200 }
201 
202 /*
203  * Calculate a hash for a string
204  * Based on elf_hash algorithm, hash is case insensitive
205  * Uses tolower instead of _tolower because of I18N
206  */
207 
208 static unsigned long
209 getldap_hash(const char *str)
210 {
211 	unsigned int	hval = 0;
212 
213 	while (*str) {
214 		unsigned int	g;
215 
216 		hval = (hval << 4) + tolower(*str++);
217 		if ((g = (hval & 0xf0000000)) != 0)
218 			hval ^= g >> 24;
219 		hval &= ~g;
220 	}
221 	return ((unsigned long)hval);
222 }
223 
224 /*
225  * Remove a hash table entry.
226  * This function expects a lock in place when called.
227  */
228 
229 static cache_hash_t *
230 getldap_free_hash(cache_hash_t *p)
231 {
232 	cache_hash_t	*next;
233 
234 	p->type = CACHE_MAP_UNKNOWN;
235 	if (p->from)
236 		free(p->from);
237 	if (p->to)
238 		free(p->to);
239 	next = p->next;
240 	p->next = NULL;
241 	free(p);
242 	return (next);
243 }
244 
245 /*
246  * Scan a hash table hit for a matching hash entry.
247  * This function expects a lock in place when called.
248  */
249 static cache_hash_t *
250 getldap_scan_hash(cache_type_t type, char *from,
251 		cache_hash_t *idx)
252 {
253 	while (idx) {
254 		if (idx->type == type &&
255 		    strcasecmp(from, idx->from) == 0) {
256 			return (idx);
257 		}
258 		idx = idx->next;
259 	}
260 	return ((cache_hash_t *)NULL);
261 }
262 
263 /*
264  * Format and return the cache data statistics
265  */
266 static int
267 getldap_get_cacheData_stat(int max, int current, char **output)
268 {
269 #define	C_HEADER0	"Cache data information: "
270 #define	C_HEADER1	"  Maximum cache entries:   "
271 #define	C_HEADER2	"  Number of cache entries: "
272 	int		hdr0_len = strlen(gettext(C_HEADER0));
273 	int		hdr1_len = strlen(gettext(C_HEADER1));
274 	int		hdr2_len = strlen(gettext(C_HEADER2));
275 	int		len;
276 
277 	if (current_admin.debug_level >= DBG_ALL) {
278 		logit("getldap_get_cacheData_stat()...\n");
279 	}
280 
281 	*output = NULL;
282 
283 	len = hdr0_len + hdr1_len + hdr2_len +
284 	    3 * strlen(DOORLINESEP) + 21;
285 	*output = malloc(len);
286 	if (*output == NULL)
287 		return (-1);
288 
289 	(void) snprintf(*output, len, "%s%s%s%10d%s%s%10d%s",
290 	    gettext(C_HEADER0), DOORLINESEP,
291 	    gettext(C_HEADER1), max, DOORLINESEP,
292 	    gettext(C_HEADER2), current, DOORLINESEP);
293 
294 	return (NS_LDAP_SUCCESS);
295 }
296 
297 static int
298 getldap_cache_op(cache_op_t op, cache_type_t type,
299 			char *from, char **to)
300 {
301 #define	CACHE_HASH_MAX		257
302 #define	CACHE_HASH_MAX_ENTRY	256
303 	static cache_hash_t	*hashTbl[CACHE_HASH_MAX];
304 	cache_hash_t		*next, *idx, *newp;
305 	unsigned long		hash;
306 	static rwlock_t 	cache_lock = DEFAULTRWLOCK;
307 	int 			i;
308 	static int		entry_num = 0;
309 
310 	if (current_admin.debug_level >= DBG_ALL) {
311 		logit("getldap_cache_op()...\n");
312 	}
313 	switch (op) {
314 	case CACHE_OP_CREATE:
315 		if (current_admin.debug_level >= DBG_ALL) {
316 			logit("operation is CACHE_OP_CREATE...\n");
317 		}
318 		(void) rw_wrlock(&cache_lock);
319 
320 		for (i = 0; i < CACHE_HASH_MAX; i++) {
321 			hashTbl[i] = NULL;
322 		}
323 		entry_num = 0;
324 
325 		(void) rw_unlock(&cache_lock);
326 		break;
327 
328 	case CACHE_OP_DELETE:
329 		if (current_admin.debug_level >= DBG_ALL) {
330 			logit("operation is CACHE_OP_DELETE...\n");
331 		}
332 		(void) rw_wrlock(&cache_lock);
333 
334 		for (i = 0; i < CACHE_HASH_MAX; i++) {
335 			next = hashTbl[i];
336 			while (next != NULL) {
337 				next = getldap_free_hash(next);
338 			}
339 			hashTbl[i] = NULL;
340 		}
341 		entry_num = 0;
342 
343 		(void) rw_unlock(&cache_lock);
344 		break;
345 
346 	case CACHE_OP_ADD:
347 		if (current_admin.debug_level >= DBG_ALL) {
348 			logit("operation is CACHE_OP_ADD...\n");
349 		}
350 		if (from == NULL || to == NULL || *to == NULL)
351 			return (-1);
352 		hash = getldap_hash(from) % CACHE_HASH_MAX;
353 		(void) rw_wrlock(&cache_lock);
354 		idx = hashTbl[hash];
355 		/*
356 		 * replace old "to" value with new one
357 		 * if an entry with same "from"
358 		 * already exists
359 		 */
360 		if (idx) {
361 			newp = getldap_scan_hash(type, from, idx);
362 			if (newp) {
363 				free(newp->to);
364 				newp->to = strdup(*to);
365 				(void) rw_unlock(&cache_lock);
366 				return (NS_LDAP_SUCCESS);
367 			}
368 		}
369 
370 		if (entry_num > CACHE_HASH_MAX_ENTRY) {
371 			(void) rw_unlock(&cache_lock);
372 			return (-1);
373 		}
374 
375 		newp = (cache_hash_t *)malloc(sizeof (cache_hash_t));
376 		if (newp == NULL) {
377 			(void) rw_unlock(&cache_lock);
378 			return (NS_LDAP_MEMORY);
379 		}
380 		newp->type = type;
381 		newp->from = strdup(from);
382 		newp->to = strdup(*to);
383 		newp->next = idx;
384 		hashTbl[hash] = newp;
385 		entry_num++;
386 		(void) rw_unlock(&cache_lock);
387 		break;
388 
389 	case CACHE_OP_FIND:
390 		if (current_admin.debug_level >= DBG_ALL) {
391 			logit("operation is CACHE_OP_FIND...\n");
392 		}
393 		if (from == NULL || to == NULL)
394 			return (-1);
395 		*to = NULL;
396 		hash = getldap_hash(from) % CACHE_HASH_MAX;
397 		(void) rw_rdlock(&cache_lock);
398 		idx = hashTbl[hash];
399 		idx = getldap_scan_hash(type, from, idx);
400 		if (idx)
401 			*to = strdup(idx->to);
402 		(void) rw_unlock(&cache_lock);
403 		if (idx == NULL)
404 			return (-1);
405 		break;
406 
407 	case CACHE_OP_GETSTAT:
408 		if (current_admin.debug_level >= DBG_ALL) {
409 			logit("operation is CACHE_OP_GETSTAT...\n");
410 		}
411 		if (to == NULL)
412 			return (-1);
413 
414 		return (getldap_get_cacheData_stat(CACHE_HASH_MAX_ENTRY,
415 		    entry_num, to));
416 		break;
417 
418 	default:
419 		logit("getldap_cache_op(): "
420 		    "invalid operation code (%d).\n", op);
421 		return (-1);
422 		break;
423 	}
424 	return (NS_LDAP_SUCCESS);
425 }
426 /*
427  * Function: sync_current_with_update_copy
428  *
429  * This function syncs up the 2 sinfo copies in info.
430  *
431  * The 2 copies are identical most of time.
432  * The update copy(sinfo[1]) could be different when
433  * getldap_serverInfo_refresh thread is refreshing the server list
434  * and calls getldap_get_rootDSE to update info.  getldap_get_rootDSE
435  * calls sync_current_with_update_copy to sync up 2 copies before thr_exit.
436  * The calling sequence is
437  *  getldap_serverInfo_refresh->
438  *  getldap_get_serverInfo_op(INFO_OP_CREATE,...)->
439  *  getldap_set_serverInfo->
440  *  getldap_get_rootDSE
441  *
442  * The original server_info_t has one copy of server info. When libsldap
443  * makes door call GETLDAPSERVER to get the server info and getldap_get_rootDSE
444  * is updating the server info, it would hit a unprotected window in
445  * getldap_rootDSE. The door call  will not get server info and libsldap
446  * fails at making ldap connection.
447  *
448  * The new server_info_t provides GETLDAPSERVER thread with a current
449  * copy(sinfo[0]). getldap_get_rootDSE only works on the update copy(sinfo[1])
450  * and syncs up 2 copies before thr_exit. This will close the window in
451  * getldap_get_rootDSE.
452  *
453  */
454 static void
455 sync_current_with_update_copy(server_info_t *info)
456 {
457 	if (current_admin.debug_level >= DBG_ALL) {
458 		logit("sync_current_with_update_copy()...\n");
459 	}
460 
461 	(void) mutex_lock(&info->mutex[1]);
462 	(void) mutex_lock(&info->mutex[0]);
463 
464 	if (info->sinfo[1].server_status == INFO_SERVER_UP &&
465 	    info->sinfo[0].server_status != INFO_SERVER_UP)
466 		info->sinfo[1].change = NS_SERVER_UP;
467 	else if (info->sinfo[1].server_status != INFO_SERVER_UP &&
468 	    info->sinfo[0].server_status == INFO_SERVER_UP)
469 		info->sinfo[1].change = NS_SERVER_DOWN;
470 	else
471 		info->sinfo[1].change = 0;
472 
473 
474 	/* free memory in current copy first */
475 	if (info->sinfo[0].addr)
476 		free(info->sinfo[0].addr);
477 	info->sinfo[0].addr = NULL;
478 
479 	if (info->sinfo[0].hostname)
480 		free(info->sinfo[0].hostname);
481 	info->sinfo[0].hostname = NULL;
482 
483 	if (info->sinfo[0].rootDSE_data)
484 		free(info->sinfo[0].rootDSE_data);
485 	info->sinfo[0].rootDSE_data = NULL;
486 
487 	if (info->sinfo[0].errormsg)
488 		free(info->sinfo[0].errormsg);
489 	info->sinfo[0].errormsg = NULL;
490 
491 	/*
492 	 * make current and update copy identical
493 	 */
494 	info->sinfo[0] = info->sinfo[1];
495 
496 	/*
497 	 * getldap_get_server_stat() reads the update copy sinfo[1]
498 	 * so it can't be freed or nullified yet at this point.
499 	 *
500 	 * The sinfo[0] and sinfo[1] have identical string pointers.
501 	 * strdup the strings to avoid the double free problem.
502 	 * The strings of sinfo[1] are freed in
503 	 * getldap_get_rootDSE() and the strings of sinfo[0]
504 	 * are freed earlier in this function. If the pointers are the
505 	 * same, they will be freed twice.
506 	 */
507 	if (info->sinfo[1].addr)
508 		info->sinfo[0].addr = strdup(info->sinfo[1].addr);
509 	if (info->sinfo[1].hostname)
510 		info->sinfo[0].hostname = strdup(info->sinfo[1].hostname);
511 	if (info->sinfo[1].rootDSE_data)
512 		info->sinfo[0].rootDSE_data =
513 		    strdup(info->sinfo[1].rootDSE_data);
514 	if (info->sinfo[1].errormsg)
515 		info->sinfo[0].errormsg = strdup(info->sinfo[1].errormsg);
516 
517 	(void) mutex_unlock(&info->mutex[0]);
518 	(void) mutex_unlock(&info->mutex[1]);
519 
520 }
521 
522 static void *
523 getldap_get_rootDSE(void *arg)
524 {
525 	server_info_t	*serverInfo = (server_info_t *)arg;
526 	char		*rootDSE;
527 	int		exitrc = NS_LDAP_SUCCESS;
528 	pid_t		ppid;
529 	int		server_found = 0;
530 
531 	ns_ldap_error_t *error = NULL;
532 
533 	if (current_admin.debug_level >= DBG_ALL) {
534 		logit("getldap_get_rootDSE()....\n");
535 	}
536 
537 	/* initialize the server info element */
538 	(void) mutex_lock(&serverInfo->mutex[1]);
539 	serverInfo->sinfo[1].type	= INFO_RW_UNKNOWN;
540 	serverInfo->sinfo[1].info_status =
541 	    INFO_STATUS_UNKNOWN;
542 	/*
543 	 * When the sever list is refreshed over and over,
544 	 * this function is called each time it is refreshed.
545 	 * The previous server status of the update copy(sinfo[1])
546 	 * is the status of the current copy
547 	 */
548 	(void) mutex_lock(&serverInfo->mutex[0]);
549 	serverInfo->sinfo[1].prev_server_status =
550 	    serverInfo->sinfo[0].server_status;
551 	(void) mutex_unlock(&serverInfo->mutex[0]);
552 
553 	serverInfo->sinfo[1].server_status =
554 	    INFO_SERVER_UNKNOWN;
555 	if (serverInfo->sinfo[1].rootDSE_data)
556 		free(serverInfo->sinfo[1].rootDSE_data);
557 	serverInfo->sinfo[1].rootDSE_data	= NULL;
558 	if (serverInfo->sinfo[1].errormsg)
559 		free(serverInfo->sinfo[1].errormsg);
560 	serverInfo->sinfo[1].errormsg 		= NULL;
561 	(void) mutex_unlock(&serverInfo->mutex[1]);
562 
563 	(void) mutex_lock(&serverInfo->mutex[1]);
564 	serverInfo->sinfo[1].server_status = INFO_SERVER_CONNECTING;
565 	(void) mutex_unlock(&serverInfo->mutex[1]);
566 
567 	/*
568 	 * WARNING: anon_fallback == 1 (last argument) means that when
569 	 * __ns_ldap_getRootDSE is unable to bind using the configured
570 	 * credentials, it will try to fall back to using anonymous, non-SSL
571 	 * mode of operation.
572 	 *
573 	 * This is for backward compatibility reasons - we might have machines
574 	 * in the field with broken configuration (invalid credentials) and we
575 	 * don't want them to be disturbed.
576 	 */
577 	if (__ns_ldap_getRootDSE(serverInfo->sinfo[1].addr,
578 	    &rootDSE,
579 	    &error,
580 	    SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
581 		(void) mutex_lock(&serverInfo->mutex[1]);
582 		serverInfo->sinfo[1].server_status = INFO_SERVER_ERROR;
583 		serverInfo->sinfo[1].info_status = INFO_STATUS_ERROR;
584 		serverInfo->sinfo[1].errormsg =	strdup(error->message);
585 
586 		if (error != NULL) {
587 			(void) __ns_ldap_freeError(&error);
588 		}
589 
590 		if (current_admin.debug_level >= DBG_ALL) {
591 			logit("getldap_get_rootDSE: %s.\n",
592 			    serverInfo->sinfo[1].errormsg);
593 		}
594 		(void) mutex_unlock(&serverInfo->mutex[1]);
595 		/*
596 		 * sync sinfo copies in the serverInfo.
597 		 * protected by mutex
598 		 */
599 		sync_current_with_update_copy(serverInfo);
600 		thr_exit((void *) -1);
601 	}
602 
603 	(void) mutex_lock(&serverInfo->mutex[1]);
604 
605 	/* assume writeable, i.e., can do modify */
606 	serverInfo->sinfo[1].type		= INFO_RW_WRITEABLE;
607 	serverInfo->sinfo[1].server_status	= INFO_SERVER_UP;
608 	serverInfo->sinfo[1].info_status	= INFO_STATUS_NEW;
609 	/* remove the last DOORLINESEP */
610 	*(rootDSE+strlen(rootDSE)-1) = '\0';
611 	serverInfo->sinfo[1].rootDSE_data = rootDSE;
612 
613 	server_found = 1;
614 
615 	(void) mutex_unlock(&serverInfo->mutex[1]);
616 
617 	/*
618 	 * sync sinfo copies in the serverInfo.
619 	 * protected by mutex
620 	 */
621 	sync_current_with_update_copy(serverInfo);
622 	/*
623 	 * signal that the ldap_cachemgr parent process
624 	 * should exit now, if it is still waiting
625 	 */
626 	(void) mutex_lock(&sig_mutex);
627 	if (signal_done == FALSE && server_found) {
628 		ppid = getppid();
629 		(void) kill(ppid, SIGUSR1);
630 		if (current_admin.debug_level >= DBG_ALL) {
631 			logit("getldap_get_rootDSE(): "
632 			    "SIGUSR1 signal sent to "
633 			    "parent process(%ld).\n", ppid);
634 		}
635 		signal_done = TRUE;
636 	}
637 	(void) mutex_unlock(&sig_mutex);
638 
639 	thr_exit((void *) exitrc);
640 
641 	return ((void *) NULL);
642 }
643 
644 static int
645 getldap_init_serverInfo(server_info_t **head)
646 {
647 	char		**servers = NULL;
648 	int		rc = 0, i, exitrc = NS_LDAP_SUCCESS;
649 	ns_ldap_error_t *errorp = NULL;
650 	server_info_t	*info, *tail = NULL;
651 
652 	*head = NULL;
653 	if (current_admin.debug_level >= DBG_ALL) {
654 		logit("getldap_init_serverInfo()...\n");
655 	}
656 	rc = __s_api_getServers(&servers, &errorp);
657 
658 	if (rc != NS_LDAP_SUCCESS) {
659 		logit("getldap_init_serverInfo: "
660 		    "__s_api_getServers failed.\n");
661 		if (errorp)
662 			__ns_ldap_freeError(&errorp);
663 		return (-1);
664 	}
665 	for (i = 0; servers[i] != NULL; i++) {
666 		info = (server_info_t *)calloc(1, sizeof (server_info_t));
667 		if (info == NULL) {
668 			logit("getldap_init_serverInfo: "
669 			    "not enough memory.\n");
670 			exitrc = NS_LDAP_MEMORY;
671 			break;
672 		}
673 		if (i == 0) {
674 			*head = info;
675 			tail  = info;
676 		} else {
677 			tail->next = info;
678 			tail  = info;
679 		}
680 
681 		info->sinfo[0].addr		= strdup(servers[i]);
682 		if (info->sinfo[0].addr == NULL) {
683 			logit("getldap_init_serverInfo: "
684 			    "not enough memory.\n");
685 			exitrc = NS_LDAP_MEMORY;
686 			break;
687 		}
688 		info->sinfo[1].addr		= strdup(servers[i]);
689 		if (info->sinfo[1].addr == NULL) {
690 			logit("getldap_init_serverInfo: "
691 			    "not enough memory.\n");
692 			exitrc = NS_LDAP_MEMORY;
693 			break;
694 		}
695 
696 		info->sinfo[0].type 		= INFO_RW_UNKNOWN;
697 		info->sinfo[1].type 		= INFO_RW_UNKNOWN;
698 		info->sinfo[0].info_status	= INFO_STATUS_UNKNOWN;
699 		info->sinfo[1].info_status	= INFO_STATUS_UNKNOWN;
700 		info->sinfo[0].server_status	= INFO_SERVER_UNKNOWN;
701 		info->sinfo[1].server_status	= INFO_SERVER_UNKNOWN;
702 
703 		/*
704 		 * Assume at startup or after the configuration
705 		 * profile is refreshed, all servers are good.
706 		 */
707 		info->sinfo[0].prev_server_status =
708 		    INFO_SERVER_UP;
709 		info->sinfo[1].prev_server_status =
710 		    INFO_SERVER_UP;
711 		info->sinfo[0].hostname		= NULL;
712 		info->sinfo[1].hostname		= NULL;
713 		info->sinfo[0].rootDSE_data	= NULL;
714 		info->sinfo[1].rootDSE_data	= NULL;
715 		info->sinfo[0].errormsg 	= NULL;
716 		info->sinfo[1].errormsg 	= NULL;
717 		info->next 		= NULL;
718 	}
719 	__s_api_free2dArray(servers);
720 	if (exitrc != NS_LDAP_SUCCESS) {
721 		if (head && *head) {
722 			(void) getldap_destroy_serverInfo(*head);
723 			*head = NULL;
724 		}
725 	}
726 	return (exitrc);
727 }
728 
729 static int
730 getldap_destroy_serverInfo(server_info_t *head)
731 {
732 	server_info_t	*info, *next;
733 
734 	if (current_admin.debug_level >= DBG_ALL) {
735 		logit("getldap_destroy_serverInfo()...\n");
736 	}
737 
738 	if (head == NULL) {
739 		logit("getldap_destroy_serverInfo: "
740 		    "invalid serverInfo list.\n");
741 		return (-1);
742 	}
743 
744 	for (info = head; info; info = next) {
745 		if (info->sinfo[0].addr)
746 			free(info->sinfo[0].addr);
747 		if (info->sinfo[1].addr)
748 			free(info->sinfo[1].addr);
749 		if (info->sinfo[0].hostname)
750 			free(info->sinfo[0].hostname);
751 		if (info->sinfo[1].hostname)
752 			free(info->sinfo[1].hostname);
753 		if (info->sinfo[0].rootDSE_data)
754 			free(info->sinfo[0].rootDSE_data);
755 		if (info->sinfo[1].rootDSE_data)
756 			free(info->sinfo[1].rootDSE_data);
757 		if (info->sinfo[0].errormsg)
758 			free(info->sinfo[0].errormsg);
759 		if (info->sinfo[1].errormsg)
760 			free(info->sinfo[1].errormsg);
761 		next = info->next;
762 		free(info);
763 	}
764 	return (NS_LDAP_SUCCESS);
765 }
766 
767 static int
768 getldap_set_serverInfo(server_info_t *head, int reset_bindtime, info_op_t op)
769 {
770 	server_info_t	*info;
771 	int 		atleast1 = 0;
772 	thread_t	*tid;
773 	int 		num_threads = 0, i, j;
774 	void		*status;
775 	void		**paramVal = NULL;
776 	ns_ldap_error_t	*error = NULL;
777 
778 	if (current_admin.debug_level >= DBG_ALL) {
779 		logit("getldap_set_serverInfo()...\n");
780 	}
781 
782 	if (head == NULL) {
783 		logit("getldap_set_serverInfo: "
784 		    "invalid serverInfo list.\n");
785 		return (-1);
786 	}
787 
788 	/* Get the bind timeout value */
789 	if (reset_bindtime == 1) {
790 		tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
791 		(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P,
792 		    &paramVal, &error);
793 		if (paramVal != NULL && *paramVal != NULL) {
794 			/* convert to milliseconds */
795 			tcptimeout = **((int **)paramVal);
796 			tcptimeout *= 1000;
797 			(void) __ns_ldap_freeParam(&paramVal);
798 		}
799 		if (error)
800 			(void) __ns_ldap_freeError(&error);
801 	}
802 
803 	for (info = head; info; info = info->next)
804 		num_threads++;
805 
806 	if (num_threads == 0) {
807 		logit("getldap_set_serverInfo: "
808 		    "empty serverInfo list.\n");
809 		return (-1);
810 	}
811 
812 	tid = (thread_t *) calloc(1, sizeof (thread_t) * num_threads);
813 	if (tid == NULL) {
814 		logit("getldap_set_serverInfo: "
815 		    "No memory to create thread ID list.\n");
816 		return (-1);
817 	}
818 
819 	for (info = head, i = 0; info; info = info->next, i++) {
820 		if (thr_create(NULL, 0,
821 		    (void *(*)(void*))getldap_get_rootDSE,
822 		    (void *)info, 0, &tid[i])) {
823 			logit("getldap_set_serverInfo: "
824 			    "can not create thread %d.\n", i + 1);
825 			for (j = 0; j < i; j++)
826 				(void) thr_join(tid[j], NULL, NULL);
827 			free(tid);
828 			return (-1);
829 		}
830 	}
831 
832 	for (i = 0; i < num_threads; i++) {
833 		if (thr_join(tid[i], NULL, &status) == 0) {
834 			if ((int)status == NS_LDAP_SUCCESS)
835 				atleast1 = 1;
836 		}
837 	}
838 
839 	free(tid);
840 
841 	if (op == INFO_OP_REFRESH)
842 		test_server_change(head);
843 	if (atleast1) {
844 		return (NS_LDAP_SUCCESS);
845 	} else
846 		return (-1);
847 }
848 
849 /*
850  * getldap_get_serverInfo processes the GETLDAPSERVER door request passed
851  * to this function from getldap_serverInfo_op().
852  * input:
853  *   a buffer containing an empty string (e.g., input[0]='\0';) or a string
854  *   as the "input" in printf(input, "%s%s%s%s", req, addrtype, DOORLINESEP,
855  *   addr);
856  *   where addr is the address of a server and
857  *   req is one of the following:
858  *   NS_CACHE_NEW:    send a new server address, addr is ignored.
859  *   NS_CACHE_NORESP: send the next one, remove addr from list.
860  *   NS_CACHE_NEXT:   send the next one, keep addr on list.
861  *   NS_CACHE_WRITE:  send a non-replica server, if possible, if not, same
862  *                    as NS_CACHE_NEXT.
863  *   addrtype:
864  *   NS_CACHE_ADDR_IP: return server address as is, this is default.
865  *   NS_CACHE_ADDR_HOSTNAME: return both server address and its FQDN format,
866  *			only self credential case requires such format.
867  * output:
868  *   a buffer containing server info in the following format:
869  *   serveraddress DOORLINESEP [ serveraddress FQDN DOORLINESEP ]
870  *   [ attr=value [DOORLINESEP attr=value ]...]
871  *   For example: ( here | used as DOORLINESEP for visual purposes)
872  *   1) simple bind and sasl/DIGEST-MD5 bind :
873  *   1.2.3.4|supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL|
874  *   supportedSASLmechanisms=GSSAPI
875  *   2) sasl/GSSAPI bind (self credential):
876  *   1.2.3.4|foo.sun.com|supportedControl=1.1.1.1|
877  *   supportedSASLmechanisms=EXTERNAL|supportedSASLmechanisms=GSSAPI
878  *   NOTE: caller should free this buffer when done using it
879  */
880 static int
881 getldap_get_serverInfo(server_info_t *head, char *input,
882 		char **output, int *svr_removed)
883 {
884 	server_info_t	*info 	= NULL;
885 	server_info_t	*server	= NULL;
886 	char 		*addr	= NULL;
887 	char 		*req	= NULL;
888 	char 		req_new[] = NS_CACHE_NEW;
889 	char 		addr_type[] = NS_CACHE_ADDR_IP;
890 	int		matched = FALSE, len = 0, rc = 0;
891 	char		*ret_addr = NULL, *ret_addrFQDN = NULL;
892 	char		*new_addr = NULL;
893 	pid_t		pid;
894 
895 	if (current_admin.debug_level >= DBG_ALL) {
896 		logit("getldap_get_serverInfo()...\n");
897 	}
898 
899 	if (input == NULL || output == NULL) {
900 		logit("getldap_get_serverInfo: "
901 		    "No input or output buffer.\n");
902 		return (-1);
903 	}
904 
905 	*output = NULL;
906 	*svr_removed = FALSE;
907 
908 	if (head == NULL) {
909 		logit("getldap_get_serverInfo: "
910 		    "invalid serverInfo list.\n");
911 		return (-1);
912 	}
913 	/*
914 	 * parse the input string to get req and addr,
915 	 * if input is empty, i.e., input[0] == '\0',
916 	 * treat it as an NS_CACHE_NEW request
917 	 */
918 	req = req_new;
919 	if (input[0] != '\0') {
920 		req = input;
921 		/* Save addr type flag */
922 		addr_type[0] = input[1];
923 		input[strlen(NS_CACHE_NEW)] = '\0';
924 		/* skip acion type flag, addr type flag and DOORLINESEP */
925 		addr = input + strlen(DOORLINESEP) + strlen(NS_CACHE_NEW)
926 		    + strlen(NS_CACHE_ADDR_IP);
927 	}
928 	/*
929 	 * if NS_CACHE_NEW,
930 	 * or the server info is new,
931 	 * starts from the
932 	 * beginning of the list
933 	 */
934 	if ((strcmp(req, NS_CACHE_NEW) == 0) ||
935 	    (head->sinfo[0].info_status == INFO_STATUS_NEW))
936 		matched = TRUE;
937 	for (info = head; info; info = info->next) {
938 		/*
939 		 * make sure the server info stays the same
940 		 * while the data is being processed
941 		 */
942 
943 		/*
944 		 * This function is called to get server info list
945 		 * and pass it back to door call clients.
946 		 * Access the current copy (sinfo[0]) to get such
947 		 * information
948 		 */
949 		(void) mutex_lock(&info->mutex[0]);
950 
951 		if (matched == FALSE &&
952 		    strcmp(info->sinfo[0].addr, addr) == 0) {
953 			matched = TRUE;
954 			if (strcmp(req, NS_CACHE_NORESP) == 0) {
955 				if (chg_is_called_from_nscd_or_peruser_nscd(
956 				    "REMOVE SERVER", &pid) == 0) {
957 					(void) mutex_unlock(&info->mutex[0]);
958 					if (current_admin.debug_level >=
959 					    DBG_ALL)
960 						logit("Only nscd can remove "
961 						    "servers. pid %ld", pid);
962 					continue;
963 				}
964 
965 				/*
966 				 * if the information is new,
967 				 * give this server one more chance
968 				 */
969 				if (info->sinfo[0].info_status ==
970 				    INFO_STATUS_NEW &&
971 				    info->sinfo[0].server_status  ==
972 				    INFO_SERVER_UP) {
973 					server = info;
974 					break;
975 				} else {
976 					/*
977 					 * it is recommended that
978 					 * before removing the
979 					 * server from the list,
980 					 * the server should be
981 					 * contacted one more time
982 					 * to make sure that it is
983 					 * really unavailable.
984 					 * For now, just trust the client
985 					 * (i.e., the sldap library)
986 					 * that it knows what it is
987 					 * doing and would not try
988 					 * to mess up the server
989 					 * list.
990 					 */
991 					/*
992 					 * Make a copy of addr to contact
993 					 * it later. It's not doing it here
994 					 * to avoid long wait and possible
995 					 * recursion to contact an LDAP server.
996 					 */
997 					new_addr = strdup(info->sinfo[0].addr);
998 					if (new_addr)
999 						remove_server(new_addr);
1000 					*svr_removed = TRUE;
1001 					(void) mutex_unlock(&info->mutex[0]);
1002 					break;
1003 				}
1004 			} else {
1005 				/*
1006 				 * req == NS_CACHE_NEXT or NS_CACHE_WRITE
1007 				 */
1008 				(void) mutex_unlock(&info->mutex[0]);
1009 				continue;
1010 			}
1011 		}
1012 
1013 		if (matched) {
1014 			if (strcmp(req, NS_CACHE_WRITE) == 0) {
1015 				if (info->sinfo[0].type ==
1016 				    INFO_RW_WRITEABLE &&
1017 				    info->sinfo[0].server_status  ==
1018 				    INFO_SERVER_UP) {
1019 					server = info;
1020 					break;
1021 				}
1022 			} else if (info->sinfo[0].server_status ==
1023 			    INFO_SERVER_UP) {
1024 				server = info;
1025 				break;
1026 			}
1027 		}
1028 
1029 		(void) mutex_unlock(&info->mutex[0]);
1030 	}
1031 
1032 	if (server) {
1033 		if (strcmp(addr_type, NS_CACHE_ADDR_HOSTNAME) == 0) {
1034 			/*
1035 			 * In SASL/GSSAPI case, a hostname is required for
1036 			 * Kerberos's service principal.
1037 			 * e.g.
1038 			 * ldap/foo.sun.com@SUN.COM
1039 			 */
1040 			if (server->sinfo[0].hostname == NULL) {
1041 				rc = __s_api_ip2hostname(server->sinfo[0].addr,
1042 				    &server->sinfo[0].hostname);
1043 				if (rc != NS_LDAP_SUCCESS) {
1044 					(void) mutex_unlock(&info->mutex[0]);
1045 					return (rc);
1046 				}
1047 				if (current_admin.debug_level >= DBG_ALL) {
1048 					logit("getldap_get_serverInfo: "
1049 					    "%s is converted to %s\n",
1050 					    server->sinfo[0].addr,
1051 					    server->sinfo[0].hostname);
1052 				}
1053 			}
1054 			ret_addr = server->sinfo[0].addr;
1055 			ret_addrFQDN = server->sinfo[0].hostname;
1056 
1057 		} else
1058 			ret_addr = server->sinfo[0].addr;
1059 
1060 
1061 		len = strlen(ret_addr) +
1062 		    strlen(server->sinfo[0].rootDSE_data) +
1063 		    strlen(DOORLINESEP) + 1;
1064 		if (ret_addrFQDN != NULL)
1065 			len += strlen(ret_addrFQDN) + strlen(DOORLINESEP);
1066 		*output = (char *)malloc(len);
1067 		if (*output == NULL) {
1068 			(void) mutex_unlock(&info->mutex[0]);
1069 			return (NS_LDAP_MEMORY);
1070 		}
1071 		if (ret_addrFQDN == NULL)
1072 			(void) snprintf(*output, len, "%s%s%s",
1073 			    ret_addr, DOORLINESEP,
1074 			    server->sinfo[0].rootDSE_data);
1075 		else
1076 			(void) snprintf(*output, len, "%s%s%s%s%s",
1077 			    ret_addr, DOORLINESEP,
1078 			    ret_addrFQDN, DOORLINESEP,
1079 			    server->sinfo[0].rootDSE_data);
1080 		server->sinfo[0].info_status = INFO_STATUS_OLD;
1081 		(void) mutex_unlock(&info->mutex[0]);
1082 		return (NS_LDAP_SUCCESS);
1083 	}
1084 	else
1085 		return (-99);
1086 }
1087 
1088 /*
1089  * Format previous and next refresh time
1090  */
1091 static int
1092 getldap_format_refresh_time(char **output, time_t *prev, time_t *next)
1093 {
1094 #define	TIME_FORMAT	"%Y/%m/%d %H:%M:%S"
1095 #define	TIME_HEADER1	"  Previous refresh time: "
1096 #define	TIME_HEADER2	"  Next refresh time:     "
1097 	int		hdr1_len = strlen(gettext(TIME_HEADER1));
1098 	int		hdr2_len = strlen(gettext(TIME_HEADER2));
1099 	struct	tm 	tm;
1100 	char		nbuf[256];
1101 	char		pbuf[256];
1102 	int		len;
1103 
1104 	if (current_admin.debug_level >= DBG_ALL) {
1105 		logit("getldap_format_refresh_time()...\n");
1106 	}
1107 
1108 	*output = NULL;
1109 
1110 	/* format the time of previous refresh  */
1111 	if (*prev != 0) {
1112 		(void) localtime_r(prev, &tm);
1113 		(void) strftime(pbuf, sizeof (pbuf) - 1, TIME_FORMAT, &tm);
1114 	} else {
1115 		(void) strcpy(pbuf, gettext("NOT DONE"));
1116 	}
1117 
1118 	/* format the time of next refresh  */
1119 	if (*next != 0) {
1120 		(void) localtime_r(next, &tm);
1121 		(void) strftime(nbuf, sizeof (nbuf) - 1, TIME_FORMAT, &tm);
1122 	} else {
1123 		(void) strcpy(nbuf, gettext("NOT SET"));
1124 	}
1125 
1126 	len = hdr1_len + hdr2_len + strlen(nbuf) +
1127 	    strlen(pbuf) + 2 * strlen(DOORLINESEP) + 1;
1128 
1129 	*output = malloc(len);
1130 	if (*output == NULL)
1131 		return (-1);
1132 
1133 	(void) snprintf(*output, len, "%s%s%s%s%s%s",
1134 	    gettext(TIME_HEADER1), pbuf, DOORLINESEP,
1135 	    gettext(TIME_HEADER2), nbuf, DOORLINESEP);
1136 
1137 	return (NS_LDAP_SUCCESS);
1138 }
1139 
1140 /*
1141  * getldap_get_server_stat processes the GETSTAT request passed
1142  * to this function from getldap_serverInfo_op().
1143  * output:
1144  *   a buffer containing info for all the servers.
1145  *   For each server, the data is in the following format:
1146  *   server: server address or name, status: unknown|up|down|removed DOORLINESEP
1147  *   for example: ( here | used as DOORLINESEP for visual purposes)
1148  *   server: 1.2.3.4, status: down|server: 2.2.2.2, status: up|
1149  *   NOTE: caller should free this buffer when done using it
1150  */
1151 static int
1152 getldap_get_server_stat(server_info_t *head, char **output,
1153 		time_t *prev, time_t *next)
1154 {
1155 #define	S_HEADER	"Server information: "
1156 #define	S_FORMAT	"  server: %s, status: %s%s"
1157 #define	S_ERROR		"    error message: %s%s"
1158 	server_info_t	*info 	= NULL;
1159 	int	header_len = strlen(gettext(S_HEADER));
1160 	int	format_len = strlen(gettext(S_FORMAT));
1161 	int	error_len = strlen(gettext(S_ERROR));
1162 	int	len = header_len + strlen(DOORLINESEP);
1163 	int	len1 = 0;
1164 	char	*status, *output1 = NULL, *tmpptr;
1165 
1166 	*output = NULL;
1167 
1168 	if (current_admin.debug_level >= DBG_ALL) {
1169 		logit("getldap_get_server_stat()...\n");
1170 	}
1171 
1172 	if (head == NULL) {
1173 		logit("getldap_get_server_stat: "
1174 		    "invalid serverInfo list.\n");
1175 		return (-1);
1176 	}
1177 
1178 	/* format previous and next refresh time */
1179 	(void) getldap_format_refresh_time(&output1, prev, next);
1180 	if (output1 == NULL)
1181 		return (-1);
1182 	len += strlen(output1);
1183 	len1 = len + strlen(DOORLINESEP) + 1;
1184 
1185 	*output = (char *)calloc(1, len1);
1186 	if (*output == NULL) {
1187 		free(output1);
1188 		return (-1);
1189 	}
1190 
1191 	/* insert header string and refresh time info */
1192 	(void) snprintf(*output, len1, "%s%s%s",
1193 	    gettext(S_HEADER), DOORLINESEP, output1);
1194 
1195 	for (info = head; info; info = info->next) {
1196 
1197 		/*
1198 		 * make sure the server info stays the same
1199 		 * while the data is being processed
1200 		 */
1201 		(void) mutex_lock(&info->mutex[1]);
1202 
1203 		/*
1204 		 * When the updating process is under way(getldap_get_rootDSE)
1205 		 * the update copy(sinfo[1] is the latest copy.
1206 		 * When the updating process
1207 		 * is done, the current copy (sinfo[0]) has the latest status,
1208 		 * which is still identical to the update copy.
1209 		 * So update copy has the latest status.
1210 		 * Use the update copy(sinfo[1]) to show status
1211 		 * (ldap_cachemgr -g).
1212 		 *
1213 		 */
1214 
1215 		switch (info->sinfo[1].server_status) {
1216 		case INFO_SERVER_UNKNOWN:
1217 			status = gettext("UNKNOWN");
1218 			break;
1219 		case INFO_SERVER_CONNECTING:
1220 			status = gettext("CONNECTING");
1221 			break;
1222 		case INFO_SERVER_UP:
1223 			status = gettext("UP");
1224 			break;
1225 		case INFO_SERVER_ERROR:
1226 			status = gettext("ERROR");
1227 			break;
1228 		case INFO_SERVER_REMOVED:
1229 			status = gettext("REMOVED");
1230 			break;
1231 		}
1232 
1233 		len += format_len + strlen(status) +
1234 		    strlen(info->sinfo[1].addr) +
1235 		    strlen(DOORLINESEP);
1236 		if (info->sinfo[1].errormsg != NULL)
1237 			len += error_len +
1238 			    strlen(info->sinfo[1].errormsg) +
1239 			    strlen(DOORLINESEP);
1240 
1241 		tmpptr = (char *)realloc(*output, len);
1242 		if (tmpptr == NULL) {
1243 			free(output1);
1244 			free(*output);
1245 			*output = NULL;
1246 			(void) mutex_unlock(&info->mutex[1]);
1247 			return (-1);
1248 		} else
1249 			*output = tmpptr;
1250 
1251 		/* insert server IP addr or name and status */
1252 		len1 = len - strlen(*output);
1253 		(void) snprintf(*output + strlen(*output), len1,
1254 		    gettext(S_FORMAT), info->sinfo[1].addr,
1255 		    status, DOORLINESEP);
1256 		/* insert error message if any */
1257 		len1 = len - strlen(*output);
1258 		if (info->sinfo[1].errormsg != NULL)
1259 			(void) snprintf(*output + strlen(*output), len1,
1260 			    gettext(S_ERROR),
1261 			    info->sinfo[1].errormsg,
1262 			    DOORLINESEP);
1263 
1264 		(void) mutex_unlock(&info->mutex[1]);
1265 
1266 	}
1267 
1268 	free(output1);
1269 	return (NS_LDAP_SUCCESS);
1270 }
1271 
1272 /*
1273  * Format and return the refresh time statistics
1274  */
1275 static int
1276 getldap_get_refresh_stat(char **output)
1277 {
1278 #define	R_HEADER0	"Configuration refresh information: "
1279 #define	R_HEADER1	"  Configured to NO REFRESH."
1280 	int		hdr0_len = strlen(gettext(R_HEADER0));
1281 	int		hdr1_len = strlen(gettext(R_HEADER1));
1282 	int		cache_ttl = -1, len = 0;
1283 	time_t 		expire = 0;
1284 	void		**paramVal = NULL;
1285 	ns_ldap_error_t	*errorp = NULL;
1286 	char		*output1 = NULL;
1287 
1288 	if (current_admin.debug_level >= DBG_ALL) {
1289 		logit("getldap_get_refresh_stat()...\n");
1290 	}
1291 
1292 	*output = NULL;
1293 
1294 	/* get configured cache TTL */
1295 	if ((__ns_ldap_getParam(NS_LDAP_CACHETTL_P,
1296 	    &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
1297 	    paramVal != NULL &&
1298 	    (char *)*paramVal != NULL) {
1299 			cache_ttl = atol((char *)*paramVal);
1300 	} else {
1301 		if (errorp)
1302 			__ns_ldap_freeError(&errorp);
1303 	}
1304 	(void) __ns_ldap_freeParam(&paramVal);
1305 
1306 	/* cound not get cache TTL */
1307 	if (cache_ttl == -1)
1308 		return (-1);
1309 
1310 	if (cache_ttl == 0) {
1311 		len = hdr0_len + hdr1_len +
1312 		    2 * strlen(DOORLINESEP) + 1;
1313 		*output = malloc(len);
1314 		if (*output == NULL)
1315 			return (-1);
1316 		(void) snprintf(*output, len, "%s%s%s%s",
1317 		    gettext(R_HEADER0), DOORLINESEP,
1318 		    gettext(R_HEADER1), DOORLINESEP);
1319 	} else {
1320 
1321 		/* get configuration expiration time */
1322 		if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
1323 		    &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
1324 		    paramVal != NULL &&
1325 		    (char *)*paramVal != NULL) {
1326 				expire = (time_t)atol((char *)*paramVal);
1327 		} else {
1328 			if (errorp)
1329 				__ns_ldap_freeError(&errorp);
1330 		}
1331 
1332 		(void) __ns_ldap_freeParam(&paramVal);
1333 
1334 		/* cound not get expiration time */
1335 		if (expire == -1)
1336 			return (-1);
1337 
1338 		/* format previous and next refresh time */
1339 		(void) getldap_format_refresh_time(&output1,
1340 		    &prev_refresh_time, &expire);
1341 		if (output1 == NULL)
1342 			return (-1);
1343 
1344 		len = hdr0_len + strlen(output1) +
1345 		    2 * strlen(DOORLINESEP) + 1;
1346 		*output = malloc(len);
1347 		if (*output == NULL) {
1348 			free(output1);
1349 			return (-1);
1350 		}
1351 		(void) snprintf(*output, len, "%s%s%s%s",
1352 		    gettext(R_HEADER0), DOORLINESEP,
1353 		    output1, DOORLINESEP);
1354 		free(output1);
1355 	}
1356 
1357 	return (NS_LDAP_SUCCESS);
1358 }
1359 
1360 static int
1361 getldap_get_cacheTTL()
1362 {
1363 	void		**paramVal = NULL;
1364 	ns_ldap_error_t	*error;
1365 	int		rc = 0, cachettl;
1366 
1367 
1368 	if (current_admin.debug_level >= DBG_ALL) {
1369 		logit("getldap_get_cacheTTL()....\n");
1370 	}
1371 
1372 	if ((rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P,
1373 	    &paramVal, &error)) != NS_LDAP_SUCCESS) {
1374 		if (error != NULL && error->message != NULL)
1375 			logit("Error: Unable to get configuration "
1376 			    "refresh TTL: %s\n",
1377 			    error->message);
1378 		else {
1379 			char *tmp;
1380 
1381 			__ns_ldap_err2str(rc, &tmp);
1382 			logit("Error: Unable to get configuration "
1383 			    "refresh TTL: %s\n", tmp);
1384 		}
1385 		(void) __ns_ldap_freeParam(&paramVal);
1386 		(void) __ns_ldap_freeError(&error);
1387 		return (-1);
1388 	}
1389 	if (paramVal == NULL || (char *)*paramVal == NULL)
1390 			return (-1);
1391 	cachettl = atol((char *)*paramVal);
1392 	(void) __ns_ldap_freeParam(&paramVal);
1393 	return (cachettl);
1394 }
1395 
1396 
1397 /*
1398  * This function implements the adaptive server list refresh
1399  * algorithm used by ldap_cachemgr. The idea is to have the
1400  * refresh TTL adjust itself between maximum and minimum
1401  * values. If the server list has been walked three times
1402  * in a row without errors, the TTL will be doubled. This will
1403  * be done repeatedly until the maximum value is reached
1404  * or passed. If passed, the maximum value will be used.
1405  * If any time a server is found to be down/bad, either
1406  * after another server list walk or informed by libsldap via
1407  * the GETLDAPSERVER door calls, the TTL will be set to half
1408  * of its value, again repeatedly, but no less than the minimum
1409  * value. Also, at any time, if all the servers on the list
1410  * are found to be down/bad, the TTL will be set to minimum,
1411  * so that a "no-server" refresh loop should be entered to try
1412  * to find a good server as soon as possible. The caller
1413  * could check the no_gd_server flag for this situation.
1414  * The maximum and minimum values are initialized when the input
1415  * refresh_ttl is set to zero, this should occur during
1416  * ldap_cachemgr startup or every time the server list is
1417  * recreated after the configuration profile is refreshed
1418  * from an LDAP server. The maximum is set to the value of
1419  * the NS_LDAP_CACHETTL parameter (configuration profile
1420  * refresh TTL), but if it is zero (never refreshed) or can
1421  * not be retrieved, the maximum is set to the macro
1422  * REFRESHTTL_MAX (12 hours) defined below. The minimum is
1423  * set to REFRESHTTL_MIN, which is the TCP connection timeout
1424  * (tcptimeout) set via the LDAP API ldap_set_option()
1425  * with the new LDAP_X_OPT_CONNECT_TIMEOUT option plus 10 seconds.
1426  * This accounts for the maximum possible timeout value for an
1427  * LDAP TCP connect call.The first refresh TTL, initial value of
1428  * refresh_ttl, will be set to the smaller of the two,
1429  * REFRESHTTL_REGULAR (10 minutes) or (REFRESHTTL_MAX + REFRESHTTL_MIN)/2.
1430  * The idea is to have a low starting value and have the value
1431  * stay low if the network/server is unstable, but eventually
1432  * the value will move up to maximum and stay there if the
1433  * network/server is stable.
1434  */
1435 static int
1436 getldap_set_refresh_ttl(server_info_t *head, int *refresh_ttl,
1437 		int *no_gd_server)
1438 {
1439 #define	REFRESHTTL_REGULAR	600
1440 #define	REFRESHTTL_MAX		43200
1441 /* tcptimeout is in milliseconds */
1442 #define	REFRESHTTL_MIN		(tcptimeout/1000) + 10
1443 #define	UP_REFRESH_TTL_NUM	2
1444 
1445 	static mutex_t		refresh_mutex;
1446 	static int		refresh_ttl_max = 0;
1447 	static int		refresh_ttl_min = 0;
1448 	static int		num_walked_ok = 0;
1449 	int			num_servers = 0;
1450 	int			num_good_servers = 0;
1451 	int			num_prev_good_servers = 0;
1452 	server_info_t		*info;
1453 
1454 	/* allow one thread at a time */
1455 	(void) mutex_lock(&refresh_mutex);
1456 
1457 	if (current_admin.debug_level >= DBG_ALL) {
1458 		logit("getldap_set_refresh_ttl()...\n");
1459 	}
1460 
1461 	if (!head || !refresh_ttl || !no_gd_server) {
1462 		logit("getldap_set_refresh_ttl: head is "
1463 		    "NULL or refresh_ttl is NULL or "
1464 		    "no_gd_server is NULL");
1465 		(void) mutex_unlock(&refresh_mutex);
1466 		return (-1);
1467 	}
1468 	*no_gd_server = FALSE;
1469 
1470 	/*
1471 	 * init max. min. TTLs if first time through or a fresh one
1472 	 */
1473 	if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1474 		logit("getldap_set_refresh_ttl:(1) refresh ttl is %d "
1475 		    "seconds\n", *refresh_ttl);
1476 	}
1477 	if (*refresh_ttl == 0) {
1478 		num_walked_ok = 0;
1479 		/*
1480 		 * init cache manager server list TTL:
1481 		 *
1482 		 * init the min. TTL to
1483 		 * REFRESHTTL_MIN ( 2*(TCP MSL) + 10 seconds)
1484 		 */
1485 		refresh_ttl_min = REFRESHTTL_MIN;
1486 
1487 		/*
1488 		 * try to set the max. TTL to
1489 		 * configuration refresh TTL (NS_LDAP_CACHETTL),
1490 		 * if error (-1), or never refreshed (0),
1491 		 * set it to REFRESHTTL_MAX (12 hours)
1492 		 */
1493 		refresh_ttl_max = getldap_get_cacheTTL();
1494 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1495 			logit("getldap_set_refresh_ttl:(2) refresh ttl is %d "
1496 			    "seconds\n", *refresh_ttl);
1497 			logit("getldap_set_refresh_ttl:(2) max ttl is %d, "
1498 			    "min ttl is %d seconds\n",
1499 			    refresh_ttl_max, refresh_ttl_min);
1500 		}
1501 		if (refresh_ttl_max <= 0)
1502 			refresh_ttl_max = REFRESHTTL_MAX;
1503 		else if (refresh_ttl_max < refresh_ttl_min)
1504 			refresh_ttl_max = refresh_ttl_min;
1505 
1506 		/*
1507 		 * init the first TTL to the smaller of the two:
1508 		 * REFRESHTTL_REGULAR ( 10 minutes),
1509 		 * (refresh_ttl_max + refresh_ttl_min)/2
1510 		 */
1511 		*refresh_ttl = REFRESHTTL_REGULAR;
1512 		if (*refresh_ttl > (refresh_ttl_max + refresh_ttl_min) / 2)
1513 			*refresh_ttl = (refresh_ttl_max + refresh_ttl_min) / 2;
1514 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1515 			logit("getldap_set_refresh_ttl:(3) refresh ttl is %d "
1516 			    "seconds\n", *refresh_ttl);
1517 			logit("getldap_set_refresh_ttl:(3) max ttl is %d, "
1518 			    "min ttl is %d seconds\n",
1519 			    refresh_ttl_max, refresh_ttl_min);
1520 		}
1521 	}
1522 
1523 	/*
1524 	 * get the servers statistics:
1525 	 * number of servers on list
1526 	 * number of good servers on list
1527 	 * number of pevious good servers on list
1528 	 */
1529 	for (info = head; info; info = info->next) {
1530 		num_servers++;
1531 		(void) mutex_lock(&info->mutex[0]);
1532 		if (info->sinfo[0].server_status  == INFO_SERVER_UP)
1533 			num_good_servers++;
1534 		/*
1535 		 * Server's previous status could be UNKNOWN
1536 		 * only between the very first and second
1537 		 * refresh. Treat that UNKNOWN status as up
1538 		 */
1539 		if (info->sinfo[0].prev_server_status
1540 		    == INFO_SERVER_UP ||
1541 		    info->sinfo[0].prev_server_status
1542 		    == INFO_SERVER_UNKNOWN)
1543 			num_prev_good_servers++;
1544 		(void) mutex_unlock(&info->mutex[0]);
1545 	}
1546 
1547 	/*
1548 	 * if the server list is walked three times in a row
1549 	 * without problems, double the refresh TTL but no more
1550 	 * than the max. refresh TTL
1551 	 */
1552 	if (num_good_servers == num_servers) {
1553 		num_walked_ok++;
1554 		if (num_walked_ok > UP_REFRESH_TTL_NUM)  {
1555 
1556 			*refresh_ttl = *refresh_ttl * 2;
1557 			if (*refresh_ttl > refresh_ttl_max)
1558 				*refresh_ttl = refresh_ttl_max;
1559 
1560 			num_walked_ok = 0;
1561 		}
1562 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1563 			logit("getldap_set_refresh_ttl:(4) refresh ttl is %d "
1564 			    "seconds\n", *refresh_ttl);
1565 		}
1566 	} else if (num_good_servers == 0) {
1567 		/*
1568 		 * if no good server found,
1569 		 * set refresh TTL to miminum
1570 		 */
1571 		*refresh_ttl = refresh_ttl_min;
1572 		*no_gd_server = TRUE;
1573 		num_walked_ok = 0;
1574 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1575 			logit("getldap_set_refresh_ttl:(5) refresh ttl is %d "
1576 			    "seconds\n", *refresh_ttl);
1577 		}
1578 	} else if (num_prev_good_servers > num_good_servers) {
1579 		/*
1580 		 * if more down/bad servers found,
1581 		 * decrease the refresh TTL by half
1582 		 * but no less than the min. refresh TTL
1583 		 */
1584 		*refresh_ttl = *refresh_ttl / 2;
1585 		if (*refresh_ttl < refresh_ttl_min)
1586 			*refresh_ttl = refresh_ttl_min;
1587 		num_walked_ok = 0;
1588 		logit("getldap_set_refresh_ttl:(6) refresh ttl is %d "
1589 		    "seconds\n", *refresh_ttl);
1590 
1591 	}
1592 
1593 	if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1594 		logit("getldap_set_refresh_ttl:(7) refresh ttl is %d seconds\n",
1595 		    *refresh_ttl);
1596 	}
1597 	(void) mutex_unlock(&refresh_mutex);
1598 	return (0);
1599 }
1600 
1601 static int
1602 getldap_serverInfo_op(info_op_t op, char *input, char **output)
1603 {
1604 
1605 	static rwlock_t 	info_lock = DEFAULTRWLOCK;
1606 	static rwlock_t 	info_lock_old = DEFAULTRWLOCK;
1607 	static mutex_t		info_mutex;
1608 	static cond_t		info_cond;
1609 	static int		creating = FALSE;
1610 	static int		refresh_ttl = 0;
1611 	static int		sec_to_refresh = 0;
1612 	static int		in_no_server_mode = FALSE;
1613 
1614 	static server_info_t 	*serverInfo = NULL;
1615 	static server_info_t 	*serverInfo_old = NULL;
1616 	server_info_t 		*serverInfo_1;
1617 	int 			is_creating;
1618 	int 			err, no_server_good = FALSE;
1619 	int			server_removed = FALSE;
1620 	int			fall_thru = FALSE;
1621 	static struct timespec	timeout;
1622 	struct timespec		new_timeout;
1623 	struct timeval		tp;
1624 	static time_t		prev_refresh = 0, next_refresh = 0;
1625 	ns_server_status_t		changed = 0;
1626 
1627 	if (current_admin.debug_level >= DBG_ALL) {
1628 		logit("getldap_serverInfo_op()...\n");
1629 	}
1630 	switch (op) {
1631 	case INFO_OP_CREATE:
1632 		if (current_admin.debug_level >= DBG_ALL) {
1633 			logit("operation is INFO_OP_CREATE...\n");
1634 		}
1635 
1636 		/*
1637 		 * indicate that the server info is being
1638 		 * (re)created, so that the refresh thread
1639 		 * will not refresh the info list right
1640 		 * after the list got (re)created
1641 		 */
1642 		(void) mutex_lock(&info_mutex);
1643 		is_creating = creating;
1644 		creating = TRUE;
1645 		(void) mutex_unlock(&info_mutex);
1646 
1647 		if (is_creating)
1648 			break;
1649 		/*
1650 		 * create an empty info list
1651 		 */
1652 		(void) getldap_init_serverInfo(&serverInfo_1);
1653 		/*
1654 		 * exit if list not created
1655 		 */
1656 		if (serverInfo_1 == NULL) {
1657 			(void) mutex_lock(&info_mutex);
1658 			creating = FALSE;
1659 			(void) mutex_unlock(&info_mutex);
1660 			break;
1661 		}
1662 		/*
1663 		 * make the new server info available:
1664 		 * use writer lock here, so that the switch
1665 		 * is done after all the reader locks have
1666 		 * been released.
1667 		 */
1668 		(void) rw_wrlock(&info_lock);
1669 		serverInfo = serverInfo_1;
1670 		/*
1671 		 * if this is the first time
1672 		 * the server list is being created,
1673 		 * (i.e., serverInfo_old is NULL)
1674 		 * make the old list same as the new
1675 		 * so the GETSERVER code can do its work
1676 		 */
1677 		if (serverInfo_old == NULL)
1678 			serverInfo_old = serverInfo_1;
1679 		(void) rw_unlock(&info_lock);
1680 
1681 		/*
1682 		 * fill the new info list
1683 		 */
1684 		(void) rw_rdlock(&info_lock);
1685 		/* reset bind time (tcptimeout) */
1686 		(void) getldap_set_serverInfo(serverInfo, 1, INFO_OP_CREATE);
1687 
1688 		(void) mutex_lock(&info_mutex);
1689 		/*
1690 		 * set cache manager server list TTL,
1691 		 * set refresh_ttl to zero to indicate a fresh one
1692 		 */
1693 		refresh_ttl = 0;
1694 		(void) getldap_set_refresh_ttl(serverInfo,
1695 		    &refresh_ttl, &no_server_good);
1696 		sec_to_refresh = refresh_ttl;
1697 
1698 		/* statistics: previous refresh time */
1699 		if (gettimeofday(&tp, NULL) == 0)
1700 			prev_refresh = tp.tv_sec;
1701 
1702 		creating = FALSE;
1703 
1704 		/*
1705 		 * if no server found or available,
1706 		 * tell the server info refresh thread
1707 		 * to start the "no-server" refresh loop
1708 		 * otherwise reset the in_no_server_mode flag
1709 		 */
1710 		if (no_server_good) {
1711 			sec_to_refresh = 0;
1712 			in_no_server_mode = TRUE;
1713 		} else
1714 			in_no_server_mode = FALSE;
1715 		/*
1716 		 * awake the sleeping refresh thread
1717 		 */
1718 		(void) cond_signal(&info_cond);
1719 
1720 		(void) mutex_unlock(&info_mutex);
1721 		(void) rw_unlock(&info_lock);
1722 
1723 		/*
1724 		 * delete the old server info
1725 		 */
1726 		(void) rw_wrlock(&info_lock_old);
1727 		if (serverInfo_old != serverInfo)
1728 			(void) getldap_destroy_serverInfo(serverInfo_old);
1729 		/*
1730 		 * serverInfo_old needs to be the same as
1731 		 * serverinfo now.
1732 		 * it will be used by GETSERVER processing.
1733 		 */
1734 		serverInfo_old = serverInfo;
1735 		(void) rw_unlock(&info_lock_old);
1736 		break;
1737 	case INFO_OP_DELETE:
1738 		if (current_admin.debug_level >= DBG_ALL) {
1739 			logit("operation is INFO_OP_DELETE...\n");
1740 		}
1741 		/*
1742 		 * use writer lock here, so that the delete would
1743 		 * not start until all the reader locks have
1744 		 * been released.
1745 		 */
1746 		(void) rw_wrlock(&info_lock);
1747 		if (serverInfo)
1748 			(void) getldap_destroy_serverInfo(serverInfo);
1749 		serverInfo = NULL;
1750 		(void) rw_unlock(&info_lock);
1751 		break;
1752 	case INFO_OP_REFRESH:
1753 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1754 			logit("operation is INFO_OP_REFRESH...\n");
1755 		}
1756 		/*
1757 		 * if server info is currently being
1758 		 * (re)created, do nothing
1759 		 */
1760 		(void) mutex_lock(&info_mutex);
1761 		is_creating = creating;
1762 		(void) mutex_unlock(&info_mutex);
1763 		if (is_creating)
1764 			break;
1765 
1766 		(void) rw_rdlock(&info_lock);
1767 		if (serverInfo) {
1768 			/* do not reset bind time (tcptimeout) */
1769 			(void) getldap_set_serverInfo(serverInfo, 0,
1770 			    INFO_OP_REFRESH);
1771 
1772 			(void) mutex_lock(&info_mutex);
1773 
1774 			/* statistics: previous refresh time */
1775 			if (gettimeofday(&tp, NULL) == 0)
1776 				prev_refresh = tp.tv_sec;
1777 			/*
1778 			 * set cache manager server list TTL
1779 			 */
1780 			(void) getldap_set_refresh_ttl(serverInfo,
1781 			    &refresh_ttl, &no_server_good);
1782 			/*
1783 			 * if no good server found,
1784 			 * tell the server info refresh thread
1785 			 * to start the "no-server" refresh loop
1786 			 * otherwise reset the in_no_server_mode flag
1787 			 */
1788 			if (no_server_good) {
1789 				in_no_server_mode = TRUE;
1790 				sec_to_refresh = 0;
1791 			} else {
1792 				in_no_server_mode = FALSE;
1793 				sec_to_refresh = refresh_ttl;
1794 			}
1795 			if (current_admin.debug_level >=
1796 			    DBG_SERVER_LIST_REFRESH) {
1797 				logit("getldap_serverInfo_op("
1798 				    "INFO_OP_REFRESH):"
1799 				    " seconds refresh: %d second(s)....\n",
1800 				    sec_to_refresh);
1801 			}
1802 			(void) mutex_unlock(&info_mutex);
1803 		}
1804 		(void) rw_unlock(&info_lock);
1805 
1806 		break;
1807 	case INFO_OP_REFRESH_WAIT:
1808 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1809 			logit("operation is INFO_OP_REFRESH_WAIT...\n");
1810 		}
1811 		(void) cond_init(&info_cond, NULL, NULL);
1812 		(void) mutex_lock(&info_mutex);
1813 		err = 0;
1814 		while (err != ETIME) {
1815 			int sleeptime;
1816 			/*
1817 			 * if need to go into the "no-server" refresh
1818 			 * loop, set timout value to
1819 			 * REFRESH_DELAY_WHEN_NO_SERVER
1820 			 */
1821 			if (sec_to_refresh == 0) {
1822 				sec_to_refresh = refresh_ttl;
1823 				timeout.tv_sec = time(NULL) +
1824 				    REFRESH_DELAY_WHEN_NO_SERVER;
1825 				sleeptime = REFRESH_DELAY_WHEN_NO_SERVER;
1826 				if (current_admin.debug_level >=
1827 				    DBG_SERVER_LIST_REFRESH) {
1828 					logit("getldap_serverInfo_op("
1829 					    "INFO_OP_REFRESH_WAIT):"
1830 					    " entering no-server "
1831 					    "refresh loop...\n");
1832 				}
1833 			} else {
1834 				timeout.tv_sec = time(NULL) + sec_to_refresh;
1835 				sleeptime = sec_to_refresh;
1836 			}
1837 			timeout.tv_nsec = 0;
1838 
1839 			/* statistics: next refresh time */
1840 			next_refresh = timeout.tv_sec;
1841 
1842 			if (current_admin.debug_level >=
1843 			    DBG_SERVER_LIST_REFRESH) {
1844 				logit("getldap_serverInfo_op("
1845 				    "INFO_OP_REFRESH_WAIT):"
1846 				    " about to sleep for %d second(s)...\n",
1847 				    sleeptime);
1848 			}
1849 			err = cond_timedwait(&info_cond,
1850 			    &info_mutex, &timeout);
1851 		}
1852 		(void) cond_destroy(&info_cond);
1853 		(void) mutex_unlock(&info_mutex);
1854 		break;
1855 	case INFO_OP_GETSERVER:
1856 		if (current_admin.debug_level >= DBG_ALL) {
1857 			logit("operation is INFO_OP_GETSERVER...\n");
1858 		}
1859 		*output = NULL;
1860 		/*
1861 		 * GETSERVER processing always use
1862 		 * serverInfo_old to retrieve server infomation.
1863 		 * serverInfo_old is equal to serverInfo
1864 		 * most of the time, except when a new
1865 		 * server list is being created.
1866 		 * This is why the check for is_creating
1867 		 * is needed below.
1868 		 */
1869 		(void) rw_rdlock(&info_lock_old);
1870 
1871 		if (serverInfo_old == NULL) {
1872 			(void) rw_unlock(&info_lock_old);
1873 			break;
1874 		} else
1875 			(void) getldap_get_serverInfo(serverInfo_old,
1876 			    input, output, &server_removed);
1877 		(void) rw_unlock(&info_lock_old);
1878 
1879 		/*
1880 		 * Return here and let remove server thread do its job in
1881 		 * another thread. It executes INFO_OP_REMOVESERVER code later.
1882 		 */
1883 		if (server_removed)
1884 			break;
1885 
1886 		fall_thru = TRUE;
1887 
1888 		/* FALL THROUGH */
1889 
1890 	case INFO_OP_REMOVESERVER:
1891 		/*
1892 		 * INFO_OP_GETSERVER and INFO_OP_REMOVESERVER share the
1893 		 * following code except (!fall thru) part.
1894 		 */
1895 
1896 		/*
1897 		 * if server info is currently being
1898 		 * (re)created, do nothing
1899 		 */
1900 
1901 		(void) mutex_lock(&info_mutex);
1902 		is_creating = creating;
1903 		(void) mutex_unlock(&info_mutex);
1904 		if (is_creating)
1905 			break;
1906 
1907 		if (!fall_thru) {
1908 			if (current_admin.debug_level >= DBG_ALL)
1909 				logit("operation is INFO_OP_REMOVESERVER...\n");
1910 			(void) rw_rdlock(&info_lock_old);
1911 			changed = set_server_status(input, serverInfo_old);
1912 			(void) rw_unlock(&info_lock_old);
1913 			if (changed)
1914 				create_buf_and_notify(input, changed);
1915 			else
1916 				break;
1917 		}
1918 
1919 		/*
1920 		 * set cache manager server list TTL if necessary
1921 		 */
1922 		if (*output == NULL || changed) {
1923 			(void) rw_rdlock(&info_lock);
1924 			(void) mutex_lock(&info_mutex);
1925 
1926 			(void) getldap_set_refresh_ttl(serverInfo,
1927 			    &refresh_ttl, &no_server_good);
1928 
1929 			/*
1930 			 * if no good server found, need to go into
1931 			 * the "no-server" refresh loop
1932 			 * to find a server as soon as possible
1933 			 * otherwise reset the in_no_server_mode flag
1934 			 */
1935 			if (no_server_good) {
1936 				/*
1937 				 * if already in no-server mode,
1938 				 * don't brother
1939 				 */
1940 				if (in_no_server_mode == FALSE) {
1941 					sec_to_refresh = 0;
1942 					in_no_server_mode = TRUE;
1943 					(void) cond_signal(&info_cond);
1944 				}
1945 				(void) mutex_unlock(&info_mutex);
1946 				(void) rw_unlock(&info_lock);
1947 				break;
1948 			} else {
1949 				in_no_server_mode = FALSE;
1950 				sec_to_refresh = refresh_ttl;
1951 			}
1952 			/*
1953 			 * if the refresh thread will be timed out
1954 			 * longer than refresh_ttl seconds,
1955 			 * wake it up to make it wait on the new
1956 			 * time out value
1957 			 */
1958 			new_timeout.tv_sec = time(NULL) + refresh_ttl;
1959 			if (new_timeout.tv_sec < timeout.tv_sec)
1960 				(void) cond_signal(&info_cond);
1961 
1962 			(void) mutex_unlock(&info_mutex);
1963 			(void) rw_unlock(&info_lock);
1964 		}
1965 		break;
1966 	case INFO_OP_GETSTAT:
1967 		if (current_admin.debug_level >= DBG_ALL) {
1968 			logit("operation is INFO_OP_GETSTAT...\n");
1969 		}
1970 		*output = NULL;
1971 		(void) rw_rdlock(&info_lock);
1972 		if (serverInfo) {
1973 			(void) getldap_get_server_stat(serverInfo,
1974 			    output, &prev_refresh, &next_refresh);
1975 		}
1976 		(void) rw_unlock(&info_lock);
1977 		break;
1978 	default:
1979 		logit("getldap_serverInfo_op(): "
1980 		    "invalid operation code (%d).\n", op);
1981 		return (-1);
1982 		break;
1983 	}
1984 	return (NS_LDAP_SUCCESS);
1985 }
1986 
1987 void
1988 getldap_serverInfo_refresh()
1989 {
1990 	int always = 1;
1991 
1992 	if (current_admin.debug_level >= DBG_ALL) {
1993 		logit("getldap_serverInfo_refresh()...\n");
1994 	}
1995 
1996 	/* create the server info list */
1997 	(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
1998 
1999 	while (always) {
2000 		/*
2001 		 * the operation INFO_OP_REFRESH_WAIT
2002 		 * causes this thread to wait until
2003 		 * it is time to do refresh,
2004 		 * see getldap_serverInfo_op() for details
2005 		 */
2006 		(void) getldap_serverInfo_op(INFO_OP_REFRESH_WAIT, NULL, NULL);
2007 		(void) getldap_serverInfo_op(INFO_OP_REFRESH, NULL, NULL);
2008 	}
2009 }
2010 
2011 void
2012 getldap_getserver(LineBuf *config_info, ldap_call_t *in)
2013 {
2014 	char 		req[] = "0";
2015 
2016 	if (current_admin.debug_level >= DBG_ALL) {
2017 		logit("getldap_getserver()...\n");
2018 	}
2019 
2020 	config_info->len = 0;
2021 
2022 	/* make sure the request is valid */
2023 	req[0] = (in->ldap_u.servername)[0];
2024 	if ((req[0] != '\0') &&
2025 	    (strcmp(req, NS_CACHE_NEW) != 0) &&
2026 	    (strcmp(req, NS_CACHE_NORESP)  != 0) &&
2027 	    (strcmp(req, NS_CACHE_NEXT)    != 0) &&
2028 	    (strcmp(req, NS_CACHE_WRITE)   != 0)) {
2029 		return;
2030 	}
2031 
2032 	(void) getldap_serverInfo_op(INFO_OP_GETSERVER,
2033 	    in->ldap_u.domainname, &config_info->str);
2034 
2035 	if (config_info->str == NULL)
2036 		return;
2037 
2038 	config_info->len = strlen(config_info->str) + 1;
2039 
2040 	if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2041 		/* Log server IP */
2042 		char	*ptr,
2043 		    separator;
2044 		ptr = strstr(config_info->str, DOORLINESEP);
2045 		if (ptr) {
2046 			separator = *ptr;
2047 			*ptr = '\0';
2048 			logit("getldap_getserver: got server %s\n",
2049 			    config_info->str);
2050 			*ptr = separator;
2051 		} else
2052 			logit("getldap_getserver: Missing %s."
2053 			    " Internal error\n", DOORLINESEP);
2054 	}
2055 }
2056 
2057 void
2058 getldap_get_cacheData(LineBuf *config_info, ldap_call_t *in)
2059 {
2060 	char	*instr = NULL;
2061 	int	datatype = CACHE_MAP_UNKNOWN;
2062 
2063 	if (current_admin.debug_level >= DBG_ALL) {
2064 		logit("getldap_get_cacheData()...\n");
2065 	}
2066 
2067 	config_info->len = 0;
2068 	config_info->str = NULL;
2069 
2070 	/* make sure the request is valid */
2071 	if (strncmp(in->ldap_u.servername,
2072 	    NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
2073 		datatype = CACHE_MAP_DN2DOMAIN;
2074 
2075 	if (datatype == CACHE_MAP_UNKNOWN)
2076 		return;
2077 
2078 	instr = strstr(in->ldap_u.servername, DOORLINESEP);
2079 	if (instr == NULL)
2080 		return;
2081 	instr += strlen(DOORLINESEP);
2082 	if (*instr == '\0')
2083 		return;
2084 
2085 	(void) getldap_cache_op(CACHE_OP_FIND, datatype,
2086 	    instr, &config_info->str);
2087 
2088 	if (config_info->str != NULL) {
2089 		config_info->len = strlen(config_info->str) + 1;
2090 	}
2091 }
2092 
2093 int
2094 getldap_set_cacheData(ldap_call_t *in)
2095 {
2096 	char	*instr1 = NULL;
2097 	char	*instr2 = NULL;
2098 	int	datatype = CACHE_MAP_UNKNOWN;
2099 	int	rc = 0;
2100 
2101 	if (current_admin.debug_level >= DBG_ALL) {
2102 		logit("getldap_set_cacheData()...\n");
2103 	}
2104 
2105 	/* make sure the request is valid */
2106 	if (strncmp(in->ldap_u.servername,
2107 	    NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
2108 		datatype = CACHE_MAP_DN2DOMAIN;
2109 
2110 	if (datatype == CACHE_MAP_UNKNOWN)
2111 		return (-1);
2112 
2113 	instr1 = strstr(in->ldap_u.servername, DOORLINESEP);
2114 	if (instr1 == NULL)
2115 		return (-1);
2116 	*instr1 = '\0';
2117 	instr1 += strlen(DOORLINESEP);
2118 	if (*instr1 == '\0')
2119 		return (-1);
2120 	instr2 = strstr(instr1, DOORLINESEP);
2121 	if (instr2 == NULL)
2122 		return (-1);
2123 	*instr2 = '\0';
2124 	instr2 += strlen(DOORLINESEP);
2125 	if (*instr2 == '\0')
2126 		return (-1);
2127 
2128 	rc = getldap_cache_op(CACHE_OP_ADD, datatype,
2129 	    instr1, &instr2);
2130 	if (rc != NS_LDAP_SUCCESS)
2131 		return (-1);
2132 
2133 	return (0);
2134 }
2135 
2136 void
2137 getldap_get_cacheStat(LineBuf *stat_info)
2138 {
2139 	char	*foutstr = NULL;
2140 	char	*soutstr = NULL;
2141 	char	*coutstr = NULL;
2142 	int	infoSize;
2143 
2144 	if (current_admin.debug_level >= DBG_ALL) {
2145 		logit("getldap_get_cacheStat()...\n");
2146 	}
2147 
2148 	stat_info->str = NULL;
2149 	stat_info->len = 0;
2150 
2151 	/* get refersh statisitcs */
2152 	(void) getldap_get_refresh_stat(&foutstr);
2153 	if (foutstr == NULL)
2154 		return;
2155 
2156 	/* get server statisitcs */
2157 	(void) getldap_serverInfo_op(INFO_OP_GETSTAT, NULL, &soutstr);
2158 	if (soutstr == NULL) {
2159 		free(foutstr);
2160 		return;
2161 	}
2162 	/* get cache data statisitcs */
2163 	(void) getldap_cache_op(CACHE_OP_GETSTAT, NULL, NULL, &coutstr);
2164 	if (coutstr == NULL) {
2165 		free(foutstr);
2166 		free(soutstr);
2167 		return;
2168 	}
2169 
2170 	infoSize = strlen(foutstr) + strlen(soutstr) + strlen(coutstr) + 3;
2171 	stat_info->str = calloc(infoSize, sizeof (char));
2172 	if (stat_info->str != NULL) {
2173 		(void) strncpy(stat_info->str,
2174 		    foutstr,
2175 		    strlen(foutstr) + 1);
2176 		(void) strncat(stat_info->str,
2177 		    soutstr,
2178 		    strlen(soutstr) + 1);
2179 		(void) strncat(stat_info->str,
2180 		    coutstr,
2181 		    strlen(coutstr) + 1);
2182 		stat_info->len = infoSize;
2183 	}
2184 
2185 	free(foutstr);
2186 	free(soutstr);
2187 	free(coutstr);
2188 }
2189 
2190 static int
2191 checkupdate(int sighup)
2192 {
2193 	int	value;
2194 
2195 	(void) rw_wrlock(&ldap_lock);
2196 	value = sighup;
2197 	(void) rw_unlock(&ldap_lock);
2198 
2199 	return (value == TRUE);
2200 }
2201 
2202 
2203 static int
2204 update_from_profile(int *change_status)
2205 {
2206 	ns_ldap_result_t *result = NULL;
2207 	char		searchfilter[BUFSIZ];
2208 	ns_ldap_error_t	*error;
2209 	int		rc;
2210 	void		**paramVal = NULL;
2211 	ns_config_t	*ptr = NULL;
2212 	char		*profile = NULL;
2213 	char		errstr[MAXERROR];
2214 
2215 	if (current_admin.debug_level >= DBG_ALL) {
2216 		logit("update_from_profile....\n");
2217 	}
2218 	do {
2219 		(void) rw_wrlock(&ldap_lock);
2220 		sighup_update = FALSE;
2221 		(void) rw_unlock(&ldap_lock);
2222 
2223 		if ((rc = __ns_ldap_getParam(NS_LDAP_PROFILE_P,
2224 		    &paramVal, &error)) != NS_LDAP_SUCCESS) {
2225 			if (error != NULL && error->message != NULL)
2226 				logit("Error: Unable to  profile name: %s\n",
2227 				    error->message);
2228 			else {
2229 				char *tmp;
2230 
2231 				__ns_ldap_err2str(rc, &tmp);
2232 				logit("Error: Unable to  profile name: %s\n",
2233 				    tmp);
2234 			}
2235 			(void) __ns_ldap_freeParam(&paramVal);
2236 			(void) __ns_ldap_freeError(&error);
2237 			return (-1);
2238 		}
2239 
2240 		if (paramVal && *paramVal)
2241 			profile = strdup((char *)*paramVal);
2242 		(void) __ns_ldap_freeParam(&paramVal);
2243 
2244 		if (profile == NULL) {
2245 			return (-1);
2246 		}
2247 
2248 		(void) snprintf(searchfilter, BUFSIZ, _PROFILE_FILTER,
2249 		    _PROFILE1_OBJECTCLASS, _PROFILE2_OBJECTCLASS, profile);
2250 
2251 		if ((rc = __ns_ldap_list(_PROFILE_CONTAINER,
2252 		    (const char *)searchfilter, NULL,
2253 		    NULL, NULL, 0,
2254 		    &result, &error, NULL, NULL)) != NS_LDAP_SUCCESS) {
2255 
2256 			/*
2257 			 * Is profile name the DEFAULTCONFIGNAME?
2258 			 * syslog Warning, otherwise syslog error.
2259 			 */
2260 			if (strcmp(profile, DEFAULTCONFIGNAME) == 0) {
2261 				syslog(LOG_WARNING,
2262 				    "Ignoring attempt to refresh nonexistent "
2263 				    "default profile: %s.\n",
2264 				    profile);
2265 				logit("Ignoring attempt to refresh nonexistent "
2266 				    "default profile: %s.\n",
2267 				    profile);
2268 			} else if ((error != NULL) &&
2269 			    (error->message != NULL)) {
2270 				syslog(LOG_ERR,
2271 				    "Error: Unable to refresh profile:%s:"
2272 				    " %s\n", profile, error->message);
2273 				logit("Error: Unable to refresh profile:"
2274 				    "%s:%s\n", profile, error->message);
2275 			} else {
2276 				syslog(LOG_ERR, "Error: Unable to refresh "
2277 				    "from profile:%s. (error=%d)\n",
2278 				    profile, rc);
2279 				logit("Error: Unable to refresh from profile "
2280 				    "%s (error=%d)\n", profile, rc);
2281 			}
2282 
2283 			(void) __ns_ldap_freeError(&error);
2284 			(void) __ns_ldap_freeResult(&result);
2285 			free(profile);
2286 			return (-1);
2287 		}
2288 		free(profile);
2289 
2290 
2291 	} while (checkupdate(sighup_update) == TRUE);
2292 
2293 	(void) rw_wrlock(&ldap_lock);
2294 
2295 	ptr = __ns_ldap_make_config(result);
2296 	(void) __ns_ldap_freeResult(&result);
2297 
2298 	if (ptr == NULL) {
2299 		logit("Error: __ns_ldap_make_config failed.\n");
2300 		(void) rw_unlock(&ldap_lock);
2301 		return (-1);
2302 	}
2303 
2304 	/*
2305 	 * cross check the config parameters
2306 	 */
2307 	if (__s_api_crosscheck(ptr, errstr, B_TRUE) == NS_SUCCESS) {
2308 		/*
2309 		 * reset the local profile TTL
2310 		 */
2311 		if (ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc)
2312 			current_admin.ldap_stat.ldap_ttl =
2313 			    atol(ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc);
2314 
2315 		if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2316 			logit("update_from_profile: reset profile TTL to %d"
2317 			    "  seconds\n",
2318 			    current_admin.ldap_stat.ldap_ttl);
2319 			logit("update_from_profile: expire time %ld "
2320 			    "seconds\n",
2321 			    ptr->paramList[NS_LDAP_EXP_P].ns_tm);
2322 		}
2323 
2324 		/* set ptr as current_config if the config is changed */
2325 		chg_test_config_change(ptr, change_status);
2326 		rc = 0;
2327 	} else {
2328 		__s_api_destroy_config(ptr);
2329 		logit("Error: downloaded profile failed to pass "
2330 		    "crosscheck (%s).\n", errstr);
2331 		syslog(LOG_ERR, "ldap_cachemgr: %s", errstr);
2332 		rc = -1;
2333 	}
2334 	(void) rw_unlock(&ldap_lock);
2335 
2336 	return (rc);
2337 }
2338 
2339 int
2340 getldap_init()
2341 {
2342 	ns_ldap_error_t	*error;
2343 	struct timeval	tp;
2344 	ldap_get_chg_cookie_t	cookie;
2345 
2346 	if (current_admin.debug_level >= DBG_ALL) {
2347 		logit("getldap_init()...\n");
2348 	}
2349 
2350 	(void) __ns_ldap_setServer(TRUE);
2351 
2352 	(void) rw_wrlock(&ldap_lock);
2353 	if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
2354 		logit("Error: Unable to read '%s': %s\n",
2355 		    NSCONFIGFILE, error->message);
2356 		(void) fprintf(stderr,
2357 		    gettext("\nError: Unable to read '%s': %s\n"),
2358 		    NSCONFIGFILE, error->message);
2359 		__ns_ldap_freeError(&error);
2360 		(void) rw_unlock(&ldap_lock);
2361 		return (-1);
2362 	}
2363 	(void) rw_unlock(&ldap_lock);
2364 
2365 	if (gettimeofday(&tp, NULL) == 0) {
2366 		/* statistics: previous refresh time */
2367 		prev_refresh_time = tp.tv_sec;
2368 	}
2369 
2370 	/* initialize the data cache */
2371 	(void) getldap_cache_op(CACHE_OP_CREATE,
2372 	    0, NULL, NULL);
2373 
2374 	cookie.mgr_pid = getpid();
2375 	cookie.seq_num = 0;
2376 	chg_config_cookie_set(&cookie);
2377 	return (0);
2378 }
2379 
2380 static void
2381 perform_update(void)
2382 {
2383 	ns_ldap_error_t	*error = NULL;
2384 	struct timeval	tp;
2385 	char		buf[20];
2386 	int		rc, rc1;
2387 	int		changed = 0;
2388 	void		**paramVal = NULL;
2389 	ns_ldap_self_gssapi_config_t	config;
2390 
2391 	if (current_admin.debug_level >= DBG_ALL) {
2392 		logit("perform_update()...\n");
2393 	}
2394 
2395 	(void) __ns_ldap_setServer(TRUE);
2396 
2397 	if (gettimeofday(&tp, NULL) != 0)
2398 		return;
2399 
2400 	rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P, &paramVal, &error);
2401 
2402 	if (rc == NS_LDAP_SUCCESS && paramVal != NULL) {
2403 		current_admin.ldap_stat.ldap_ttl = atol((char *)*paramVal);
2404 	}
2405 
2406 	if (error != NULL)
2407 		(void) __ns_ldap_freeError(&error);
2408 
2409 	if (paramVal != NULL)
2410 		(void) __ns_ldap_freeParam(&paramVal);
2411 
2412 	if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2413 		logit("perform_update: current profile TTL is %d seconds\n",
2414 		    current_admin.ldap_stat.ldap_ttl);
2415 	}
2416 
2417 	if (current_admin.ldap_stat.ldap_ttl > 0) {
2418 		/*
2419 		 * set the profile TTL parameter, just
2420 		 * in case that the downloading of
2421 		 * the profile from server would fail
2422 		 */
2423 
2424 		/*
2425 		 * NS_LDAP_EXP_P is a no op for __ns_ldap_setParam
2426 		 * It depends on NS_LDAP_CACHETTL_P to set it's value
2427 		 * Set NS_LDAP_CACHETTL_P here so NS_LDAP_EXP_P value
2428 		 * can be set.
2429 		 * NS_LDAP_CACHETTL_P value can be reset after the profile is
2430 		 * downloaded from the server, so is NS_LDAP_EXP_P.
2431 		 */
2432 		buf[19] = '\0'; /* null terminated the buffer */
2433 		if (__ns_ldap_setParam(NS_LDAP_CACHETTL_P,
2434 		    lltostr((long long)current_admin.ldap_stat.ldap_ttl,
2435 		    &buf[19]),
2436 		    &error) != NS_LDAP_SUCCESS) {
2437 			logit("Error: __ns_ldap_setParam failed, status: %d "
2438 			    "message: %s\n", error->status, error->message);
2439 			(void)  __ns_ldap_freeError(&error);
2440 			return;
2441 		}
2442 
2443 		(void) rw_wrlock(&ldap_lock);
2444 		sighup_update = FALSE;
2445 		(void) rw_unlock(&ldap_lock);
2446 
2447 		do {
2448 			rc = update_from_profile(&changed);
2449 			if (rc != 0) {
2450 				logit("Error: Unable to update from profile\n");
2451 			}
2452 		} while (checkupdate(sighup_update) == TRUE);
2453 	} else {
2454 		rc = 0;
2455 	}
2456 
2457 	/*
2458 	 * recreate the server info list
2459 	 */
2460 	if (rc == 0) {
2461 		(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
2462 
2463 		/* flush the data cache */
2464 		(void) getldap_cache_op(CACHE_OP_DELETE,
2465 		    0, NULL, NULL);
2466 
2467 		/* statistics: previous refresh time */
2468 		prev_refresh_time = tp.tv_sec;
2469 	}
2470 	rc1 = __ns_ldap_self_gssapi_config(&config);
2471 	if (rc1 == NS_LDAP_SUCCESS) {
2472 		if (config != NS_LDAP_SELF_GSSAPI_CONFIG_NONE) {
2473 			rc1 = __ns_ldap_check_all_preq(0, 0, 0, config, &error);
2474 			(void)  __ns_ldap_freeError(&error);
2475 			if (rc1 != NS_LDAP_SUCCESS) {
2476 				logit("Error: Check on self credential "
2477 				    "prerquesites failed: %d\n",
2478 				    rc1);
2479 				exit(rc1);
2480 			}
2481 		}
2482 	} else {
2483 		logit("Error: Failed to get self credential configuration %d\n",
2484 		    rc1);
2485 			exit(rc1);
2486 	}
2487 
2488 	if (!changed)
2489 		return;
2490 
2491 	(void) rw_rdlock(&ldap_lock);
2492 	if (((error = __ns_ldap_DumpConfiguration(NSCONFIGREFRESH)) != NULL) ||
2493 	    ((error = __ns_ldap_DumpConfiguration(NSCREDREFRESH)) != NULL)) {
2494 		logit("Error: __ns_ldap_DumpConfiguration failed, "
2495 		    "status: %d message: %s\n", error->status, error->message);
2496 		__ns_ldap_freeError(&error);
2497 		(void) rw_unlock(&ldap_lock);
2498 		return;
2499 	}
2500 	if (rename(NSCONFIGREFRESH, NSCONFIGFILE) != 0) {
2501 		logit("Error: unlink failed - errno: %s\n", strerror(errno));
2502 		syslog(LOG_ERR, "Unable to refresh profile, LDAP configuration"
2503 		    "files not written");
2504 		(void) rw_unlock(&ldap_lock);
2505 		return;
2506 	}
2507 	if (rename(NSCREDREFRESH, NSCREDFILE) != 0) {
2508 		/*
2509 		 * We probably have inconsistent configuration at this point.
2510 		 * If we were to create a backup file and rename it here, that
2511 		 * operation might also fail. Consequently there is no safe way
2512 		 * to roll back.
2513 		 */
2514 		logit("Error: unlink failed - errno: %s\n", strerror(errno));
2515 		syslog(LOG_ERR, "Unable to refresh profile consistently, "
2516 		    "LDAP configuration files inconsistent");
2517 		(void) rw_unlock(&ldap_lock);
2518 		return;
2519 	}
2520 
2521 	(void) rw_unlock(&ldap_lock);
2522 }
2523 
2524 void
2525 getldap_refresh()
2526 {
2527 	struct timespec	timeout;
2528 	int		sleeptime;
2529 	struct timeval	tp;
2530 	long		expire = 0;
2531 	void		**paramVal = NULL;
2532 	ns_ldap_error_t	*errorp;
2533 	int		always = 1, err;
2534 	int		first_time = 1;
2535 	int		sig_done = 0;
2536 	int		dbg_level;
2537 
2538 	if (current_admin.debug_level >= DBG_ALL) {
2539 		logit("getldap_refresh()...\n");
2540 	}
2541 
2542 	/*
2543 	 * wait for an available server
2544 	 */
2545 	while (sig_done == 0) {
2546 		(void) mutex_lock(&sig_mutex);
2547 		sig_done = signal_done;
2548 		(void) mutex_unlock(&sig_mutex);
2549 	}
2550 
2551 	(void) __ns_ldap_setServer(TRUE);
2552 	while (always) {
2553 		dbg_level = current_admin.debug_level;
2554 		(void) rw_rdlock(&ldap_lock);
2555 		sleeptime = current_admin.ldap_stat.ldap_ttl;
2556 		if (dbg_level >= DBG_PROFILE_REFRESH) {
2557 			logit("getldap_refresh: current profile TTL is %d "
2558 			"seconds\n", current_admin.ldap_stat.ldap_ttl);
2559 		}
2560 		if (gettimeofday(&tp, NULL) == 0) {
2561 			if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
2562 			    &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
2563 			    paramVal != NULL &&
2564 			    (char *)*paramVal != NULL) {
2565 				errno = 0;
2566 				expire = atol((char *)*paramVal);
2567 				(void) __ns_ldap_freeParam(&paramVal);
2568 				if (errno == 0) {
2569 					if (expire == 0) {
2570 						first_time = 0;
2571 						(void) rw_unlock(&ldap_lock);
2572 						(void) cond_init(&cond,
2573 						    NULL, NULL);
2574 						(void) mutex_lock(&sighuplock);
2575 						timeout.tv_sec =
2576 						    CACHESLEEPTIME;
2577 						timeout.tv_nsec = 0;
2578 						if (dbg_level >=
2579 						    DBG_PROFILE_REFRESH) {
2580 							logit("getldap_refresh:"
2581 							    "(1)about to sleep"
2582 							    " for %d seconds\n",
2583 							    CACHESLEEPTIME);
2584 						}
2585 						err = cond_reltimedwait(&cond,
2586 						    &sighuplock, &timeout);
2587 						(void) cond_destroy(&cond);
2588 						(void) mutex_unlock(
2589 						    &sighuplock);
2590 						/*
2591 						 * if woke up by
2592 						 * getldap_revalidate(),
2593 						 * do update right away
2594 						 */
2595 						if (err == ETIME)
2596 							continue;
2597 						else {
2598 							/*
2599 							 * if load
2600 							 * configuration failed
2601 							 * don't do update
2602 							 */
2603 							if (load_config())
2604 								perform_update
2605 								    ();
2606 							continue;
2607 						}
2608 					}
2609 					sleeptime = expire - tp.tv_sec;
2610 					if (dbg_level >= DBG_PROFILE_REFRESH) {
2611 						logit("getldap_refresh: expire "
2612 						    "time = %ld\n", expire);
2613 					}
2614 
2615 				}
2616 			}
2617 		}
2618 
2619 		(void) rw_unlock(&ldap_lock);
2620 
2621 		/*
2622 		 * if this is the first time downloading
2623 		 * the profile or expire time already passed,
2624 		 * do not wait, do update
2625 		 */
2626 		if (first_time == 0 && sleeptime > 0) {
2627 			if (dbg_level >= DBG_PROFILE_REFRESH) {
2628 				logit("getldap_refresh: (2)about to sleep "
2629 				"for %d seconds\n", sleeptime);
2630 			}
2631 			(void) cond_init(&cond, NULL, NULL);
2632 			(void) mutex_lock(&sighuplock);
2633 			timeout.tv_sec = sleeptime;
2634 			timeout.tv_nsec = 0;
2635 			err = cond_reltimedwait(&cond,
2636 			    &sighuplock, &timeout);
2637 			(void) cond_destroy(&cond);
2638 			(void) mutex_unlock(&sighuplock);
2639 		}
2640 		/*
2641 		 * if load concfiguration failed
2642 		 * don't do update
2643 		 */
2644 		if (load_config())
2645 			perform_update();
2646 		first_time = 0;
2647 	}
2648 }
2649 
2650 void
2651 getldap_revalidate()
2652 {
2653 	if (current_admin.debug_level >= DBG_ALL) {
2654 		logit("getldap_revalidate()...\n");
2655 	}
2656 	/* block signal SIGHUP */
2657 	(void) sighold(SIGHUP);
2658 
2659 	/* now awake the sleeping refresh thread */
2660 	(void) cond_signal(&cond);
2661 
2662 	/* release signal SIGHUP */
2663 	(void) sigrelse(SIGHUP);
2664 
2665 }
2666 
2667 void
2668 getldap_admincred(LineBuf *config_info, ldap_call_t *in)
2669 {
2670 	ns_ldap_error_t	*error;
2671 	ldap_config_out_t *cout;
2672 	ucred_t *uc = NULL;
2673 
2674 	if (current_admin.debug_level >= DBG_ALL) {
2675 		logit("getldap_admincred()...\n");
2676 	}
2677 	/* check privileges */
2678 	if (is_root_or_all_privs("GETADMINCRED", &uc) == 0) {
2679 		logit("admin credential requested by a non-root and no ALL "
2680 		    "privilege user not allowed");
2681 		config_info->str = NULL;
2682 		config_info->len = 0;
2683 	} else {
2684 		(void) rw_rdlock(&ldap_lock);
2685 		if ((error = __ns_ldap_LoadDoorInfo(config_info,
2686 		    in->ldap_u.domainname, NULL, 1)) != NULL) {
2687 			if (error != NULL && error->message != NULL)
2688 				logit("Error: ldap_lookup: %s\n",
2689 				    error->message);
2690 			(void) __ns_ldap_freeError(&error);
2691 
2692 			config_info->str = NULL;
2693 			config_info->len = 0;
2694 		}
2695 		/* set change cookie */
2696 		cout = (ldap_config_out_t *)config_info->str;
2697 		if (cout)
2698 			cout->cookie = chg_config_cookie_get();
2699 		(void) rw_unlock(&ldap_lock);
2700 	}
2701 }
2702 
2703 void
2704 getldap_lookup(LineBuf *config_info, ldap_call_t *in)
2705 {
2706 	ns_ldap_error_t	*error;
2707 	ldap_config_out_t *cout;
2708 
2709 	if (current_admin.debug_level >= DBG_ALL) {
2710 		logit("getldap_lookup()...\n");
2711 	}
2712 	(void) rw_rdlock(&ldap_lock);
2713 	if ((error = __ns_ldap_LoadDoorInfo(config_info,
2714 	    in->ldap_u.domainname, NULL, 0)) != NULL) {
2715 		if (error != NULL && error->message != NULL)
2716 			logit("Error: ldap_lookup: %s\n", error->message);
2717 		(void) __ns_ldap_freeError(&error);
2718 
2719 		config_info->str = NULL;
2720 		config_info->len = 0;
2721 	}
2722 	/* set change cookie */
2723 	cout = (ldap_config_out_t *)config_info->str;
2724 	if (cout)
2725 		cout->cookie = chg_config_cookie_get();
2726 	(void) rw_unlock(&ldap_lock);
2727 }
2728 /*
2729  * It creates the header and data stream to be door returned and notify
2730  * chg_get_statusChange() threads.
2731  * This is called after all getldap_get_rootDSE() threads are joined.
2732  */
2733 void
2734 test_server_change(server_info_t *head)
2735 {
2736 	server_info_t *info;
2737 	int	len = 0, num = 0, ds_len = 0, new_len = 0, tlen = 0;
2738 	char	*tmp_buf = NULL, *ptr = NULL, *status = NULL;
2739 	ldap_get_change_out_t *cout;
2740 
2741 	ds_len = strlen(DOORLINESEP);
2742 
2743 	for (info = head; info; info = info->next) {
2744 		(void) mutex_lock(&info->mutex[0]);
2745 		if (info->sinfo[0].change != 0) {
2746 			/* "9.9.9.9|NS_SERVER_CHANGE_UP|" */
2747 			len += 2 * ds_len + strlen(info->sinfo[0].addr) +
2748 			    strlen(NS_SERVER_CHANGE_UP);
2749 			num++;
2750 		}
2751 		(void) mutex_unlock(&info->mutex[0]);
2752 	}
2753 
2754 	if (len == 0)
2755 		return;
2756 
2757 	len++; /* '\0' */
2758 
2759 	tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
2760 	if ((tmp_buf = malloc(tlen)) == NULL)
2761 		return;
2762 
2763 	cout = (ldap_get_change_out_t *)tmp_buf;
2764 	cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
2765 	/* cout->cookie is set by chg_notify_statusChange */
2766 	cout->server_count = num;
2767 	cout->data_size = len;
2768 
2769 	/* Create IP|UP or DOWN|IP|UP or DOWN| ... */
2770 	ptr = cout->data;
2771 	new_len = len;
2772 	for (info = head; info; info = info->next) {
2773 		(void) mutex_lock(&info->mutex[0]);
2774 		if (info->sinfo[0].change == 0) {
2775 			(void) mutex_unlock(&info->mutex[0]);
2776 			continue;
2777 		}
2778 
2779 		if (info->sinfo[0].change == NS_SERVER_UP)
2780 			status = NS_SERVER_CHANGE_UP;
2781 		else if (info->sinfo[0].change == NS_SERVER_DOWN)
2782 			status = NS_SERVER_CHANGE_DOWN;
2783 		else {
2784 			syslog(LOG_WARNING, gettext("Bad change value %d"),
2785 			    info->sinfo[0].change);
2786 			(void) mutex_unlock(&info->mutex[0]);
2787 			free(tmp_buf);
2788 			return;
2789 		}
2790 
2791 		if ((snprintf(ptr, new_len, "%s%s%s%s",
2792 		    info->sinfo[0].addr, DOORLINESEP,
2793 		    status, DOORLINESEP)) >= new_len) {
2794 			(void) mutex_unlock(&info->mutex[0]);
2795 			break;
2796 		}
2797 		new_len -= strlen(ptr);
2798 		ptr += strlen(ptr);
2799 
2800 		(void) mutex_unlock(&info->mutex[0]);
2801 	}
2802 	(void) chg_notify_statusChange(tmp_buf);
2803 }
2804 /*
2805  * It creates the header and data stream to be door returned and notify
2806  * chg_get_statusChange() threads.
2807  * This is called in removing server case.
2808  */
2809 static void
2810 create_buf_and_notify(char *input, ns_server_status_t st)
2811 {
2812 	rm_svr_t *rms = (rm_svr_t *)input;
2813 	char	*tmp_buf, *ptr, *status;
2814 	int	len, tlen;
2815 	ldap_get_change_out_t *cout;
2816 
2817 	/* IP|UP or DOWN| */
2818 	len = 2 * strlen(DOORLINESEP) + strlen(rms->addr) +
2819 	    strlen(NS_SERVER_CHANGE_UP) + 1;
2820 
2821 	tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
2822 
2823 	if ((tmp_buf = malloc(tlen)) == NULL)
2824 		return;
2825 
2826 	cout = (ldap_get_change_out_t *)tmp_buf;
2827 	cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
2828 	/* cout->cookie is set by chg_notify_statusChange */
2829 	cout->server_count = 1;
2830 	cout->data_size = len;
2831 
2832 	/* Create IP|DOWN| */
2833 	ptr = cout->data;
2834 	if (st == NS_SERVER_UP)
2835 		status = NS_SERVER_CHANGE_UP;
2836 	else if (st == NS_SERVER_DOWN)
2837 		status = NS_SERVER_CHANGE_DOWN;
2838 
2839 	(void) snprintf(ptr, len, "%s%s%s%s",
2840 	    rms->addr, DOORLINESEP, status, DOORLINESEP);
2841 
2842 	(void) chg_notify_statusChange(tmp_buf);
2843 
2844 }
2845 
2846 /*
2847  * Return: 0 server is down, 1 server is up
2848  */
2849 static int
2850 contact_server(char *addr)
2851 {
2852 	char		*rootDSE = NULL;
2853 	ns_ldap_error_t	*error = NULL;
2854 	int		rc;
2855 
2856 	if (__ns_ldap_getRootDSE(addr, &rootDSE, &error,
2857 	    SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
2858 		if (current_admin.debug_level >= DBG_ALL)
2859 			logit("get rootDSE %s failed. %s", addr,
2860 			    error->message ? error->message : "");
2861 		rc = 0;
2862 	} else
2863 		rc = 1;
2864 
2865 	if (rootDSE)
2866 		free(rootDSE);
2867 	if (error)
2868 		(void) __ns_ldap_freeError(&error);
2869 
2870 	return (rc);
2871 }
2872 
2873 /*
2874  * The thread is spawned to do contact_server() so it won't be blocking
2875  * getldap_serverInfo_op(INFO_OP_GETSERVER, ...) case.
2876  * After contact_server() is done, it calls
2877  * getldap_serverInfo_op(INFO_OP_REMOVESERVER, ...) to return to the remaining
2878  * program flow. It's meant to maintain the original program flow yet be
2879  * non-blocking when it's contacting server.
2880  */
2881 static void *
2882 remove_server_thread(void *arg)
2883 {
2884 	char *addr = (char *)arg, *out = NULL;
2885 	int up;
2886 	rm_svr_t rms;
2887 
2888 	up = contact_server(addr);
2889 
2890 	rms.addr = addr;
2891 	rms.up = up;
2892 
2893 	(void) getldap_serverInfo_op(INFO_OP_REMOVESERVER, (char *)&rms, &out);
2894 
2895 	free(addr);
2896 
2897 	thr_exit(NULL);
2898 	return (NULL);
2899 }
2900 /*
2901  * addr is allocated and is freed by remove_server_thread
2902  * It starts a thread to contact server and remove server to avoid long wait
2903  * or recursion.
2904  */
2905 static void
2906 remove_server(char *addr)
2907 {
2908 	if (thr_create(NULL, 0, remove_server_thread,
2909 	    (void *)addr, THR_BOUND|THR_DETACHED, NULL) != 0) {
2910 		free(addr);
2911 		syslog(LOG_ERR, "thr_create failed for remove_server_thread");
2912 	}
2913 }
2914 /*
2915  * Compare the server_status and mark it up or down accordingly.
2916  * This is called in removing server case.
2917  */
2918 static ns_server_status_t
2919 set_server_status(char *input, server_info_t *head)
2920 {
2921 	rm_svr_t *rms = (rm_svr_t *)input;
2922 	ns_server_status_t changed = 0;
2923 	server_info_t *info;
2924 
2925 	for (info = head; info != NULL; info = info->next) {
2926 		(void) mutex_lock(&info->mutex[0]);
2927 		if (strcmp(info->sinfo[0].addr, rms->addr) == 0) {
2928 			if (info->sinfo[0].server_status == INFO_SERVER_UP &&
2929 			    rms->up == FALSE) {
2930 				info->sinfo[0].prev_server_status =
2931 				    info->sinfo[0].server_status;
2932 				info->sinfo[0].server_status =
2933 				    INFO_SERVER_ERROR;
2934 				info->sinfo[0].change = NS_SERVER_DOWN;
2935 				changed = NS_SERVER_DOWN;
2936 
2937 			} else if (info->sinfo[0].server_status ==
2938 			    INFO_SERVER_ERROR && rms->up == TRUE) {
2939 				/*
2940 				 * It should be INFO_SERVER_UP, but check here
2941 				 */
2942 				info->sinfo[0].prev_server_status =
2943 				    info->sinfo[0].server_status;
2944 				info->sinfo[0].server_status =
2945 				    INFO_SERVER_UP;
2946 				info->sinfo[0].change = NS_SERVER_UP;
2947 				changed = NS_SERVER_UP;
2948 			}
2949 			(void) mutex_unlock(&info->mutex[0]);
2950 			break;
2951 		}
2952 		(void) mutex_unlock(&info->mutex[0]);
2953 	}
2954 	if (changed) {
2955 		/* ldap_cachemgr -g option looks up [1] */
2956 		(void) mutex_lock(&info->mutex[1]);
2957 		info->sinfo[1].prev_server_status =
2958 		    info->sinfo[1].server_status;
2959 		if (changed == NS_SERVER_DOWN)
2960 			info->sinfo[1].server_status = INFO_SERVER_ERROR;
2961 		else if (changed == NS_SERVER_UP)
2962 			info->sinfo[1].server_status = INFO_SERVER_UP;
2963 		(void) mutex_unlock(&info->mutex[1]);
2964 	}
2965 	return (changed);
2966 }
2967