#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>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ucred.h>
#include "cachemgr.h"
#include "solaris-priv.h"
#include "ns_connmgmt.h"
static rwlock_t ldap_lock = DEFAULTRWLOCK;
static int sighup_update = FALSE;
extern admin_t current_admin;
extern int is_root_or_all_privs(char *dc_str, ucred_t **ucp);
static mutex_t sighuplock;
static cond_t cond;
static time_t prev_refresh_time = 0;
static mutex_t sig_mutex;
static int signal_done = FALSE;
static int tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
#ifdef SLP
extern int use_slp;
#endif
#define _NIS_FILTER "objectclass=nisDomainObject"
#define _NIS_DOMAIN "nisdomain"
#define CACHESLEEPTIME 600
#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_REMOVESERVER = 6
} 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;
ns_server_status_t change;
} server_info_ext_t;
typedef struct server_info {
struct server_info *next;
mutex_t mutex[2];
server_info_ext_t sinfo[2];
} server_info_t;
typedef struct cache_hash {
cache_type_t type;
char *from;
char *to;
struct cache_hash *next;
} cache_hash_t;
typedef struct rm_svr {
char *addr;
int up;
} rm_svr_t;
static int getldap_destroy_serverInfo(server_info_t *head);
static void test_server_change(server_info_t *head);
static void remove_server(char *addr);
static ns_server_status_t set_server_status(char *input, server_info_t *head);
static void create_buf_and_notify(char *input, ns_server_status_t st);
static int
load_config(void)
{
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;
} else
sighup_update = TRUE;
(void) rw_unlock(&ldap_lock);
return (rc);
}
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);
}
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);
}
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);
}
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];
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);
}
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]);
if (info->sinfo[1].server_status == INFO_SERVER_UP &&
info->sinfo[0].server_status != INFO_SERVER_UP)
info->sinfo[1].change = NS_SERVER_UP;
else if (info->sinfo[1].server_status != INFO_SERVER_UP &&
info->sinfo[0].server_status == INFO_SERVER_UP)
info->sinfo[1].change = NS_SERVER_DOWN;
else
info->sinfo[1].change = 0;
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;
info->sinfo[0] = info->sinfo[1];
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;
char *rootDSE;
int exitrc = NS_LDAP_SUCCESS;
pid_t ppid;
int server_found = 0;
char errmsg[MAXERROR];
ns_ldap_return_code rc;
ns_ldap_error_t *error = NULL;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_rootDSE()....\n");
}
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].type = INFO_RW_UNKNOWN;
serverInfo->sinfo[1].info_status =
INFO_STATUS_UNKNOWN;
(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]);
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].server_status = INFO_SERVER_CONNECTING;
(void) mutex_unlock(&serverInfo->mutex[1]);
if (rc = __ns_ldap_getRootDSE(serverInfo->sinfo[1].addr,
&rootDSE,
&error,
SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].server_status = INFO_SERVER_ERROR;
serverInfo->sinfo[1].info_status = INFO_STATUS_ERROR;
if (error && error->message) {
serverInfo->sinfo[1].errormsg = strdup(error->message);
} else {
(void) snprintf(errmsg, sizeof (errmsg), "%s %s "
"(rc = %d)", gettext("Can not get the root DSE from"
" server"), serverInfo->sinfo[1].addr, rc);
serverInfo->sinfo[1].errormsg = strdup(errmsg);
}
if (error != NULL) {
(void) __ns_ldap_freeError(&error);
}
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_get_rootDSE: %s.\n",
serverInfo->sinfo[1].errormsg);
}
(void) mutex_unlock(&serverInfo->mutex[1]);
sync_current_with_update_copy(serverInfo);
thr_exit((void *) -1);
}
(void) mutex_lock(&serverInfo->mutex[1]);
serverInfo->sinfo[1].type = INFO_RW_WRITEABLE;
serverInfo->sinfo[1].server_status = INFO_SERVER_UP;
serverInfo->sinfo[1].info_status = INFO_STATUS_NEW;
*(rootDSE+strlen(rootDSE)-1) = '\0';
serverInfo->sinfo[1].rootDSE_data = rootDSE;
server_found = 1;
(void) mutex_unlock(&serverInfo->mutex[1]);
sync_current_with_update_copy(serverInfo);
(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;
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, info_op_t op)
{
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);
}
if (reset_bindtime == 1) {
tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P,
¶mVal, &error);
if (paramVal != NULL && *paramVal != NULL) {
tcptimeout = **((int **)paramVal);
tcptimeout *= 1000;
(void) __ns_ldap_freeParam(¶mVal);
}
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 (op == INFO_OP_REFRESH)
test_server_change(head);
if (atleast1) {
return (NS_LDAP_SUCCESS);
} else
return (-1);
}
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 = 0, rc = 0;
char *ret_addr = NULL, *ret_addrFQDN = NULL;
char *new_addr = NULL;
pid_t pid;
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);
}
req = req_new;
if (input[0] != '\0') {
req = input;
addr_type[0] = input[1];
input[strlen(NS_CACHE_NEW)] = '\0';
addr = input + strlen(DOORLINESEP) + strlen(NS_CACHE_NEW)
+ strlen(NS_CACHE_ADDR_IP);
}
if ((strcmp(req, NS_CACHE_NEW) == 0) ||
(head->sinfo[0].info_status == INFO_STATUS_NEW))
matched = TRUE;
for (info = head; info; info = info->next) {
(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 (chg_is_called_from_nscd_or_peruser_nscd(
"REMOVE SERVER", &pid) == 0) {
(void) mutex_unlock(&info->mutex[0]);
if (current_admin.debug_level >=
DBG_ALL)
logit("Only nscd can remove "
"servers. pid %ld", pid);
continue;
}
if (info->sinfo[0].info_status ==
INFO_STATUS_NEW &&
info->sinfo[0].server_status ==
INFO_SERVER_UP) {
server = info;
break;
} else {
new_addr = strdup(info->sinfo[0].addr);
if (new_addr)
remove_server(new_addr);
*svr_removed = TRUE;
(void) mutex_unlock(&info->mutex[0]);
break;
}
} else {
(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) {
if (server->sinfo[0].hostname == NULL) {
rc = __s_api_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);
}
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;
if (*prev != 0) {
(void) localtime_r(prev, &tm);
(void) strftime(pbuf, sizeof (pbuf) - 1, TIME_FORMAT, &tm);
} else {
(void) strcpy(pbuf, gettext("NOT DONE"));
}
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);
}
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);
}
(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);
}
(void) snprintf(*output, len1, "%s%s%s",
gettext(S_HEADER), DOORLINESEP, output1);
for (info = head; info; info = info->next) {
(void) mutex_lock(&info->mutex[1]);
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;
len1 = len - strlen(*output);
(void) snprintf(*output + strlen(*output), len1,
gettext(S_FORMAT), info->sinfo[1].addr,
status, DOORLINESEP);
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);
}
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;
if ((__ns_ldap_getParam(NS_LDAP_CACHETTL_P,
¶mVal, &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(¶mVal);
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 {
if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
¶mVal, &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(¶mVal);
if (expire == -1)
return (-1);
(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,
¶mVal, &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(¶mVal);
(void) __ns_ldap_freeError(&error);
return (-1);
}
if (paramVal == NULL || (char *)*paramVal == NULL)
return (-1);
cachettl = atol((char *)*paramVal);
(void) __ns_ldap_freeParam(¶mVal);
return (cachettl);
}
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
#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;
(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;
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;
refresh_ttl_min = REFRESHTTL_MIN;
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;
*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);
}
}
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++;
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 (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) {
*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) {
*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;
int fall_thru = FALSE;
static struct timespec timeout;
struct timespec new_timeout;
struct timeval tp;
static time_t prev_refresh = 0, next_refresh = 0;
ns_server_status_t changed = 0;
(void) pthread_setname_np(pthread_self(), "getldap_serverinfo");
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");
}
(void) mutex_lock(&info_mutex);
is_creating = creating;
creating = TRUE;
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
(void) getldap_init_serverInfo(&serverInfo_1);
if (serverInfo_1 == NULL) {
(void) mutex_lock(&info_mutex);
creating = FALSE;
(void) mutex_unlock(&info_mutex);
break;
}
(void) rw_wrlock(&info_lock);
serverInfo = serverInfo_1;
if (serverInfo_old == NULL)
serverInfo_old = serverInfo_1;
(void) rw_unlock(&info_lock);
(void) rw_rdlock(&info_lock);
(void) getldap_set_serverInfo(serverInfo, 1, INFO_OP_CREATE);
(void) mutex_lock(&info_mutex);
refresh_ttl = 0;
(void) getldap_set_refresh_ttl(serverInfo,
&refresh_ttl, &no_server_good);
sec_to_refresh = refresh_ttl;
if (gettimeofday(&tp, NULL) == 0)
prev_refresh = tp.tv_sec;
creating = FALSE;
if (no_server_good) {
sec_to_refresh = 0;
in_no_server_mode = TRUE;
} else
in_no_server_mode = FALSE;
(void) cond_signal(&info_cond);
(void) mutex_unlock(&info_mutex);
(void) rw_unlock(&info_lock);
(void) rw_wrlock(&info_lock_old);
if (serverInfo_old != serverInfo)
(void) getldap_destroy_serverInfo(serverInfo_old);
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");
}
(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");
}
(void) mutex_lock(&info_mutex);
is_creating = creating;
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
(void) rw_rdlock(&info_lock);
if (serverInfo) {
(void) getldap_set_serverInfo(serverInfo, 0,
INFO_OP_REFRESH);
(void) mutex_lock(&info_mutex);
if (gettimeofday(&tp, NULL) == 0)
prev_refresh = tp.tv_sec;
(void) getldap_set_refresh_ttl(serverInfo,
&refresh_ttl, &no_server_good);
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, USYNC_THREAD, NULL);
(void) mutex_lock(&info_mutex);
err = 0;
while (err != ETIME) {
int sleeptime;
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;
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;
(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_removed)
break;
fall_thru = TRUE;
case INFO_OP_REMOVESERVER:
(void) mutex_lock(&info_mutex);
is_creating = creating;
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
if (!fall_thru) {
if (current_admin.debug_level >= DBG_ALL)
logit("operation is INFO_OP_REMOVESERVER...\n");
(void) rw_rdlock(&info_lock_old);
changed = set_server_status(input, serverInfo_old);
(void) rw_unlock(&info_lock_old);
if (changed)
create_buf_and_notify(input, changed);
else
break;
}
if (*output == NULL || changed) {
(void) rw_rdlock(&info_lock);
(void) mutex_lock(&info_mutex);
(void) getldap_set_refresh_ttl(serverInfo,
&refresh_ttl, &no_server_good);
if (no_server_good) {
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;
}
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");
}
(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
while (always) {
(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;
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) {
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;
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");
}
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;
(void) getldap_get_refresh_stat(&foutstr);
if (foutstr == NULL)
return;
(void) getldap_serverInfo_op(INFO_OP_GETSTAT, NULL, &soutstr);
if (soutstr == NULL) {
free(foutstr);
return;
}
(void) getldap_cache_op(CACHE_OP_GETSTAT, 0, 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(int *change_status)
{
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,
¶mVal, &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(¶mVal);
(void) __ns_ldap_freeError(&error);
return (-1);
}
if (paramVal && *paramVal)
profile = strdup((char *)*paramVal);
(void) __ns_ldap_freeParam(¶mVal);
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) {
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);
}
if (__s_api_crosscheck(ptr, errstr, B_TRUE) == NS_SUCCESS) {
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);
}
chg_test_config_change(ptr, change_status);
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;
ldap_get_chg_cookie_t cookie;
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) {
prev_refresh_time = tp.tv_sec;
}
(void) getldap_cache_op(CACHE_OP_CREATE,
0, NULL, NULL);
cookie.mgr_pid = getpid();
cookie.seq_num = 0;
chg_config_cookie_set(&cookie);
return (0);
}
static void
perform_update(void)
{
ns_ldap_error_t *error = NULL;
struct timeval tp;
char buf[20];
int rc, rc1;
int changed = 0;
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, ¶mVal, &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(¶mVal);
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) {
buf[19] = '\0';
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(&changed);
if (rc != 0) {
logit("Error: Unable to update from profile\n");
}
} while (checkupdate(sighup_update) == TRUE);
} else {
rc = 0;
}
if (rc == 0) {
(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
(void) getldap_cache_op(CACHE_OP_DELETE,
0, NULL, NULL);
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);
}
if (!changed)
return;
(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) {
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;
(void) pthread_setname_np(pthread_self(), "getldap_refresh");
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_refresh()...\n");
}
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,
¶mVal, &errorp) == NS_LDAP_SUCCESS) &&
paramVal != NULL &&
(char *)*paramVal != NULL) {
errno = 0;
expire = atol((char *)*paramVal);
(void) __ns_ldap_freeParam(¶mVal);
if (errno == 0) {
if (expire == 0) {
first_time = 0;
(void) rw_unlock(&ldap_lock);
(void) cond_init(&cond,
USYNC_THREAD, 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 (err == ETIME)
continue;
else {
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 (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, USYNC_THREAD, 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_config())
perform_update();
first_time = 0;
}
}
void
getldap_revalidate(int signal __unused)
{
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_revalidate()...\n");
}
(void) sighold(SIGHUP);
(void) cond_signal(&cond);
(void) sigrelse(SIGHUP);
}
void
getldap_admincred(LineBuf *config_info, ldap_call_t *in)
{
ns_ldap_error_t *error;
ldap_config_out_t *cout;
ucred_t *uc = NULL;
if (current_admin.debug_level >= DBG_ALL) {
logit("getldap_admincred()...\n");
}
if (is_root_or_all_privs("GETADMINCRED", &uc) == 0) {
logit("admin credential requested by a non-root and no ALL "
"privilege user not allowed");
config_info->str = NULL;
config_info->len = 0;
} else {
(void) rw_rdlock(&ldap_lock);
if ((error = __ns_ldap_LoadDoorInfo(config_info,
in->ldap_u.domainname, NULL, 1)) != 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;
}
cout = (ldap_config_out_t *)config_info->str;
if (cout)
cout->cookie = chg_config_cookie_get();
(void) rw_unlock(&ldap_lock);
}
}
void
getldap_lookup(LineBuf *config_info, ldap_call_t *in)
{
ns_ldap_error_t *error;
ldap_config_out_t *cout;
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, 0)) != 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;
}
cout = (ldap_config_out_t *)config_info->str;
if (cout)
cout->cookie = chg_config_cookie_get();
(void) rw_unlock(&ldap_lock);
}
void
test_server_change(server_info_t *head)
{
server_info_t *info;
int len = 0, num = 0, ds_len = 0, new_len = 0, tlen = 0;
char *tmp_buf = NULL, *ptr = NULL, *status = NULL;
ldap_get_change_out_t *cout;
ds_len = strlen(DOORLINESEP);
for (info = head; info; info = info->next) {
(void) mutex_lock(&info->mutex[0]);
if (info->sinfo[0].change != 0) {
len += 2 * ds_len + strlen(info->sinfo[0].addr) +
strlen(NS_SERVER_CHANGE_UP);
num++;
}
(void) mutex_unlock(&info->mutex[0]);
}
if (len == 0)
return;
len++;
tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
if ((tmp_buf = malloc(tlen)) == NULL)
return;
cout = (ldap_get_change_out_t *)tmp_buf;
cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
cout->server_count = num;
cout->data_size = len;
ptr = cout->data;
new_len = len;
for (info = head; info; info = info->next) {
(void) mutex_lock(&info->mutex[0]);
if (info->sinfo[0].change == 0) {
(void) mutex_unlock(&info->mutex[0]);
continue;
}
if (info->sinfo[0].change == NS_SERVER_UP)
status = NS_SERVER_CHANGE_UP;
else if (info->sinfo[0].change == NS_SERVER_DOWN)
status = NS_SERVER_CHANGE_DOWN;
else {
syslog(LOG_WARNING, gettext("Bad change value %d"),
info->sinfo[0].change);
(void) mutex_unlock(&info->mutex[0]);
free(tmp_buf);
return;
}
if ((snprintf(ptr, new_len, "%s%s%s%s",
info->sinfo[0].addr, DOORLINESEP,
status, DOORLINESEP)) >= new_len) {
(void) mutex_unlock(&info->mutex[0]);
break;
}
new_len -= strlen(ptr);
ptr += strlen(ptr);
(void) mutex_unlock(&info->mutex[0]);
}
(void) chg_notify_statusChange(tmp_buf);
}
static void
create_buf_and_notify(char *input, ns_server_status_t st)
{
rm_svr_t *rms = (rm_svr_t *)input;
char *tmp_buf, *ptr, *status;
int len, tlen;
ldap_get_change_out_t *cout;
len = 2 * strlen(DOORLINESEP) + strlen(rms->addr) +
strlen(NS_SERVER_CHANGE_UP) + 1;
tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
if ((tmp_buf = malloc(tlen)) == NULL)
return;
cout = (ldap_get_change_out_t *)tmp_buf;
cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
cout->server_count = 1;
cout->data_size = len;
ptr = cout->data;
if (st == NS_SERVER_UP)
status = NS_SERVER_CHANGE_UP;
else if (st == NS_SERVER_DOWN)
status = NS_SERVER_CHANGE_DOWN;
(void) snprintf(ptr, len, "%s%s%s%s",
rms->addr, DOORLINESEP, status, DOORLINESEP);
(void) chg_notify_statusChange(tmp_buf);
}
static int
contact_server(char *addr)
{
char *rootDSE = NULL;
ns_ldap_error_t *error = NULL;
int rc;
if (__ns_ldap_getRootDSE(addr, &rootDSE, &error,
SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
if (current_admin.debug_level >= DBG_ALL)
logit("get rootDSE %s failed. %s", addr,
error->message ? error->message : "");
rc = 0;
} else
rc = 1;
if (rootDSE)
free(rootDSE);
if (error)
(void) __ns_ldap_freeError(&error);
return (rc);
}
static void *
remove_server_thread(void *arg)
{
char *addr = (char *)arg, *out = NULL;
int up;
rm_svr_t rms;
(void) pthread_setname_np(pthread_self(), "remove_server");
up = contact_server(addr);
rms.addr = addr;
rms.up = up;
(void) getldap_serverInfo_op(INFO_OP_REMOVESERVER, (char *)&rms, &out);
free(addr);
thr_exit(NULL);
return (NULL);
}
static void
remove_server(char *addr)
{
if (thr_create(NULL, 0, remove_server_thread,
(void *)addr, THR_BOUND|THR_DETACHED, NULL) != 0) {
free(addr);
syslog(LOG_ERR, "thr_create failed for remove_server_thread");
}
}
static ns_server_status_t
set_server_status(char *input, server_info_t *head)
{
rm_svr_t *rms = (rm_svr_t *)input;
ns_server_status_t changed = 0;
server_info_t *info;
for (info = head; info != NULL; info = info->next) {
(void) mutex_lock(&info->mutex[0]);
if (strcmp(info->sinfo[0].addr, rms->addr) == 0) {
if (info->sinfo[0].server_status == INFO_SERVER_UP &&
rms->up == FALSE) {
info->sinfo[0].prev_server_status =
info->sinfo[0].server_status;
info->sinfo[0].server_status =
INFO_SERVER_ERROR;
info->sinfo[0].change = NS_SERVER_DOWN;
changed = NS_SERVER_DOWN;
} else if (info->sinfo[0].server_status ==
INFO_SERVER_ERROR && rms->up == TRUE) {
info->sinfo[0].prev_server_status =
info->sinfo[0].server_status;
info->sinfo[0].server_status =
INFO_SERVER_UP;
info->sinfo[0].change = NS_SERVER_UP;
changed = NS_SERVER_UP;
}
(void) mutex_unlock(&info->mutex[0]);
break;
}
(void) mutex_unlock(&info->mutex[0]);
}
if (changed) {
(void) mutex_lock(&info->mutex[1]);
info->sinfo[1].prev_server_status =
info->sinfo[1].server_status;
if (changed == NS_SERVER_DOWN)
info->sinfo[1].server_status = INFO_SERVER_ERROR;
else if (changed == NS_SERVER_UP)
info->sinfo[1].server_status = INFO_SERVER_UP;
(void) mutex_unlock(&info->mutex[1]);
}
return (changed);
}