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
* 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 <synch.h>
#include <pthread.h>
#include <unistd.h>
#include <lber.h>
#include <ldap.h>
#include <ctype.h> /* tolower */
#include "cachemgr.h"
#include "solaris-priv.h"
static int sighup_update = FALSE;
extern admin_t current_admin;
/* variables used for SIGHUP wakeup on sleep */
static mutex_t sighuplock;
/* refresh time statistics */
static time_t prev_refresh_time = 0;
/* variables used for signaling parent process */
static int signal_done = FALSE;
/* TCP connection timeout (in milliseconds) */
/* 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_t;
typedef enum {
INFO_SERVER_JUST_INITED = -1,
INFO_SERVER_UNKNOWN = 0,
INFO_SERVER_UP = 2,
INFO_SERVER_ERROR = 3,
typedef enum {
INFO_STATUS_UNKNOWN = 0,
INFO_STATUS_ERROR = 1,
INFO_STATUS_NEW = 2,
INFO_STATUS_OLD = 3
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_type_t;
typedef struct server_info_ext {
char *addr;
char *hostname;
char *rootDSE_data;
char *errormsg;
typedef struct server_info {
struct server_info *next;
/* 1: update copy lock */
typedef struct cache_hash {
char *from;
char *to;
struct cache_hash *next;
} cache_hash_t;
/*
* 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() {
int rc = 1;
(void) __ns_ldap_setServer(TRUE);
logit("Error: Unable to read '%s': %s\n",
rc = 0; /* FAIL */
} else
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;
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 *
{
p->type = CACHE_MAP_UNKNOWN;
if (p->from)
if (p->to)
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 *
{
while (idx) {
return (idx);
}
}
return ((cache_hash_t *)NULL);
}
/*
* Format and return the cache data statistics
*/
static int
{
#define C_HEADER0 "Cache data information: "
#define C_HEADER1 " Maximum cache entries: "
#define C_HEADER2 " Number of cache entries: "
int len;
logit("getldap_get_cacheData_stat()...\n");
}
return (-1);
return (NS_LDAP_SUCCESS);
}
static int
{
#define CACHE_HASH_MAX 257
#define CACHE_HASH_MAX_ENTRY 256
unsigned long hash;
int i;
static int entry_num = 0;
logit("getldap_cache_op()...\n");
}
switch (op) {
case CACHE_OP_CREATE:
logit("operation is CACHE_OP_CREATE...\n");
}
(void) rw_wrlock(&cache_lock);
for (i = 0; i < CACHE_HASH_MAX; i++) {
}
entry_num = 0;
(void) rw_unlock(&cache_lock);
break;
case CACHE_OP_DELETE:
logit("operation is CACHE_OP_DELETE...\n");
}
(void) rw_wrlock(&cache_lock);
for (i = 0; i < CACHE_HASH_MAX; i++) {
}
}
entry_num = 0;
(void) rw_unlock(&cache_lock);
break;
case CACHE_OP_ADD:
logit("operation is CACHE_OP_ADD...\n");
}
return (-1);
(void) rw_wrlock(&cache_lock);
/*
* replace old "to" value with new one
* if an entry with same "from"
* already exists
*/
if (idx) {
if (newp) {
(void) rw_unlock(&cache_lock);
return (NS_LDAP_SUCCESS);
}
}
if (entry_num > CACHE_HASH_MAX_ENTRY) {
(void) rw_unlock(&cache_lock);
return (-1);
}
(void) rw_unlock(&cache_lock);
return (NS_LDAP_MEMORY);
}
entry_num++;
(void) rw_unlock(&cache_lock);
break;
case CACHE_OP_FIND:
logit("operation is CACHE_OP_FIND...\n");
}
return (-1);
(void) rw_rdlock(&cache_lock);
if (idx)
(void) rw_unlock(&cache_lock);
return (-1);
break;
case CACHE_OP_GETSTAT:
logit("operation is CACHE_OP_GETSTAT...\n");
}
return (-1);
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
{
logit("sync_current_with_update_copy()...\n");
}
/* free memory in current copy first */
/*
* make current and update copy identical
*/
/*
* 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.
*/
}
static void *
getldap_get_rootDSE(void *arg)
{
int ldapVersion = LDAP_VERSION3;
LDAPMessage *e;
char *rootDSE;
char *attrs[3];
char *a;
char **vals;
int ldaperrno = 0;
int i = 0, len = 0;
int server_found = 0;
logit("getldap_get_rootDSE()....\n");
}
/* initialize the server info element */
/*
* 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
*/
/* SKIP ldap data base to prevent recursion */
/* in gethostbyname when resolving hostname */
logit("getldap_get_rootDSE: %s.\n",
}
/*
* sync sinfo copies in the serverInfo.
* protected by mutex
*/
thr_exit((void *) -1);
}
/* currently, only interested in two attributes */
attrs[0] = "supportedControl";
"(objectclass=*)",
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:
logit("getldap_get_rootDSE: Root DSE not found."
" %s is not an LDAPv3 server (%s).\n",
}
if (resultMsg)
/*
* sync sinfo copies in the serverInfo.
* protected by mutex
*/
thr_exit((void *) -1);
break;
}
/* calculate length of root DSE data */
a != NULL;
}
}
ldap_memfree(a);
}
/* copy root DSE data */
if (len) {
/* add 1 for the last '\0' */
/* make it an empty string first */
*rootDSE = '\0';
a != NULL;
a = ldap_next_attribute(
if ((vals = ldap_get_values(
i++) {
int len;
+ 2;
(void) snprintf(
rootDSE +
len, "%s=%s%s",
a, vals[i],
}
}
ldap_memfree(a);
}
} else
len = 0;
}
}
/* error, if no root DSE data */
if (len == 0) {
logit("getldap_get_rootDSE: %s.\n",
}
exitrc = -1;
} else {
/* assume writeable, i.e., can do modify */
/* remove the last DOORLINESEP */
server_found = 1;
}
if (resultMsg)
/*
* sync sinfo copies in the serverInfo.
* protected by mutex
*/
/*
* signal that the ldap_cachemgr parent process
* should exit now, if it is still waiting
*/
(void) mutex_lock(&sig_mutex);
logit("getldap_get_rootDSE(): "
"SIGUSR1 signal sent to "
"parent process(%ld).\n", ppid);
}
signal_done = TRUE;
}
(void) mutex_unlock(&sig_mutex);
return ((void *) NULL);
}
static int
{
logit("getldap_init_serverInfo()...\n");
}
if (rc != NS_LDAP_SUCCESS) {
logit("getldap_init_serverInfo: "
"__s_api_getServers failed.\n");
if (errorp)
return (-1);
}
logit("getldap_init_serverInfo: "
"not enough memory.\n");
break;
}
if (i == 0) {
} else {
}
logit("getldap_init_serverInfo: "
"not enough memory.\n");
break;
}
logit("getldap_init_serverInfo: "
"not enough memory.\n");
break;
}
/*
* Assume at startup or after the configuration
* profile is refreshed, all servers are good.
*/
}
if (exitrc != NS_LDAP_SUCCESS) {
(void) getldap_destroy_serverInfo(*head);
}
}
return (exitrc);
}
static int
{
logit("getldap_destroy_serverInfo()...\n");
}
logit("getldap_destroy_serverInfo: "
"invalid serverInfo list.\n");
return (-1);
}
}
return (NS_LDAP_SUCCESS);
}
static int
int reset_bindtime)
{
int atleast1 = 0;
int num_threads = 0, i, j;
void *status;
logit("getldap_set_serverInfo()...\n");
}
logit("getldap_set_serverInfo: "
"invalid serverInfo list.\n");
return (-1);
}
/* Get the bind timeout value */
if (reset_bindtime == 1) {
/* convert to milliseconds */
tcptimeout = **((int **)paramVal);
tcptimeout *= 1000;
(void) __ns_ldap_freeParam(¶mVal);
}
if (error)
(void) __ns_ldap_freeError(&error);
/* get search timeout value */
search_timeout = **((int **)paramVal);
(void) __ns_ldap_freeParam(¶mVal);
}
if (error)
(void) __ns_ldap_freeError(&error);
}
num_threads++;
if (num_threads == 0) {
logit("getldap_set_serverInfo: "
"empty serverInfo list.\n");
return (-1);
}
logit("getldap_set_serverInfo: "
"No memory to create thread ID list.\n");
return (-1);
}
if (thr_create(NULL, 0,
(void *(*)(void*))getldap_get_rootDSE,
logit("getldap_set_serverInfo: "
"can not create thread %d.\n", i + 1);
for (j = 0; j < i; j++)
return (-1);
}
}
for (i = 0; i < num_threads; i++) {
if ((int)status == NS_LDAP_SUCCESS)
atleast1 = 1;
}
}
if (atleast1)
return (NS_LDAP_SUCCESS);
else
return (-1);
}
/*
* Convert an IP to a host name
*/
static int
return (NS_LDAP_INVALID_PARAM);
return (NS_LDAP_MEMORY);
if (addr[0] == '[') {
/*
* Assume it's [ipv6]:port
* Extract ipv6 IP
*/
*end = '\0';
delim = ']';
/* extract port */
} else {
return (NS_LDAP_INVALID_PARAM);
}
/* assume it's ipv4:port */
*end = '\0';
delim = ':';
} else
/* No port */
/* IPv4 */
/* hostname + '\0' */
if (port)
/* ':' + port */
return (NS_LDAP_MEMORY);
}
if (port)
else
return (NS_LDAP_SUCCESS);
} else {
return (NS_LDAP_NOTFOUND);
}
/* IPv6 */
/* hostname + '\0' */
if (port)
/* ':' + port */
return (NS_LDAP_MEMORY);
}
if (port)
else
return (NS_LDAP_SUCCESS);
} else {
return (NS_LDAP_NOTFOUND);
}
} else {
/*
* A hostname
* Return it as is
*/
if (end)
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
* 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
char **output, int *svr_removed)
{
char req_new[] = NS_CACHE_NEW;
char addr_type[] = NS_CACHE_ADDR_IP;
logit("getldap_get_serverInfo()...\n");
}
logit("getldap_get_serverInfo: "
"No input or output buffer.\n");
return (-1);
}
*svr_removed = FALSE;
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
*/
if (input[0] != '\0') {
/* Save addr type flag */
/* skip acion type flag, addr type flag and DOORLINESEP */
}
/*
* if NS_CACHE_NEW,
* or the server info is new,
* starts from the
* beginning of the list
*/
/*
* 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
*/
/*
* if the server has already been removed,
* don't bother
*/
continue;
}
/*
* if the information is new,
* give this server one more chance
*/
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.
*/
/*
* make sure this will be seen
* if a user query the server
* status via the ldap_cachemgr's
* -g option
*/
*svr_removed = TRUE;
continue;
}
} else {
/*
* req == NS_CACHE_NEXT or NS_CACHE_WRITE
*/
continue;
}
}
if (matched) {
break;
}
break;
}
}
}
if (server) {
/*
* Kerberos's service principal.
* e.g.
* ldap/foo.sun.com@SUN.COM
*/
if (rc != NS_LDAP_SUCCESS) {
return (rc);
}
logit("getldap_get_serverInfo: "
"%s is converted to %s\n",
}
}
} else
if (ret_addrFQDN != NULL)
return (NS_LDAP_MEMORY);
}
if (ret_addrFQDN == NULL)
else
return (NS_LDAP_SUCCESS);
}
else
return (-99);
}
/*
* Format previous and next refresh time
*/
static int
{
#define TIME_FORMAT "%Y/%m/%d %H:%M:%S"
#define TIME_HEADER1 " Previous refresh time: "
#define TIME_HEADER2 " Next refresh time: "
char nbuf[256];
char pbuf[256];
int len;
logit("getldap_format_refresh_time()...\n");
}
/* format the time of previous refresh */
if (*prev != 0) {
} else {
}
/* format the time of next refresh */
if (*next != 0) {
} else {
}
return (-1);
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
{
#define S_HEADER "Server information: "
#define S_FORMAT " server: %s, status: %s%s"
#define S_ERROR " error message: %s%s"
int len1 = 0;
logit("getldap_get_server_stat()...\n");
}
logit("getldap_get_server_stat: "
"invalid serverInfo list.\n");
return (-1);
}
/* format previous and next refresh time */
return (-1);
return (-1);
}
/* insert header string and refresh time info */
/*
* make sure the server info stays the same
* while the data is being processed
*/
/*
* 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).
*
*/
case INFO_SERVER_UNKNOWN:
break;
case INFO_SERVER_CONNECTING:
break;
case INFO_SERVER_UP:
break;
case INFO_SERVER_ERROR:
break;
case INFO_SERVER_REMOVED:
break;
}
return (-1);
} else
/* insert server IP addr or name and status */
/* insert error message if any */
}
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."
logit("getldap_get_refresh_stat()...\n");
}
/* get configured cache TTL */
} else {
if (errorp)
}
(void) __ns_ldap_freeParam(¶mVal);
/* cound not get cache TTL */
if (cache_ttl == -1)
return (-1);
if (cache_ttl == 0) {
return (-1);
} else {
/* get configuration expiration time */
} else {
if (errorp)
}
(void) __ns_ldap_freeParam(¶mVal);
/* 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);
return (-1);
return (-1);
}
}
return (NS_LDAP_SUCCESS);
}
static int
{
logit("getldap_get_cacheTTL()....\n");
}
logit("Error: Unable to get configuration "
"refresh TTL: %s\n",
else {
char *tmp;
logit("Error: Unable to get configuration "
"refresh TTL: %s\n", tmp);
}
(void) __ns_ldap_freeParam(¶mVal);
(void) __ns_ldap_freeError(&error);
return (-1);
}
return (-1);
(void) __ns_ldap_freeParam(¶mVal);
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.
* 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
* 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
* the value will move up to maximum and stay there if the
*/
static int
int *no_gd_server)
{
#define REFRESHTTL_REGULAR 600
#define REFRESHTTL_MAX 43200
/* tcptimeout is in milliseconds */
#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;
/* allow one thread at a time */
(void) mutex_lock(&refresh_mutex);
logit("getldap_set_refresh_ttl()...\n");
}
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
*/
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)
*/
/*
* 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)
*/
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",
}
if (refresh_ttl_max <= 0)
else if (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
*/
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",
}
}
/*
* get the servers statistics:
* number of servers on list
* number of good servers on list
* number of pevious good servers on list
*/
num_servers++;
/*
* Server's previous status could be UNKNOWN
* only between the very first and second
* refresh. Treat that UNKNOWN status as up
*/
== INFO_SERVER_UP ||
}
/*
* 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) {
if (num_walked_ok > UP_REFRESH_TTL_NUM) {
if (*refresh_ttl > refresh_ttl_max)
num_walked_ok = 0;
}
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
*/
*no_gd_server = TRUE;
num_walked_ok = 0;
logit("getldap_set_refresh_ttl:(5) refresh ttl is %d "
"seconds\n", *refresh_ttl);
}
} else if (num_prev_good_servers > num_good_servers) {
/*
* decrease the refresh TTL by half
* but no less than the min. refresh TTL
*/
if (*refresh_ttl < refresh_ttl_min)
num_walked_ok = 0;
logit("getldap_set_refresh_ttl:(6) refresh ttl is %d "
"seconds\n", *refresh_ttl);
}
logit("getldap_set_refresh_ttl:(7) refresh ttl is %d seconds\n",
*refresh_ttl);
}
(void) mutex_unlock(&refresh_mutex);
return (0);
}
static int
{
static mutex_t info_mutex;
static int refresh_ttl = 0;
static int sec_to_refresh = 0;
static int in_no_server_mode = FALSE;
int is_creating;
int server_removed = FALSE;
struct timespec new_timeout;
logit("getldap_serverInfo_op()...\n");
}
switch (op) {
case INFO_OP_CREATE:
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);
(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);
(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.
*/
/*
* 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)
/*
* fill the new info list
*/
/* reset bind time (tcptimeout) */
(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,
/* statistics: previous refresh time */
/*
* 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;
} else
/*
* awake the sleeping refresh thread
*/
(void) cond_signal(&info_cond);
(void) mutex_unlock(&info_mutex);
/*
* 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.
*/
(void) rw_unlock(&info_lock_old);
break;
case INFO_OP_DELETE:
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.
*/
if (serverInfo)
(void) getldap_destroy_serverInfo(serverInfo);
serverInfo = NULL;
break;
case INFO_OP_REFRESH:
logit("operation is INFO_OP_REFRESH...\n");
}
/*
* if server info is currently being
* (re)created, do nothing
*/
(void) mutex_lock(&info_mutex);
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
if (serverInfo) {
/* do not reset bind time (tcptimeout) */
(void) getldap_set_serverInfo(serverInfo, 0);
(void) mutex_lock(&info_mutex);
/* statistics: previous refresh time */
/*
* set cache manager server list TTL
*/
(void) getldap_set_refresh_ttl(serverInfo,
/*
* 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) {
sec_to_refresh = 0;
} else {
}
if (current_admin.debug_level >=
logit("getldap_serverInfo_op("
"INFO_OP_REFRESH):"
" seconds refresh: %d second(s)....\n",
}
(void) mutex_unlock(&info_mutex);
}
break;
case INFO_OP_REFRESH_WAIT:
logit("operation is INFO_OP_REFRESH_WAIT...\n");
}
(void) mutex_lock(&info_mutex);
err = 0;
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) {
if (current_admin.debug_level >=
logit("getldap_serverInfo_op("
"INFO_OP_REFRESH_WAIT):"
" entering no-server "
"refresh loop...\n");
}
} else {
}
/* statistics: next refresh time */
if (current_admin.debug_level >=
logit("getldap_serverInfo_op("
"INFO_OP_REFRESH_WAIT):"
" about to sleep for %d second(s)...\n",
}
&info_mutex, &timeout);
}
(void) cond_destroy(&info_cond);
(void) mutex_unlock(&info_mutex);
break;
case INFO_OP_GETSERVER:
logit("operation is INFO_OP_GETSERVER...\n");
}
/*
* 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,
(void) rw_unlock(&info_lock_old);
/*
* if server info is currently being
* (re)created, do nothing
*/
(void) mutex_lock(&info_mutex);
(void) mutex_unlock(&info_mutex);
if (is_creating)
break;
/*
* set cache manager server list TTL if necessary
*/
(void) mutex_lock(&info_mutex);
(void) getldap_set_refresh_ttl(serverInfo,
/*
* 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;
(void) cond_signal(&info_cond);
}
(void) mutex_unlock(&info_mutex);
break;
} else {
}
/*
* 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
*/
(void) cond_signal(&info_cond);
(void) mutex_unlock(&info_mutex);
}
break;
case INFO_OP_GETSTAT:
logit("operation is INFO_OP_GETSTAT...\n");
}
if (serverInfo) {
(void) getldap_get_server_stat(serverInfo,
}
break;
default:
logit("getldap_serverInfo_op(): "
"invalid operation code (%d).\n", op);
return (-1);
break;
}
return (NS_LDAP_SUCCESS);
}
void
{
int always = 1;
logit("getldap_serverInfo_refresh()...\n");
}
/* create the server info list */
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
{
char req[] = "0";
logit("getldap_getserver()...\n");
}
config_info->len = 0;
/* make sure the request is valid */
if ((req[0] != '\0') &&
return;
}
return;
/* Log server IP */
char *ptr,
if (ptr) {
*ptr = '\0';
logit("getldap_getserver: got server %s\n",
config_info->str);
} else
logit("getldap_getserver: Missing %s."
" Internal error\n", DOORLINESEP);
}
}
void
{
int datatype = CACHE_MAP_UNKNOWN;
logit("getldap_get_cacheData()...\n");
}
config_info->len = 0;
/* make sure the request is valid */
if (datatype == CACHE_MAP_UNKNOWN)
return;
return;
if (*instr == '\0')
return;
}
}
int
{
int datatype = CACHE_MAP_UNKNOWN;
int rc = 0;
logit("getldap_set_cacheData()...\n");
}
/* make sure the request is valid */
if (datatype == CACHE_MAP_UNKNOWN)
return (-1);
return (-1);
*instr1 = '\0';
if (*instr1 == '\0')
return (-1);
return (-1);
*instr2 = '\0';
if (*instr2 == '\0')
return (-1);
if (rc != NS_LDAP_SUCCESS)
return (-1);
return (0);
}
void
{
int infoSize;
logit("getldap_get_cacheStat()...\n");
}
/* get refersh statisitcs */
(void) getldap_get_refresh_stat(&foutstr);
return;
/* get server statisitcs */
return;
}
/* get cache data statisitcs */
return;
}
}
}
static int
checkupdate(int sighup)
{
int value;
}
static int
{
char searchfilter[BUFSIZ];
int rc;
logit("update_from_profile....\n");
}
do {
logit("Error: Unable to profile name: %s\n",
else {
char *tmp;
logit("Error: Unable to profile name: %s\n",
tmp);
}
(void) __ns_ldap_freeParam(¶mVal);
(void) __ns_ldap_freeError(&error);
return (-1);
}
(void) __ns_ldap_freeParam(¶mVal);
return (-1);
}
(const char *)searchfilter, NULL,
/*
* Is profile name the DEFAULTCONFIGNAME?
* syslog Warning, otherwise syslog error.
*/
"Ignoring attempt to refresh nonexistent "
"default profile: %s.\n",
profile);
logit("Ignoring attempt to refresh nonexistent "
"default profile: %s.\n",
profile);
"Error: Unable to refresh profile:%s:"
logit("Error: Unable to refresh profile:"
} else {
"from profile:%s. (error=%d)\n",
logit("Error: Unable to refresh from profile "
}
(void) __ns_ldap_freeError(&error);
(void) __ns_ldap_freeResult(&result);
return (-1);
}
(void) __ns_ldap_freeResult(&result);
logit("Error: __ns_ldap_make_config failed.\n");
return (-1);
}
/*
* cross check the config parameters
*/
/*
* reset the local profile TTL
*/
logit("update_from_profile: reset profile TTL to %d"
" seconds\n",
logit("update_from_profile: expire time %ld "
"seconds\n",
}
/* set ptr as current_config */
rc = 0;
} else {
logit("Error: downloaded profile failed to pass "
"crosscheck (%s).\n", errstr);
rc = -1;
}
return (rc);
}
int
{
logit("getldap_init()...\n");
}
(void) __ns_ldap_setServer(TRUE);
logit("Error: Unable to read '%s': %s\n",
gettext("\nError: Unable to read '%s': %s\n"),
return (-1);
}
/* statistics: previous refresh time */
}
/* initialize the data cache */
(void) getldap_cache_op(CACHE_OP_CREATE,
return (0);
}
static void
perform_update(void)
{
char buf[20];
logit("perform_update()...\n");
}
(void) __ns_ldap_setServer(TRUE);
return;
}
(void) __ns_ldap_freeError(&error);
(void) __ns_ldap_freeParam(¶mVal);
logit("perform_update: current profile TTL is %d seconds\n",
}
/*
* 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]),
&error) != NS_LDAP_SUCCESS) {
logit("Error: __ns_ldap_setParam failed, status: %d "
(void) __ns_ldap_freeError(&error);
return;
}
do {
rc = update_from_profile();
if (rc != 0) {
logit("Error: Unable to update from profile\n");
}
} else {
rc = 0;
}
/*
* recreate the server info list
*/
if (rc == 0) {
/* flush the data cache */
(void) getldap_cache_op(CACHE_OP_DELETE,
/* statistics: previous refresh time */
}
if (rc1 == NS_LDAP_SUCCESS) {
if (config != NS_LDAP_SELF_GSSAPI_CONFIG_NONE) {
(void) __ns_ldap_freeError(&error);
if (rc1 != NS_LDAP_SUCCESS) {
logit("Error: Check on self credential "
"prerquesites failed: %d\n",
rc1);
}
}
} else {
logit("Error: Failed to get self credential configuration %d\n",
rc1);
}
logit("Error: __ns_ldap_DumpConfiguration failed, "
return;
}
"files not written");
return;
}
/*
* 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.
*/
"LDAP configuration files inconsistent");
return;
}
}
void
{
int sleeptime;
long expire = 0;
int first_time = 1;
int sig_done = 0;
int dbg_level;
logit("getldap_refresh()...\n");
}
/*
* wait for an available server
*/
while (sig_done == 0) {
(void) mutex_lock(&sig_mutex);
(void) mutex_unlock(&sig_mutex);
}
(void) __ns_ldap_setServer(TRUE);
while (always) {
if (dbg_level >= DBG_PROFILE_REFRESH) {
logit("getldap_refresh: current profile TTL is %d "
}
errno = 0;
(void) __ns_ldap_freeParam(¶mVal);
if (errno == 0) {
if (expire == 0) {
first_time = 0;
(void) mutex_lock(&sighuplock);
if (dbg_level >=
logit("getldap_refresh:"
"(1)about to sleep"
" for %d seconds\n",
}
&sighuplock, &timeout);
(void) cond_destroy(&cond);
(void) mutex_unlock(
&sighuplock);
/*
* if woke up by
* getldap_revalidate(),
* do update right away
*/
continue;
else {
/*
* if load
* configuration failed
* don't do update
*/
if (load_config())
();
continue;
}
}
if (dbg_level >= DBG_PROFILE_REFRESH) {
logit("getldap_refresh: expire "
"time = %ld\n", expire);
}
}
}
}
/*
* 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) mutex_lock(&sighuplock);
&sighuplock, &timeout);
(void) cond_destroy(&cond);
(void) mutex_unlock(&sighuplock);
}
/*
* if load concfiguration failed
* don't do update
*/
if (load_config())
first_time = 0;
}
}
void
{
logit("getldap_revalidate()...\n");
}
/* block signal SIGHUP */
/* now awake the sleeping refresh thread */
(void) cond_signal(&cond);
/* release signal SIGHUP */
}
void
{
logit("getldap_lookup()...\n");
}
(void) __ns_ldap_freeError(&error);
config_info->len = 0;
}
}