8158 Want named threads API
[unleashed.git] / usr / src / cmd / ldapcachemgr / cachemgr_getldap.c
blobb9bc3f77b62f680ca7d5cbc72ce8cbe4ce167bdf
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2018 Joyent, Inc.
27 #include <assert.h>
28 #include <errno.h>
29 #include <memory.h>
30 #include <signal.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <libintl.h>
35 #include <syslog.h>
36 #include <sys/door.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <synch.h>
42 #include <pthread.h>
43 #include <unistd.h>
44 #include <lber.h>
45 #include <ldap.h>
46 #include <ctype.h> /* tolower */
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <ucred.h>
51 #include "cachemgr.h"
52 #include "solaris-priv.h"
53 #include "ns_connmgmt.h"
55 static rwlock_t ldap_lock = DEFAULTRWLOCK;
56 static int sighup_update = FALSE;
57 extern admin_t current_admin;
59 extern int is_root_or_all_privs(char *dc_str, ucred_t **ucp);
61 /* variables used for SIGHUP wakeup on sleep */
62 static mutex_t sighuplock;
63 static cond_t cond;
65 /* refresh time statistics */
66 static time_t prev_refresh_time = 0;
68 /* variables used for signaling parent process */
69 static mutex_t sig_mutex;
70 static int signal_done = FALSE;
72 /* TCP connection timeout (in milliseconds) */
73 static int tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
75 #ifdef SLP
76 extern int use_slp;
77 #endif /* SLP */
79 /* nis domain information */
80 #define _NIS_FILTER "objectclass=nisDomainObject"
81 #define _NIS_DOMAIN "nisdomain"
83 #define CACHESLEEPTIME 600
85 * server list refresh delay when in "no server" mode
86 * (1 second)
88 #define REFRESH_DELAY_WHEN_NO_SERVER 1
90 typedef enum {
91 INFO_OP_CREATE = 0,
92 INFO_OP_DELETE = 1,
93 INFO_OP_REFRESH = 2,
94 INFO_OP_REFRESH_WAIT = 3,
95 INFO_OP_GETSERVER = 4,
96 INFO_OP_GETSTAT = 5,
97 INFO_OP_REMOVESERVER = 6
98 } info_op_t;
100 typedef enum {
101 INFO_RW_UNKNOWN = 0,
102 INFO_RW_READONLY = 1,
103 INFO_RW_WRITEABLE = 2
104 } info_rw_t;
106 typedef enum {
107 INFO_SERVER_JUST_INITED = -1,
108 INFO_SERVER_UNKNOWN = 0,
109 INFO_SERVER_CONNECTING = 1,
110 INFO_SERVER_UP = 2,
111 INFO_SERVER_ERROR = 3,
112 INFO_SERVER_REMOVED = 4
113 } info_server_t;
115 typedef enum {
116 INFO_STATUS_UNKNOWN = 0,
117 INFO_STATUS_ERROR = 1,
118 INFO_STATUS_NEW = 2,
119 INFO_STATUS_OLD = 3
120 } info_status_t;
122 typedef enum {
123 CACHE_OP_CREATE = 0,
124 CACHE_OP_DELETE = 1,
125 CACHE_OP_FIND = 2,
126 CACHE_OP_ADD = 3,
127 CACHE_OP_GETSTAT = 4
128 } cache_op_t;
130 typedef enum {
131 CACHE_MAP_UNKNOWN = 0,
132 CACHE_MAP_DN2DOMAIN = 1
133 } cache_type_t;
135 typedef struct server_info_ext {
136 char *addr;
137 char *hostname;
138 char *rootDSE_data;
139 char *errormsg;
140 info_rw_t type;
141 info_server_t server_status;
142 info_server_t prev_server_status;
143 info_status_t info_status;
144 ns_server_status_t change;
145 } server_info_ext_t;
147 typedef struct server_info {
148 struct server_info *next;
149 mutex_t mutex[2]; /* 0: current copy lock */
150 /* 1: update copy lock */
151 server_info_ext_t sinfo[2]; /* 0: current, 1: update copy */
152 } server_info_t;
154 typedef struct cache_hash {
155 cache_type_t type;
156 char *from;
157 char *to;
158 struct cache_hash *next;
159 } cache_hash_t;
162 * The status of a server to be removed. It can be up or down.
164 typedef struct rm_svr {
165 char *addr;
166 int up; /* 1: up, 0: down */
167 } rm_svr_t;
169 static int getldap_destroy_serverInfo(server_info_t *head);
170 static void test_server_change(server_info_t *head);
171 static void remove_server(char *addr);
172 static ns_server_status_t set_server_status(char *input, server_info_t *head);
173 static void create_buf_and_notify(char *input, ns_server_status_t st);
176 * Load configuration
177 * The code was in signal handler getldap_revalidate
178 * It's moved out of the handler because it could cause deadlock
179 * return: 1 SUCCESS
180 * 0 FAIL
182 static int
183 load_config() {
184 ns_ldap_error_t *error;
185 int rc = 1;
187 (void) __ns_ldap_setServer(TRUE);
189 (void) rw_wrlock(&ldap_lock);
190 if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
191 logit("Error: Unable to read '%s': %s\n",
192 NSCONFIGFILE, error->message);
193 __ns_ldap_freeError(&error);
194 rc = 0; /* FAIL */
195 } else
196 sighup_update = TRUE;
198 (void) rw_unlock(&ldap_lock);
200 return (rc);
204 * Calculate a hash for a string
205 * Based on elf_hash algorithm, hash is case insensitive
206 * Uses tolower instead of _tolower because of I18N
209 static unsigned long
210 getldap_hash(const char *str)
212 unsigned int hval = 0;
214 while (*str) {
215 unsigned int g;
217 hval = (hval << 4) + tolower(*str++);
218 if ((g = (hval & 0xf0000000)) != 0)
219 hval ^= g >> 24;
220 hval &= ~g;
222 return ((unsigned long)hval);
226 * Remove a hash table entry.
227 * This function expects a lock in place when called.
230 static cache_hash_t *
231 getldap_free_hash(cache_hash_t *p)
233 cache_hash_t *next;
235 p->type = CACHE_MAP_UNKNOWN;
236 if (p->from)
237 free(p->from);
238 if (p->to)
239 free(p->to);
240 next = p->next;
241 p->next = NULL;
242 free(p);
243 return (next);
247 * Scan a hash table hit for a matching hash entry.
248 * This function expects a lock in place when called.
250 static cache_hash_t *
251 getldap_scan_hash(cache_type_t type, char *from,
252 cache_hash_t *idx)
254 while (idx) {
255 if (idx->type == type &&
256 strcasecmp(from, idx->from) == 0) {
257 return (idx);
259 idx = idx->next;
261 return ((cache_hash_t *)NULL);
265 * Format and return the cache data statistics
267 static int
268 getldap_get_cacheData_stat(int max, int current, char **output)
270 #define C_HEADER0 "Cache data information: "
271 #define C_HEADER1 " Maximum cache entries: "
272 #define C_HEADER2 " Number of cache entries: "
273 int hdr0_len = strlen(gettext(C_HEADER0));
274 int hdr1_len = strlen(gettext(C_HEADER1));
275 int hdr2_len = strlen(gettext(C_HEADER2));
276 int len;
278 if (current_admin.debug_level >= DBG_ALL) {
279 logit("getldap_get_cacheData_stat()...\n");
282 *output = NULL;
284 len = hdr0_len + hdr1_len + hdr2_len +
285 3 * strlen(DOORLINESEP) + 21;
286 *output = malloc(len);
287 if (*output == NULL)
288 return (-1);
290 (void) snprintf(*output, len, "%s%s%s%10d%s%s%10d%s",
291 gettext(C_HEADER0), DOORLINESEP,
292 gettext(C_HEADER1), max, DOORLINESEP,
293 gettext(C_HEADER2), current, DOORLINESEP);
295 return (NS_LDAP_SUCCESS);
298 static int
299 getldap_cache_op(cache_op_t op, cache_type_t type,
300 char *from, char **to)
302 #define CACHE_HASH_MAX 257
303 #define CACHE_HASH_MAX_ENTRY 256
304 static cache_hash_t *hashTbl[CACHE_HASH_MAX];
305 cache_hash_t *next, *idx, *newp;
306 unsigned long hash;
307 static rwlock_t cache_lock = DEFAULTRWLOCK;
308 int i;
309 static int entry_num = 0;
311 if (current_admin.debug_level >= DBG_ALL) {
312 logit("getldap_cache_op()...\n");
314 switch (op) {
315 case CACHE_OP_CREATE:
316 if (current_admin.debug_level >= DBG_ALL) {
317 logit("operation is CACHE_OP_CREATE...\n");
319 (void) rw_wrlock(&cache_lock);
321 for (i = 0; i < CACHE_HASH_MAX; i++) {
322 hashTbl[i] = NULL;
324 entry_num = 0;
326 (void) rw_unlock(&cache_lock);
327 break;
329 case CACHE_OP_DELETE:
330 if (current_admin.debug_level >= DBG_ALL) {
331 logit("operation is CACHE_OP_DELETE...\n");
333 (void) rw_wrlock(&cache_lock);
335 for (i = 0; i < CACHE_HASH_MAX; i++) {
336 next = hashTbl[i];
337 while (next != NULL) {
338 next = getldap_free_hash(next);
340 hashTbl[i] = NULL;
342 entry_num = 0;
344 (void) rw_unlock(&cache_lock);
345 break;
347 case CACHE_OP_ADD:
348 if (current_admin.debug_level >= DBG_ALL) {
349 logit("operation is CACHE_OP_ADD...\n");
351 if (from == NULL || to == NULL || *to == NULL)
352 return (-1);
353 hash = getldap_hash(from) % CACHE_HASH_MAX;
354 (void) rw_wrlock(&cache_lock);
355 idx = hashTbl[hash];
357 * replace old "to" value with new one
358 * if an entry with same "from"
359 * already exists
361 if (idx) {
362 newp = getldap_scan_hash(type, from, idx);
363 if (newp) {
364 free(newp->to);
365 newp->to = strdup(*to);
366 (void) rw_unlock(&cache_lock);
367 return (NS_LDAP_SUCCESS);
371 if (entry_num > CACHE_HASH_MAX_ENTRY) {
372 (void) rw_unlock(&cache_lock);
373 return (-1);
376 newp = (cache_hash_t *)malloc(sizeof (cache_hash_t));
377 if (newp == NULL) {
378 (void) rw_unlock(&cache_lock);
379 return (NS_LDAP_MEMORY);
381 newp->type = type;
382 newp->from = strdup(from);
383 newp->to = strdup(*to);
384 newp->next = idx;
385 hashTbl[hash] = newp;
386 entry_num++;
387 (void) rw_unlock(&cache_lock);
388 break;
390 case CACHE_OP_FIND:
391 if (current_admin.debug_level >= DBG_ALL) {
392 logit("operation is CACHE_OP_FIND...\n");
394 if (from == NULL || to == NULL)
395 return (-1);
396 *to = NULL;
397 hash = getldap_hash(from) % CACHE_HASH_MAX;
398 (void) rw_rdlock(&cache_lock);
399 idx = hashTbl[hash];
400 idx = getldap_scan_hash(type, from, idx);
401 if (idx)
402 *to = strdup(idx->to);
403 (void) rw_unlock(&cache_lock);
404 if (idx == NULL)
405 return (-1);
406 break;
408 case CACHE_OP_GETSTAT:
409 if (current_admin.debug_level >= DBG_ALL) {
410 logit("operation is CACHE_OP_GETSTAT...\n");
412 if (to == NULL)
413 return (-1);
415 return (getldap_get_cacheData_stat(CACHE_HASH_MAX_ENTRY,
416 entry_num, to));
417 break;
419 default:
420 logit("getldap_cache_op(): "
421 "invalid operation code (%d).\n", op);
422 return (-1);
423 break;
425 return (NS_LDAP_SUCCESS);
428 * Function: sync_current_with_update_copy
430 * This function syncs up the 2 sinfo copies in info.
432 * The 2 copies are identical most of time.
433 * The update copy(sinfo[1]) could be different when
434 * getldap_serverInfo_refresh thread is refreshing the server list
435 * and calls getldap_get_rootDSE to update info. getldap_get_rootDSE
436 * calls sync_current_with_update_copy to sync up 2 copies before thr_exit.
437 * The calling sequence is
438 * getldap_serverInfo_refresh->
439 * getldap_get_serverInfo_op(INFO_OP_CREATE,...)->
440 * getldap_set_serverInfo->
441 * getldap_get_rootDSE
443 * The original server_info_t has one copy of server info. When libsldap
444 * makes door call GETLDAPSERVER to get the server info and getldap_get_rootDSE
445 * is updating the server info, it would hit a unprotected window in
446 * getldap_rootDSE. The door call will not get server info and libsldap
447 * fails at making ldap connection.
449 * The new server_info_t provides GETLDAPSERVER thread with a current
450 * copy(sinfo[0]). getldap_get_rootDSE only works on the update copy(sinfo[1])
451 * and syncs up 2 copies before thr_exit. This will close the window in
452 * getldap_get_rootDSE.
455 static void
456 sync_current_with_update_copy(server_info_t *info)
458 if (current_admin.debug_level >= DBG_ALL) {
459 logit("sync_current_with_update_copy()...\n");
462 (void) mutex_lock(&info->mutex[1]);
463 (void) mutex_lock(&info->mutex[0]);
465 if (info->sinfo[1].server_status == INFO_SERVER_UP &&
466 info->sinfo[0].server_status != INFO_SERVER_UP)
467 info->sinfo[1].change = NS_SERVER_UP;
468 else if (info->sinfo[1].server_status != INFO_SERVER_UP &&
469 info->sinfo[0].server_status == INFO_SERVER_UP)
470 info->sinfo[1].change = NS_SERVER_DOWN;
471 else
472 info->sinfo[1].change = 0;
475 /* free memory in current copy first */
476 if (info->sinfo[0].addr)
477 free(info->sinfo[0].addr);
478 info->sinfo[0].addr = NULL;
480 if (info->sinfo[0].hostname)
481 free(info->sinfo[0].hostname);
482 info->sinfo[0].hostname = NULL;
484 if (info->sinfo[0].rootDSE_data)
485 free(info->sinfo[0].rootDSE_data);
486 info->sinfo[0].rootDSE_data = NULL;
488 if (info->sinfo[0].errormsg)
489 free(info->sinfo[0].errormsg);
490 info->sinfo[0].errormsg = NULL;
493 * make current and update copy identical
495 info->sinfo[0] = info->sinfo[1];
498 * getldap_get_server_stat() reads the update copy sinfo[1]
499 * so it can't be freed or nullified yet at this point.
501 * The sinfo[0] and sinfo[1] have identical string pointers.
502 * strdup the strings to avoid the double free problem.
503 * The strings of sinfo[1] are freed in
504 * getldap_get_rootDSE() and the strings of sinfo[0]
505 * are freed earlier in this function. If the pointers are the
506 * same, they will be freed twice.
508 if (info->sinfo[1].addr)
509 info->sinfo[0].addr = strdup(info->sinfo[1].addr);
510 if (info->sinfo[1].hostname)
511 info->sinfo[0].hostname = strdup(info->sinfo[1].hostname);
512 if (info->sinfo[1].rootDSE_data)
513 info->sinfo[0].rootDSE_data =
514 strdup(info->sinfo[1].rootDSE_data);
515 if (info->sinfo[1].errormsg)
516 info->sinfo[0].errormsg = strdup(info->sinfo[1].errormsg);
518 (void) mutex_unlock(&info->mutex[0]);
519 (void) mutex_unlock(&info->mutex[1]);
523 static void *
524 getldap_get_rootDSE(void *arg)
526 server_info_t *serverInfo = (server_info_t *)arg;
527 char *rootDSE;
528 int exitrc = NS_LDAP_SUCCESS;
529 pid_t ppid;
530 int server_found = 0;
531 char errmsg[MAXERROR];
532 ns_ldap_return_code rc;
533 ns_ldap_error_t *error = NULL;
535 if (current_admin.debug_level >= DBG_ALL) {
536 logit("getldap_get_rootDSE()....\n");
539 /* initialize the server info element */
540 (void) mutex_lock(&serverInfo->mutex[1]);
541 serverInfo->sinfo[1].type = INFO_RW_UNKNOWN;
542 serverInfo->sinfo[1].info_status =
543 INFO_STATUS_UNKNOWN;
545 * When the sever list is refreshed over and over,
546 * this function is called each time it is refreshed.
547 * The previous server status of the update copy(sinfo[1])
548 * is the status of the current copy
550 (void) mutex_lock(&serverInfo->mutex[0]);
551 serverInfo->sinfo[1].prev_server_status =
552 serverInfo->sinfo[0].server_status;
553 (void) mutex_unlock(&serverInfo->mutex[0]);
555 serverInfo->sinfo[1].server_status =
556 INFO_SERVER_UNKNOWN;
557 if (serverInfo->sinfo[1].rootDSE_data)
558 free(serverInfo->sinfo[1].rootDSE_data);
559 serverInfo->sinfo[1].rootDSE_data = NULL;
560 if (serverInfo->sinfo[1].errormsg)
561 free(serverInfo->sinfo[1].errormsg);
562 serverInfo->sinfo[1].errormsg = NULL;
563 (void) mutex_unlock(&serverInfo->mutex[1]);
565 (void) mutex_lock(&serverInfo->mutex[1]);
566 serverInfo->sinfo[1].server_status = INFO_SERVER_CONNECTING;
567 (void) mutex_unlock(&serverInfo->mutex[1]);
570 * WARNING: anon_fallback == 1 (last argument) means that when
571 * __ns_ldap_getRootDSE is unable to bind using the configured
572 * credentials, it will try to fall back to using anonymous, non-SSL
573 * mode of operation.
575 * This is for backward compatibility reasons - we might have machines
576 * in the field with broken configuration (invalid credentials) and we
577 * don't want them to be disturbed.
579 if (rc = __ns_ldap_getRootDSE(serverInfo->sinfo[1].addr,
580 &rootDSE,
581 &error,
582 SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
583 (void) mutex_lock(&serverInfo->mutex[1]);
584 serverInfo->sinfo[1].server_status = INFO_SERVER_ERROR;
585 serverInfo->sinfo[1].info_status = INFO_STATUS_ERROR;
586 if (error && error->message) {
587 serverInfo->sinfo[1].errormsg = strdup(error->message);
588 } else {
589 (void) snprintf(errmsg, sizeof (errmsg), "%s %s "
590 "(rc = %d)", gettext("Can not get the root DSE from"
591 " server"), serverInfo->sinfo[1].addr, rc);
592 serverInfo->sinfo[1].errormsg = strdup(errmsg);
595 if (error != NULL) {
596 (void) __ns_ldap_freeError(&error);
599 if (current_admin.debug_level >= DBG_ALL) {
600 logit("getldap_get_rootDSE: %s.\n",
601 serverInfo->sinfo[1].errormsg);
603 (void) mutex_unlock(&serverInfo->mutex[1]);
605 * sync sinfo copies in the serverInfo.
606 * protected by mutex
608 sync_current_with_update_copy(serverInfo);
609 thr_exit((void *) -1);
612 (void) mutex_lock(&serverInfo->mutex[1]);
614 /* assume writeable, i.e., can do modify */
615 serverInfo->sinfo[1].type = INFO_RW_WRITEABLE;
616 serverInfo->sinfo[1].server_status = INFO_SERVER_UP;
617 serverInfo->sinfo[1].info_status = INFO_STATUS_NEW;
618 /* remove the last DOORLINESEP */
619 *(rootDSE+strlen(rootDSE)-1) = '\0';
620 serverInfo->sinfo[1].rootDSE_data = rootDSE;
622 server_found = 1;
624 (void) mutex_unlock(&serverInfo->mutex[1]);
627 * sync sinfo copies in the serverInfo.
628 * protected by mutex
630 sync_current_with_update_copy(serverInfo);
632 * signal that the ldap_cachemgr parent process
633 * should exit now, if it is still waiting
635 (void) mutex_lock(&sig_mutex);
636 if (signal_done == FALSE && server_found) {
637 ppid = getppid();
638 (void) kill(ppid, SIGUSR1);
639 if (current_admin.debug_level >= DBG_ALL) {
640 logit("getldap_get_rootDSE(): "
641 "SIGUSR1 signal sent to "
642 "parent process(%ld).\n", ppid);
644 signal_done = TRUE;
646 (void) mutex_unlock(&sig_mutex);
648 thr_exit((void *) exitrc);
650 return ((void *) NULL);
653 static int
654 getldap_init_serverInfo(server_info_t **head)
656 char **servers = NULL;
657 int rc = 0, i, exitrc = NS_LDAP_SUCCESS;
658 ns_ldap_error_t *errorp = NULL;
659 server_info_t *info, *tail = NULL;
661 *head = NULL;
662 if (current_admin.debug_level >= DBG_ALL) {
663 logit("getldap_init_serverInfo()...\n");
665 rc = __s_api_getServers(&servers, &errorp);
667 if (rc != NS_LDAP_SUCCESS) {
668 logit("getldap_init_serverInfo: "
669 "__s_api_getServers failed.\n");
670 if (errorp)
671 __ns_ldap_freeError(&errorp);
672 return (-1);
674 for (i = 0; servers[i] != NULL; i++) {
675 info = (server_info_t *)calloc(1, sizeof (server_info_t));
676 if (info == NULL) {
677 logit("getldap_init_serverInfo: "
678 "not enough memory.\n");
679 exitrc = NS_LDAP_MEMORY;
680 break;
682 if (i == 0) {
683 *head = info;
684 tail = info;
685 } else {
686 tail->next = info;
687 tail = info;
690 info->sinfo[0].addr = strdup(servers[i]);
691 if (info->sinfo[0].addr == NULL) {
692 logit("getldap_init_serverInfo: "
693 "not enough memory.\n");
694 exitrc = NS_LDAP_MEMORY;
695 break;
697 info->sinfo[1].addr = strdup(servers[i]);
698 if (info->sinfo[1].addr == NULL) {
699 logit("getldap_init_serverInfo: "
700 "not enough memory.\n");
701 exitrc = NS_LDAP_MEMORY;
702 break;
705 info->sinfo[0].type = INFO_RW_UNKNOWN;
706 info->sinfo[1].type = INFO_RW_UNKNOWN;
707 info->sinfo[0].info_status = INFO_STATUS_UNKNOWN;
708 info->sinfo[1].info_status = INFO_STATUS_UNKNOWN;
709 info->sinfo[0].server_status = INFO_SERVER_UNKNOWN;
710 info->sinfo[1].server_status = INFO_SERVER_UNKNOWN;
713 * Assume at startup or after the configuration
714 * profile is refreshed, all servers are good.
716 info->sinfo[0].prev_server_status =
717 INFO_SERVER_UP;
718 info->sinfo[1].prev_server_status =
719 INFO_SERVER_UP;
720 info->sinfo[0].hostname = NULL;
721 info->sinfo[1].hostname = NULL;
722 info->sinfo[0].rootDSE_data = NULL;
723 info->sinfo[1].rootDSE_data = NULL;
724 info->sinfo[0].errormsg = NULL;
725 info->sinfo[1].errormsg = NULL;
726 info->next = NULL;
728 __s_api_free2dArray(servers);
729 if (exitrc != NS_LDAP_SUCCESS) {
730 if (head && *head) {
731 (void) getldap_destroy_serverInfo(*head);
732 *head = NULL;
735 return (exitrc);
738 static int
739 getldap_destroy_serverInfo(server_info_t *head)
741 server_info_t *info, *next;
743 if (current_admin.debug_level >= DBG_ALL) {
744 logit("getldap_destroy_serverInfo()...\n");
747 if (head == NULL) {
748 logit("getldap_destroy_serverInfo: "
749 "invalid serverInfo list.\n");
750 return (-1);
753 for (info = head; info; info = next) {
754 if (info->sinfo[0].addr)
755 free(info->sinfo[0].addr);
756 if (info->sinfo[1].addr)
757 free(info->sinfo[1].addr);
758 if (info->sinfo[0].hostname)
759 free(info->sinfo[0].hostname);
760 if (info->sinfo[1].hostname)
761 free(info->sinfo[1].hostname);
762 if (info->sinfo[0].rootDSE_data)
763 free(info->sinfo[0].rootDSE_data);
764 if (info->sinfo[1].rootDSE_data)
765 free(info->sinfo[1].rootDSE_data);
766 if (info->sinfo[0].errormsg)
767 free(info->sinfo[0].errormsg);
768 if (info->sinfo[1].errormsg)
769 free(info->sinfo[1].errormsg);
770 next = info->next;
771 free(info);
773 return (NS_LDAP_SUCCESS);
776 static int
777 getldap_set_serverInfo(server_info_t *head, int reset_bindtime, info_op_t op)
779 server_info_t *info;
780 int atleast1 = 0;
781 thread_t *tid;
782 int num_threads = 0, i, j;
783 void *status;
784 void **paramVal = NULL;
785 ns_ldap_error_t *error = NULL;
787 if (current_admin.debug_level >= DBG_ALL) {
788 logit("getldap_set_serverInfo()...\n");
791 if (head == NULL) {
792 logit("getldap_set_serverInfo: "
793 "invalid serverInfo list.\n");
794 return (-1);
797 /* Get the bind timeout value */
798 if (reset_bindtime == 1) {
799 tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
800 (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P,
801 &paramVal, &error);
802 if (paramVal != NULL && *paramVal != NULL) {
803 /* convert to milliseconds */
804 tcptimeout = **((int **)paramVal);
805 tcptimeout *= 1000;
806 (void) __ns_ldap_freeParam(&paramVal);
808 if (error)
809 (void) __ns_ldap_freeError(&error);
812 for (info = head; info; info = info->next)
813 num_threads++;
815 if (num_threads == 0) {
816 logit("getldap_set_serverInfo: "
817 "empty serverInfo list.\n");
818 return (-1);
821 tid = (thread_t *) calloc(1, sizeof (thread_t) * num_threads);
822 if (tid == NULL) {
823 logit("getldap_set_serverInfo: "
824 "No memory to create thread ID list.\n");
825 return (-1);
828 for (info = head, i = 0; info; info = info->next, i++) {
829 if (thr_create(NULL, 0,
830 (void *(*)(void*))getldap_get_rootDSE,
831 (void *)info, 0, &tid[i])) {
832 logit("getldap_set_serverInfo: "
833 "can not create thread %d.\n", i + 1);
834 for (j = 0; j < i; j++)
835 (void) thr_join(tid[j], NULL, NULL);
836 free(tid);
837 return (-1);
841 for (i = 0; i < num_threads; i++) {
842 if (thr_join(tid[i], NULL, &status) == 0) {
843 if ((int)status == NS_LDAP_SUCCESS)
844 atleast1 = 1;
848 free(tid);
850 if (op == INFO_OP_REFRESH)
851 test_server_change(head);
852 if (atleast1) {
853 return (NS_LDAP_SUCCESS);
854 } else
855 return (-1);
859 * getldap_get_serverInfo processes the GETLDAPSERVER door request passed
860 * to this function from getldap_serverInfo_op().
861 * input:
862 * a buffer containing an empty string (e.g., input[0]='\0';) or a string
863 * as the "input" in printf(input, "%s%s%s%s", req, addrtype, DOORLINESEP,
864 * addr);
865 * where addr is the address of a server and
866 * req is one of the following:
867 * NS_CACHE_NEW: send a new server address, addr is ignored.
868 * NS_CACHE_NORESP: send the next one, remove addr from list.
869 * NS_CACHE_NEXT: send the next one, keep addr on list.
870 * NS_CACHE_WRITE: send a non-replica server, if possible, if not, same
871 * as NS_CACHE_NEXT.
872 * addrtype:
873 * NS_CACHE_ADDR_IP: return server address as is, this is default.
874 * NS_CACHE_ADDR_HOSTNAME: return both server address and its FQDN format,
875 * only self credential case requires such format.
876 * output:
877 * a buffer containing server info in the following format:
878 * serveraddress DOORLINESEP [ serveraddress FQDN DOORLINESEP ]
879 * [ attr=value [DOORLINESEP attr=value ]...]
880 * For example: ( here | used as DOORLINESEP for visual purposes)
881 * 1) simple bind and sasl/DIGEST-MD5 bind :
882 * 1.2.3.4|supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL|
883 * supportedSASLmechanisms=GSSAPI
884 * 2) sasl/GSSAPI bind (self credential):
885 * 1.2.3.4|foo.sun.com|supportedControl=1.1.1.1|
886 * supportedSASLmechanisms=EXTERNAL|supportedSASLmechanisms=GSSAPI
887 * NOTE: caller should free this buffer when done using it
889 static int
890 getldap_get_serverInfo(server_info_t *head, char *input,
891 char **output, int *svr_removed)
893 server_info_t *info = NULL;
894 server_info_t *server = NULL;
895 char *addr = NULL;
896 char *req = NULL;
897 char req_new[] = NS_CACHE_NEW;
898 char addr_type[] = NS_CACHE_ADDR_IP;
899 int matched = FALSE, len = 0, rc = 0;
900 char *ret_addr = NULL, *ret_addrFQDN = NULL;
901 char *new_addr = NULL;
902 pid_t pid;
904 if (current_admin.debug_level >= DBG_ALL) {
905 logit("getldap_get_serverInfo()...\n");
908 if (input == NULL || output == NULL) {
909 logit("getldap_get_serverInfo: "
910 "No input or output buffer.\n");
911 return (-1);
914 *output = NULL;
915 *svr_removed = FALSE;
917 if (head == NULL) {
918 logit("getldap_get_serverInfo: "
919 "invalid serverInfo list.\n");
920 return (-1);
923 * parse the input string to get req and addr,
924 * if input is empty, i.e., input[0] == '\0',
925 * treat it as an NS_CACHE_NEW request
927 req = req_new;
928 if (input[0] != '\0') {
929 req = input;
930 /* Save addr type flag */
931 addr_type[0] = input[1];
932 input[strlen(NS_CACHE_NEW)] = '\0';
933 /* skip acion type flag, addr type flag and DOORLINESEP */
934 addr = input + strlen(DOORLINESEP) + strlen(NS_CACHE_NEW)
935 + strlen(NS_CACHE_ADDR_IP);
938 * if NS_CACHE_NEW,
939 * or the server info is new,
940 * starts from the
941 * beginning of the list
943 if ((strcmp(req, NS_CACHE_NEW) == 0) ||
944 (head->sinfo[0].info_status == INFO_STATUS_NEW))
945 matched = TRUE;
946 for (info = head; info; info = info->next) {
948 * make sure the server info stays the same
949 * while the data is being processed
953 * This function is called to get server info list
954 * and pass it back to door call clients.
955 * Access the current copy (sinfo[0]) to get such
956 * information
958 (void) mutex_lock(&info->mutex[0]);
960 if (matched == FALSE &&
961 strcmp(info->sinfo[0].addr, addr) == 0) {
962 matched = TRUE;
963 if (strcmp(req, NS_CACHE_NORESP) == 0) {
964 if (chg_is_called_from_nscd_or_peruser_nscd(
965 "REMOVE SERVER", &pid) == 0) {
966 (void) mutex_unlock(&info->mutex[0]);
967 if (current_admin.debug_level >=
968 DBG_ALL)
969 logit("Only nscd can remove "
970 "servers. pid %ld", pid);
971 continue;
975 * if the information is new,
976 * give this server one more chance
978 if (info->sinfo[0].info_status ==
979 INFO_STATUS_NEW &&
980 info->sinfo[0].server_status ==
981 INFO_SERVER_UP) {
982 server = info;
983 break;
984 } else {
986 * it is recommended that
987 * before removing the
988 * server from the list,
989 * the server should be
990 * contacted one more time
991 * to make sure that it is
992 * really unavailable.
993 * For now, just trust the client
994 * (i.e., the sldap library)
995 * that it knows what it is
996 * doing and would not try
997 * to mess up the server
998 * list.
1001 * Make a copy of addr to contact
1002 * it later. It's not doing it here
1003 * to avoid long wait and possible
1004 * recursion to contact an LDAP server.
1006 new_addr = strdup(info->sinfo[0].addr);
1007 if (new_addr)
1008 remove_server(new_addr);
1009 *svr_removed = TRUE;
1010 (void) mutex_unlock(&info->mutex[0]);
1011 break;
1013 } else {
1015 * req == NS_CACHE_NEXT or NS_CACHE_WRITE
1017 (void) mutex_unlock(&info->mutex[0]);
1018 continue;
1022 if (matched) {
1023 if (strcmp(req, NS_CACHE_WRITE) == 0) {
1024 if (info->sinfo[0].type ==
1025 INFO_RW_WRITEABLE &&
1026 info->sinfo[0].server_status ==
1027 INFO_SERVER_UP) {
1028 server = info;
1029 break;
1031 } else if (info->sinfo[0].server_status ==
1032 INFO_SERVER_UP) {
1033 server = info;
1034 break;
1038 (void) mutex_unlock(&info->mutex[0]);
1041 if (server) {
1042 if (strcmp(addr_type, NS_CACHE_ADDR_HOSTNAME) == 0) {
1044 * In SASL/GSSAPI case, a hostname is required for
1045 * Kerberos's service principal.
1046 * e.g.
1047 * ldap/foo.sun.com@SUN.COM
1049 if (server->sinfo[0].hostname == NULL) {
1050 rc = __s_api_ip2hostname(server->sinfo[0].addr,
1051 &server->sinfo[0].hostname);
1052 if (rc != NS_LDAP_SUCCESS) {
1053 (void) mutex_unlock(&info->mutex[0]);
1054 return (rc);
1056 if (current_admin.debug_level >= DBG_ALL) {
1057 logit("getldap_get_serverInfo: "
1058 "%s is converted to %s\n",
1059 server->sinfo[0].addr,
1060 server->sinfo[0].hostname);
1063 ret_addr = server->sinfo[0].addr;
1064 ret_addrFQDN = server->sinfo[0].hostname;
1066 } else
1067 ret_addr = server->sinfo[0].addr;
1070 len = strlen(ret_addr) +
1071 strlen(server->sinfo[0].rootDSE_data) +
1072 strlen(DOORLINESEP) + 1;
1073 if (ret_addrFQDN != NULL)
1074 len += strlen(ret_addrFQDN) + strlen(DOORLINESEP);
1075 *output = (char *)malloc(len);
1076 if (*output == NULL) {
1077 (void) mutex_unlock(&info->mutex[0]);
1078 return (NS_LDAP_MEMORY);
1080 if (ret_addrFQDN == NULL)
1081 (void) snprintf(*output, len, "%s%s%s",
1082 ret_addr, DOORLINESEP,
1083 server->sinfo[0].rootDSE_data);
1084 else
1085 (void) snprintf(*output, len, "%s%s%s%s%s",
1086 ret_addr, DOORLINESEP,
1087 ret_addrFQDN, DOORLINESEP,
1088 server->sinfo[0].rootDSE_data);
1089 server->sinfo[0].info_status = INFO_STATUS_OLD;
1090 (void) mutex_unlock(&info->mutex[0]);
1091 return (NS_LDAP_SUCCESS);
1093 else
1094 return (-99);
1098 * Format previous and next refresh time
1100 static int
1101 getldap_format_refresh_time(char **output, time_t *prev, time_t *next)
1103 #define TIME_FORMAT "%Y/%m/%d %H:%M:%S"
1104 #define TIME_HEADER1 " Previous refresh time: "
1105 #define TIME_HEADER2 " Next refresh time: "
1106 int hdr1_len = strlen(gettext(TIME_HEADER1));
1107 int hdr2_len = strlen(gettext(TIME_HEADER2));
1108 struct tm tm;
1109 char nbuf[256];
1110 char pbuf[256];
1111 int len;
1113 if (current_admin.debug_level >= DBG_ALL) {
1114 logit("getldap_format_refresh_time()...\n");
1117 *output = NULL;
1119 /* format the time of previous refresh */
1120 if (*prev != 0) {
1121 (void) localtime_r(prev, &tm);
1122 (void) strftime(pbuf, sizeof (pbuf) - 1, TIME_FORMAT, &tm);
1123 } else {
1124 (void) strcpy(pbuf, gettext("NOT DONE"));
1127 /* format the time of next refresh */
1128 if (*next != 0) {
1129 (void) localtime_r(next, &tm);
1130 (void) strftime(nbuf, sizeof (nbuf) - 1, TIME_FORMAT, &tm);
1131 } else {
1132 (void) strcpy(nbuf, gettext("NOT SET"));
1135 len = hdr1_len + hdr2_len + strlen(nbuf) +
1136 strlen(pbuf) + 2 * strlen(DOORLINESEP) + 1;
1138 *output = malloc(len);
1139 if (*output == NULL)
1140 return (-1);
1142 (void) snprintf(*output, len, "%s%s%s%s%s%s",
1143 gettext(TIME_HEADER1), pbuf, DOORLINESEP,
1144 gettext(TIME_HEADER2), nbuf, DOORLINESEP);
1146 return (NS_LDAP_SUCCESS);
1150 * getldap_get_server_stat processes the GETSTAT request passed
1151 * to this function from getldap_serverInfo_op().
1152 * output:
1153 * a buffer containing info for all the servers.
1154 * For each server, the data is in the following format:
1155 * server: server address or name, status: unknown|up|down|removed DOORLINESEP
1156 * for example: ( here | used as DOORLINESEP for visual purposes)
1157 * server: 1.2.3.4, status: down|server: 2.2.2.2, status: up|
1158 * NOTE: caller should free this buffer when done using it
1160 static int
1161 getldap_get_server_stat(server_info_t *head, char **output,
1162 time_t *prev, time_t *next)
1164 #define S_HEADER "Server information: "
1165 #define S_FORMAT " server: %s, status: %s%s"
1166 #define S_ERROR " error message: %s%s"
1167 server_info_t *info = NULL;
1168 int header_len = strlen(gettext(S_HEADER));
1169 int format_len = strlen(gettext(S_FORMAT));
1170 int error_len = strlen(gettext(S_ERROR));
1171 int len = header_len + strlen(DOORLINESEP);
1172 int len1 = 0;
1173 char *status, *output1 = NULL, *tmpptr;
1175 *output = NULL;
1177 if (current_admin.debug_level >= DBG_ALL) {
1178 logit("getldap_get_server_stat()...\n");
1181 if (head == NULL) {
1182 logit("getldap_get_server_stat: "
1183 "invalid serverInfo list.\n");
1184 return (-1);
1187 /* format previous and next refresh time */
1188 (void) getldap_format_refresh_time(&output1, prev, next);
1189 if (output1 == NULL)
1190 return (-1);
1191 len += strlen(output1);
1192 len1 = len + strlen(DOORLINESEP) + 1;
1194 *output = (char *)calloc(1, len1);
1195 if (*output == NULL) {
1196 free(output1);
1197 return (-1);
1200 /* insert header string and refresh time info */
1201 (void) snprintf(*output, len1, "%s%s%s",
1202 gettext(S_HEADER), DOORLINESEP, output1);
1204 for (info = head; info; info = info->next) {
1207 * make sure the server info stays the same
1208 * while the data is being processed
1210 (void) mutex_lock(&info->mutex[1]);
1213 * When the updating process is under way(getldap_get_rootDSE)
1214 * the update copy(sinfo[1] is the latest copy.
1215 * When the updating process
1216 * is done, the current copy (sinfo[0]) has the latest status,
1217 * which is still identical to the update copy.
1218 * So update copy has the latest status.
1219 * Use the update copy(sinfo[1]) to show status
1220 * (ldap_cachemgr -g).
1224 switch (info->sinfo[1].server_status) {
1225 case INFO_SERVER_UNKNOWN:
1226 status = gettext("UNKNOWN");
1227 break;
1228 case INFO_SERVER_CONNECTING:
1229 status = gettext("CONNECTING");
1230 break;
1231 case INFO_SERVER_UP:
1232 status = gettext("UP");
1233 break;
1234 case INFO_SERVER_ERROR:
1235 status = gettext("ERROR");
1236 break;
1237 case INFO_SERVER_REMOVED:
1238 status = gettext("REMOVED");
1239 break;
1242 len += format_len + strlen(status) +
1243 strlen(info->sinfo[1].addr) +
1244 strlen(DOORLINESEP);
1245 if (info->sinfo[1].errormsg != NULL)
1246 len += error_len +
1247 strlen(info->sinfo[1].errormsg) +
1248 strlen(DOORLINESEP);
1250 tmpptr = (char *)realloc(*output, len);
1251 if (tmpptr == NULL) {
1252 free(output1);
1253 free(*output);
1254 *output = NULL;
1255 (void) mutex_unlock(&info->mutex[1]);
1256 return (-1);
1257 } else
1258 *output = tmpptr;
1260 /* insert server IP addr or name and status */
1261 len1 = len - strlen(*output);
1262 (void) snprintf(*output + strlen(*output), len1,
1263 gettext(S_FORMAT), info->sinfo[1].addr,
1264 status, DOORLINESEP);
1265 /* insert error message if any */
1266 len1 = len - strlen(*output);
1267 if (info->sinfo[1].errormsg != NULL)
1268 (void) snprintf(*output + strlen(*output), len1,
1269 gettext(S_ERROR),
1270 info->sinfo[1].errormsg,
1271 DOORLINESEP);
1273 (void) mutex_unlock(&info->mutex[1]);
1277 free(output1);
1278 return (NS_LDAP_SUCCESS);
1282 * Format and return the refresh time statistics
1284 static int
1285 getldap_get_refresh_stat(char **output)
1287 #define R_HEADER0 "Configuration refresh information: "
1288 #define R_HEADER1 " Configured to NO REFRESH."
1289 int hdr0_len = strlen(gettext(R_HEADER0));
1290 int hdr1_len = strlen(gettext(R_HEADER1));
1291 int cache_ttl = -1, len = 0;
1292 time_t expire = 0;
1293 void **paramVal = NULL;
1294 ns_ldap_error_t *errorp = NULL;
1295 char *output1 = NULL;
1297 if (current_admin.debug_level >= DBG_ALL) {
1298 logit("getldap_get_refresh_stat()...\n");
1301 *output = NULL;
1303 /* get configured cache TTL */
1304 if ((__ns_ldap_getParam(NS_LDAP_CACHETTL_P,
1305 &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
1306 paramVal != NULL &&
1307 (char *)*paramVal != NULL) {
1308 cache_ttl = atol((char *)*paramVal);
1309 } else {
1310 if (errorp)
1311 __ns_ldap_freeError(&errorp);
1313 (void) __ns_ldap_freeParam(&paramVal);
1315 /* cound not get cache TTL */
1316 if (cache_ttl == -1)
1317 return (-1);
1319 if (cache_ttl == 0) {
1320 len = hdr0_len + hdr1_len +
1321 2 * strlen(DOORLINESEP) + 1;
1322 *output = malloc(len);
1323 if (*output == NULL)
1324 return (-1);
1325 (void) snprintf(*output, len, "%s%s%s%s",
1326 gettext(R_HEADER0), DOORLINESEP,
1327 gettext(R_HEADER1), DOORLINESEP);
1328 } else {
1330 /* get configuration expiration time */
1331 if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
1332 &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
1333 paramVal != NULL &&
1334 (char *)*paramVal != NULL) {
1335 expire = (time_t)atol((char *)*paramVal);
1336 } else {
1337 if (errorp)
1338 __ns_ldap_freeError(&errorp);
1341 (void) __ns_ldap_freeParam(&paramVal);
1343 /* cound not get expiration time */
1344 if (expire == -1)
1345 return (-1);
1347 /* format previous and next refresh time */
1348 (void) getldap_format_refresh_time(&output1,
1349 &prev_refresh_time, &expire);
1350 if (output1 == NULL)
1351 return (-1);
1353 len = hdr0_len + strlen(output1) +
1354 2 * strlen(DOORLINESEP) + 1;
1355 *output = malloc(len);
1356 if (*output == NULL) {
1357 free(output1);
1358 return (-1);
1360 (void) snprintf(*output, len, "%s%s%s%s",
1361 gettext(R_HEADER0), DOORLINESEP,
1362 output1, DOORLINESEP);
1363 free(output1);
1366 return (NS_LDAP_SUCCESS);
1369 static int
1370 getldap_get_cacheTTL()
1372 void **paramVal = NULL;
1373 ns_ldap_error_t *error;
1374 int rc = 0, cachettl;
1377 if (current_admin.debug_level >= DBG_ALL) {
1378 logit("getldap_get_cacheTTL()....\n");
1381 if ((rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P,
1382 &paramVal, &error)) != NS_LDAP_SUCCESS) {
1383 if (error != NULL && error->message != NULL)
1384 logit("Error: Unable to get configuration "
1385 "refresh TTL: %s\n",
1386 error->message);
1387 else {
1388 char *tmp;
1390 __ns_ldap_err2str(rc, &tmp);
1391 logit("Error: Unable to get configuration "
1392 "refresh TTL: %s\n", tmp);
1394 (void) __ns_ldap_freeParam(&paramVal);
1395 (void) __ns_ldap_freeError(&error);
1396 return (-1);
1398 if (paramVal == NULL || (char *)*paramVal == NULL)
1399 return (-1);
1400 cachettl = atol((char *)*paramVal);
1401 (void) __ns_ldap_freeParam(&paramVal);
1402 return (cachettl);
1407 * This function implements the adaptive server list refresh
1408 * algorithm used by ldap_cachemgr. The idea is to have the
1409 * refresh TTL adjust itself between maximum and minimum
1410 * values. If the server list has been walked three times
1411 * in a row without errors, the TTL will be doubled. This will
1412 * be done repeatedly until the maximum value is reached
1413 * or passed. If passed, the maximum value will be used.
1414 * If any time a server is found to be down/bad, either
1415 * after another server list walk or informed by libsldap via
1416 * the GETLDAPSERVER door calls, the TTL will be set to half
1417 * of its value, again repeatedly, but no less than the minimum
1418 * value. Also, at any time, if all the servers on the list
1419 * are found to be down/bad, the TTL will be set to minimum,
1420 * so that a "no-server" refresh loop should be entered to try
1421 * to find a good server as soon as possible. The caller
1422 * could check the no_gd_server flag for this situation.
1423 * The maximum and minimum values are initialized when the input
1424 * refresh_ttl is set to zero, this should occur during
1425 * ldap_cachemgr startup or every time the server list is
1426 * recreated after the configuration profile is refreshed
1427 * from an LDAP server. The maximum is set to the value of
1428 * the NS_LDAP_CACHETTL parameter (configuration profile
1429 * refresh TTL), but if it is zero (never refreshed) or can
1430 * not be retrieved, the maximum is set to the macro
1431 * REFRESHTTL_MAX (12 hours) defined below. The minimum is
1432 * set to REFRESHTTL_MIN, which is the TCP connection timeout
1433 * (tcptimeout) set via the LDAP API ldap_set_option()
1434 * with the new LDAP_X_OPT_CONNECT_TIMEOUT option plus 10 seconds.
1435 * This accounts for the maximum possible timeout value for an
1436 * LDAP TCP connect call.The first refresh TTL, initial value of
1437 * refresh_ttl, will be set to the smaller of the two,
1438 * REFRESHTTL_REGULAR (10 minutes) or (REFRESHTTL_MAX + REFRESHTTL_MIN)/2.
1439 * The idea is to have a low starting value and have the value
1440 * stay low if the network/server is unstable, but eventually
1441 * the value will move up to maximum and stay there if the
1442 * network/server is stable.
1444 static int
1445 getldap_set_refresh_ttl(server_info_t *head, int *refresh_ttl,
1446 int *no_gd_server)
1448 #define REFRESHTTL_REGULAR 600
1449 #define REFRESHTTL_MAX 43200
1450 /* tcptimeout is in milliseconds */
1451 #define REFRESHTTL_MIN (tcptimeout/1000) + 10
1452 #define UP_REFRESH_TTL_NUM 2
1454 static mutex_t refresh_mutex;
1455 static int refresh_ttl_max = 0;
1456 static int refresh_ttl_min = 0;
1457 static int num_walked_ok = 0;
1458 int num_servers = 0;
1459 int num_good_servers = 0;
1460 int num_prev_good_servers = 0;
1461 server_info_t *info;
1463 /* allow one thread at a time */
1464 (void) mutex_lock(&refresh_mutex);
1466 if (current_admin.debug_level >= DBG_ALL) {
1467 logit("getldap_set_refresh_ttl()...\n");
1470 if (!head || !refresh_ttl || !no_gd_server) {
1471 logit("getldap_set_refresh_ttl: head is "
1472 "NULL or refresh_ttl is NULL or "
1473 "no_gd_server is NULL");
1474 (void) mutex_unlock(&refresh_mutex);
1475 return (-1);
1477 *no_gd_server = FALSE;
1480 * init max. min. TTLs if first time through or a fresh one
1482 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1483 logit("getldap_set_refresh_ttl:(1) refresh ttl is %d "
1484 "seconds\n", *refresh_ttl);
1486 if (*refresh_ttl == 0) {
1487 num_walked_ok = 0;
1489 * init cache manager server list TTL:
1491 * init the min. TTL to
1492 * REFRESHTTL_MIN ( 2*(TCP MSL) + 10 seconds)
1494 refresh_ttl_min = REFRESHTTL_MIN;
1497 * try to set the max. TTL to
1498 * configuration refresh TTL (NS_LDAP_CACHETTL),
1499 * if error (-1), or never refreshed (0),
1500 * set it to REFRESHTTL_MAX (12 hours)
1502 refresh_ttl_max = getldap_get_cacheTTL();
1503 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1504 logit("getldap_set_refresh_ttl:(2) refresh ttl is %d "
1505 "seconds\n", *refresh_ttl);
1506 logit("getldap_set_refresh_ttl:(2) max ttl is %d, "
1507 "min ttl is %d seconds\n",
1508 refresh_ttl_max, refresh_ttl_min);
1510 if (refresh_ttl_max <= 0)
1511 refresh_ttl_max = REFRESHTTL_MAX;
1512 else if (refresh_ttl_max < refresh_ttl_min)
1513 refresh_ttl_max = refresh_ttl_min;
1516 * init the first TTL to the smaller of the two:
1517 * REFRESHTTL_REGULAR ( 10 minutes),
1518 * (refresh_ttl_max + refresh_ttl_min)/2
1520 *refresh_ttl = REFRESHTTL_REGULAR;
1521 if (*refresh_ttl > (refresh_ttl_max + refresh_ttl_min) / 2)
1522 *refresh_ttl = (refresh_ttl_max + refresh_ttl_min) / 2;
1523 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1524 logit("getldap_set_refresh_ttl:(3) refresh ttl is %d "
1525 "seconds\n", *refresh_ttl);
1526 logit("getldap_set_refresh_ttl:(3) max ttl is %d, "
1527 "min ttl is %d seconds\n",
1528 refresh_ttl_max, refresh_ttl_min);
1533 * get the servers statistics:
1534 * number of servers on list
1535 * number of good servers on list
1536 * number of pevious good servers on list
1538 for (info = head; info; info = info->next) {
1539 num_servers++;
1540 (void) mutex_lock(&info->mutex[0]);
1541 if (info->sinfo[0].server_status == INFO_SERVER_UP)
1542 num_good_servers++;
1544 * Server's previous status could be UNKNOWN
1545 * only between the very first and second
1546 * refresh. Treat that UNKNOWN status as up
1548 if (info->sinfo[0].prev_server_status
1549 == INFO_SERVER_UP ||
1550 info->sinfo[0].prev_server_status
1551 == INFO_SERVER_UNKNOWN)
1552 num_prev_good_servers++;
1553 (void) mutex_unlock(&info->mutex[0]);
1557 * if the server list is walked three times in a row
1558 * without problems, double the refresh TTL but no more
1559 * than the max. refresh TTL
1561 if (num_good_servers == num_servers) {
1562 num_walked_ok++;
1563 if (num_walked_ok > UP_REFRESH_TTL_NUM) {
1565 *refresh_ttl = *refresh_ttl * 2;
1566 if (*refresh_ttl > refresh_ttl_max)
1567 *refresh_ttl = refresh_ttl_max;
1569 num_walked_ok = 0;
1571 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1572 logit("getldap_set_refresh_ttl:(4) refresh ttl is %d "
1573 "seconds\n", *refresh_ttl);
1575 } else if (num_good_servers == 0) {
1577 * if no good server found,
1578 * set refresh TTL to miminum
1580 *refresh_ttl = refresh_ttl_min;
1581 *no_gd_server = TRUE;
1582 num_walked_ok = 0;
1583 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1584 logit("getldap_set_refresh_ttl:(5) refresh ttl is %d "
1585 "seconds\n", *refresh_ttl);
1587 } else if (num_prev_good_servers > num_good_servers) {
1589 * if more down/bad servers found,
1590 * decrease the refresh TTL by half
1591 * but no less than the min. refresh TTL
1593 *refresh_ttl = *refresh_ttl / 2;
1594 if (*refresh_ttl < refresh_ttl_min)
1595 *refresh_ttl = refresh_ttl_min;
1596 num_walked_ok = 0;
1597 logit("getldap_set_refresh_ttl:(6) refresh ttl is %d "
1598 "seconds\n", *refresh_ttl);
1602 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1603 logit("getldap_set_refresh_ttl:(7) refresh ttl is %d seconds\n",
1604 *refresh_ttl);
1606 (void) mutex_unlock(&refresh_mutex);
1607 return (0);
1610 static int
1611 getldap_serverInfo_op(info_op_t op, char *input, char **output)
1614 static rwlock_t info_lock = DEFAULTRWLOCK;
1615 static rwlock_t info_lock_old = DEFAULTRWLOCK;
1616 static mutex_t info_mutex;
1617 static cond_t info_cond;
1618 static int creating = FALSE;
1619 static int refresh_ttl = 0;
1620 static int sec_to_refresh = 0;
1621 static int in_no_server_mode = FALSE;
1623 static server_info_t *serverInfo = NULL;
1624 static server_info_t *serverInfo_old = NULL;
1625 server_info_t *serverInfo_1;
1626 int is_creating;
1627 int err, no_server_good = FALSE;
1628 int server_removed = FALSE;
1629 int fall_thru = FALSE;
1630 static struct timespec timeout;
1631 struct timespec new_timeout;
1632 struct timeval tp;
1633 static time_t prev_refresh = 0, next_refresh = 0;
1634 ns_server_status_t changed = 0;
1636 (void) pthread_setname_np(pthread_self(), "getldap_serverinfo");
1638 if (current_admin.debug_level >= DBG_ALL) {
1639 logit("getldap_serverInfo_op()...\n");
1641 switch (op) {
1642 case INFO_OP_CREATE:
1643 if (current_admin.debug_level >= DBG_ALL) {
1644 logit("operation is INFO_OP_CREATE...\n");
1648 * indicate that the server info is being
1649 * (re)created, so that the refresh thread
1650 * will not refresh the info list right
1651 * after the list got (re)created
1653 (void) mutex_lock(&info_mutex);
1654 is_creating = creating;
1655 creating = TRUE;
1656 (void) mutex_unlock(&info_mutex);
1658 if (is_creating)
1659 break;
1661 * create an empty info list
1663 (void) getldap_init_serverInfo(&serverInfo_1);
1665 * exit if list not created
1667 if (serverInfo_1 == NULL) {
1668 (void) mutex_lock(&info_mutex);
1669 creating = FALSE;
1670 (void) mutex_unlock(&info_mutex);
1671 break;
1674 * make the new server info available:
1675 * use writer lock here, so that the switch
1676 * is done after all the reader locks have
1677 * been released.
1679 (void) rw_wrlock(&info_lock);
1680 serverInfo = serverInfo_1;
1682 * if this is the first time
1683 * the server list is being created,
1684 * (i.e., serverInfo_old is NULL)
1685 * make the old list same as the new
1686 * so the GETSERVER code can do its work
1688 if (serverInfo_old == NULL)
1689 serverInfo_old = serverInfo_1;
1690 (void) rw_unlock(&info_lock);
1693 * fill the new info list
1695 (void) rw_rdlock(&info_lock);
1696 /* reset bind time (tcptimeout) */
1697 (void) getldap_set_serverInfo(serverInfo, 1, INFO_OP_CREATE);
1699 (void) mutex_lock(&info_mutex);
1701 * set cache manager server list TTL,
1702 * set refresh_ttl to zero to indicate a fresh one
1704 refresh_ttl = 0;
1705 (void) getldap_set_refresh_ttl(serverInfo,
1706 &refresh_ttl, &no_server_good);
1707 sec_to_refresh = refresh_ttl;
1709 /* statistics: previous refresh time */
1710 if (gettimeofday(&tp, NULL) == 0)
1711 prev_refresh = tp.tv_sec;
1713 creating = FALSE;
1716 * if no server found or available,
1717 * tell the server info refresh thread
1718 * to start the "no-server" refresh loop
1719 * otherwise reset the in_no_server_mode flag
1721 if (no_server_good) {
1722 sec_to_refresh = 0;
1723 in_no_server_mode = TRUE;
1724 } else
1725 in_no_server_mode = FALSE;
1727 * awake the sleeping refresh thread
1729 (void) cond_signal(&info_cond);
1731 (void) mutex_unlock(&info_mutex);
1732 (void) rw_unlock(&info_lock);
1735 * delete the old server info
1737 (void) rw_wrlock(&info_lock_old);
1738 if (serverInfo_old != serverInfo)
1739 (void) getldap_destroy_serverInfo(serverInfo_old);
1741 * serverInfo_old needs to be the same as
1742 * serverinfo now.
1743 * it will be used by GETSERVER processing.
1745 serverInfo_old = serverInfo;
1746 (void) rw_unlock(&info_lock_old);
1747 break;
1748 case INFO_OP_DELETE:
1749 if (current_admin.debug_level >= DBG_ALL) {
1750 logit("operation is INFO_OP_DELETE...\n");
1753 * use writer lock here, so that the delete would
1754 * not start until all the reader locks have
1755 * been released.
1757 (void) rw_wrlock(&info_lock);
1758 if (serverInfo)
1759 (void) getldap_destroy_serverInfo(serverInfo);
1760 serverInfo = NULL;
1761 (void) rw_unlock(&info_lock);
1762 break;
1763 case INFO_OP_REFRESH:
1764 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1765 logit("operation is INFO_OP_REFRESH...\n");
1768 * if server info is currently being
1769 * (re)created, do nothing
1771 (void) mutex_lock(&info_mutex);
1772 is_creating = creating;
1773 (void) mutex_unlock(&info_mutex);
1774 if (is_creating)
1775 break;
1777 (void) rw_rdlock(&info_lock);
1778 if (serverInfo) {
1779 /* do not reset bind time (tcptimeout) */
1780 (void) getldap_set_serverInfo(serverInfo, 0,
1781 INFO_OP_REFRESH);
1783 (void) mutex_lock(&info_mutex);
1785 /* statistics: previous refresh time */
1786 if (gettimeofday(&tp, NULL) == 0)
1787 prev_refresh = tp.tv_sec;
1789 * set cache manager server list TTL
1791 (void) getldap_set_refresh_ttl(serverInfo,
1792 &refresh_ttl, &no_server_good);
1794 * if no good server found,
1795 * tell the server info refresh thread
1796 * to start the "no-server" refresh loop
1797 * otherwise reset the in_no_server_mode flag
1799 if (no_server_good) {
1800 in_no_server_mode = TRUE;
1801 sec_to_refresh = 0;
1802 } else {
1803 in_no_server_mode = FALSE;
1804 sec_to_refresh = refresh_ttl;
1806 if (current_admin.debug_level >=
1807 DBG_SERVER_LIST_REFRESH) {
1808 logit("getldap_serverInfo_op("
1809 "INFO_OP_REFRESH):"
1810 " seconds refresh: %d second(s)....\n",
1811 sec_to_refresh);
1813 (void) mutex_unlock(&info_mutex);
1815 (void) rw_unlock(&info_lock);
1817 break;
1818 case INFO_OP_REFRESH_WAIT:
1819 if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1820 logit("operation is INFO_OP_REFRESH_WAIT...\n");
1822 (void) cond_init(&info_cond, NULL, NULL);
1823 (void) mutex_lock(&info_mutex);
1824 err = 0;
1825 while (err != ETIME) {
1826 int sleeptime;
1828 * if need to go into the "no-server" refresh
1829 * loop, set timout value to
1830 * REFRESH_DELAY_WHEN_NO_SERVER
1832 if (sec_to_refresh == 0) {
1833 sec_to_refresh = refresh_ttl;
1834 timeout.tv_sec = time(NULL) +
1835 REFRESH_DELAY_WHEN_NO_SERVER;
1836 sleeptime = REFRESH_DELAY_WHEN_NO_SERVER;
1837 if (current_admin.debug_level >=
1838 DBG_SERVER_LIST_REFRESH) {
1839 logit("getldap_serverInfo_op("
1840 "INFO_OP_REFRESH_WAIT):"
1841 " entering no-server "
1842 "refresh loop...\n");
1844 } else {
1845 timeout.tv_sec = time(NULL) + sec_to_refresh;
1846 sleeptime = sec_to_refresh;
1848 timeout.tv_nsec = 0;
1850 /* statistics: next refresh time */
1851 next_refresh = timeout.tv_sec;
1853 if (current_admin.debug_level >=
1854 DBG_SERVER_LIST_REFRESH) {
1855 logit("getldap_serverInfo_op("
1856 "INFO_OP_REFRESH_WAIT):"
1857 " about to sleep for %d second(s)...\n",
1858 sleeptime);
1860 err = cond_timedwait(&info_cond,
1861 &info_mutex, &timeout);
1863 (void) cond_destroy(&info_cond);
1864 (void) mutex_unlock(&info_mutex);
1865 break;
1866 case INFO_OP_GETSERVER:
1867 if (current_admin.debug_level >= DBG_ALL) {
1868 logit("operation is INFO_OP_GETSERVER...\n");
1870 *output = NULL;
1872 * GETSERVER processing always use
1873 * serverInfo_old to retrieve server infomation.
1874 * serverInfo_old is equal to serverInfo
1875 * most of the time, except when a new
1876 * server list is being created.
1877 * This is why the check for is_creating
1878 * is needed below.
1880 (void) rw_rdlock(&info_lock_old);
1882 if (serverInfo_old == NULL) {
1883 (void) rw_unlock(&info_lock_old);
1884 break;
1885 } else
1886 (void) getldap_get_serverInfo(serverInfo_old,
1887 input, output, &server_removed);
1888 (void) rw_unlock(&info_lock_old);
1891 * Return here and let remove server thread do its job in
1892 * another thread. It executes INFO_OP_REMOVESERVER code later.
1894 if (server_removed)
1895 break;
1897 fall_thru = TRUE;
1899 /* FALL THROUGH */
1901 case INFO_OP_REMOVESERVER:
1903 * INFO_OP_GETSERVER and INFO_OP_REMOVESERVER share the
1904 * following code except (!fall thru) part.
1908 * if server info is currently being
1909 * (re)created, do nothing
1912 (void) mutex_lock(&info_mutex);
1913 is_creating = creating;
1914 (void) mutex_unlock(&info_mutex);
1915 if (is_creating)
1916 break;
1918 if (!fall_thru) {
1919 if (current_admin.debug_level >= DBG_ALL)
1920 logit("operation is INFO_OP_REMOVESERVER...\n");
1921 (void) rw_rdlock(&info_lock_old);
1922 changed = set_server_status(input, serverInfo_old);
1923 (void) rw_unlock(&info_lock_old);
1924 if (changed)
1925 create_buf_and_notify(input, changed);
1926 else
1927 break;
1931 * set cache manager server list TTL if necessary
1933 if (*output == NULL || changed) {
1934 (void) rw_rdlock(&info_lock);
1935 (void) mutex_lock(&info_mutex);
1937 (void) getldap_set_refresh_ttl(serverInfo,
1938 &refresh_ttl, &no_server_good);
1941 * if no good server found, need to go into
1942 * the "no-server" refresh loop
1943 * to find a server as soon as possible
1944 * otherwise reset the in_no_server_mode flag
1946 if (no_server_good) {
1948 * if already in no-server mode,
1949 * don't brother
1951 if (in_no_server_mode == FALSE) {
1952 sec_to_refresh = 0;
1953 in_no_server_mode = TRUE;
1954 (void) cond_signal(&info_cond);
1956 (void) mutex_unlock(&info_mutex);
1957 (void) rw_unlock(&info_lock);
1958 break;
1959 } else {
1960 in_no_server_mode = FALSE;
1961 sec_to_refresh = refresh_ttl;
1964 * if the refresh thread will be timed out
1965 * longer than refresh_ttl seconds,
1966 * wake it up to make it wait on the new
1967 * time out value
1969 new_timeout.tv_sec = time(NULL) + refresh_ttl;
1970 if (new_timeout.tv_sec < timeout.tv_sec)
1971 (void) cond_signal(&info_cond);
1973 (void) mutex_unlock(&info_mutex);
1974 (void) rw_unlock(&info_lock);
1976 break;
1977 case INFO_OP_GETSTAT:
1978 if (current_admin.debug_level >= DBG_ALL) {
1979 logit("operation is INFO_OP_GETSTAT...\n");
1981 *output = NULL;
1982 (void) rw_rdlock(&info_lock);
1983 if (serverInfo) {
1984 (void) getldap_get_server_stat(serverInfo,
1985 output, &prev_refresh, &next_refresh);
1987 (void) rw_unlock(&info_lock);
1988 break;
1989 default:
1990 logit("getldap_serverInfo_op(): "
1991 "invalid operation code (%d).\n", op);
1992 return (-1);
1993 break;
1995 return (NS_LDAP_SUCCESS);
1998 void
1999 getldap_serverInfo_refresh()
2001 int always = 1;
2003 if (current_admin.debug_level >= DBG_ALL) {
2004 logit("getldap_serverInfo_refresh()...\n");
2007 /* create the server info list */
2008 (void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
2010 while (always) {
2012 * the operation INFO_OP_REFRESH_WAIT
2013 * causes this thread to wait until
2014 * it is time to do refresh,
2015 * see getldap_serverInfo_op() for details
2017 (void) getldap_serverInfo_op(INFO_OP_REFRESH_WAIT, NULL, NULL);
2018 (void) getldap_serverInfo_op(INFO_OP_REFRESH, NULL, NULL);
2022 void
2023 getldap_getserver(LineBuf *config_info, ldap_call_t *in)
2025 char req[] = "0";
2027 if (current_admin.debug_level >= DBG_ALL) {
2028 logit("getldap_getserver()...\n");
2031 config_info->len = 0;
2033 /* make sure the request is valid */
2034 req[0] = (in->ldap_u.servername)[0];
2035 if ((req[0] != '\0') &&
2036 (strcmp(req, NS_CACHE_NEW) != 0) &&
2037 (strcmp(req, NS_CACHE_NORESP) != 0) &&
2038 (strcmp(req, NS_CACHE_NEXT) != 0) &&
2039 (strcmp(req, NS_CACHE_WRITE) != 0)) {
2040 return;
2043 (void) getldap_serverInfo_op(INFO_OP_GETSERVER,
2044 in->ldap_u.domainname, &config_info->str);
2046 if (config_info->str == NULL)
2047 return;
2049 config_info->len = strlen(config_info->str) + 1;
2051 if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2052 /* Log server IP */
2053 char *ptr,
2054 separator;
2055 ptr = strstr(config_info->str, DOORLINESEP);
2056 if (ptr) {
2057 separator = *ptr;
2058 *ptr = '\0';
2059 logit("getldap_getserver: got server %s\n",
2060 config_info->str);
2061 *ptr = separator;
2062 } else
2063 logit("getldap_getserver: Missing %s."
2064 " Internal error\n", DOORLINESEP);
2068 void
2069 getldap_get_cacheData(LineBuf *config_info, ldap_call_t *in)
2071 char *instr = NULL;
2072 int datatype = CACHE_MAP_UNKNOWN;
2074 if (current_admin.debug_level >= DBG_ALL) {
2075 logit("getldap_get_cacheData()...\n");
2078 config_info->len = 0;
2079 config_info->str = NULL;
2081 /* make sure the request is valid */
2082 if (strncmp(in->ldap_u.servername,
2083 NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
2084 datatype = CACHE_MAP_DN2DOMAIN;
2086 if (datatype == CACHE_MAP_UNKNOWN)
2087 return;
2089 instr = strstr(in->ldap_u.servername, DOORLINESEP);
2090 if (instr == NULL)
2091 return;
2092 instr += strlen(DOORLINESEP);
2093 if (*instr == '\0')
2094 return;
2096 (void) getldap_cache_op(CACHE_OP_FIND, datatype,
2097 instr, &config_info->str);
2099 if (config_info->str != NULL) {
2100 config_info->len = strlen(config_info->str) + 1;
2105 getldap_set_cacheData(ldap_call_t *in)
2107 char *instr1 = NULL;
2108 char *instr2 = NULL;
2109 int datatype = CACHE_MAP_UNKNOWN;
2110 int rc = 0;
2112 if (current_admin.debug_level >= DBG_ALL) {
2113 logit("getldap_set_cacheData()...\n");
2116 /* make sure the request is valid */
2117 if (strncmp(in->ldap_u.servername,
2118 NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
2119 datatype = CACHE_MAP_DN2DOMAIN;
2121 if (datatype == CACHE_MAP_UNKNOWN)
2122 return (-1);
2124 instr1 = strstr(in->ldap_u.servername, DOORLINESEP);
2125 if (instr1 == NULL)
2126 return (-1);
2127 *instr1 = '\0';
2128 instr1 += strlen(DOORLINESEP);
2129 if (*instr1 == '\0')
2130 return (-1);
2131 instr2 = strstr(instr1, DOORLINESEP);
2132 if (instr2 == NULL)
2133 return (-1);
2134 *instr2 = '\0';
2135 instr2 += strlen(DOORLINESEP);
2136 if (*instr2 == '\0')
2137 return (-1);
2139 rc = getldap_cache_op(CACHE_OP_ADD, datatype,
2140 instr1, &instr2);
2141 if (rc != NS_LDAP_SUCCESS)
2142 return (-1);
2144 return (0);
2147 void
2148 getldap_get_cacheStat(LineBuf *stat_info)
2150 char *foutstr = NULL;
2151 char *soutstr = NULL;
2152 char *coutstr = NULL;
2153 int infoSize;
2155 if (current_admin.debug_level >= DBG_ALL) {
2156 logit("getldap_get_cacheStat()...\n");
2159 stat_info->str = NULL;
2160 stat_info->len = 0;
2162 /* get refersh statisitcs */
2163 (void) getldap_get_refresh_stat(&foutstr);
2164 if (foutstr == NULL)
2165 return;
2167 /* get server statisitcs */
2168 (void) getldap_serverInfo_op(INFO_OP_GETSTAT, NULL, &soutstr);
2169 if (soutstr == NULL) {
2170 free(foutstr);
2171 return;
2173 /* get cache data statisitcs */
2174 (void) getldap_cache_op(CACHE_OP_GETSTAT, NULL, NULL, &coutstr);
2175 if (coutstr == NULL) {
2176 free(foutstr);
2177 free(soutstr);
2178 return;
2181 infoSize = strlen(foutstr) + strlen(soutstr) + strlen(coutstr) + 3;
2182 stat_info->str = calloc(infoSize, sizeof (char));
2183 if (stat_info->str != NULL) {
2184 (void) strncpy(stat_info->str,
2185 foutstr,
2186 strlen(foutstr) + 1);
2187 (void) strncat(stat_info->str,
2188 soutstr,
2189 strlen(soutstr) + 1);
2190 (void) strncat(stat_info->str,
2191 coutstr,
2192 strlen(coutstr) + 1);
2193 stat_info->len = infoSize;
2196 free(foutstr);
2197 free(soutstr);
2198 free(coutstr);
2201 static int
2202 checkupdate(int sighup)
2204 int value;
2206 (void) rw_wrlock(&ldap_lock);
2207 value = sighup;
2208 (void) rw_unlock(&ldap_lock);
2210 return (value == TRUE);
2214 static int
2215 update_from_profile(int *change_status)
2217 ns_ldap_result_t *result = NULL;
2218 char searchfilter[BUFSIZ];
2219 ns_ldap_error_t *error;
2220 int rc;
2221 void **paramVal = NULL;
2222 ns_config_t *ptr = NULL;
2223 char *profile = NULL;
2224 char errstr[MAXERROR];
2226 if (current_admin.debug_level >= DBG_ALL) {
2227 logit("update_from_profile....\n");
2229 do {
2230 (void) rw_wrlock(&ldap_lock);
2231 sighup_update = FALSE;
2232 (void) rw_unlock(&ldap_lock);
2234 if ((rc = __ns_ldap_getParam(NS_LDAP_PROFILE_P,
2235 &paramVal, &error)) != NS_LDAP_SUCCESS) {
2236 if (error != NULL && error->message != NULL)
2237 logit("Error: Unable to profile name: %s\n",
2238 error->message);
2239 else {
2240 char *tmp;
2242 __ns_ldap_err2str(rc, &tmp);
2243 logit("Error: Unable to profile name: %s\n",
2244 tmp);
2246 (void) __ns_ldap_freeParam(&paramVal);
2247 (void) __ns_ldap_freeError(&error);
2248 return (-1);
2251 if (paramVal && *paramVal)
2252 profile = strdup((char *)*paramVal);
2253 (void) __ns_ldap_freeParam(&paramVal);
2255 if (profile == NULL) {
2256 return (-1);
2259 (void) snprintf(searchfilter, BUFSIZ, _PROFILE_FILTER,
2260 _PROFILE1_OBJECTCLASS, _PROFILE2_OBJECTCLASS, profile);
2262 if ((rc = __ns_ldap_list(_PROFILE_CONTAINER,
2263 (const char *)searchfilter, NULL,
2264 NULL, NULL, 0,
2265 &result, &error, NULL, NULL)) != NS_LDAP_SUCCESS) {
2268 * Is profile name the DEFAULTCONFIGNAME?
2269 * syslog Warning, otherwise syslog error.
2271 if (strcmp(profile, DEFAULTCONFIGNAME) == 0) {
2272 syslog(LOG_WARNING,
2273 "Ignoring attempt to refresh nonexistent "
2274 "default profile: %s.\n",
2275 profile);
2276 logit("Ignoring attempt to refresh nonexistent "
2277 "default profile: %s.\n",
2278 profile);
2279 } else if ((error != NULL) &&
2280 (error->message != NULL)) {
2281 syslog(LOG_ERR,
2282 "Error: Unable to refresh profile:%s:"
2283 " %s\n", profile, error->message);
2284 logit("Error: Unable to refresh profile:"
2285 "%s:%s\n", profile, error->message);
2286 } else {
2287 syslog(LOG_ERR, "Error: Unable to refresh "
2288 "from profile:%s. (error=%d)\n",
2289 profile, rc);
2290 logit("Error: Unable to refresh from profile "
2291 "%s (error=%d)\n", profile, rc);
2294 (void) __ns_ldap_freeError(&error);
2295 (void) __ns_ldap_freeResult(&result);
2296 free(profile);
2297 return (-1);
2299 free(profile);
2302 } while (checkupdate(sighup_update) == TRUE);
2304 (void) rw_wrlock(&ldap_lock);
2306 ptr = __ns_ldap_make_config(result);
2307 (void) __ns_ldap_freeResult(&result);
2309 if (ptr == NULL) {
2310 logit("Error: __ns_ldap_make_config failed.\n");
2311 (void) rw_unlock(&ldap_lock);
2312 return (-1);
2316 * cross check the config parameters
2318 if (__s_api_crosscheck(ptr, errstr, B_TRUE) == NS_SUCCESS) {
2320 * reset the local profile TTL
2322 if (ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc)
2323 current_admin.ldap_stat.ldap_ttl =
2324 atol(ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc);
2326 if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2327 logit("update_from_profile: reset profile TTL to %d"
2328 " seconds\n",
2329 current_admin.ldap_stat.ldap_ttl);
2330 logit("update_from_profile: expire time %ld "
2331 "seconds\n",
2332 ptr->paramList[NS_LDAP_EXP_P].ns_tm);
2335 /* set ptr as current_config if the config is changed */
2336 chg_test_config_change(ptr, change_status);
2337 rc = 0;
2338 } else {
2339 __s_api_destroy_config(ptr);
2340 logit("Error: downloaded profile failed to pass "
2341 "crosscheck (%s).\n", errstr);
2342 syslog(LOG_ERR, "ldap_cachemgr: %s", errstr);
2343 rc = -1;
2345 (void) rw_unlock(&ldap_lock);
2347 return (rc);
2351 getldap_init()
2353 ns_ldap_error_t *error;
2354 struct timeval tp;
2355 ldap_get_chg_cookie_t cookie;
2357 if (current_admin.debug_level >= DBG_ALL) {
2358 logit("getldap_init()...\n");
2361 (void) __ns_ldap_setServer(TRUE);
2363 (void) rw_wrlock(&ldap_lock);
2364 if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
2365 logit("Error: Unable to read '%s': %s\n",
2366 NSCONFIGFILE, error->message);
2367 (void) fprintf(stderr,
2368 gettext("\nError: Unable to read '%s': %s\n"),
2369 NSCONFIGFILE, error->message);
2370 __ns_ldap_freeError(&error);
2371 (void) rw_unlock(&ldap_lock);
2372 return (-1);
2374 (void) rw_unlock(&ldap_lock);
2376 if (gettimeofday(&tp, NULL) == 0) {
2377 /* statistics: previous refresh time */
2378 prev_refresh_time = tp.tv_sec;
2381 /* initialize the data cache */
2382 (void) getldap_cache_op(CACHE_OP_CREATE,
2383 0, NULL, NULL);
2385 cookie.mgr_pid = getpid();
2386 cookie.seq_num = 0;
2387 chg_config_cookie_set(&cookie);
2388 return (0);
2391 static void
2392 perform_update(void)
2394 ns_ldap_error_t *error = NULL;
2395 struct timeval tp;
2396 char buf[20];
2397 int rc, rc1;
2398 int changed = 0;
2399 void **paramVal = NULL;
2400 ns_ldap_self_gssapi_config_t config;
2402 if (current_admin.debug_level >= DBG_ALL) {
2403 logit("perform_update()...\n");
2406 (void) __ns_ldap_setServer(TRUE);
2408 if (gettimeofday(&tp, NULL) != 0)
2409 return;
2411 rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P, &paramVal, &error);
2413 if (rc == NS_LDAP_SUCCESS && paramVal != NULL) {
2414 current_admin.ldap_stat.ldap_ttl = atol((char *)*paramVal);
2417 if (error != NULL)
2418 (void) __ns_ldap_freeError(&error);
2420 if (paramVal != NULL)
2421 (void) __ns_ldap_freeParam(&paramVal);
2423 if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2424 logit("perform_update: current profile TTL is %d seconds\n",
2425 current_admin.ldap_stat.ldap_ttl);
2428 if (current_admin.ldap_stat.ldap_ttl > 0) {
2430 * set the profile TTL parameter, just
2431 * in case that the downloading of
2432 * the profile from server would fail
2436 * NS_LDAP_EXP_P is a no op for __ns_ldap_setParam
2437 * It depends on NS_LDAP_CACHETTL_P to set it's value
2438 * Set NS_LDAP_CACHETTL_P here so NS_LDAP_EXP_P value
2439 * can be set.
2440 * NS_LDAP_CACHETTL_P value can be reset after the profile is
2441 * downloaded from the server, so is NS_LDAP_EXP_P.
2443 buf[19] = '\0'; /* null terminated the buffer */
2444 if (__ns_ldap_setParam(NS_LDAP_CACHETTL_P,
2445 lltostr((long long)current_admin.ldap_stat.ldap_ttl,
2446 &buf[19]),
2447 &error) != NS_LDAP_SUCCESS) {
2448 logit("Error: __ns_ldap_setParam failed, status: %d "
2449 "message: %s\n", error->status, error->message);
2450 (void) __ns_ldap_freeError(&error);
2451 return;
2454 (void) rw_wrlock(&ldap_lock);
2455 sighup_update = FALSE;
2456 (void) rw_unlock(&ldap_lock);
2458 do {
2459 rc = update_from_profile(&changed);
2460 if (rc != 0) {
2461 logit("Error: Unable to update from profile\n");
2463 } while (checkupdate(sighup_update) == TRUE);
2464 } else {
2465 rc = 0;
2469 * recreate the server info list
2471 if (rc == 0) {
2472 (void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
2474 /* flush the data cache */
2475 (void) getldap_cache_op(CACHE_OP_DELETE,
2476 0, NULL, NULL);
2478 /* statistics: previous refresh time */
2479 prev_refresh_time = tp.tv_sec;
2481 rc1 = __ns_ldap_self_gssapi_config(&config);
2482 if (rc1 == NS_LDAP_SUCCESS) {
2483 if (config != NS_LDAP_SELF_GSSAPI_CONFIG_NONE) {
2484 rc1 = __ns_ldap_check_all_preq(0, 0, 0, config, &error);
2485 (void) __ns_ldap_freeError(&error);
2486 if (rc1 != NS_LDAP_SUCCESS) {
2487 logit("Error: Check on self credential "
2488 "prerquesites failed: %d\n",
2489 rc1);
2490 exit(rc1);
2493 } else {
2494 logit("Error: Failed to get self credential configuration %d\n",
2495 rc1);
2496 exit(rc1);
2499 if (!changed)
2500 return;
2502 (void) rw_rdlock(&ldap_lock);
2503 if (((error = __ns_ldap_DumpConfiguration(NSCONFIGREFRESH)) != NULL) ||
2504 ((error = __ns_ldap_DumpConfiguration(NSCREDREFRESH)) != NULL)) {
2505 logit("Error: __ns_ldap_DumpConfiguration failed, "
2506 "status: %d message: %s\n", error->status, error->message);
2507 __ns_ldap_freeError(&error);
2508 (void) rw_unlock(&ldap_lock);
2509 return;
2511 if (rename(NSCONFIGREFRESH, NSCONFIGFILE) != 0) {
2512 logit("Error: unlink failed - errno: %s\n", strerror(errno));
2513 syslog(LOG_ERR, "Unable to refresh profile, LDAP configuration"
2514 "files not written");
2515 (void) rw_unlock(&ldap_lock);
2516 return;
2518 if (rename(NSCREDREFRESH, NSCREDFILE) != 0) {
2520 * We probably have inconsistent configuration at this point.
2521 * If we were to create a backup file and rename it here, that
2522 * operation might also fail. Consequently there is no safe way
2523 * to roll back.
2525 logit("Error: unlink failed - errno: %s\n", strerror(errno));
2526 syslog(LOG_ERR, "Unable to refresh profile consistently, "
2527 "LDAP configuration files inconsistent");
2528 (void) rw_unlock(&ldap_lock);
2529 return;
2532 (void) rw_unlock(&ldap_lock);
2535 void
2536 getldap_refresh()
2538 struct timespec timeout;
2539 int sleeptime;
2540 struct timeval tp;
2541 long expire = 0;
2542 void **paramVal = NULL;
2543 ns_ldap_error_t *errorp;
2544 int always = 1, err;
2545 int first_time = 1;
2546 int sig_done = 0;
2547 int dbg_level;
2549 (void) pthread_setname_np(pthread_self(), "getldap_refresh");
2551 if (current_admin.debug_level >= DBG_ALL) {
2552 logit("getldap_refresh()...\n");
2556 * wait for an available server
2558 while (sig_done == 0) {
2559 (void) mutex_lock(&sig_mutex);
2560 sig_done = signal_done;
2561 (void) mutex_unlock(&sig_mutex);
2564 (void) __ns_ldap_setServer(TRUE);
2565 while (always) {
2566 dbg_level = current_admin.debug_level;
2567 (void) rw_rdlock(&ldap_lock);
2568 sleeptime = current_admin.ldap_stat.ldap_ttl;
2569 if (dbg_level >= DBG_PROFILE_REFRESH) {
2570 logit("getldap_refresh: current profile TTL is %d "
2571 "seconds\n", current_admin.ldap_stat.ldap_ttl);
2573 if (gettimeofday(&tp, NULL) == 0) {
2574 if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
2575 &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
2576 paramVal != NULL &&
2577 (char *)*paramVal != NULL) {
2578 errno = 0;
2579 expire = atol((char *)*paramVal);
2580 (void) __ns_ldap_freeParam(&paramVal);
2581 if (errno == 0) {
2582 if (expire == 0) {
2583 first_time = 0;
2584 (void) rw_unlock(&ldap_lock);
2585 (void) cond_init(&cond,
2586 NULL, NULL);
2587 (void) mutex_lock(&sighuplock);
2588 timeout.tv_sec =
2589 CACHESLEEPTIME;
2590 timeout.tv_nsec = 0;
2591 if (dbg_level >=
2592 DBG_PROFILE_REFRESH) {
2593 logit("getldap_refresh:"
2594 "(1)about to sleep"
2595 " for %d seconds\n",
2596 CACHESLEEPTIME);
2598 err = cond_reltimedwait(&cond,
2599 &sighuplock, &timeout);
2600 (void) cond_destroy(&cond);
2601 (void) mutex_unlock(
2602 &sighuplock);
2604 * if woke up by
2605 * getldap_revalidate(),
2606 * do update right away
2608 if (err == ETIME)
2609 continue;
2610 else {
2612 * if load
2613 * configuration failed
2614 * don't do update
2616 if (load_config())
2617 perform_update
2619 continue;
2622 sleeptime = expire - tp.tv_sec;
2623 if (dbg_level >= DBG_PROFILE_REFRESH) {
2624 logit("getldap_refresh: expire "
2625 "time = %ld\n", expire);
2632 (void) rw_unlock(&ldap_lock);
2635 * if this is the first time downloading
2636 * the profile or expire time already passed,
2637 * do not wait, do update
2639 if (first_time == 0 && sleeptime > 0) {
2640 if (dbg_level >= DBG_PROFILE_REFRESH) {
2641 logit("getldap_refresh: (2)about to sleep "
2642 "for %d seconds\n", sleeptime);
2644 (void) cond_init(&cond, NULL, NULL);
2645 (void) mutex_lock(&sighuplock);
2646 timeout.tv_sec = sleeptime;
2647 timeout.tv_nsec = 0;
2648 err = cond_reltimedwait(&cond,
2649 &sighuplock, &timeout);
2650 (void) cond_destroy(&cond);
2651 (void) mutex_unlock(&sighuplock);
2654 * if load concfiguration failed
2655 * don't do update
2657 if (load_config())
2658 perform_update();
2659 first_time = 0;
2663 void
2664 getldap_revalidate()
2666 if (current_admin.debug_level >= DBG_ALL) {
2667 logit("getldap_revalidate()...\n");
2669 /* block signal SIGHUP */
2670 (void) sighold(SIGHUP);
2672 /* now awake the sleeping refresh thread */
2673 (void) cond_signal(&cond);
2675 /* release signal SIGHUP */
2676 (void) sigrelse(SIGHUP);
2680 void
2681 getldap_admincred(LineBuf *config_info, ldap_call_t *in)
2683 ns_ldap_error_t *error;
2684 ldap_config_out_t *cout;
2685 ucred_t *uc = NULL;
2687 if (current_admin.debug_level >= DBG_ALL) {
2688 logit("getldap_admincred()...\n");
2690 /* check privileges */
2691 if (is_root_or_all_privs("GETADMINCRED", &uc) == 0) {
2692 logit("admin credential requested by a non-root and no ALL "
2693 "privilege user not allowed");
2694 config_info->str = NULL;
2695 config_info->len = 0;
2696 } else {
2697 (void) rw_rdlock(&ldap_lock);
2698 if ((error = __ns_ldap_LoadDoorInfo(config_info,
2699 in->ldap_u.domainname, NULL, 1)) != NULL) {
2700 if (error != NULL && error->message != NULL)
2701 logit("Error: ldap_lookup: %s\n",
2702 error->message);
2703 (void) __ns_ldap_freeError(&error);
2705 config_info->str = NULL;
2706 config_info->len = 0;
2708 /* set change cookie */
2709 cout = (ldap_config_out_t *)config_info->str;
2710 if (cout)
2711 cout->cookie = chg_config_cookie_get();
2712 (void) rw_unlock(&ldap_lock);
2716 void
2717 getldap_lookup(LineBuf *config_info, ldap_call_t *in)
2719 ns_ldap_error_t *error;
2720 ldap_config_out_t *cout;
2722 if (current_admin.debug_level >= DBG_ALL) {
2723 logit("getldap_lookup()...\n");
2725 (void) rw_rdlock(&ldap_lock);
2726 if ((error = __ns_ldap_LoadDoorInfo(config_info,
2727 in->ldap_u.domainname, NULL, 0)) != NULL) {
2728 if (error != NULL && error->message != NULL)
2729 logit("Error: ldap_lookup: %s\n", error->message);
2730 (void) __ns_ldap_freeError(&error);
2732 config_info->str = NULL;
2733 config_info->len = 0;
2735 /* set change cookie */
2736 cout = (ldap_config_out_t *)config_info->str;
2737 if (cout)
2738 cout->cookie = chg_config_cookie_get();
2739 (void) rw_unlock(&ldap_lock);
2742 * It creates the header and data stream to be door returned and notify
2743 * chg_get_statusChange() threads.
2744 * This is called after all getldap_get_rootDSE() threads are joined.
2746 void
2747 test_server_change(server_info_t *head)
2749 server_info_t *info;
2750 int len = 0, num = 0, ds_len = 0, new_len = 0, tlen = 0;
2751 char *tmp_buf = NULL, *ptr = NULL, *status = NULL;
2752 ldap_get_change_out_t *cout;
2754 ds_len = strlen(DOORLINESEP);
2756 for (info = head; info; info = info->next) {
2757 (void) mutex_lock(&info->mutex[0]);
2758 if (info->sinfo[0].change != 0) {
2759 /* "9.9.9.9|NS_SERVER_CHANGE_UP|" */
2760 len += 2 * ds_len + strlen(info->sinfo[0].addr) +
2761 strlen(NS_SERVER_CHANGE_UP);
2762 num++;
2764 (void) mutex_unlock(&info->mutex[0]);
2767 if (len == 0)
2768 return;
2770 len++; /* '\0' */
2772 tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
2773 if ((tmp_buf = malloc(tlen)) == NULL)
2774 return;
2776 cout = (ldap_get_change_out_t *)tmp_buf;
2777 cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
2778 /* cout->cookie is set by chg_notify_statusChange */
2779 cout->server_count = num;
2780 cout->data_size = len;
2782 /* Create IP|UP or DOWN|IP|UP or DOWN| ... */
2783 ptr = cout->data;
2784 new_len = len;
2785 for (info = head; info; info = info->next) {
2786 (void) mutex_lock(&info->mutex[0]);
2787 if (info->sinfo[0].change == 0) {
2788 (void) mutex_unlock(&info->mutex[0]);
2789 continue;
2792 if (info->sinfo[0].change == NS_SERVER_UP)
2793 status = NS_SERVER_CHANGE_UP;
2794 else if (info->sinfo[0].change == NS_SERVER_DOWN)
2795 status = NS_SERVER_CHANGE_DOWN;
2796 else {
2797 syslog(LOG_WARNING, gettext("Bad change value %d"),
2798 info->sinfo[0].change);
2799 (void) mutex_unlock(&info->mutex[0]);
2800 free(tmp_buf);
2801 return;
2804 if ((snprintf(ptr, new_len, "%s%s%s%s",
2805 info->sinfo[0].addr, DOORLINESEP,
2806 status, DOORLINESEP)) >= new_len) {
2807 (void) mutex_unlock(&info->mutex[0]);
2808 break;
2810 new_len -= strlen(ptr);
2811 ptr += strlen(ptr);
2813 (void) mutex_unlock(&info->mutex[0]);
2815 (void) chg_notify_statusChange(tmp_buf);
2818 * It creates the header and data stream to be door returned and notify
2819 * chg_get_statusChange() threads.
2820 * This is called in removing server case.
2822 static void
2823 create_buf_and_notify(char *input, ns_server_status_t st)
2825 rm_svr_t *rms = (rm_svr_t *)input;
2826 char *tmp_buf, *ptr, *status;
2827 int len, tlen;
2828 ldap_get_change_out_t *cout;
2830 /* IP|UP or DOWN| */
2831 len = 2 * strlen(DOORLINESEP) + strlen(rms->addr) +
2832 strlen(NS_SERVER_CHANGE_UP) + 1;
2834 tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
2836 if ((tmp_buf = malloc(tlen)) == NULL)
2837 return;
2839 cout = (ldap_get_change_out_t *)tmp_buf;
2840 cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
2841 /* cout->cookie is set by chg_notify_statusChange */
2842 cout->server_count = 1;
2843 cout->data_size = len;
2845 /* Create IP|DOWN| */
2846 ptr = cout->data;
2847 if (st == NS_SERVER_UP)
2848 status = NS_SERVER_CHANGE_UP;
2849 else if (st == NS_SERVER_DOWN)
2850 status = NS_SERVER_CHANGE_DOWN;
2852 (void) snprintf(ptr, len, "%s%s%s%s",
2853 rms->addr, DOORLINESEP, status, DOORLINESEP);
2855 (void) chg_notify_statusChange(tmp_buf);
2860 * Return: 0 server is down, 1 server is up
2862 static int
2863 contact_server(char *addr)
2865 char *rootDSE = NULL;
2866 ns_ldap_error_t *error = NULL;
2867 int rc;
2869 if (__ns_ldap_getRootDSE(addr, &rootDSE, &error,
2870 SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
2871 if (current_admin.debug_level >= DBG_ALL)
2872 logit("get rootDSE %s failed. %s", addr,
2873 error->message ? error->message : "");
2874 rc = 0;
2875 } else
2876 rc = 1;
2878 if (rootDSE)
2879 free(rootDSE);
2880 if (error)
2881 (void) __ns_ldap_freeError(&error);
2883 return (rc);
2887 * The thread is spawned to do contact_server() so it won't be blocking
2888 * getldap_serverInfo_op(INFO_OP_GETSERVER, ...) case.
2889 * After contact_server() is done, it calls
2890 * getldap_serverInfo_op(INFO_OP_REMOVESERVER, ...) to return to the remaining
2891 * program flow. It's meant to maintain the original program flow yet be
2892 * non-blocking when it's contacting server.
2894 static void *
2895 remove_server_thread(void *arg)
2897 char *addr = (char *)arg, *out = NULL;
2898 int up;
2899 rm_svr_t rms;
2901 (void) pthread_setname_np(pthread_self(), "remove_server");
2903 up = contact_server(addr);
2905 rms.addr = addr;
2906 rms.up = up;
2908 (void) getldap_serverInfo_op(INFO_OP_REMOVESERVER, (char *)&rms, &out);
2910 free(addr);
2912 thr_exit(NULL);
2913 return (NULL);
2916 * addr is allocated and is freed by remove_server_thread
2917 * It starts a thread to contact server and remove server to avoid long wait
2918 * or recursion.
2920 static void
2921 remove_server(char *addr)
2923 if (thr_create(NULL, 0, remove_server_thread,
2924 (void *)addr, THR_BOUND|THR_DETACHED, NULL) != 0) {
2925 free(addr);
2926 syslog(LOG_ERR, "thr_create failed for remove_server_thread");
2930 * Compare the server_status and mark it up or down accordingly.
2931 * This is called in removing server case.
2933 static ns_server_status_t
2934 set_server_status(char *input, server_info_t *head)
2936 rm_svr_t *rms = (rm_svr_t *)input;
2937 ns_server_status_t changed = 0;
2938 server_info_t *info;
2940 for (info = head; info != NULL; info = info->next) {
2941 (void) mutex_lock(&info->mutex[0]);
2942 if (strcmp(info->sinfo[0].addr, rms->addr) == 0) {
2943 if (info->sinfo[0].server_status == INFO_SERVER_UP &&
2944 rms->up == FALSE) {
2945 info->sinfo[0].prev_server_status =
2946 info->sinfo[0].server_status;
2947 info->sinfo[0].server_status =
2948 INFO_SERVER_ERROR;
2949 info->sinfo[0].change = NS_SERVER_DOWN;
2950 changed = NS_SERVER_DOWN;
2952 } else if (info->sinfo[0].server_status ==
2953 INFO_SERVER_ERROR && rms->up == TRUE) {
2955 * It should be INFO_SERVER_UP, but check here
2957 info->sinfo[0].prev_server_status =
2958 info->sinfo[0].server_status;
2959 info->sinfo[0].server_status =
2960 INFO_SERVER_UP;
2961 info->sinfo[0].change = NS_SERVER_UP;
2962 changed = NS_SERVER_UP;
2964 (void) mutex_unlock(&info->mutex[0]);
2965 break;
2967 (void) mutex_unlock(&info->mutex[0]);
2969 if (changed) {
2970 /* ldap_cachemgr -g option looks up [1] */
2971 (void) mutex_lock(&info->mutex[1]);
2972 info->sinfo[1].prev_server_status =
2973 info->sinfo[1].server_status;
2974 if (changed == NS_SERVER_DOWN)
2975 info->sinfo[1].server_status = INFO_SERVER_ERROR;
2976 else if (changed == NS_SERVER_UP)
2977 info->sinfo[1].server_status = INFO_SERVER_UP;
2978 (void) mutex_unlock(&info->mutex[1]);
2980 return (changed);