cachemgr_getldap.c revision 82c6b8c665c7ab56878da3140ddcb452dbc040b4
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <errno.h>
#include <memory.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libintl.h>
#include <syslog.h>
#include <sys/door.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <synch.h>
#include <pthread.h>
#include <unistd.h>
#include <lber.h>
#include <ldap.h>
#include <ctype.h> /* tolower */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "cachemgr.h"
#include "solaris-priv.h"
static rwlock_t ldap_lock = DEFAULTRWLOCK;
static int sighup_update = FALSE;
extern admin_t current_admin;
/* variables used for SIGHUP wakeup on sleep */
static mutex_t sighuplock;
static cond_t cond;
/* refresh time statistics */
static time_t prev_refresh_time = 0;
/* variables used for signaling parent process */
static mutex_t sig_mutex;
static int signal_done = FALSE;
/* TCP connection timeout (in milliseconds) */
static int tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
/* search timeout (in seconds) */
static int search_timeout = NS_DEFAULT_SEARCH_TIMEOUT;
#ifdef SLP
extern int use_slp;
#endif /* SLP */
/* nis domain information */
#define _NIS_FILTER "objectclass=nisDomainObject"
#define _NIS_DOMAIN "nisdomain"
#define CACHESLEEPTIME 600
/*
* server list refresh delay when in "no server" mode
* (1 second)
*/
#define REFRESH_DELAY_WHEN_NO_SERVER 1
typedef enum {
INFO_OP_CREATE = 0,
INFO_OP_DELETE = 1,
INFO_OP_REFRESH = 2,
INFO_OP_REFRESH_WAIT = 3,
INFO_OP_GETSERVER = 4,
INFO_OP_GETSTAT = 5
} info_op_t;
typedef enum {
INFO_RW_UNKNOWN = 0,
INFO_RW_READONLY = 1,
INFO_RW_WRITEABLE = 2
} info_rw_t;
typedef enum {
INFO_SERVER_JUST_INITED = -1,
INFO_SERVER_UNKNOWN = 0,
INFO_SERVER_CONNECTING = 1,
INFO_SERVER_UP = 2,
INFO_SERVER_ERROR = 3,
INFO_SERVER_REMOVED = 4
} info_server_t;
typedef enum {
INFO_STATUS_UNKNOWN = 0,
INFO_STATUS_ERROR = 1,
INFO_STATUS_NEW = 2,
INFO_STATUS_OLD = 3
} info_status_t;
typedef enum {
CACHE_OP_CREATE = 0,
CACHE_OP_DELETE = 1,
CACHE_OP_FIND = 2,
CACHE_OP_ADD = 3,
CACHE_OP_GETSTAT = 4
} cache_op_t;
typedef enum {
CACHE_MAP_UNKNOWN = 0,
CACHE_MAP_DN2DOMAIN = 1
} cache_type_t;
typedef struct server_info_ext {
char *addr;
char *hostname;
char *rootDSE_data;
char *errormsg;
info_rw_t type;
info_server_t server_status;
info_server_t prev_server_status;
info_status_t info_status;
} server_info_ext_t;
typedef struct server_info {
struct server_info *next;
mutex_t mutex[2]; /* 0: current copy lock */
/* 1: update copy lock */
server_info_ext_t sinfo[2]; /* 0: current, 1: update copy */
} server_info_t;
typedef struct cache_hash {
cache_type_t type;
char *from;
char *to;
struct cache_hash *next;
} cache_hash_t;
static int getldap_destroy_serverInfo(server_info_t *head);
/*
* Load configuration
* The code was in signal handler getldap_revalidate
* It's moved out of the handler because it could cause deadlock
* return: 1 SUCCESS
* 0 FAIL
*/
static int
load_config() {
ns_ldap_error_t *error;
int rc = 1;
(void) __ns_ldap_setServer(TRUE);
(void) rw_wrlock(&ldap_lock);
if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
logit("Error: Unable to read '%s': %s\n",
NSCONFIGFILE, error->message);
__ns_ldap_freeError(&error);
rc = 0; /* FAIL */
} else
sighup_update = TRUE;
(void) rw_unlock(&ldap_lock);
return (rc);
}
/*
* Calculate a hash for a string
* Based on elf_hash algorithm, hash is case insensitive
* Uses tolower instead of _tolower because of I18N
*/
static unsigned long
getldap_hash(const char *str)
{
unsigned int hval = 0;
while (*str) {
unsigned int g;
hval = (hval << 4) + tolower(*str++);
if ((g = (hval & 0xf0000000)) != 0)
hval ^= g >> 24;
hval &= ~g;
}
return ((unsigned long)hval);
}
/*
* Remove a hash table entry.
* This function expects a lock in place when called.
*/
static cache_hash_t *
getldap_free_hash(cache_hash_t *p)
{
cache_hash_t *next;
p->type = CACHE_MAP_UNKNOWN;
if (p->from)
free(p->from);
if (p->to)
free(p->to);
next = p->next;
p->next = NULL;
free(p);
return (next);
}
/*
* Scan a hash table hit for a matching hash entry.
* This function expects a lock in place when called.
*/
static cache_hash_t *
getldap_scan_hash(cache_type_t type, char *from,
cache_hash_t *idx)
{
while (idx) {
if (idx->type == type &&
strcasecmp(from, idx->from) == 0) {
return (idx);
}
idx = idx->next;
}
return ((cache_hash_t *)NULL);
}
/*
* Format and return the cache data statistics
*/
static int
getldap_get_cacheData_stat(int max, int current, char **output)
{
#define C_HEADER0 "Cache data information: "
#define C_HEADER1 " Maximum cache entries: "
#define C_HEADER2 " Number of cache entries: "
int hdr0_len = strlen(gettext(C_HEADER0));
int hdr1_len = strlen(gettext(C_HEADER1));
int hdr2_len = strlen(gettext(C_HEADER2));
int len;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_cacheData_stat()...\n");
}
*output = NULL;
len = hdr0_len + hdr1_len + hdr2_len +
3 * strlen(DOORLINESEP) + 21;
*output = malloc(len);
if (*output == NULL)
return (-1);
(void) snprintf(*output, len, "%s%s%s%10d%s%s%10d%s",
gettext(C_HEADER0), DOORLINESEP,
gettext(C_HEADER1), max, DOORLINESEP,
gettext(C_HEADER2), current, DOORLINESEP);
return (NS_LDAP_SUCCESS);
}
static int
getldap_cache_op(cache_op_t op, cache_type_t type,
char *from, char **to)
{
#define CACHE_HASH_MAX 257
#define CACHE_HASH_MAX_ENTRY 256
static cache_hash_t *hashTbl[CACHE_HASH_MAX];
cache_hash_t *next, *idx, *newp;
unsigned long hash;
static rwlock_t cache_lock = DEFAULTRWLOCK;
int i;
static int entry_num = 0;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_cache_op()...\n");
}
switch (op) {
case CACHE_OP_CREATE:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is CACHE_OP_CREATE...\n");
}
(void) rw_wrlock(&cache_lock);
for (i = 0; i < CACHE_HASH_MAX; i++) {
hashTbl[i] = NULL;
}
entry_num = 0;
(void) rw_unlock(&cache_lock);
break;
case CACHE_OP_DELETE:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is CACHE_OP_DELETE...\n");
}
(void) rw_wrlock(&cache_lock);
for (i = 0; i < CACHE_HASH_MAX; i++) {
next = hashTbl[i];
while (next != NULL) {
next = getldap_free_hash(next);
}
hashTbl[i] = NULL;
}
entry_num = 0;
(void) rw_unlock(&cache_lock);
break;
case CACHE_OP_ADD:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is CACHE_OP_ADD...\n");
}
if (from == NULL || to == NULL || *to == NULL)
return (-1);
hash = getldap_hash(from) % CACHE_HASH_MAX;
(void) rw_wrlock(&cache_lock);
idx = hashTbl[hash];
/*
* replace old "to" value with new one
* if an entry with same "from"
* already exists
*/
if (idx) {
newp = getldap_scan_hash(type, from, idx);
if (newp) {
free(newp->to);
newp->to = strdup(*to);
(void) rw_unlock(&cache_lock);
return (NS_LDAP_SUCCESS);
}
}
if (entry_num > CACHE_HASH_MAX_ENTRY) {
(void) rw_unlock(&cache_lock);
return (-1);
}
newp = (cache_hash_t *)malloc(sizeof (cache_hash_t));
if (newp == NULL) {
(void) rw_unlock(&cache_lock);
return (NS_LDAP_MEMORY);
}
newp->type = type;
newp->from = strdup(from);
newp->to = strdup(*to);
newp->next = idx;
hashTbl[hash] = newp;
entry_num++;
(void) rw_unlock(&cache_lock);
break;
case CACHE_OP_FIND:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is CACHE_OP_FIND...\n");
}
if (from == NULL || to == NULL)
return (-1);
*to = NULL;
hash = getldap_hash(from) % CACHE_HASH_MAX;
(void) rw_rdlock(&cache_lock);
idx = hashTbl[hash];
idx = getldap_scan_hash(type, from, idx);
if (idx)
*to = strdup(idx->to);
(void) rw_unlock(&cache_lock);
if (idx == NULL)
return (-1);
break;
case CACHE_OP_GETSTAT:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is CACHE_OP_GETSTAT...\n");
}
if (to == NULL)
return (-1);
return (getldap_get_cacheData_stat(CACHE_HASH_MAX_ENTRY,
entry_num, to));
break;
default:
logit("getldap_cache_op(): "
"invalid operation code (%d).\n", op);
return (-1);
break;
}
return (NS_LDAP_SUCCESS);
}
/*
* Function: sync_current_with_update_copy
*
* This function syncs up the 2 sinfo copies in info.
*
* The 2 copies are identical most of time.
* The update copy(sinfo[1]) could be different when
* getldap_serverInfo_refresh thread is refreshing the server list
* and calls getldap_get_rootDSE to update info. getldap_get_rootDSE
* calls sync_current_with_update_copy to sync up 2 copies before thr_exit.
* The calling sequence is
* getldap_serverInfo_refresh->
* getldap_get_serverInfo_op(INFO_OP_CREATE,...)->
* getldap_set_serverInfo->
* getldap_get_rootDSE
*
* The original server_info_t has one copy of server info. When libsldap
* makes door call GETLDAPSERVER to get the server info and getldap_get_rootDSE
* is updating the server info, it would hit a unprotected window in
* getldap_rootDSE. The door call will not get server info and libsldap
* fails at making ldap connection.
*
* The new server_info_t provides GETLDAPSERVER thread with a current
* copy(sinfo[0]). getldap_get_rootDSE only works on the update copy(sinfo[1])
* and syncs up 2 copies before thr_exit. This will close the window in
* getldap_get_rootDSE.
*
*/
static void
sync_current_with_update_copy(server_info_t *info)
{
if (current_admin.debug_level >= DBG_ALL) {
logit("sync_current_with_update_copy()...\n");
}
(void) mutex_lock(&info->mutex[1]);
(void) mutex_lock(&info->mutex[0]);
/* free memory in current copy first */
if (info->sinfo[0].addr)
free(info->sinfo[0].addr);
info->sinfo[0].addr = NULL;
if (info->sinfo[0].hostname)
free(info->sinfo[0].hostname);
info->sinfo[0].hostname = NULL;
if (info->sinfo[0].rootDSE_data)
free(info->sinfo[0].rootDSE_data);
info->sinfo[0].rootDSE_data = NULL;
if (info->sinfo[0].errormsg)
free(info->sinfo[0].errormsg);
info->sinfo[0].errormsg = NULL;
/*
* make current and update copy identical
*/
info->sinfo[0] = info->sinfo[1];
/*
* getldap_get_server_stat() reads the update copy sinfo[1]
* so it can't be freed or nullified yet at this point.
*
* The sinfo[0] and sinfo[1] have identical string pointers.
* strdup the strings to avoid the double free problem.
* The strings of sinfo[1] are freed in
* getldap_get_rootDSE() and the strings of sinfo[0]
* are freed earlier in this function. If the pointers are the
* same, they will be freed twice.
*/
if (info->sinfo[1].addr)
info->sinfo[0].addr = strdup(info->sinfo[1].addr);
if (info->sinfo[1].hostname)
info->sinfo[0].hostname = strdup(info->sinfo[1].hostname);
if (info->sinfo[1].rootDSE_data)
info->sinfo[0].rootDSE_data =
strdup(info->sinfo[1].rootDSE_data);
if (info->sinfo[1].errormsg)
info->sinfo[0].errormsg = strdup(info->sinfo[1].errormsg);
(void) mutex_unlock(&info->mutex[0]);
(void) mutex_unlock(&info->mutex[1]);
}
static void *
getldap_get_rootDSE(void *arg)
{
server_info_t *serverInfo = (server_info_t *)arg;
int ldapVersion = LDAP_VERSION3;
LDAP *ld;
LDAPMessage *resultMsg = NULL;
LDAPMessage *e;
BerElement *ber;
char errmsg[MAXERROR];
char *rootDSE;
char *attrs[3];
char *a;
char **vals;
int ldaperrno = 0;
int rc = 0, exitrc = NS_LDAP_SUCCESS;
int i = 0, len = 0;
pid_t ppid;
struct timeval tv;
int server_found = 0;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_rootDSE()....\n");
}
/* initialize the server info element */
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].type = INFO_RW_UNKNOWN;
serverInfo->sinfo[1].info_status =
INFO_STATUS_UNKNOWN;
/*
* When the sever list is refreshed over and over,
* this function is called each time it is refreshed.
* The previous server status of the update copy(sinfo[1])
* is the status of the current copy
*/
(void) mutex_lock(&serverInfo->mutex[0]);
serverInfo->sinfo[1].prev_server_status =
serverInfo->sinfo[0].server_status;
(void) mutex_unlock(&serverInfo->mutex[0]);
serverInfo->sinfo[1].server_status =
INFO_SERVER_UNKNOWN;
if (serverInfo->sinfo[1].rootDSE_data)
free(serverInfo->sinfo[1].rootDSE_data);
serverInfo->sinfo[1].rootDSE_data = NULL;
if (serverInfo->sinfo[1].errormsg)
free(serverInfo->sinfo[1].errormsg);
serverInfo->sinfo[1].errormsg = NULL;
(void) mutex_unlock(&serverInfo->mutex[1]);
if ((ld = ldap_init(serverInfo->sinfo[1].addr,
LDAP_PORT)) == NULL ||
/* SKIP ldap data base to prevent recursion */
/* in gethostbyname when resolving hostname */
0 != ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB, "ldap")) {
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].server_status =
INFO_SERVER_ERROR;
serverInfo->sinfo[1].info_status =
INFO_STATUS_ERROR;
serverInfo->sinfo[1].errormsg =
strdup(gettext("ldap_init failed"));
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_rootDSE: %s.\n",
serverInfo->sinfo[1].errormsg);
}
(void) mutex_unlock(&serverInfo->mutex[1]);
/*
* sync sinfo copies in the serverInfo.
* protected by mutex
*/
sync_current_with_update_copy(serverInfo);
thr_exit((void *) -1);
}
ldap_set_option(ld,
LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
ldap_set_option(ld,
LDAP_X_OPT_CONNECT_TIMEOUT, &tcptimeout);
/* currently, only interested in two attributes */
attrs[0] = "supportedControl";
attrs[1] = "supportedsaslmechanisms";
attrs[2] = NULL;
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].server_status = INFO_SERVER_CONNECTING;
(void) mutex_unlock(&serverInfo->mutex[1]);
tv.tv_sec = search_timeout;
tv.tv_usec = 0;
rc = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE,
"(objectclass=*)",
attrs, 0, NULL, NULL, &tv, 0, &resultMsg);
switch (rc) {
/* If successful, the root DSE was found. */
case LDAP_SUCCESS:
break;
/*
* If the root DSE was not found, the server does
* not comply with the LDAP v3 protocol.
*/
default:
ldap_get_option(ld,
LDAP_OPT_ERROR_NUMBER, &ldaperrno);
(void) snprintf(errmsg, sizeof (errmsg),
gettext(ldap_err2string(ldaperrno)));
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_rootDSE: Root DSE not found."
" %s is not an LDAPv3 server (%s).\n",
serverInfo->sinfo[1].addr, errmsg);
}
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].errormsg
= strdup(errmsg);
serverInfo->sinfo[1].info_status
= INFO_STATUS_ERROR;
serverInfo->sinfo[1].server_status
= INFO_SERVER_ERROR;
(void) mutex_unlock(&serverInfo->mutex[1]);
if (resultMsg)
ldap_msgfree(resultMsg);
ldap_unbind(ld);
/*
* sync sinfo copies in the serverInfo.
* protected by mutex
*/
sync_current_with_update_copy(serverInfo);
thr_exit((void *) -1);
break;
}
if ((e = ldap_first_entry(ld, resultMsg)) != NULL) {
/* calculate length of root DSE data */
for (a = ldap_first_attribute(ld, e, &ber);
a != NULL;
a = ldap_next_attribute(ld, e, ber)) {
if ((vals = ldap_get_values(ld, e, a)) != NULL) {
for (i = 0; vals[i] != NULL; i++) {
len += strlen(a) +
strlen(vals[i]) +
strlen(DOORLINESEP) +1;
}
ldap_value_free(vals);
}
ldap_memfree(a);
}
if (ber != NULL)
ber_free(ber, 0);
/* copy root DSE data */
if (len) {
/* add 1 for the last '\0' */
rootDSE = (char *)malloc(len + 1);
if (rootDSE != NULL) {
/* make it an empty string first */
*rootDSE = '\0';
for (a = ldap_first_attribute(ld, e, &ber);
a != NULL;
a = ldap_next_attribute(
ld, e, ber)) {
if ((vals = ldap_get_values(
ld, e, a)) != NULL) {
for (i = 0; vals[i] != NULL;
i++) {
int len;
len = strlen(a) +
strlen(vals[i]) +
strlen(DOORLINESEP)
+ 2;
(void) snprintf(
rootDSE +
strlen(rootDSE),
len, "%s=%s%s",
a, vals[i],
DOORLINESEP);
}
ldap_value_free(vals);
}
ldap_memfree(a);
}
if (ber != NULL)
ber_free(ber, 0);
} else
len = 0;
}
}
/* error, if no root DSE data */
(void) mutex_lock(&serverInfo->mutex[1]);
if (len == 0) {
serverInfo->sinfo[1].errormsg =
strdup(gettext("No root DSE data returned."));
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_rootDSE: %s.\n",
serverInfo->sinfo[1].errormsg);
}
serverInfo->sinfo[1].type
= INFO_RW_UNKNOWN;
serverInfo->sinfo[1].info_status
= INFO_STATUS_ERROR;
serverInfo->sinfo[1].server_status = INFO_SERVER_ERROR;
exitrc = -1;
} else {
/* assume writeable, i.e., can do modify */
serverInfo->sinfo[1].type = INFO_RW_WRITEABLE;
serverInfo->sinfo[1].server_status
= INFO_SERVER_UP;
serverInfo->sinfo[1].info_status = INFO_STATUS_NEW;
/* remove the last DOORLINESEP */
*(rootDSE+strlen(rootDSE)-1) = '\0';
serverInfo->sinfo[1].rootDSE_data = rootDSE;
server_found = 1;
exitrc = NS_LDAP_SUCCESS;
}
(void) mutex_unlock(&serverInfo->mutex[1]);
if (resultMsg)
ldap_msgfree(resultMsg);
ldap_unbind(ld);
/*
* sync sinfo copies in the serverInfo.
* protected by mutex
*/
sync_current_with_update_copy(serverInfo);
/*
* signal that the ldap_cachemgr parent process
* should exit now, if it is still waiting
*/
(void) mutex_lock(&sig_mutex);
if (signal_done == FALSE && server_found) {
ppid = getppid();
(void) kill(ppid, SIGUSR1);
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_rootDSE(): "
"SIGUSR1 signal sent to "
"parent process(%ld).\n", ppid);
}
signal_done = TRUE;
}
(void) mutex_unlock(&sig_mutex);
thr_exit((void *) exitrc);
return ((void *) NULL);
}
static int
getldap_init_serverInfo(server_info_t **head)
{
char **servers = NULL;
int rc = 0, i, exitrc = NS_LDAP_SUCCESS;
ns_ldap_error_t *errorp = NULL;
server_info_t *info, *tail = NULL;
*head = NULL;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_init_serverInfo()...\n");
}
rc = __s_api_getServers(&servers, &errorp);
if (rc != NS_LDAP_SUCCESS) {
logit("getldap_init_serverInfo: "
"__s_api_getServers failed.\n");
if (errorp)
__ns_ldap_freeError(&errorp);
return (-1);
}
for (i = 0; servers[i] != NULL; i++) {
info = (server_info_t *)calloc(1, sizeof (server_info_t));
if (info == NULL) {
logit("getldap_init_serverInfo: "
"not enough memory.\n");
exitrc = NS_LDAP_MEMORY;
break;
}
if (i == 0) {
*head = info;
tail = info;
} else {
tail->next = info;
tail = info;
}
info->sinfo[0].addr = strdup(servers[i]);
if (info->sinfo[0].addr == NULL) {
logit("getldap_init_serverInfo: "
"not enough memory.\n");
exitrc = NS_LDAP_MEMORY;
break;
}
info->sinfo[1].addr = strdup(servers[i]);
if (info->sinfo[1].addr == NULL) {
logit("getldap_init_serverInfo: "
"not enough memory.\n");
exitrc = NS_LDAP_MEMORY;
break;
}
info->sinfo[0].type = INFO_RW_UNKNOWN;
info->sinfo[1].type = INFO_RW_UNKNOWN;
info->sinfo[0].info_status = INFO_STATUS_UNKNOWN;
info->sinfo[1].info_status = INFO_STATUS_UNKNOWN;
info->sinfo[0].server_status = INFO_SERVER_UNKNOWN;
info->sinfo[1].server_status = INFO_SERVER_UNKNOWN;
/*
* Assume at startup or after the configuration
* profile is refreshed, all servers are good.
*/
info->sinfo[0].prev_server_status =
INFO_SERVER_UP;
info->sinfo[1].prev_server_status =
INFO_SERVER_UP;
info->sinfo[0].hostname = NULL;
info->sinfo[1].hostname = NULL;
info->sinfo[0].rootDSE_data = NULL;
info->sinfo[1].rootDSE_data = NULL;
info->sinfo[0].errormsg = NULL;
info->sinfo[1].errormsg = NULL;
info->next = NULL;
}
__s_api_free2dArray(servers);
if (exitrc != NS_LDAP_SUCCESS) {
if (head && *head) {
(void) getldap_destroy_serverInfo(*head);
*head = NULL;
}
}
return (exitrc);
}
static int
getldap_destroy_serverInfo(server_info_t *head)
{
server_info_t *info, *next;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_destroy_serverInfo()...\n");
}
if (head == NULL) {
logit("getldap_destroy_serverInfo: "
"invalid serverInfo list.\n");
return (-1);
}
for (info = head; info; info = next) {
if (info->sinfo[0].addr)
free(info->sinfo[0].addr);
if (info->sinfo[1].addr)
free(info->sinfo[1].addr);
if (info->sinfo[0].hostname)
free(info->sinfo[0].hostname);
if (info->sinfo[1].hostname)
free(info->sinfo[1].hostname);
if (info->sinfo[0].rootDSE_data)
free(info->sinfo[0].rootDSE_data);
if (info->sinfo[1].rootDSE_data)
free(info->sinfo[1].rootDSE_data);
if (info->sinfo[0].errormsg)
free(info->sinfo[0].errormsg);
if (info->sinfo[1].errormsg)
free(info->sinfo[1].errormsg);
next = info->next;
free(info);
}
return (NS_LDAP_SUCCESS);
}
static int
getldap_set_serverInfo(server_info_t *head,
int reset_bindtime)
{
server_info_t *info;
int atleast1 = 0;
thread_t *tid;
int num_threads = 0, i, j;
void *status;
void **paramVal = NULL;
ns_ldap_error_t *error = NULL;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_set_serverInfo()...\n");
}
if (head == NULL) {
logit("getldap_set_serverInfo: "
"invalid serverInfo list.\n");
return (-1);
}
/* Get the bind timeout value */
if (reset_bindtime == 1) {
tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P,
&paramVal, &error);
if (paramVal != NULL && *paramVal != NULL) {
/* convert to milliseconds */
tcptimeout = **((int **)paramVal);
tcptimeout *= 1000;
(void) __ns_ldap_freeParam(&paramVal);
}
if (error)
(void) __ns_ldap_freeError(&error);
/* get search timeout value */
search_timeout = NS_DEFAULT_SEARCH_TIMEOUT;
(void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P,
&paramVal, &error);
if (paramVal != NULL && *paramVal != NULL) {
search_timeout = **((int **)paramVal);
(void) __ns_ldap_freeParam(&paramVal);
}
if (error)
(void) __ns_ldap_freeError(&error);
}
for (info = head; info; info = info->next)
num_threads++;
if (num_threads == 0) {
logit("getldap_set_serverInfo: "
"empty serverInfo list.\n");
return (-1);
}
tid = (thread_t *) calloc(1, sizeof (thread_t) * num_threads);
if (tid == NULL) {
logit("getldap_set_serverInfo: "
"No memory to create thread ID list.\n");
return (-1);
}
for (info = head, i = 0; info; info = info->next, i++) {
if (thr_create(NULL, 0,
(void *(*)(void*))getldap_get_rootDSE,
(void *)info, 0, &tid[i])) {
logit("getldap_set_serverInfo: "
"can not create thread %d.\n", i + 1);
for (j = 0; j < i; j++)
(void) thr_join(tid[j], NULL, NULL);
free(tid);
return (-1);
}
}
for (i = 0; i < num_threads; i++) {
if (thr_join(tid[i], NULL, &status) == 0) {
if ((int)status == NS_LDAP_SUCCESS)
atleast1 = 1;
}
}
free(tid);
if (atleast1)
return (NS_LDAP_SUCCESS);
else
return (-1);
}
/*
* Convert an IP to a host name
*/
static int
getldap_ip2hostname(char *ipaddr, char **hostname) {
struct in_addr in;
struct in6_addr in6;
struct hostent *hp = NULL;
char *start = NULL, *end = NULL, delim = '\0';
char *port = NULL, *addr = NULL;
int error_num = 0, len = 0;
if (ipaddr == NULL || hostname == NULL)
return (NS_LDAP_INVALID_PARAM);
*hostname = NULL;
if ((addr = strdup(ipaddr)) == NULL)
return (NS_LDAP_MEMORY);
if (addr[0] == '[') {
/*
* Assume it's [ipv6]:port
* Extract ipv6 IP
*/
start = &addr[1];
if ((end = strchr(addr, ']')) != NULL) {
*end = '\0';
delim = ']';
if (*(end + 1) == ':')
/* extract port */
port = end + 2;
} else {
return (NS_LDAP_INVALID_PARAM);
}
} else if ((end = strchr(addr, ':')) != NULL) {
/* assume it's ipv4:port */
*end = '\0';
delim = ':';
start = addr;
port = end + 1;
} else
/* No port */
start = addr;
if (inet_pton(AF_INET, start, &in) == 1) {
/* IPv4 */
hp = getipnodebyaddr((char *)&in,
sizeof (struct in_addr), AF_INET, &error_num);
if (hp && hp->h_name) {
/* hostname + '\0' */
len = strlen(hp->h_name) + 1;
if (port)
/* ':' + port */
len += strlen(port) + 1;
if ((*hostname = malloc(len)) == NULL) {
free(addr);
freehostent(hp);
return (NS_LDAP_MEMORY);
}
if (port)
(void) snprintf(*hostname, len, "%s:%s",
hp->h_name, port);
else
(void) strlcpy(*hostname, hp->h_name, len);
free(addr);
freehostent(hp);
return (NS_LDAP_SUCCESS);
} else {
return (NS_LDAP_NOTFOUND);
}
} else if (inet_pton(AF_INET6, start, &in6) == 1) {
/* IPv6 */
hp = getipnodebyaddr((char *)&in6,
sizeof (struct in6_addr), AF_INET6, &error_num);
if (hp && hp->h_name) {
/* hostname + '\0' */
len = strlen(hp->h_name) + 1;
if (port)
/* ':' + port */
len += strlen(port) + 1;
if ((*hostname = malloc(len)) == NULL) {
free(addr);
freehostent(hp);
return (NS_LDAP_MEMORY);
}
if (port)
(void) snprintf(*hostname, len, "%s:%s",
hp->h_name, port);
else
(void) strlcpy(*hostname, hp->h_name, len);
free(addr);
freehostent(hp);
return (NS_LDAP_SUCCESS);
} else {
return (NS_LDAP_NOTFOUND);
}
} else {
/*
* A hostname
* Return it as is
*/
if (end)
*end = delim;
*hostname = addr;
return (NS_LDAP_SUCCESS);
}
}
/*
* getldap_get_serverInfo processes the GETLDAPSERVER door request passed
* to this function from getldap_serverInfo_op().
* input:
* a buffer containing an empty string (e.g., input[0]='\0';) or a string
* as the "input" in printf(input, "%s%s%s%s", req, addrtype, DOORLINESEP,
* addr);
* where addr is the address of a server and
* req is one of the following:
* NS_CACHE_NEW: send a new server address, addr is ignored.
* NS_CACHE_NORESP: send the next one, remove addr from list.
* NS_CACHE_NEXT: send the next one, keep addr on list.
* NS_CACHE_WRITE: send a non-replica server, if possible, if not, same
* as NS_CACHE_NEXT.
* addrtype:
* NS_CACHE_ADDR_IP: return server address as is, this is default.
* NS_CACHE_ADDR_HOSTNAME: return both server address and its FQDN format,
* only self credential case requires such format.
* output:
* a buffer containing server info in the following format:
* serveraddress DOORLINESEP [ serveraddress FQDN DOORLINESEP ]
* [ attr=value [DOORLINESEP attr=value ]...]
* For example: ( here | used as DOORLINESEP for visual purposes)
* 1) simple bind and sasl/DIGEST-MD5 bind :
* 1.2.3.4|supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL|
* supportedSASLmechanisms=GSSAPI
* 2) sasl/GSSAPI bind (self credential):
* 1.2.3.4|foo.sun.com|supportedControl=1.1.1.1|
* supportedSASLmechanisms=EXTERNAL|supportedSASLmechanisms=GSSAPI
* NOTE: caller should free this buffer when done using it
*/
static int
getldap_get_serverInfo(server_info_t *head, char *input,
char **output, int *svr_removed)
{
server_info_t *info = NULL;
server_info_t *server = NULL;
char *addr = NULL;
char *req = NULL;
char req_new[] = NS_CACHE_NEW;
char addr_type[] = NS_CACHE_ADDR_IP;
int matched = FALSE, len, rc = 0;
char *ret_addr = NULL, *ret_addrFQDN = NULL;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_serverInfo()...\n");
}
if (input == NULL || output == NULL) {
logit("getldap_get_serverInfo: "
"No input or output buffer.\n");
return (-1);
}
*output = NULL;
*svr_removed = FALSE;
if (head == NULL) {
logit("getldap_get_serverInfo: "
"invalid serverInfo list.\n");
return (-1);
}
/*
* parse the input string to get req and addr,
* if input is empty, i.e., input[0] == '\0',
* treat it as an NS_CACHE_NEW request
*/
req = req_new;
if (input[0] != '\0') {
req = input;
/* Save addr type flag */
addr_type[0] = input[1];
input[strlen(NS_CACHE_NEW)] = '\0';
/* skip acion type flag, addr type flag and DOORLINESEP */
addr = input + strlen(DOORLINESEP) + strlen(NS_CACHE_NEW)
+ strlen(NS_CACHE_ADDR_IP);
}
/*
* if NS_CACHE_NEW,
* or the server info is new,
* starts from the
* beginning of the list
*/
if ((strcmp(req, NS_CACHE_NEW) == 0) ||
(head->sinfo[0].info_status == INFO_STATUS_NEW))
matched = TRUE;
for (info = head; info; info = info->next) {
/*
* make sure the server info stays the same
* while the data is being processed
*/
/*
* This function is called to get server info list
* and pass it back to door call clients.
* Access the current copy (sinfo[0]) to get such
* information
*/
(void) mutex_lock(&info->mutex[0]);
if (matched == FALSE &&
strcmp(info->sinfo[0].addr, addr) == 0) {
matched = TRUE;
if (strcmp(req, NS_CACHE_NORESP) == 0) {
/*
* if the server has already been removed,
* don't bother
*/
if (info->sinfo[0].server_status ==
INFO_SERVER_REMOVED) {
(void) mutex_unlock(&info->mutex[0]);
continue;
}
/*
* if the information is new,
* give this server one more chance
*/
if (info->sinfo[0].info_status ==
INFO_STATUS_NEW &&
info->sinfo[0].server_status ==
INFO_SERVER_UP) {
server = info;
break;
} else {
/*
* it is recommended that
* before removing the
* server from the list,
* the server should be
* contacted one more time
* to make sure that it is
* really unavailable.
* For now, just trust the client
* (i.e., the sldap library)
* that it knows what it is
* doing and would not try
* to mess up the server
* list.
*/
info->sinfo[0].prev_server_status =
info->sinfo[0].server_status;
info->sinfo[0].server_status =
INFO_SERVER_REMOVED;
/*
* make sure this will be seen
* if a user query the server
* status via the ldap_cachemgr's
* -g option
*/
info->sinfo[1].server_status =
INFO_SERVER_REMOVED;
*svr_removed = TRUE;
(void) mutex_unlock(&info->mutex[0]);
continue;
}
} else {
/*
* req == NS_CACHE_NEXT or NS_CACHE_WRITE
*/
(void) mutex_unlock(&info->mutex[0]);
continue;
}
}
if (matched) {
if (strcmp(req, NS_CACHE_WRITE) == 0) {
if (info->sinfo[0].type ==
INFO_RW_WRITEABLE &&
info->sinfo[0].server_status ==
INFO_SERVER_UP) {
server = info;
break;
}
} else if (info->sinfo[0].server_status ==
INFO_SERVER_UP) {
server = info;
break;
}
}
(void) mutex_unlock(&info->mutex[0]);
}
if (server) {
if (strcmp(addr_type, NS_CACHE_ADDR_HOSTNAME) == 0) {
/*
* In SASL/GSSAPI case, a hostname is required for
* Kerberos's service principal.
* e.g.
* ldap/foo.sun.com@SUN.COM
*/
if (server->sinfo[0].hostname == NULL) {
rc = getldap_ip2hostname(server->sinfo[0].addr,
&server->sinfo[0].hostname);
if (rc != NS_LDAP_SUCCESS) {
(void) mutex_unlock(&info->mutex[0]);
return (rc);
}
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_serverInfo: "
"%s is converted to %s\n",
server->sinfo[0].addr,
server->sinfo[0].hostname);
}
}
ret_addr = server->sinfo[0].addr;
ret_addrFQDN = server->sinfo[0].hostname;
} else
ret_addr = server->sinfo[0].addr;
len = strlen(ret_addr) +
strlen(server->sinfo[0].rootDSE_data) +
strlen(DOORLINESEP) + 1;
if (ret_addrFQDN != NULL)
len += strlen(ret_addrFQDN) + strlen(DOORLINESEP);
*output = (char *)malloc(len);
if (*output == NULL) {
(void) mutex_unlock(&info->mutex[0]);
return (NS_LDAP_MEMORY);
}
if (ret_addrFQDN == NULL)
(void) snprintf(*output, len, "%s%s%s",
ret_addr, DOORLINESEP,
server->sinfo[0].rootDSE_data);
else
(void) snprintf(*output, len, "%s%s%s%s%s",
ret_addr, DOORLINESEP,
ret_addrFQDN, DOORLINESEP,
server->sinfo[0].rootDSE_data);
server->sinfo[0].info_status = INFO_STATUS_OLD;
(void) mutex_unlock(&info->mutex[0]);
return (NS_LDAP_SUCCESS);
}
else
return (-99);
}
/*
* Format previous and next refresh time
*/
static int
getldap_format_refresh_time(char **output, time_t *prev, time_t *next)
{
#define TIME_FORMAT "%Y/%m/%d %H:%M:%S"
#define TIME_HEADER1 " Previous refresh time: "
#define TIME_HEADER2 " Next refresh time: "
int hdr1_len = strlen(gettext(TIME_HEADER1));
int hdr2_len = strlen(gettext(TIME_HEADER2));
struct tm tm;
char nbuf[256];
char pbuf[256];
int len;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_format_refresh_time()...\n");
}
*output = NULL;
/* format the time of previous refresh */
if (*prev != 0) {
(void) localtime_r(prev, &tm);
(void) strftime(pbuf, sizeof (pbuf) - 1, TIME_FORMAT, &tm);
} else {
(void) strcpy(pbuf, gettext("NOT DONE"));
}
/* format the time of next refresh */
if (*next != 0) {
(void) localtime_r(next, &tm);
(void) strftime(nbuf, sizeof (nbuf) - 1, TIME_FORMAT, &tm);
} else {
(void) strcpy(nbuf, gettext("NOT SET"));
}
len = hdr1_len + hdr2_len + strlen(nbuf) +
strlen(pbuf) + 2 * strlen(DOORLINESEP) + 1;
*output = malloc(len);
if (*output == NULL)
return (-1);
(void) snprintf(*output, len, "%s%s%s%s%s%s",
gettext(TIME_HEADER1), pbuf, DOORLINESEP,
gettext(TIME_HEADER2), nbuf, DOORLINESEP);
return (NS_LDAP_SUCCESS);
}
/*
* getldap_get_server_stat processes the GETSTAT request passed
* to this function from getldap_serverInfo_op().
* output:
* a buffer containing info for all the servers.
* For each server, the data is in the following format:
* server: server address or name, status: unknown|up|down|removed DOORLINESEP
* for example: ( here | used as DOORLINESEP for visual purposes)
* server: 1.2.3.4, status: down|server: 2.2.2.2, status: up|
* NOTE: caller should free this buffer when done using it
*/
static int
getldap_get_server_stat(server_info_t *head, char **output,
time_t *prev, time_t *next)
{
#define S_HEADER "Server information: "
#define S_FORMAT " server: %s, status: %s%s"
#define S_ERROR " error message: %s%s"
server_info_t *info = NULL;
int header_len = strlen(gettext(S_HEADER));
int format_len = strlen(gettext(S_FORMAT));
int error_len = strlen(gettext(S_ERROR));
int len = header_len + strlen(DOORLINESEP);
int len1 = 0;
char *status, *output1 = NULL, *tmpptr;
*output = NULL;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_server_stat()...\n");
}
if (head == NULL) {
logit("getldap_get_server_stat: "
"invalid serverInfo list.\n");
return (-1);
}
/* format previous and next refresh time */
(void) getldap_format_refresh_time(&output1, prev, next);
if (output1 == NULL)
return (-1);
len += strlen(output1);
len1 = len + strlen(DOORLINESEP) + 1;
*output = (char *)calloc(1, len1);
if (*output == NULL) {
free(output1);
return (-1);
}
/* insert header string and refresh time info */
(void) snprintf(*output, len1, "%s%s%s",
gettext(S_HEADER), DOORLINESEP, output1);
for (info = head; info; info = info->next) {
/*
* make sure the server info stays the same
* while the data is being processed
*/
(void) mutex_lock(&info->mutex[1]);
/*
* When the updating process is under way(getldap_get_rootDSE)
* the update copy(sinfo[1] is the latest copy.
* When the updating process
* is done, the current copy (sinfo[0]) has the latest status,
* which is still identical to the update copy.
* So update copy has the latest status.
* Use the update copy(sinfo[1]) to show status
* (ldap_cachemgr -g).
*
*/
switch (info->sinfo[1].server_status) {
case INFO_SERVER_UNKNOWN:
status = gettext("UNKNOWN");
break;
case INFO_SERVER_CONNECTING:
status = gettext("CONNECTING");
break;
case INFO_SERVER_UP:
status = gettext("UP");
break;
case INFO_SERVER_ERROR:
status = gettext("ERROR");
break;
case INFO_SERVER_REMOVED:
status = gettext("REMOVED");
break;
}
len += format_len + strlen(status) +
strlen(info->sinfo[1].addr) +
strlen(DOORLINESEP);
if (info->sinfo[1].errormsg != NULL)
len += error_len +
strlen(info->sinfo[1].errormsg) +
strlen(DOORLINESEP);
tmpptr = (char *)realloc(*output, len);
if (tmpptr == NULL) {
free(output1);
free(*output);
*output = NULL;
(void) mutex_unlock(&info->mutex[1]);
return (-1);
} else
*output = tmpptr;
/* insert server IP addr or name and status */
len1 = len - strlen(*output);
(void) snprintf(*output + strlen(*output), len1,
gettext(S_FORMAT), info->sinfo[1].addr,
status, DOORLINESEP);
/* insert error message if any */
len1 = len - strlen(*output);
if (info->sinfo[1].errormsg != NULL)
(void) snprintf(*output + strlen(*output), len1,
gettext(S_ERROR),
info->sinfo[1].errormsg,
DOORLINESEP);
(void) mutex_unlock(&info->mutex[1]);
}
free(output1);
return (NS_LDAP_SUCCESS);
}
/*
* Format and return the refresh time statistics
*/
static int
getldap_get_refresh_stat(char **output)
{
#define R_HEADER0 "Configuration refresh information: "
#define R_HEADER1 " Configured to NO REFRESH."
int hdr0_len = strlen(gettext(R_HEADER0));
int hdr1_len = strlen(gettext(R_HEADER1));
int cache_ttl = -1, len = 0;
time_t expire = 0;
void **paramVal = NULL;
ns_ldap_error_t *errorp = NULL;
char *output1 = NULL;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_refresh_stat()...\n");
}
*output = NULL;
/* get configured cache TTL */
if ((__ns_ldap_getParam(NS_LDAP_CACHETTL_P,
&paramVal, &errorp) == NS_LDAP_SUCCESS) &&
paramVal != NULL &&
(char *)*paramVal != NULL) {
cache_ttl = atol((char *)*paramVal);
} else {
if (errorp)
__ns_ldap_freeError(&errorp);
}
(void) __ns_ldap_freeParam(&paramVal);
/* cound not get cache TTL */
if (cache_ttl == -1)
return (-1);
if (cache_ttl == 0) {
len = hdr0_len + hdr1_len +
2 * strlen(DOORLINESEP) + 1;
*output = malloc(len);
if (*output == NULL)
return (-1);
(void) snprintf(*output, len, "%s%s%s%s",
gettext(R_HEADER0), DOORLINESEP,
gettext(R_HEADER1), DOORLINESEP);
} else {
/* get configuration expiration time */
if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
&paramVal, &errorp) == NS_LDAP_SUCCESS) &&
paramVal != NULL &&
(char *)*paramVal != NULL) {
expire = (time_t)atol((char *)*paramVal);
} else {
if (errorp)
__ns_ldap_freeError(&errorp);
}
(void) __ns_ldap_freeParam(&paramVal);
/* cound not get expiration time */
if (expire == -1)
return (-1);
/* format previous and next refresh time */
(void) getldap_format_refresh_time(&output1,
&prev_refresh_time, &expire);
if (output1 == NULL)
return (-1);
len = hdr0_len + strlen(output1) +
2 * strlen(DOORLINESEP) + 1;
*output = malloc(len);
if (*output == NULL) {
free(output1);
return (-1);
}
(void) snprintf(*output, len, "%s%s%s%s",
gettext(R_HEADER0), DOORLINESEP,
output1, DOORLINESEP);
free(output1);
}
return (NS_LDAP_SUCCESS);
}
static int
getldap_get_cacheTTL()
{
void **paramVal = NULL;
ns_ldap_error_t *error;
int rc = 0, cachettl;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_cacheTTL()....\n");
}
if ((rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P,
&paramVal, &error)) != NS_LDAP_SUCCESS) {
if (error != NULL && error->message != NULL)
logit("Error: Unable to get configuration "
"refresh TTL: %s\n",
error->message);
else {
char *tmp;
__ns_ldap_err2str(rc, &tmp);
logit("Error: Unable to get configuration "
"refresh TTL: %s\n", tmp);
}
(void) __ns_ldap_freeParam(&paramVal);
(void) __ns_ldap_freeError(&error);
return (-1);
}
if (paramVal == NULL || (char *)*paramVal == NULL)
return (-1);
cachettl = atol((char *)*paramVal);
(void) __ns_ldap_freeParam(&paramVal);
return (cachettl);
}
/*
* This function implements the adaptive server list refresh
* algorithm used by ldap_cachemgr. The idea is to have the
* refresh TTL adjust itself between maximum and minimum
* values. If the server list has been walked three times
* in a row without errors, the TTL will be doubled. This will
* be done repeatedly until the maximum value is reached
* or passed. If passed, the maximum value will be used.
* If any time a server is found to be down/bad, either
* after another server list walk or informed by libsldap via
* the GETLDAPSERVER door calls, the TTL will be set to half
* of its value, again repeatedly, but no less than the minimum
* value. Also, at any time, if all the servers on the list
* are found to be down/bad, the TTL will be set to minimum,
* so that a "no-server" refresh loop should be entered to try
* to find a good server as soon as possible. The caller
* could check the no_gd_server flag for this situation.
* The maximum and minimum values are initialized when the input
* refresh_ttl is set to zero, this should occur during
* ldap_cachemgr startup or every time the server list is
* recreated after the configuration profile is refreshed
* from an LDAP server. The maximum is set to the value of
* the NS_LDAP_CACHETTL parameter (configuration profile
* refresh TTL), but if it is zero (never refreshed) or can
* not be retrieved, the maximum is set to the macro
* REFRESHTTL_MAX (12 hours) defined below. The minimum is
* set to REFRESHTTL_MIN, which is the TCP connection timeout
* (tcptimeout) set via the LDAP API ldap_set_option()
* with the new LDAP_X_OPT_CONNECT_TIMEOUT option plus 10 seconds.
* This accounts for the maximum possible timeout value for an
* LDAP TCP connect call.The first refresh TTL, initial value of
* refresh_ttl, will be set to the smaller of the two,
* REFRESHTTL_REGULAR (10 minutes) or (REFRESHTTL_MAX + REFRESHTTL_MIN)/2.
* The idea is to have a low starting value and have the value
* stay low if the network/server is unstable, but eventually
* the value will move up to maximum and stay there if the
* network/server is stable.
*/
static int
getldap_set_refresh_ttl(server_info_t *head, int *refresh_ttl,
int *no_gd_server)
{
#define REFRESHTTL_REGULAR 600
#define REFRESHTTL_MAX 43200
/* tcptimeout is in milliseconds */
#define REFRESHTTL_MIN (tcptimeout/1000) + 10
#define UP_REFRESH_TTL_NUM 2
static mutex_t refresh_mutex;
static int refresh_ttl_max = 0;
static int refresh_ttl_min = 0;
static int num_walked_ok = 0;
int num_servers = 0;
int num_good_servers = 0;
int num_prev_good_servers = 0;
server_info_t *info;
/* allow one thread at a time */
(void) mutex_lock(&refresh_mutex);
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_set_refresh_ttl()...\n");
}
if (!head || !refresh_ttl || !no_gd_server) {
logit("getldap_set_refresh_ttl: head is "
"NULL or refresh_ttl is NULL or "
"no_gd_server is NULL");
(void) mutex_unlock(&refresh_mutex);
return (-1);
}
*no_gd_server = FALSE;
/*
* init max. min. TTLs if first time through or a fresh one
*/
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("getldap_set_refresh_ttl:(1) refresh ttl is %d "
"seconds\n", *refresh_ttl);
}
if (*refresh_ttl == 0) {
num_walked_ok = 0;
/*
* init cache manager server list TTL:
*
* init the min. TTL to
* REFRESHTTL_MIN ( 2*(TCP MSL) + 10 seconds)
*/
refresh_ttl_min = REFRESHTTL_MIN;
/*
* try to set the max. TTL to
* configuration refresh TTL (NS_LDAP_CACHETTL),
* if error (-1), or never refreshed (0),
* set it to REFRESHTTL_MAX (12 hours)
*/
refresh_ttl_max = getldap_get_cacheTTL();
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("getldap_set_refresh_ttl:(2) refresh ttl is %d "
"seconds\n", *refresh_ttl);
logit("getldap_set_refresh_ttl:(2) max ttl is %d, "
"min ttl is %d seconds\n",
refresh_ttl_max, refresh_ttl_min);
}
if (refresh_ttl_max <= 0)
refresh_ttl_max = REFRESHTTL_MAX;
else if (refresh_ttl_max < refresh_ttl_min)
refresh_ttl_max = refresh_ttl_min;
/*
* init the first TTL to the smaller of the two:
* REFRESHTTL_REGULAR ( 10 minutes),
* (refresh_ttl_max + refresh_ttl_min)/2
*/
*refresh_ttl = REFRESHTTL_REGULAR;
if (*refresh_ttl > (refresh_ttl_max + refresh_ttl_min) / 2)
*refresh_ttl = (refresh_ttl_max + refresh_ttl_min) / 2;
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("getldap_set_refresh_ttl:(3) refresh ttl is %d "
"seconds\n", *refresh_ttl);
logit("getldap_set_refresh_ttl:(3) max ttl is %d, "
"min ttl is %d seconds\n",
refresh_ttl_max, refresh_ttl_min);
}
}
/*
* get the servers statistics:
* number of servers on list
* number of good servers on list
* number of pevious good servers on list
*/
for (info = head; info; info = info->next) {
num_servers++;
(void) mutex_lock(&info->mutex[0]);
if (info->sinfo[0].server_status == INFO_SERVER_UP)
num_good_servers++;
/*
* Server's previous status could be UNKNOWN
* only between the very first and second
* refresh. Treat that UNKNOWN status as up
*/
if (info->sinfo[0].prev_server_status
== INFO_SERVER_UP ||
info->sinfo[0].prev_server_status
== INFO_SERVER_UNKNOWN)
num_prev_good_servers++;
(void) mutex_unlock(&info->mutex[0]);
}
/*
* if the server list is walked three times in a row
* without problems, double the refresh TTL but no more
* than the max. refresh TTL
*/
if (num_good_servers == num_servers) {
num_walked_ok++;
if (num_walked_ok > UP_REFRESH_TTL_NUM) {
*refresh_ttl = *refresh_ttl * 2;
if (*refresh_ttl > refresh_ttl_max)
*refresh_ttl = refresh_ttl_max;
num_walked_ok = 0;
}
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("getldap_set_refresh_ttl:(4) refresh ttl is %d "
"seconds\n", *refresh_ttl);
}
} else if (num_good_servers == 0) {
/*
* if no good server found,
* set refresh TTL to miminum
*/
*refresh_ttl = refresh_ttl_min;
*no_gd_server = TRUE;
num_walked_ok = 0;
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("getldap_set_refresh_ttl:(5) refresh ttl is %d "
"seconds\n", *refresh_ttl);
}
} else if (num_prev_good_servers > num_good_servers) {
/*
* if more down/bad servers found,
* decrease the refresh TTL by half
* but no less than the min. refresh TTL
*/
*refresh_ttl = *refresh_ttl / 2;
if (*refresh_ttl < refresh_ttl_min)
*refresh_ttl = refresh_ttl_min;
num_walked_ok = 0;
logit("getldap_set_refresh_ttl:(6) refresh ttl is %d "
"seconds\n", *refresh_ttl);
}
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("getldap_set_refresh_ttl:(7) refresh ttl is %d seconds\n",
*refresh_ttl);
}
(void) mutex_unlock(&refresh_mutex);
return (0);
}
static int
getldap_serverInfo_op(info_op_t op, char *input, char **output)
{
static rwlock_t info_lock = DEFAULTRWLOCK;
static rwlock_t info_lock_old = DEFAULTRWLOCK;
static mutex_t info_mutex;
static cond_t info_cond;
static int creating = FALSE;
static int refresh_ttl = 0;
static int sec_to_refresh = 0;
static int in_no_server_mode = FALSE;
static server_info_t *serverInfo = NULL;
static server_info_t *serverInfo_old = NULL;
server_info_t *serverInfo_1;
int is_creating;
int err, no_server_good = FALSE;
int server_removed = FALSE;
static struct timespec timeout;
struct timespec new_timeout;
struct timeval tp;
static time_t prev_refresh = 0, next_refresh = 0;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_serverInfo_op()...\n");
}
switch (op) {
case INFO_OP_CREATE:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is INFO_OP_CREATE...\n");
}
/*
* indicate that the server info is being
* (re)created, so that the refresh thread
* will not refresh the info list right
* after the list got (re)created
*/
(void) mutex_lock(&info_mutex);
is_creating = creating;
creating = TRUE;
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
/*
* create an empty info list
*/
(void) getldap_init_serverInfo(&serverInfo_1);
/*
* exit if list not created
*/
if (serverInfo_1 == NULL) {
(void) mutex_lock(&info_mutex);
creating = FALSE;
(void) mutex_unlock(&info_mutex);
break;
}
/*
* make the new server info available:
* use writer lock here, so that the switch
* is done after all the reader locks have
* been released.
*/
(void) rw_wrlock(&info_lock);
serverInfo = serverInfo_1;
/*
* if this is the first time
* the server list is being created,
* (i.e., serverInfo_old is NULL)
* make the old list same as the new
* so the GETSERVER code can do its work
*/
if (serverInfo_old == NULL)
serverInfo_old = serverInfo_1;
(void) rw_unlock(&info_lock);
/*
* fill the new info list
*/
(void) rw_rdlock(&info_lock);
/* reset bind time (tcptimeout) */
(void) getldap_set_serverInfo(serverInfo, 1);
(void) mutex_lock(&info_mutex);
/*
* set cache manager server list TTL,
* set refresh_ttl to zero to indicate a fresh one
*/
refresh_ttl = 0;
(void) getldap_set_refresh_ttl(serverInfo,
&refresh_ttl, &no_server_good);
sec_to_refresh = refresh_ttl;
/* statistics: previous refresh time */
if (gettimeofday(&tp, NULL) == 0)
prev_refresh = tp.tv_sec;
creating = FALSE;
/*
* if no server found or available,
* tell the server info refresh thread
* to start the "no-server" refresh loop
* otherwise reset the in_no_server_mode flag
*/
if (no_server_good) {
sec_to_refresh = 0;
in_no_server_mode = TRUE;
} else
in_no_server_mode = FALSE;
/*
* awake the sleeping refresh thread
*/
(void) cond_signal(&info_cond);
(void) mutex_unlock(&info_mutex);
(void) rw_unlock(&info_lock);
/*
* delete the old server info
*/
(void) rw_wrlock(&info_lock_old);
if (serverInfo_old != serverInfo)
(void) getldap_destroy_serverInfo(serverInfo_old);
/*
* serverInfo_old needs to be the same as
* serverinfo now.
* it will be used by GETSERVER processing.
*/
serverInfo_old = serverInfo;
(void) rw_unlock(&info_lock_old);
break;
case INFO_OP_DELETE:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is INFO_OP_DELETE...\n");
}
/*
* use writer lock here, so that the delete would
* not start until all the reader locks have
* been released.
*/
(void) rw_wrlock(&info_lock);
if (serverInfo)
(void) getldap_destroy_serverInfo(serverInfo);
serverInfo = NULL;
(void) rw_unlock(&info_lock);
break;
case INFO_OP_REFRESH:
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("operation is INFO_OP_REFRESH...\n");
}
/*
* if server info is currently being
* (re)created, do nothing
*/
(void) mutex_lock(&info_mutex);
is_creating = creating;
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
(void) rw_rdlock(&info_lock);
if (serverInfo) {
/* do not reset bind time (tcptimeout) */
(void) getldap_set_serverInfo(serverInfo, 0);
(void) mutex_lock(&info_mutex);
/* statistics: previous refresh time */
if (gettimeofday(&tp, NULL) == 0)
prev_refresh = tp.tv_sec;
/*
* set cache manager server list TTL
*/
(void) getldap_set_refresh_ttl(serverInfo,
&refresh_ttl, &no_server_good);
/*
* if no good server found,
* tell the server info refresh thread
* to start the "no-server" refresh loop
* otherwise reset the in_no_server_mode flag
*/
if (no_server_good) {
in_no_server_mode = TRUE;
sec_to_refresh = 0;
} else {
in_no_server_mode = FALSE;
sec_to_refresh = refresh_ttl;
}
if (current_admin.debug_level >=
DBG_SERVER_LIST_REFRESH) {
logit("getldap_serverInfo_op("
"INFO_OP_REFRESH):"
" seconds refresh: %d second(s)....\n",
sec_to_refresh);
}
(void) mutex_unlock(&info_mutex);
}
(void) rw_unlock(&info_lock);
break;
case INFO_OP_REFRESH_WAIT:
if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
logit("operation is INFO_OP_REFRESH_WAIT...\n");
}
(void) cond_init(&info_cond, NULL, NULL);
(void) mutex_lock(&info_mutex);
err = 0;
while (err != ETIME) {
int sleeptime;
/*
* if need to go into the "no-server" refresh
* loop, set timout value to
* REFRESH_DELAY_WHEN_NO_SERVER
*/
if (sec_to_refresh == 0) {
sec_to_refresh = refresh_ttl;
timeout.tv_sec = time(NULL) +
REFRESH_DELAY_WHEN_NO_SERVER;
sleeptime = REFRESH_DELAY_WHEN_NO_SERVER;
if (current_admin.debug_level >=
DBG_SERVER_LIST_REFRESH) {
logit("getldap_serverInfo_op("
"INFO_OP_REFRESH_WAIT):"
" entering no-server "
"refresh loop...\n");
}
} else {
timeout.tv_sec = time(NULL) + sec_to_refresh;
sleeptime = sec_to_refresh;
}
timeout.tv_nsec = 0;
/* statistics: next refresh time */
next_refresh = timeout.tv_sec;
if (current_admin.debug_level >=
DBG_SERVER_LIST_REFRESH) {
logit("getldap_serverInfo_op("
"INFO_OP_REFRESH_WAIT):"
" about to sleep for %d second(s)...\n",
sleeptime);
}
err = cond_timedwait(&info_cond,
&info_mutex, &timeout);
}
(void) cond_destroy(&info_cond);
(void) mutex_unlock(&info_mutex);
break;
case INFO_OP_GETSERVER:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is INFO_OP_GETSERVER...\n");
}
*output = NULL;
/*
* GETSERVER processing always use
* serverInfo_old to retrieve server infomation.
* serverInfo_old is equal to serverInfo
* most of the time, except when a new
* server list is being created.
* This is why the check for is_creating
* is needed below.
*/
(void) rw_rdlock(&info_lock_old);
if (serverInfo_old == NULL) {
(void) rw_unlock(&info_lock_old);
break;
} else
(void) getldap_get_serverInfo(serverInfo_old,
input, output, &server_removed);
(void) rw_unlock(&info_lock_old);
/*
* if server info is currently being
* (re)created, do nothing
*/
(void) mutex_lock(&info_mutex);
is_creating = creating;
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
/*
* set cache manager server list TTL if necessary
*/
if (*output == NULL || server_removed) {
(void) rw_rdlock(&info_lock);
(void) mutex_lock(&info_mutex);
(void) getldap_set_refresh_ttl(serverInfo,
&refresh_ttl, &no_server_good);
/*
* if no good server found, need to go into
* the "no-server" refresh loop
* to find a server as soon as possible
* otherwise reset the in_no_server_mode flag
*/
if (no_server_good) {
/*
* if already in no-server mode,
* don't brother
*/
if (in_no_server_mode == FALSE) {
sec_to_refresh = 0;
in_no_server_mode = TRUE;
(void) cond_signal(&info_cond);
}
(void) mutex_unlock(&info_mutex);
(void) rw_unlock(&info_lock);
break;
} else {
in_no_server_mode = FALSE;
sec_to_refresh = refresh_ttl;
}
/*
* if the refresh thread will be timed out
* longer than refresh_ttl seconds,
* wake it up to make it wait on the new
* time out value
*/
new_timeout.tv_sec = time(NULL) + refresh_ttl;
if (new_timeout.tv_sec < timeout.tv_sec)
(void) cond_signal(&info_cond);
(void) mutex_unlock(&info_mutex);
(void) rw_unlock(&info_lock);
}
break;
case INFO_OP_GETSTAT:
if (current_admin.debug_level >= DBG_ALL) {
logit("operation is INFO_OP_GETSTAT...\n");
}
*output = NULL;
(void) rw_rdlock(&info_lock);
if (serverInfo) {
(void) getldap_get_server_stat(serverInfo,
output, &prev_refresh, &next_refresh);
}
(void) rw_unlock(&info_lock);
break;
default:
logit("getldap_serverInfo_op(): "
"invalid operation code (%d).\n", op);
return (-1);
break;
}
return (NS_LDAP_SUCCESS);
}
void
getldap_serverInfo_refresh()
{
int always = 1;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_serverInfo_refresh()...\n");
}
/* create the server info list */
(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
while (always) {
/*
* the operation INFO_OP_REFRESH_WAIT
* causes this thread to wait until
* it is time to do refresh,
* see getldap_serverInfo_op() for details
*/
(void) getldap_serverInfo_op(INFO_OP_REFRESH_WAIT, NULL, NULL);
(void) getldap_serverInfo_op(INFO_OP_REFRESH, NULL, NULL);
}
}
void
getldap_getserver(LineBuf *config_info, ldap_call_t *in)
{
char req[] = "0";
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_getserver()...\n");
}
config_info->len = 0;
/* make sure the request is valid */
req[0] = (in->ldap_u.servername)[0];
if ((req[0] != '\0') &&
(strcmp(req, NS_CACHE_NEW) != 0) &&
(strcmp(req, NS_CACHE_NORESP) != 0) &&
(strcmp(req, NS_CACHE_NEXT) != 0) &&
(strcmp(req, NS_CACHE_WRITE) != 0)) {
return;
}
(void) getldap_serverInfo_op(INFO_OP_GETSERVER,
in->ldap_u.domainname, &config_info->str);
if (config_info->str == NULL)
return;
config_info->len = strlen(config_info->str) + 1;
if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
/* Log server IP */
char *ptr,
separator;
ptr = strstr(config_info->str, DOORLINESEP);
if (ptr) {
separator = *ptr;
*ptr = '\0';
logit("getldap_getserver: got server %s\n",
config_info->str);
*ptr = separator;
} else
logit("getldap_getserver: Missing %s."
" Internal error\n", DOORLINESEP);
}
}
void
getldap_get_cacheData(LineBuf *config_info, ldap_call_t *in)
{
char *instr = NULL;
int datatype = CACHE_MAP_UNKNOWN;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_cacheData()...\n");
}
config_info->len = 0;
config_info->str = NULL;
/* make sure the request is valid */
if (strncmp(in->ldap_u.servername,
NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
datatype = CACHE_MAP_DN2DOMAIN;
if (datatype == CACHE_MAP_UNKNOWN)
return;
instr = strstr(in->ldap_u.servername, DOORLINESEP);
if (instr == NULL)
return;
instr += strlen(DOORLINESEP);
if (*instr == '\0')
return;
(void) getldap_cache_op(CACHE_OP_FIND, datatype,
instr, &config_info->str);
if (config_info->str != NULL) {
config_info->len = strlen(config_info->str) + 1;
}
}
int
getldap_set_cacheData(ldap_call_t *in)
{
char *instr1 = NULL;
char *instr2 = NULL;
int datatype = CACHE_MAP_UNKNOWN;
int rc = 0;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_set_cacheData()...\n");
}
/* make sure the request is valid */
if (strncmp(in->ldap_u.servername,
NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
datatype = CACHE_MAP_DN2DOMAIN;
if (datatype == CACHE_MAP_UNKNOWN)
return (-1);
instr1 = strstr(in->ldap_u.servername, DOORLINESEP);
if (instr1 == NULL)
return (-1);
*instr1 = '\0';
instr1 += strlen(DOORLINESEP);
if (*instr1 == '\0')
return (-1);
instr2 = strstr(instr1, DOORLINESEP);
if (instr2 == NULL)
return (-1);
*instr2 = '\0';
instr2 += strlen(DOORLINESEP);
if (*instr2 == '\0')
return (-1);
rc = getldap_cache_op(CACHE_OP_ADD, datatype,
instr1, &instr2);
if (rc != NS_LDAP_SUCCESS)
return (-1);
return (0);
}
void
getldap_get_cacheStat(LineBuf *stat_info)
{
char *foutstr = NULL;
char *soutstr = NULL;
char *coutstr = NULL;
int infoSize;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_cacheStat()...\n");
}
stat_info->str = NULL;
stat_info->len = 0;
/* get refersh statisitcs */
(void) getldap_get_refresh_stat(&foutstr);
if (foutstr == NULL)
return;
/* get server statisitcs */
(void) getldap_serverInfo_op(INFO_OP_GETSTAT, NULL, &soutstr);
if (soutstr == NULL) {
free(foutstr);
return;
}
/* get cache data statisitcs */
(void) getldap_cache_op(CACHE_OP_GETSTAT, NULL, NULL, &coutstr);
if (coutstr == NULL) {
free(foutstr);
free(soutstr);
return;
}
infoSize = strlen(foutstr) + strlen(soutstr) + strlen(coutstr) + 3;
stat_info->str = calloc(infoSize, sizeof (char));
if (stat_info->str != NULL) {
(void) strncpy(stat_info->str,
foutstr,
strlen(foutstr) + 1);
(void) strncat(stat_info->str,
soutstr,
strlen(soutstr) + 1);
(void) strncat(stat_info->str,
coutstr,
strlen(coutstr) + 1);
stat_info->len = infoSize;
}
free(foutstr);
free(soutstr);
free(coutstr);
}
static int
checkupdate(int sighup)
{
int value;
(void) rw_wrlock(&ldap_lock);
value = sighup;
(void) rw_unlock(&ldap_lock);
return (value == TRUE);
}
static int
update_from_profile()
{
ns_ldap_result_t *result = NULL;
char searchfilter[BUFSIZ];
ns_ldap_error_t *error;
int rc;
void **paramVal = NULL;
ns_config_t *ptr = NULL;
char *profile = NULL;
char errstr[MAXERROR];
if (current_admin.debug_level >= DBG_ALL) {
logit("update_from_profile....\n");
}
do {
(void) rw_wrlock(&ldap_lock);
sighup_update = FALSE;
(void) rw_unlock(&ldap_lock);
if ((rc = __ns_ldap_getParam(NS_LDAP_PROFILE_P,
&paramVal, &error)) != NS_LDAP_SUCCESS) {
if (error != NULL && error->message != NULL)
logit("Error: Unable to profile name: %s\n",
error->message);
else {
char *tmp;
__ns_ldap_err2str(rc, &tmp);
logit("Error: Unable to profile name: %s\n",
tmp);
}
(void) __ns_ldap_freeParam(&paramVal);
(void) __ns_ldap_freeError(&error);
return (-1);
}
if (paramVal && *paramVal)
profile = strdup((char *)*paramVal);
(void) __ns_ldap_freeParam(&paramVal);
if (profile == NULL) {
return (-1);
}
(void) snprintf(searchfilter, BUFSIZ, _PROFILE_FILTER,
_PROFILE1_OBJECTCLASS, _PROFILE2_OBJECTCLASS, profile);
if ((rc = __ns_ldap_list(_PROFILE_CONTAINER,
(const char *)searchfilter, NULL,
NULL, NULL, 0,
&result, &error, NULL, NULL)) != NS_LDAP_SUCCESS) {
/*
* Is profile name the DEFAULTCONFIGNAME?
* syslog Warning, otherwise syslog error.
*/
if (strcmp(profile, DEFAULTCONFIGNAME) == 0) {
syslog(LOG_WARNING,
"Ignoring attempt to refresh nonexistent "
"default profile: %s.\n",
profile);
logit("Ignoring attempt to refresh nonexistent "
"default profile: %s.\n",
profile);
} else if ((error != NULL) &&
(error->message != NULL)) {
syslog(LOG_ERR,
"Error: Unable to refresh profile:%s:"
" %s\n", profile, error->message);
logit("Error: Unable to refresh profile:"
"%s:%s\n", profile, error->message);
} else {
syslog(LOG_ERR, "Error: Unable to refresh "
"from profile:%s. (error=%d)\n",
profile, rc);
logit("Error: Unable to refresh from profile "
"%s (error=%d)\n", profile, rc);
}
(void) __ns_ldap_freeError(&error);
(void) __ns_ldap_freeResult(&result);
free(profile);
return (-1);
}
free(profile);
} while (checkupdate(sighup_update) == TRUE);
(void) rw_wrlock(&ldap_lock);
ptr = __ns_ldap_make_config(result);
(void) __ns_ldap_freeResult(&result);
if (ptr == NULL) {
logit("Error: __ns_ldap_make_config failed.\n");
(void) rw_unlock(&ldap_lock);
return (-1);
}
/*
* cross check the config parameters
*/
if (__s_api_crosscheck(ptr, errstr, B_TRUE) == NS_SUCCESS) {
/*
* reset the local profile TTL
*/
if (ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc)
current_admin.ldap_stat.ldap_ttl =
atol(ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc);
if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
logit("update_from_profile: reset profile TTL to %d"
" seconds\n",
current_admin.ldap_stat.ldap_ttl);
logit("update_from_profile: expire time %ld "
"seconds\n",
ptr->paramList[NS_LDAP_EXP_P].ns_tm);
}
/* set ptr as current_config */
__s_api_init_config(ptr);
rc = 0;
} else {
__s_api_destroy_config(ptr);
logit("Error: downloaded profile failed to pass "
"crosscheck (%s).\n", errstr);
syslog(LOG_ERR, "ldap_cachemgr: %s", errstr);
rc = -1;
}
(void) rw_unlock(&ldap_lock);
return (rc);
}
int
getldap_init()
{
ns_ldap_error_t *error;
struct timeval tp;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_init()...\n");
}
(void) __ns_ldap_setServer(TRUE);
(void) rw_wrlock(&ldap_lock);
if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
logit("Error: Unable to read '%s': %s\n",
NSCONFIGFILE, error->message);
(void) fprintf(stderr,
gettext("\nError: Unable to read '%s': %s\n"),
NSCONFIGFILE, error->message);
__ns_ldap_freeError(&error);
(void) rw_unlock(&ldap_lock);
return (-1);
}
(void) rw_unlock(&ldap_lock);
if (gettimeofday(&tp, NULL) == 0) {
/* statistics: previous refresh time */
prev_refresh_time = tp.tv_sec;
}
/* initialize the data cache */
(void) getldap_cache_op(CACHE_OP_CREATE,
0, NULL, NULL);
return (0);
}
static void
perform_update(void)
{
ns_ldap_error_t *error = NULL;
struct timeval tp;
char buf[20];
int rc, rc1;
void **paramVal = NULL;
ns_ldap_self_gssapi_config_t config;
if (current_admin.debug_level >= DBG_ALL) {
logit("perform_update()...\n");
}
(void) __ns_ldap_setServer(TRUE);
if (gettimeofday(&tp, NULL) != 0)
return;
rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P, &paramVal, &error);
if (rc == NS_LDAP_SUCCESS && paramVal != NULL) {
current_admin.ldap_stat.ldap_ttl = atol((char *)*paramVal);
}
if (error != NULL)
(void) __ns_ldap_freeError(&error);
if (paramVal != NULL)
(void) __ns_ldap_freeParam(&paramVal);
if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
logit("perform_update: current profile TTL is %d seconds\n",
current_admin.ldap_stat.ldap_ttl);
}
if (current_admin.ldap_stat.ldap_ttl > 0) {
/*
* set the profile TTL parameter, just
* in case that the downloading of
* the profile from server would fail
*/
/*
* NS_LDAP_EXP_P is a no op for __ns_ldap_setParam
* It depends on NS_LDAP_CACHETTL_P to set it's value
* Set NS_LDAP_CACHETTL_P here so NS_LDAP_EXP_P value
* can be set.
* NS_LDAP_CACHETTL_P value can be reset after the profile is
* downloaded from the server, so is NS_LDAP_EXP_P.
*/
buf[19] = '\0'; /* null terminated the buffer */
if (__ns_ldap_setParam(NS_LDAP_CACHETTL_P,
lltostr((long long)current_admin.ldap_stat.ldap_ttl,
&buf[19]),
&error) != NS_LDAP_SUCCESS) {
logit("Error: __ns_ldap_setParam failed, status: %d "
"message: %s\n", error->status, error->message);
(void) __ns_ldap_freeError(&error);
return;
}
(void) rw_wrlock(&ldap_lock);
sighup_update = FALSE;
(void) rw_unlock(&ldap_lock);
do {
rc = update_from_profile();
if (rc != 0) {
logit("Error: Unable to update from profile\n");
}
} while (checkupdate(sighup_update) == TRUE);
} else {
rc = 0;
}
/*
* recreate the server info list
*/
if (rc == 0) {
(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
/* flush the data cache */
(void) getldap_cache_op(CACHE_OP_DELETE,
0, NULL, NULL);
/* statistics: previous refresh time */
prev_refresh_time = tp.tv_sec;
}
rc1 = __ns_ldap_self_gssapi_config(&config);
if (rc1 == NS_LDAP_SUCCESS) {
if (config != NS_LDAP_SELF_GSSAPI_CONFIG_NONE) {
rc1 = __ns_ldap_check_all_preq(0, 0, 0, config, &error);
(void) __ns_ldap_freeError(&error);
if (rc1 != NS_LDAP_SUCCESS) {
logit("Error: Check on self credential "
"prerquesites failed: %d\n",
rc1);
exit(rc1);
}
}
} else {
logit("Error: Failed to get self credential configuration %d\n",
rc1);
exit(rc1);
}
(void) rw_rdlock(&ldap_lock);
if (((error = __ns_ldap_DumpConfiguration(NSCONFIGREFRESH)) != NULL) ||
((error = __ns_ldap_DumpConfiguration(NSCREDREFRESH)) != NULL)) {
logit("Error: __ns_ldap_DumpConfiguration failed, "
"status: %d message: %s\n", error->status, error->message);
__ns_ldap_freeError(&error);
(void) rw_unlock(&ldap_lock);
return;
}
if (rename(NSCONFIGREFRESH, NSCONFIGFILE) != 0) {
logit("Error: unlink failed - errno: %s\n", strerror(errno));
syslog(LOG_ERR, "Unable to refresh profile, LDAP configuration"
"files not written");
(void) rw_unlock(&ldap_lock);
return;
}
if (rename(NSCREDREFRESH, NSCREDFILE) != 0) {
/*
* We probably have inconsistent configuration at this point.
* If we were to create a backup file and rename it here, that
* operation might also fail. Consequently there is no safe way
* to roll back.
*/
logit("Error: unlink failed - errno: %s\n", strerror(errno));
syslog(LOG_ERR, "Unable to refresh profile consistently, "
"LDAP configuration files inconsistent");
(void) rw_unlock(&ldap_lock);
return;
}
(void) rw_unlock(&ldap_lock);
}
void
getldap_refresh()
{
struct timespec timeout;
int sleeptime;
struct timeval tp;
long expire = 0;
void **paramVal = NULL;
ns_ldap_error_t *errorp;
int always = 1, err;
int first_time = 1;
int sig_done = 0;
int dbg_level;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_refresh()...\n");
}
/*
* wait for an available server
*/
while (sig_done == 0) {
(void) mutex_lock(&sig_mutex);
sig_done = signal_done;
(void) mutex_unlock(&sig_mutex);
}
(void) __ns_ldap_setServer(TRUE);
while (always) {
dbg_level = current_admin.debug_level;
(void) rw_rdlock(&ldap_lock);
sleeptime = current_admin.ldap_stat.ldap_ttl;
if (dbg_level >= DBG_PROFILE_REFRESH) {
logit("getldap_refresh: current profile TTL is %d "
"seconds\n", current_admin.ldap_stat.ldap_ttl);
}
if (gettimeofday(&tp, NULL) == 0) {
if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
&paramVal, &errorp) == NS_LDAP_SUCCESS) &&
paramVal != NULL &&
(char *)*paramVal != NULL) {
errno = 0;
expire = atol((char *)*paramVal);
(void) __ns_ldap_freeParam(&paramVal);
if (errno == 0) {
if (expire == 0) {
first_time = 0;
(void) rw_unlock(&ldap_lock);
(void) cond_init(&cond,
NULL, NULL);
(void) mutex_lock(&sighuplock);
timeout.tv_sec =
CACHESLEEPTIME;
timeout.tv_nsec = 0;
if (dbg_level >=
DBG_PROFILE_REFRESH) {
logit("getldap_refresh:"
"(1)about to sleep"
" for %d seconds\n",
CACHESLEEPTIME);
}
err = cond_reltimedwait(&cond,
&sighuplock, &timeout);
(void) cond_destroy(&cond);
(void) mutex_unlock(
&sighuplock);
/*
* if woke up by
* getldap_revalidate(),
* do update right away
*/
if (err == ETIME)
continue;
else {
/*
* if load
* configuration failed
* don't do update
*/
if (load_config())
perform_update
();
continue;
}
}
sleeptime = expire - tp.tv_sec;
if (dbg_level >= DBG_PROFILE_REFRESH) {
logit("getldap_refresh: expire "
"time = %ld\n", expire);
}
}
}
}
(void) rw_unlock(&ldap_lock);
/*
* if this is the first time downloading
* the profile or expire time already passed,
* do not wait, do update
*/
if (first_time == 0 && sleeptime > 0) {
if (dbg_level >= DBG_PROFILE_REFRESH) {
logit("getldap_refresh: (2)about to sleep "
"for %d seconds\n", sleeptime);
}
(void) cond_init(&cond, NULL, NULL);
(void) mutex_lock(&sighuplock);
timeout.tv_sec = sleeptime;
timeout.tv_nsec = 0;
err = cond_reltimedwait(&cond,
&sighuplock, &timeout);
(void) cond_destroy(&cond);
(void) mutex_unlock(&sighuplock);
}
/*
* if load concfiguration failed
* don't do update
*/
if (load_config())
perform_update();
first_time = 0;
}
}
void
getldap_revalidate()
{
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_revalidate()...\n");
}
/* block signal SIGHUP */
(void) sighold(SIGHUP);
/* now awake the sleeping refresh thread */
(void) cond_signal(&cond);
/* release signal SIGHUP */
(void) sigrelse(SIGHUP);
}
void
getldap_lookup(LineBuf *config_info, ldap_call_t *in)
{
ns_ldap_error_t *error;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_lookup()...\n");
}
(void) rw_rdlock(&ldap_lock);
if ((error = __ns_ldap_LoadDoorInfo(config_info,
in->ldap_u.domainname)) != NULL) {
if (error != NULL && error->message != NULL)
logit("Error: ldap_lookup: %s\n", error->message);
(void) __ns_ldap_freeError(&error);
config_info->str = NULL;
config_info->len = 0;
}
(void) rw_unlock(&ldap_lock);
}