OpenSolaris_b135/cmd/nscd/nscd_config.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>		/* gettext */
#include <dlfcn.h>
#include <string.h>
#include <sys/varargs.h>
#include <errno.h>
#include "nscd_db.h"
#include "nscd_config.h"
#include "nscd_cfgdef.h"
#include "nscd_log.h"

typedef struct {
	rwlock_t	*global;
	rwlock_t	*alldb;
	rwlock_t	*nswdb;
} nscd_cfg_lock_t;

static rwlock_t		cfg_paramDB_rwlock = DEFAULTRWLOCK;
static nscd_db_t	*cfg_paramDB = NULL;

static	nscd_cfg_global_data_t 	*nscd_cfg_global_current;
static	nscd_cfg_nsw_db_data_t	*nscd_cfg_nsw_db_data_current;
static	nscd_cfg_nsw_db_data_t	*nscd_cfg_nsw_alldb_current;
static	rwlock_t		*nscd_cfg_global_rwlock;
static	rwlock_t		*nscd_cfg_nsw_db_data_rwlock;
static	rwlock_t		*nscd_cfg_nsw_alldb_rwlock;

extern	int			_nscd_cfg_num_nsw_src_all;
extern	nscd_cfg_id_t		*_nscd_cfg_nsw_src_all;

nscd_cfg_error_t *
_nscd_cfg_make_error(
	nscd_rc_t	rc,
	char		*msg)
{

	nscd_cfg_error_t	*ret;
	int			size, msglen;

	msglen = (msg != NULL ? strlen(msg) + 1 : 0);

	size = sizeof (nscd_cfg_error_t) + msglen;

	ret = calloc(1, size);
	if (ret == NULL)
		return (NULL);

	ret->rc = rc;
	if (msg != NULL) {
		ret->msg = (char *)ret +
		    sizeof (nscd_cfg_error_t);
		(void) memcpy(ret->msg, msg, msglen);
	}

	return (ret);
}

static nscd_rc_t
_nscd_cfg_get_list(
	nscd_cfg_list_t		**list,
	nscd_cfg_list_type_t	type)
{
	char			*me = "_nscd_cfg_get_list";
	int			i, num, size;
	nscd_cfg_id_t		*l;
	nscd_cfg_list_t 	*ret;
	nscd_cfg_param_desc_t	*pl;
	nscd_cfg_stat_desc_t	*sl;
	void			*p;

	if (list == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "invalid argument: list = %p\n", list);

		return (NSCD_INVALID_ARGUMENT);
	}
	*list = NULL;

	switch (type) {
	case NSCD_CFG_LIST_NSW_DB:

		num = _nscd_cfg_num_nsw_db;
		l = &_nscd_cfg_nsw_db[0];
		break;

	case NSCD_CFG_LIST_NSW_SRC:

		num = _nscd_cfg_num_nsw_src_all;
		l = _nscd_cfg_nsw_src_all;
		break;

	case NSCD_CFG_LIST_PARAM:

		num = _nscd_cfg_num_param;
		pl = &_nscd_cfg_param_desc[0];
		break;

	case NSCD_CFG_LIST_STAT:

		num = _nscd_cfg_num_stat;
		sl = &_nscd_cfg_stat_desc[0];
		break;

	default:
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "invalid argument: type (%d)\n", type);

		return (NSCD_INVALID_ARGUMENT);
		break;
	}

	size =  sizeof (nscd_cfg_list_t) + sizeof (nscd_cfg_id_t *) * (num + 1);

	ret = calloc(1, size);
	if (ret == NULL)
		return (NSCD_NO_MEMORY);

	ret->num = num;
	p = (char *)ret + sizeof (nscd_cfg_list_t);
	ret->list = (nscd_cfg_id_t **)p;

	if (type == NSCD_CFG_LIST_PARAM) {
		for (i = 0; i <= num; i++)
			ret->list[i] = (nscd_cfg_id_t *)&pl[i];
	} else if (type == NSCD_CFG_LIST_STAT) {
		for (i = 0; i <= num; i++)
			ret->list[i] = (nscd_cfg_id_t *)&sl[i];
	} else {
		for (i = 0; i <= num; i++)
			ret->list[i] = &l[i];
	}

	*list = ret;

	return (NSCD_SUCCESS);
}

nscd_rc_t
_nscd_cfg_get_param_desc_list(
	nscd_cfg_param_desc_list_t **list)
{
	return (_nscd_cfg_get_list((nscd_cfg_list_t **)list,
	    NSCD_CFG_LIST_PARAM));
}

/*
 * FUNCTION: _nscd_cfg_create_paramDB
 *
 * Create the internal config parameter database
 */
static nscd_db_t *
_nscd_cfg_create_paramDB()
{

	nscd_db_t	*ret;

	(void) rw_wrlock(&cfg_paramDB_rwlock);

	ret = _nscd_alloc_db(NSCD_DB_SIZE_MEDIUM);

	if (ret != NULL)
		cfg_paramDB = ret;

	(void) rw_unlock(&cfg_paramDB_rwlock);

	return (ret);
}

/*
 * FUNCTION: _nscd_cfg_add_index_entry
 *
 * Add a config index entry (a name to index mapping)
 * to the internal configuration database.
 */
static nscd_rc_t
_nscd_cfg_add_index_entry(
	char			*name,
	int			index,
	nscd_cfg_list_type_t	type)
{
	int		*idx;
	int		size;
	int		dbe_type;
	nscd_db_entry_t	*db_entry;

	if (name == NULL)
		return (NSCD_INVALID_ARGUMENT);

	if (type == NSCD_CFG_LIST_NSW_DB)
		dbe_type = NSCD_DATA_CFG_NSW_DB_INDEX;
	else if (type == NSCD_CFG_LIST_NSW_SRC)
		dbe_type = NSCD_DATA_CFG_NSW_SRC_INDEX;
	else if (type == NSCD_CFG_LIST_PARAM)
		dbe_type = NSCD_DATA_CFG_PARAM_INDEX;
	else if (type == NSCD_CFG_LIST_STAT)
		dbe_type = NSCD_DATA_CFG_STAT_INDEX;

	size = sizeof (int);

	db_entry = _nscd_alloc_db_entry(dbe_type, (const char *)name,
	    size, 1, 1);
	if (db_entry == NULL)
		return (NSCD_NO_MEMORY);

	idx = (int *)*(db_entry->data_array);
	*idx = index;

	(void) rw_wrlock(&cfg_paramDB_rwlock);
	(void) _nscd_add_db_entry(cfg_paramDB, name, db_entry,
	    NSCD_ADD_DB_ENTRY_FIRST);
	(void) rw_unlock(&cfg_paramDB_rwlock);

	return (NSCD_SUCCESS);
}

/*
 * FUNCTION: _nscd_cfg_get_index
 *
 * Get the index of a config data item by searching the internal config
 * database. Do not free the returned data.
 */
static int
_nscd_cfg_get_index(
	char			*name,
	nscd_cfg_list_type_t	type)
{
	int			index = -1, dbe_type;
	const nscd_db_entry_t	*db_entry;

	if (name == NULL)
		return (-1);

	if (type == NSCD_CFG_LIST_NSW_DB)
		dbe_type = NSCD_DATA_CFG_NSW_DB_INDEX;
	else if (type == NSCD_CFG_LIST_NSW_SRC)
		dbe_type = NSCD_DATA_CFG_NSW_SRC_INDEX;
	else if (type == NSCD_CFG_LIST_PARAM)
		dbe_type = NSCD_DATA_CFG_PARAM_INDEX;
	else if (type == NSCD_CFG_LIST_STAT)
		dbe_type = NSCD_DATA_CFG_STAT_INDEX;
	else
		return (-1);

	db_entry = _nscd_get_db_entry(cfg_paramDB, dbe_type,
	    (const char *)name, NSCD_GET_FIRST_DB_ENTRY, 0);

	if (db_entry != NULL)
		index = *(int *)*(db_entry->data_array);

	return (index);
}

static nscd_rc_t
_nscd_cfg_verify_group_info(
	nscd_cfg_group_info_t	*g_info,
	nscd_cfg_param_desc_t	*gdesc)
{

	char			*me = "_nscd_cfg_verify_group_info";
	void			*vp;
	nscd_cfg_group_info_t	*gi;

	if (_nscd_cfg_flag_is_set(gdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) {
		vp = (char *)&nscd_cfg_global_default +
		    gdesc->g_offset;
		gi = (nscd_cfg_group_info_t *)vp;
	} else {
		vp = (char *)&nscd_cfg_nsw_db_data_default +
		    gdesc->g_offset;
		gi = (nscd_cfg_group_info_t *)vp;

	}

	if (g_info->num_param == gi->num_param &&
	    _nscd_cfg_bitmap_is_equal(g_info->bitmap, gi->bitmap))
		return (NSCD_SUCCESS);

	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
	(me, "ERROR: group (%s) info mismatched: group info "
	    "(%d, %#6.4x) not equal to that of default configuration data "
	    "(%d, %#6.4x)\n", gdesc->id.name, g_info->num_param,
	    _nscd_cfg_bitmap_value(g_info->bitmap), gi->num_param,
	    _nscd_cfg_bitmap_value(gi->bitmap));

	return (NSCD_CFG_PARAM_DESC_ERROR);

}


static nscd_rc_t
_nscd_cfg_init_nsw()
{
	char			*me = "_nscd_cfg_init_nsw";
	int			i, j, idx, rc, num;
	nscd_cfg_id_t		*id;
	nscd_cfg_list_type_t	type[2] = { NSCD_CFG_LIST_NSW_DB,
					NSCD_CFG_LIST_NSW_SRC };

	nscd_cfg_id_t		*list[2] = { _nscd_cfg_nsw_db, NULL};

	list[1] = _nscd_cfg_nsw_src_all;

	for (j = 0; j < 2; j++) {

		if (j == 0)
			num = _nscd_cfg_num_nsw_db + 1;
		else
			num = _nscd_cfg_num_nsw_src_all;

		for (i = 0; i < num; i++) {

			/*
			 * _nscd_cfg_nsw_alldb is the id for the
			 * special ALLDB (defaults for all db)
			 */
			if (j == 0 && i == _nscd_cfg_num_nsw_db) {
				id = &_nscd_cfg_nsw_alldb;
				idx = NSCD_CFG_NSW_ALLDB_INDEX;
			} else {
				id = &(list[j])[i];
				id->index = idx = i;
			}

			if (id->name == NULL)
				continue;

			if ((rc = _nscd_cfg_add_index_entry(id->name,
			    idx, type[j])) != NSCD_SUCCESS) {

				_NSCD_LOG(NSCD_LOG_CONFIG,
				    NSCD_LOG_LEVEL_ERROR)
				(me, "unable to add index entry for "
				"nsswitch entry %s\n", id->name);

				_nscd_free_db(cfg_paramDB);
				return (rc);
			}
		}
	}

	return (NSCD_SUCCESS);
}

static nscd_rc_t
_nscd_cfg_init_param()
{
	char			*me = "_nscd_cfg_init_param";
	int			i, gi, fn = 0;
	nscd_cfg_id_t		*id;
	nscd_cfg_param_desc_t	*desc, *gdesc = NULL;
	nscd_cfg_group_info_t	g_info;
	nscd_cfg_list_type_t	type = NSCD_CFG_LIST_PARAM;
	nscd_rc_t		rc;
	void			*nfunc, *vfunc;

	if (_nscd_cfg_create_paramDB() == NULL)
		return (NSCD_NO_MEMORY);

	desc = &_nscd_cfg_param_desc[0];

	/*
	 * need to loop to the last (+1) param description
	 * which is a fake group and which marks the end
	 * of list. It is used to signal the end of the
	 * previous group so that the proper data will be
	 * set for that group
	 */
	for (i = 0; i < _nscd_cfg_num_param + 1; i++, desc++) {

		id = (nscd_cfg_id_t *)desc;

		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_GROUP)) {

			if (gdesc != NULL) {
				g_info.num_param = fn;
				gdesc->p_fn = fn;

				if ((rc = _nscd_cfg_verify_group_info(
				    &g_info, gdesc)) != NSCD_SUCCESS)
					return (rc);
			}

			gi = i;
			fn = 0;
			gdesc = desc;
			g_info.bitmap = NSCD_CFG_BITMAP_ZERO;

			/*
			 * set the notify/verify functions
			 */
			nfunc = (void *)gdesc->notify;
			vfunc = (void *)gdesc->verify;
		} else {
			if (i == 0) {

				_NSCD_LOG(NSCD_LOG_CONFIG,
				    NSCD_LOG_LEVEL_ERROR)
				(me, "ERROR: first parameter "
				"description is not for a group\n");

				return (NSCD_CFG_PARAM_DESC_ERROR);
			}

			/*
			 * set bitmap: the rightmost bit represents
			 * the first member (index = 0) in the group,
			 * the next bit is for the second member
			 * (index = 1), and so on
			 */
			_nscd_cfg_bitmap_set_nth(g_info.bitmap, fn);

			desc->p_fn = fn++;

			/*
			 * set the notify/verify functions
			 */
			if (desc->notify == NSCD_CFG_FUNC_NOTIFY_AS_GROUP) {
				(void) memcpy(&desc->notify, &nfunc,
				    sizeof (void *));
			}
			if (desc->verify == NSCD_CFG_FUNC_VERIFY_AS_GROUP) {
				(void) memcpy(&desc->verify, &vfunc,
				    sizeof (void *));
			}
		}

		/* if end of list reached, we are done */
		if (i == _nscd_cfg_num_param)
			break;

		desc->g_index = gi;

		id->index = i;

		if ((rc = _nscd_cfg_add_index_entry(id->name,
		    i, type)) != NSCD_SUCCESS) {

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "unable to add index entry for parameter "
			"%s\n", id->name);

			_nscd_free_db(cfg_paramDB);
			return (rc);
		} else {
			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
			(me, "index entry for parameter "
			"%s added\n", id->name);
		}
	}

	return (_nscd_cfg_init_nsw());
}

static nscd_rc_t
_nscd_cfg_init_stat()
{
	char			*me = "_nscd_cfg_init_stat";
	int			i, gi, fn = 0;
	nscd_cfg_id_t		*id;
	nscd_cfg_stat_desc_t	*desc, *gdesc = NULL;
	nscd_cfg_group_info_t	g_info;
	nscd_cfg_list_type_t	type = NSCD_CFG_LIST_STAT;
	nscd_rc_t		rc;
	void			*gsfunc;

	desc = &_nscd_cfg_stat_desc[0];

	/*
	 * need to loop to the last (+1) stat description
	 * which is a fake group and which marks the end
	 * of list. It is used to signal the end of the
	 * previous group so that the proper data will be
	 * set for that group
	 */
	for (i = 0; i < _nscd_cfg_num_stat + 1; i++, desc++) {

		id = (nscd_cfg_id_t *)desc;

		if (_nscd_cfg_flag_is_set(desc->sflag,
		    NSCD_CFG_SFLAG_GROUP)) {

			if (gdesc != NULL) {
				g_info.num_param = fn;
				gdesc->s_fn = fn;

				if (g_info.num_param !=
				    gdesc->gi.num_param ||
				    !_nscd_cfg_bitmap_is_equal(
				    g_info.bitmap, gdesc->gi.bitmap)) {

					_NSCD_LOG(NSCD_LOG_CONFIG,
					    NSCD_LOG_LEVEL_ERROR)
					(me, "ERROR: group (%s) "
					    "info mismatched: "
					    "group info (%d, %#6.4x) not "
					    "equal to the predefined one "
					    "(%d, %#6.4x)\n", gdesc->id.name,
					    g_info.num_param,
					    _nscd_cfg_bitmap_value(
					    g_info.bitmap),
					    gdesc->gi.num_param,
					    _nscd_cfg_bitmap_value(
					    gdesc->gi.bitmap));

					exit(1);
					return (NSCD_CFG_STAT_DESC_ERROR);
				}
			}

			gi = i;
			fn = 0;
			gdesc = desc;
			g_info.bitmap = NSCD_CFG_BITMAP_ZERO;

			/*
			 * set the get_stat function
			 */
			gsfunc = (void *)gdesc->get_stat;
		} else {
			if (i == 0) {

				_NSCD_LOG(NSCD_LOG_CONFIG,
				    NSCD_LOG_LEVEL_ERROR)
				(me, "ERROR: first stat "
				    "description is not for a group\n");

				return (NSCD_CFG_STAT_DESC_ERROR);
			}

			/*
			 * set bitmap: the rightmost bit represents
			 * the first member (index = 0) in the group,
			 * the next bit is for the second member
			 * (index = 1), and so on
			 */
			_nscd_cfg_bitmap_set_nth(g_info.bitmap, fn);

			desc->s_fn = fn++;

			/*
			 * set the get_stat function
			 */
			if (desc->get_stat == NSCD_CFG_FUNC_GET_STAT_AS_GROUP) {
				(void) memcpy(&desc->get_stat, &gsfunc,
				    sizeof (void *));
			}
		}

		/* if end of list reached, we are done */
		if (i == _nscd_cfg_num_stat)
			break;

		desc->g_index = gi;

		id->index = i;

		if ((rc = _nscd_cfg_add_index_entry(id->name,
		    i, type)) != NSCD_SUCCESS) {

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "unable to add index entry for stat "
			"description %s\n", id->name);

			_nscd_free_db(cfg_paramDB);
			return (rc);
		} else {
			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
			(me, "index entry for stat description "
			"%s added\n", id->name);
		}
	}

	return (NSCD_SUCCESS);
}

static nscd_rc_t
_nscd_cfg_copy_vlen_data(
	void			*data,
	void			**new_data_p,
	nscd_cfg_param_desc_t	*desc,
	int			*data_len,
	nscd_bool_t		in)
{
	int			len, dlen;
	nscd_cfg_vlen_data_t	*v = NULL;

	*new_data_p = NULL;
	*data_len = 0;

	/* it is OK if there is nothing to copy */
	if (data == NULL)
		return (NSCD_SUCCESS);

	/*
	 * if copy to the config store we need to allocate space
	 * for the extra vlen header
	 */
	if (desc->type == NSCD_CFG_DATA_STRING) {
		len = dlen = strlen((char *)data) + 1;
		if (in == nscd_true)
			len += sizeof (nscd_cfg_vlen_data_t);
	} else {
		/*
		 * should not be here, since for now
		 * only string variable length data
		 * is supported
		 */
		*new_data_p = NULL;
		return (NSCD_CFG_PARAM_DESC_ERROR);
	}

	v = calloc(1, len);
	if (v == NULL) {
		*new_data_p = NULL;
		return (NSCD_NO_MEMORY);
	}

	/*
	 * if copy to the config store, set up
	 * the extra vlen header in which the
	 * pointer to, and length of, the real
	 * data are kept. The pointer to the real
	 * data, not the vlen header, is returned.
	 */
	if (in == nscd_true) {
		v->ptr = (char *)v + sizeof (nscd_cfg_vlen_data_t);
		v->len = dlen;
		(void) memcpy(v->ptr, data, dlen);
		*new_data_p = v->ptr;
	} else {
		(void) memcpy(v, data, dlen);
		*new_data_p = v;
	}
	*data_len = dlen;

	return (NSCD_SUCCESS);
}

static void
_nscd_cfg_free_vlen_data_int(
	void	*data)
{
	nscd_cfg_vlen_data_t	*v = NULL;
	void			*p;

	if (data == NULL)
		return;

	p = (char *)data - sizeof (nscd_cfg_vlen_data_t);
	v = (nscd_cfg_vlen_data_t *)p;
	if (v->ptr == data)
		free(v);
}

static nscd_rc_t
_nscd_cfg_set_vlen_data_int(
	void		*src,
	void		*dest,
	nscd_bool_t	global)
{
	int			i, offset, dlen = 0;
	void			*s, *d, *new;
	void			*cptr;
	nscd_rc_t		rc;
	nscd_cfg_param_desc_t	*desc;

	desc = &_nscd_cfg_param_desc[0];
	for (i = 0; i < _nscd_cfg_num_param; i++, desc++) {

		if (global == nscd_true &&
		    _nscd_cfg_flag_is_not_set(desc->pflag,
		    NSCD_CFG_PFLAG_GLOBAL))
			continue;
		else if (global != nscd_true &&
		    _nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_GLOBAL))
			continue;

		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_VLEN_DATA)) {

			offset = desc->g_offset + desc->p_offset;

			s = (char *)src + offset;
			cptr = *(char **)s;

			rc = _nscd_cfg_copy_vlen_data(cptr, &new,
			    desc, &dlen, nscd_true);
			if (rc != NSCD_SUCCESS)
				return (rc);

			d = (char *)dest + offset;
			/* free the old vlen data */
			if (*(char **)d == NULL)
				_nscd_cfg_free_vlen_data_int(*(char **)d);

			*(char **)d = new;
		}
	}

	return (NSCD_SUCCESS);
}

static void *
_nscd_cfg_locate_vlen_data(
	void	*cfg_data,
	int	*len)
{
	void	*ptr, *ret;

	ptr = *(char **)cfg_data;
	ret = ptr;
	if (ret == NULL) {
		*len = 0;
		return (NULL);
	}
	ptr = (char *)ptr - sizeof (nscd_cfg_vlen_data_t);
	*len = ((nscd_cfg_vlen_data_t *)ptr)->len;

	return (ret);
}

static void
_nscd_cfg_lock(
	nscd_bool_t	is_read,
	nscd_cfg_lock_t	*cfglock)
{

	int	(*lockfunc)(rwlock_t *);

	if (cfglock == NULL)
		return;

	if (is_read == nscd_true)
		lockfunc = rw_rdlock;
	else
		lockfunc = rw_wrlock;

	if (cfglock->global != NULL) {

		(lockfunc)(cfglock->global);
		return;
	}

	if (cfglock->alldb != NULL)
		(lockfunc)(cfglock->alldb);

	if (cfglock->nswdb != NULL)
		(lockfunc)(cfglock->nswdb);
}

static void
_nscd_cfg_unlock(
	nscd_cfg_lock_t	*cfglock)
{
	if (cfglock == NULL)
		return;

	if (cfglock->global != NULL) {

		(void) rw_unlock(cfglock->global);
		free(cfglock);
		return;
	}

	if (cfglock->nswdb != NULL)
		(void) rw_unlock(cfglock->nswdb);

	if (cfglock->alldb != NULL)
		(void) rw_unlock(cfglock->alldb);

	free(cfglock);
}

/*
 * If vlen_data_addr is given, it will be set to the
 * address of the pointer pointing to the vlen data.
 * 'cfglock' will be set to point to the reader/writer
 * lock(s) protecting the (group) configuration data.
 */
static nscd_rc_t
_nscd_cfg_locate_cfg_data(
	void			**cfg_data,
	nscd_bool_t		is_read,
	nscd_cfg_param_desc_t	*desc,
	nscd_cfg_id_t		*nswdb,
	nscd_bool_t		get_group,
	void			**vlen_data_addr,
	int			*len,
	nscd_cfg_lock_t		**cfglock)
{
	int		offset;

	*cfg_data = NULL;
	if (len != NULL)
		*len = 0;
	if (vlen_data_addr != NULL)
		*vlen_data_addr = NULL;

	if (cfglock != NULL) {
		*cfglock = calloc(1, sizeof (nscd_cfg_lock_t));
		if (*cfglock == NULL)
			return (NSCD_NO_MEMORY);
	}

	/* assume if nswdb is NULL, the param is a global one */
	if (nswdb == NULL) {

		offset = desc->g_offset;
		if (get_group != nscd_true)
			offset += desc->p_offset;
		*cfg_data = (char *)nscd_cfg_global_current + offset;

		if (cfglock != NULL)
			(*cfglock)->global = nscd_cfg_global_rwlock;

	} else if (nswdb->index == NSCD_CFG_NSW_ALLDB_INDEX) {

		offset = desc->g_offset;
		if (get_group != nscd_true)
			offset += desc->p_offset;
		*cfg_data = (char *)nscd_cfg_nsw_alldb_current +
		    offset;

		if (cfglock != NULL)
			(*cfglock)->alldb = nscd_cfg_nsw_alldb_rwlock;

	} else {

		offset = nswdb->index *
		    (sizeof (nscd_cfg_nsw_db_data_t)) + desc->g_offset;
		if (get_group != nscd_true)
			offset += desc->p_offset;
		*cfg_data = (char *)nscd_cfg_nsw_db_data_current +
		    offset;

		if (cfglock != NULL) {
			(*cfglock)->nswdb =
			    &nscd_cfg_nsw_db_data_rwlock[nswdb->index];

			(*cfglock)->alldb = nscd_cfg_nsw_alldb_rwlock;
		}
	}

	/* lock the config data */
	if (cfglock != NULL)
		_nscd_cfg_lock(is_read, *cfglock);

	if (get_group != nscd_true &&
	    _nscd_cfg_flag_is_not_set(desc->pflag,
	    NSCD_CFG_PFLAG_GROUP) &&
	    (_nscd_cfg_flag_is_set(desc->pflag,
	    NSCD_CFG_PFLAG_VLEN_DATA))) {
		if (vlen_data_addr != NULL)
			*vlen_data_addr = *cfg_data;
		*cfg_data = _nscd_cfg_locate_vlen_data(*cfg_data, len);
		return (NSCD_SUCCESS);
	}

	if (len != NULL) {
		if (get_group == nscd_true)
			*len = desc->g_size;
		else
			*len = desc->p_size;
	}

	return (NSCD_SUCCESS);
}

/*
 * perform the preliminary (range) check on 'data' based on the
 * datatype (desc->datatype) of the config parameter
 */
nscd_rc_t
_nscd_cfg_prelim_check(
	nscd_cfg_param_desc_t	*desc,
	void			*data,
	nscd_cfg_error_t	**errorp)
{

	char			*me = "_nscd_cfg_prelim_check";
	char			msg[NSCD_CFG_MAX_ERR_MSG_LEN];
	nscd_cfg_str_check_t	*sc;
	nscd_cfg_int_check_t	*ic;
	nscd_cfg_bitmap_check_t	*bmc;
	nscd_rc_t		rc = NSCD_CFG_PRELIM_CHECK_FAILED;

	if ((nscd_cfg_str_check_t *)desc->p_check == NULL)
		return (NSCD_SUCCESS);

	switch (desc->type) {

	case NSCD_CFG_DATA_STRING:

		sc = (nscd_cfg_str_check_t *)desc->p_check;
		if (sc->must_not_null == nscd_true && data == NULL) {

			if (errorp == NULL)
				break;

			(void) snprintf(msg, sizeof (msg),
			    gettext("data must be specified for %s"),
			    desc->id.name);

			break;
		}

		if (data == NULL) {
			rc = NSCD_SUCCESS;
			break;
		}

		if (sc->maxlen != 0 &&
		    strlen((char *)data) > sc->maxlen) {

			if (errorp == NULL)
				break;

			(void) snprintf(msg, sizeof (msg),
			    gettext("length of data (%s) for %s larger "
			    "than %d"),
			    (char *)data, desc->id.name, sc->maxlen);
			break;
		}

		rc = NSCD_SUCCESS;

		break;

	case NSCD_CFG_DATA_INTEGER:

		ic = (nscd_cfg_int_check_t *)desc->p_check;
		if (*(int *)data > ic->max ||
		    *(int *)data < ic->min) {

			if (errorp == NULL)
				break;

			(void) snprintf(msg, sizeof (msg),
			    gettext("data (%d) for %s out of range "
			    "(%d - %d)"),
			    *(int *)data, desc->id.name,
			    ic->min, ic->max);

			break;
		}

		rc = NSCD_SUCCESS;

		break;

	case NSCD_CFG_DATA_BITMAP:

		bmc = (nscd_cfg_bitmap_check_t *)desc->p_check;
		if (_nscd_cfg_bitmap_value(*(nscd_cfg_bitmap_t *)data) &
		    ~(bmc->valid_bits)) {

			if (errorp == NULL)
				break;

			(void) snprintf(msg, sizeof (msg),
			    gettext("data (%#6.4x) for %s contain bit "
			    "not in 0x%x"),
			    _nscd_cfg_bitmap_value(
			    *(nscd_cfg_bitmap_t *)data),
			    desc->id.name,
			    _nscd_cfg_bitmap_value(bmc->valid_bits));
			break;
		}

		rc = NSCD_SUCCESS;

		break;
	}

	if (rc != NSCD_SUCCESS && errorp != NULL) {
		*errorp = _nscd_cfg_make_error(rc, msg);

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
		(me, "invalid argument: %s\n", (*errorp)->msg);
	}

	return (rc);
}

static nscd_rc_t
_nscd_cfg_notify_i(
	nscd_cfg_param_desc_t	*desc,
	nscd_cfg_id_t		*nswdb,
	int			*skip,
	nscd_cfg_error_t	**errorp)
{

	char			*me = "_nscd_cfg_notify_i";
	int			i, num, skip_bk;
	void			*cfg_data, *cdata;
	void			*cookie = NULL;
	nscd_rc_t		rc;
	nscd_cfg_flag_t		dflag, dflag1;
	nscd_cfg_bitmap_t	bitmap_c, bitmap_s, *bitmap_addr;
	nscd_cfg_group_info_t	*gi;

	if (errorp != NULL)
		*errorp = NULL;

	if (skip == NULL)
		skip = &skip_bk;

	*skip = 0;

	if (_nscd_cfg_flag_is_not_set(desc->pflag,
	    NSCD_CFG_PFLAG_GROUP)) {

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "ERROR: expect parameter description for group, "
		    "but receive parameter description is for %s\n",
		    desc->id.name);

		return (NSCD_CFG_PARAM_DESC_ERROR);
	}

	/*
	 * Set data flag going with data to be sent to the
	 * verify/notify routines. Allowing the config flag
	 * be exipandable, set the bits one by one.
	 */
	dflag = NSCD_CFG_FLAG_ZERO;
	dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_STATIC_DATA);
	dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_INIT);
	dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_GROUP);
	if (_nscd_cfg_flag_is_set(desc->pflag,
	    NSCD_CFG_PFLAG_INIT_SET_ALL_DB))
		dflag = _nscd_cfg_flag_set(dflag,
		    NSCD_CFG_DFLAG_SET_ALL_DB);

	/* get to the group data in the config store */
	rc = _nscd_cfg_locate_cfg_data(&cfg_data, nscd_true,
	    desc, nswdb, nscd_true, NULL, NULL, NULL);
	if (rc != NSCD_SUCCESS)
		goto error;

	/*
	 * the static bitmap associated with the group
	 * may be replaced before sending to the components,
	 * so save the bitmap for later use
	 */
	gi = _nscd_cfg_get_gi(cfg_data);
	bitmap_c = gi->bitmap;
	bitmap_addr = &(gi->bitmap);

	/*
	 * the elements in this group will all be handled
	 * so the caller can skip them
	 */
	*skip = desc->p_fn;

	if (_nscd_cfg_flag_is_set(desc->pflag,
	    NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP))
		/* send the entire group just once */
		num = 1;

	else { /* send individual members one by one */

		num = desc->p_fn;

		/*
		 * skip the first desc which is for the group
		 * and get to the desc for the first member
		 */
		desc++;

		dflag = _nscd_cfg_flag_unset(dflag,
		    NSCD_CFG_DFLAG_GROUP);
	}

	dflag1 = dflag;
	for (i = 0; i < num; i++, desc++) {

		dflag = dflag1;

		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_SEND_BIT_SELECTED)) {

			/* set the bitmap to select just this member */
			bitmap_s = NSCD_CFG_BITMAP_ZERO;
			_nscd_cfg_bitmap_set_nth(bitmap_s, i);
			/* replace the bitmap in the cfg data */
			_nscd_cfg_bitmap_set(bitmap_addr, bitmap_s);

			/*
			 * send the whole group but with only one
			 * member selected
			 */
			cdata = cfg_data;

			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_GROUP);
			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_BIT_SELECTED);
		} else {
			/*
			 * send param data or group data:
			 * param data - non-xero desc->p_offset
			 * group data - zero desc->p_offset
			 */
			cdata = (char *)cfg_data + desc->p_offset;

			/*
			 * if variable length data, need to send pointer
			 * to the data (not the address of the pointer)
			 */
			if (_nscd_cfg_flag_is_set(desc->pflag,
			    NSCD_CFG_PFLAG_VLEN_DATA))
				cdata = *(char **)cdata;
		}

		if (desc->verify != NULL) {
			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_VERIFY);
			rc = desc->verify(cdata, desc, nswdb,
			    dflag, errorp, &cookie);
			if (rc != NSCD_SUCCESS)
				goto error;
		}

		if (desc->notify != NULL) {
			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_NOTIFY);

			rc = desc->notify(cfg_data, desc, nswdb,
			    dflag, errorp, cookie);
			if (rc != NSCD_SUCCESS)
				goto error;
		}
	}

	rc = NSCD_SUCCESS;

	/* restore the bitmap in the cfg data */
	_nscd_cfg_bitmap_set(bitmap_addr, bitmap_c);

	error:

	return (rc);

}

static nscd_rc_t
_nscd_cfg_notify_init(
	nscd_cfg_error_t	**errorp)
{
	int			i, j, skip;
	nscd_rc_t		rc;
	nscd_cfg_id_t		*nswdb = NULL;
	nscd_cfg_param_desc_t	*desc;

	if (errorp != NULL)
		*errorp = NULL;

	for (i = 0; i < _nscd_cfg_num_param; i++) {

		desc = &_nscd_cfg_param_desc[i];

		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_GLOBAL)) { /* global cfg data */

			rc = _nscd_cfg_notify_i(desc, NULL, &skip, errorp);
		} else {

			/*
			 * if use defaults for all nsswitch database,
			 * send the config data to verify/notify once
			 */
			if (_nscd_cfg_flag_is_set(desc->pflag,
			    NSCD_CFG_PFLAG_INIT_SET_ALL_DB)) {

				nswdb = &_nscd_cfg_nsw_alldb;

				rc = _nscd_cfg_notify_i(desc, nswdb,
				    &skip, errorp);
			} else { /* send data once for each nsw db */

				for (j = 0; j < _nscd_cfg_num_nsw_db; j++) {

					nswdb = &_nscd_cfg_nsw_db[j];

					rc = _nscd_cfg_notify_i(desc,
					    nswdb, &skip, errorp);

					if (rc != NSCD_SUCCESS)
						break;
				}
			}
		}

		if (rc != NSCD_SUCCESS)
			return (rc);

		i += skip;
	}

	return (NSCD_SUCCESS);
}

nscd_rc_t
_nscd_cfg_init(
	nscd_cfg_error_t		**errorp)
{

	int				i, j, datalen;
	int				dbi = 0, dbj = 0;
	char				*dest, *src;
	char				*dbni = NULL, *dbnj = NULL;
	nscd_rc_t			rc;
	nscd_cfg_nsw_spc_default_t	*spc;

	if (errorp != NULL)
		*errorp = NULL;

	rc = _nscd_cfg_init_param();
	if (rc != NSCD_SUCCESS)
		return (rc);

	rc = _nscd_cfg_init_stat();
	if (rc != NSCD_SUCCESS)
		return (rc);

	nscd_cfg_global_current = calloc(1,
	    sizeof (nscd_cfg_global_data_t));
	if (nscd_cfg_global_current == NULL)
		return (NSCD_NO_MEMORY);

	nscd_cfg_nsw_alldb_current = calloc(1,
	    sizeof (nscd_cfg_nsw_db_data_t));
	if (nscd_cfg_nsw_alldb_current == NULL)
		return (NSCD_NO_MEMORY);

	nscd_cfg_nsw_db_data_current = calloc(_nscd_cfg_num_nsw_db,
	    sizeof (nscd_cfg_nsw_db_data_t));
	if (nscd_cfg_nsw_db_data_current == NULL)
		return (NSCD_NO_MEMORY);

	nscd_cfg_global_rwlock = calloc(1, sizeof (rwlock_t));
	if (nscd_cfg_global_rwlock == NULL)
		return (NSCD_NO_MEMORY);
	(void) rwlock_init(nscd_cfg_global_rwlock, NULL, NULL);

	*nscd_cfg_global_current = nscd_cfg_global_default;

	rc = _nscd_cfg_set_vlen_data_int(&nscd_cfg_global_default,
	    nscd_cfg_global_current, nscd_true);
	if (rc != NSCD_SUCCESS)
		return (rc);

	nscd_cfg_nsw_db_data_rwlock = calloc(_nscd_cfg_num_nsw_db,
	    sizeof (rwlock_t));
	if (nscd_cfg_nsw_db_data_rwlock == NULL)
		return (NSCD_NO_MEMORY);

	/* set per switch db config to the default for all db's */
	for (i = 0; i < _nscd_cfg_num_nsw_db; i++) {

		nscd_cfg_nsw_db_data_current[i] =
		    nscd_cfg_nsw_db_data_default;

		(void) rwlock_init(&nscd_cfg_nsw_db_data_rwlock[i],
		    NULL, NULL);
	}

	/* add db specific defaults */
	for (i = 0; i < _nscd_cfg_num_nsw_default; i++) {

		if (_nscd_cfg_nsw_spc_default[i].data == NULL)
			continue;

		if (_nscd_cfg_nsw_spc_default[i].db != dbni) {
			for (j = 0; j < _nscd_cfg_num_nsw_db; j++) {

				if (strcmp(_nscd_cfg_nsw_db[j].name,
				    _nscd_cfg_nsw_spc_default[i].db) != 0)
					continue;

				dbi = _nscd_cfg_nsw_db[j].index;
				dbni = _nscd_cfg_nsw_db[j].name;
				break;
			}
		}

		dest = (char *)&nscd_cfg_nsw_db_data_current[dbi] +
		    _nscd_cfg_nsw_spc_default[i].group_off +
		    _nscd_cfg_nsw_spc_default[i].param_off;

		src = _nscd_cfg_nsw_spc_default[i].data;
		datalen = _nscd_cfg_nsw_spc_default[i].data_len;

		(void) memcpy(dest, src, datalen);
	}

	/* add db specific defaults via links */
	for (i = 0; i < _nscd_cfg_num_link_default; i++) {

		if (_nscd_cfg_nsw_link_default[i].data == NULL)
			continue;

		spc = _nscd_cfg_nsw_link_default[i].data;

		if (_nscd_cfg_nsw_link_default[i].db != dbni) {
			for (j = 0; j < _nscd_cfg_num_nsw_db; j++) {

				if (strcmp(_nscd_cfg_nsw_db[j].name,
				    _nscd_cfg_nsw_link_default[i].db) != 0)
					continue;

				dbi = _nscd_cfg_nsw_db[j].index;
				dbni = _nscd_cfg_nsw_db[j].name;
				break;
			}
		}

		dest = (char *)&nscd_cfg_nsw_db_data_current[dbi] +
		    _nscd_cfg_nsw_link_default[i].group_off +
		    _nscd_cfg_nsw_link_default[i].param_off;

		if (_nscd_cfg_nsw_db[j].name != dbnj) {
			for (j = 0; j < _nscd_cfg_num_nsw_db; j++) {

				if (strcmp(spc->db,
				    _nscd_cfg_nsw_db[j].name) != 0)
					continue;

				dbnj = _nscd_cfg_nsw_db[j].name;
				dbj = _nscd_cfg_nsw_db[j].index;
				break;
			}
		}

		src = (char *)&nscd_cfg_nsw_db_data_current[dbj] +
		    spc->group_off + spc->param_off;
		datalen = spc->data_len;

		(void) memcpy(dest, src, datalen);
	}

	/* fix up variable length fields */
	for (i = 0; i < _nscd_cfg_num_nsw_db; i++) {

		rc = _nscd_cfg_set_vlen_data_int(
		    &nscd_cfg_nsw_db_data_current[i],
		    &nscd_cfg_nsw_db_data_current[i], nscd_false);
		if (rc != NSCD_SUCCESS)
			return (rc);
	}

	nscd_cfg_nsw_alldb_rwlock = calloc(1, sizeof (rwlock_t));
	if (nscd_cfg_nsw_alldb_rwlock == NULL)
		return (NSCD_NO_MEMORY);

	(void) rwlock_init(nscd_cfg_nsw_alldb_rwlock, NULL, NULL);

	rc = _nscd_cfg_set_vlen_data_int(
	    &nscd_cfg_nsw_db_data_default,
	    nscd_cfg_nsw_alldb_current, nscd_false);
	if (rc != NSCD_SUCCESS)
		return (rc);

	/*
	 * notify and send the configuration data to
	 * the nscd components
	 */
	rc = _nscd_cfg_notify_init(errorp);
	if (rc != NSCD_SUCCESS)
		return (rc);

	return (NSCD_SUCCESS);
}

static nscd_rc_t
_nscd_cfg_get_handle_common(
	nscd_cfg_list_type_t	type,
	char			*name,
	char			*nswdb_name,
	nscd_cfg_handle_t	**handle,
	nscd_cfg_error_t	**errorp)
{

	int			i, is_global;
	char			*desc_str;
	nscd_cfg_handle_t	*h;
	nscd_cfg_param_desc_t	*pdesc;
	nscd_cfg_stat_desc_t	*sdesc;
	char			*me = "_nscd_cfg_get_handle_common";
	char			msg[NSCD_CFG_MAX_ERR_MSG_LEN];
	nscd_rc_t		rc = NSCD_INVALID_ARGUMENT;

	if (handle == NULL) {

		(void) snprintf(msg, sizeof (msg),
		    gettext("address of handle not specified"));
		if (errorp)
			*errorp = _nscd_cfg_make_error(rc, msg);

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
		(me, "invalid argument: %s\n", msg);

		return (rc);
	}

	*handle = NULL;

	if (name == NULL) {

		(void) snprintf(msg, sizeof (msg),
		    gettext("name not specified"));
		if (errorp)
			*errorp = _nscd_cfg_make_error(rc, msg);

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
		(me, "invalid argument: %s\n");

		return (rc);
	}

	h = calloc(1, sizeof (nscd_cfg_handle_t));
	if (h == NULL)
		return (NSCD_NO_MEMORY);
	h->type = type;

	if (type == NSCD_CFG_LIST_PARAM)
		desc_str = gettext("configuration parameter");
	else
		desc_str = gettext("statistics");

	/* get param or stat descriptor */
	i = _nscd_cfg_get_index(name, type);
	if (i != -1) {

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
		(me, "%s: index of %s is %d\n", desc_str, name, i);

		if (type == NSCD_CFG_LIST_PARAM) {
			pdesc = &_nscd_cfg_param_desc[i];
			(void) memcpy(&h->desc, &pdesc, sizeof (pdesc));
			is_global = _nscd_cfg_flag_is_set(
			    pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL);

			/* hidden params are not exposed */
			if (_nscd_cfg_flag_is_set(
			    pdesc->pflag, NSCD_CFG_PFLAG_HIDDEN))
				i = -1;

			if (_nscd_cfg_flag_is_set(pdesc->pflag,
			    NSCD_CFG_PFLAG_OBSOLETE)) {
				_NSCD_LOG(NSCD_LOG_CONFIG,
				    NSCD_LOG_LEVEL_WARNING)
				(me, gettext("%s: %s is obsolete and "
				    "will be ignored\n"),
				    desc_str, name);
			}
		} else {
			sdesc = &_nscd_cfg_stat_desc[i];
			(void) memcpy(&h->desc, &sdesc, sizeof (sdesc));
			is_global = _nscd_cfg_flag_is_set(
			    sdesc->sflag, NSCD_CFG_SFLAG_GLOBAL);
		}
	}

	if (i == -1) {

		(void) snprintf(msg, sizeof (msg),
		    gettext("%s: unknown name \"%s\""), desc_str, name);
		if (errorp)
			*errorp = _nscd_cfg_make_error(rc, msg);

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "%s\n", msg);

		free(h);
		return (rc);
	}

	/*
	 * if the param/stat is not a global one, we need to
	 * know which nsswitch database we are dealing with
	 */
	if (is_global == 0) {
		if (nswdb_name == NULL) {

			(void) snprintf(msg, sizeof (msg),
			gettext("%s: switch database name not specified"),
			    desc_str);
			if (errorp)
				*errorp = _nscd_cfg_make_error(rc, msg);

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "%s for non-global param or stat %s\n",
			    msg, name);

			free(h);
			return (rc);
		}
	} else {

		if (nswdb_name != NULL) {

			(void) snprintf(msg, sizeof (msg),
			    gettext("%s: switch database specified for "
			    "global data"), desc_str);
			if (errorp)
				*errorp = _nscd_cfg_make_error(rc, msg);

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "%s %s\n", msg, name);

			free(h);
			return (rc);
		}

		*handle = h;
		return (NSCD_SUCCESS);
	}

	/* get nsw DB id */
	i = _nscd_cfg_get_index(nswdb_name, NSCD_CFG_LIST_NSW_DB);
	if (i != -1) {

		if (i == NSCD_CFG_NSW_ALLDB_INDEX)
			h->nswdb = &_nscd_cfg_nsw_alldb;
		else
			h->nswdb = &_nscd_cfg_nsw_db[i];

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
		(me, "%s: index of %s is %d\n",
		    desc_str, nswdb_name, i);
	} else {

		(void) snprintf(msg, sizeof (msg),
		    gettext("%s: unknown switch database name \"%s\""),
		    desc_str, nswdb_name);
		if (errorp)
			*errorp = _nscd_cfg_make_error(rc, msg);

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "%s\n", msg);

		free(h);
		return (NSCD_CFG_UNSUPPORTED_SWITCH_DB);
	}

	*handle = h;

	return (NSCD_SUCCESS);
}

nscd_rc_t
_nscd_cfg_get_handle(
	char			*param_name,
	char			*nswdb_name,
	nscd_cfg_handle_t	**handle,
	nscd_cfg_error_t	**errorp)
{

	return (_nscd_cfg_get_handle_common(NSCD_CFG_LIST_PARAM,
	    param_name, nswdb_name, handle, errorp));
}

nscd_rc_t
_nscd_cfg_get_stat_handle(
	char			*stat_name,
	char			*nswdb_name,
	nscd_cfg_handle_t	**handle,
	nscd_cfg_error_t	**errorp)
{

	return (_nscd_cfg_get_handle_common(NSCD_CFG_LIST_STAT,
	    stat_name, nswdb_name, handle, errorp));
}

void
_nscd_cfg_free_handle(
	nscd_cfg_handle_t	*handle)
{

	free(handle);

}

static void
_nscd_cfg_free_vlen_data_group(
	nscd_cfg_param_desc_t	*gdesc,
	void			*group_data,
	nscd_bool_t		in)
{
	int			num;
	void			*dest, *ptr;
	nscd_cfg_param_desc_t	*desc;

	desc = gdesc;

	num = ((nscd_cfg_group_info_t *)group_data)->num_param;

	while (num-- > 0) {

		desc++;

		/* skip fixed length data */
		if (_nscd_cfg_flag_is_not_set(desc->pflag,
		    NSCD_CFG_PFLAG_VLEN_DATA))
			continue;

		dest = (char *)group_data + desc->p_offset;
		ptr = *(char **)dest;
		if (ptr == NULL)
			continue;
		if (in == nscd_true)
			_nscd_cfg_free_vlen_data_int(ptr);
		else
			free(ptr);
	}
}

void
_nscd_cfg_free_param_data(
	void			*data)
{

	if (data == NULL)
		return;

	free(data);
}

void
_nscd_cfg_free_group_data(
	nscd_cfg_handle_t	*handle,
	void			*data)
{

	nscd_cfg_param_desc_t	*desc;
	nscd_cfg_group_info_t	*gi;

	if (handle == NULL || data == NULL)
		return;

	desc = _nscd_cfg_get_desc(handle);
	gi = (nscd_cfg_group_info_t *)data;
	if (desc->p_fn != gi->num_param)
		return;

	_nscd_cfg_free_vlen_data_group(desc, data, nscd_false);

	free(data);
}

void
_nscd_cfg_free_error(
	nscd_cfg_error_t	*error)
{

	if (error == NULL)
		return;

	free(error);
}

static nscd_rc_t
_nscd_cfg_copy_param_data(
	nscd_cfg_param_desc_t	*desc,
	void			*dest,
	void			*pdata,
	nscd_bool_t		in,
	nscd_bool_t		set_addr)
{

	char			*me = "_nscd_cfg_copy_param_data";
	void			*tmp;
	int			dlen;
	nscd_rc_t		rc = NSCD_SUCCESS;

	if (desc == NULL || dest == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "input desc == %p, dest == %p\n", desc, dest);
		return (NSCD_INVALID_ARGUMENT);
	}

	/* fixed length data */
	if (_nscd_cfg_flag_is_not_set(desc->pflag,
	    NSCD_CFG_PFLAG_VLEN_DATA)) {
		(void) memcpy(dest, pdata, desc->p_size);
		goto done;
	}


	/* variable length data from this point on */

	/* make a copy of the variable length data */
	rc = _nscd_cfg_copy_vlen_data(pdata, &tmp, desc, &dlen, in);
	if (rc != NSCD_SUCCESS)
		goto done;

	if (in == nscd_true) { /* data to internal */

		/* free the variable length data in the config store */
		if (*(char **)dest != NULL)
			_nscd_cfg_free_vlen_data_int(*(char **)dest);
	}

	if (set_addr == nscd_true) {
		/*
		 * set the addr of the vlen data
		 */
		*(char **)dest = tmp;
	} else {
		/*
		 * copy the data content (not address)
		 */
		(void) memcpy(dest, tmp, dlen);
	}

	done:

	return (rc);
}

static nscd_rc_t
_nscd_cfg_copy_group_data_in(
	nscd_cfg_param_desc_t	*gdesc,
	nscd_cfg_group_info_t	*gi,
	void			*group_dest,
	void			*group_src)
{
	int			i, num;
	nscd_cfg_param_desc_t	*desc;
	void			*src, *dest;

	i = 0;
	num = gi->num_param;
	desc = gdesc;

	while (num-- > 0) {

		desc++;

		/* if member not selected by bitmap, skip */
		if (_nscd_cfg_bitmap_is_not_set(gi->bitmap, i++))
			continue;

		src = (char *)group_src + desc->p_offset;
		dest = (char *)group_dest + desc->p_offset;

		/*
		 * if variable length data, free and replace the old
		 * with the new
		 */
		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_VLEN_DATA)) {
			_nscd_cfg_free_vlen_data_int(*(char **)dest);
			*(char **)dest = *(char **)src;
			*(char **)src = NULL;
		} else {
			/*
			 * fixed length data, just copy it
			 */
			(void) memcpy(dest, src, desc->p_size);
		}
	}

	return (NSCD_SUCCESS);
}

static nscd_rc_t
_nscd_cfg_copy_group_data_out(
	nscd_cfg_param_desc_t	*gdesc,
	void			*group_dest,
	void			*group_src)
{

	char			*me = "_nscd_cfg_copy_group_data_out";
	void			*src, *dest;
	int			dlen;
	int			num;
	nscd_cfg_group_info_t	*gi;
	nscd_rc_t		rc = NSCD_SUCCESS;
	nscd_cfg_param_desc_t	*desc;

	if (group_dest == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "input group_dest = NULL\n");
		return (NSCD_INVALID_ARGUMENT);
	}

	gi = _nscd_cfg_get_gi(group_src);
	num = gi->num_param;
	desc = gdesc;

	while (num-- > 0) {

		desc++;

		dest = (char *)group_dest + desc->p_offset;
		src = (char *)group_src + desc->p_offset;

		/*
		 * if variable length data, get the real
		 * address and length of the data
		 */
		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_VLEN_DATA)) {
			src = _nscd_cfg_locate_vlen_data(src, &dlen);
			if (dlen == NULL)
				continue;
		}

		/*
		 * The nscd_true asks _nscd_cfg_copy_param_data
		 * to set addr of the vlen data in 'dest' rather
		 * than copying the data content
		 */
		rc = _nscd_cfg_copy_param_data(desc, dest, src,
		    nscd_false, nscd_true);
		if (rc != NSCD_SUCCESS) {
			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "unable to copy param data for %s\n",
			    desc->id.name);

			_nscd_cfg_free_vlen_data_group(gdesc,
			    group_dest, nscd_false);

			free(group_dest);

			return (rc);
		}
	}

	/*
	 * set group bitmap
	 */
	(void) memcpy(group_dest, group_src,
	    sizeof (nscd_cfg_group_info_t));

	return (rc);
}


/*
 * group_cfg is needed always; group_src may be NULL if
 * param_index not zero and pdata not NULL; group_cfg and
 * pdata should not be both non-NULL
 */
static nscd_rc_t
_nscd_cfg_copy_group_data_merge(
	nscd_cfg_param_desc_t	*gdesc,
	void			**group_dest,
	void			*group_src,
	void			*group_cfg,
	int			param_index,
	void			*pdata)
{

	char			*me = "_nscd_cfg_copy_group_data_merge";
	void			*src, *dest, *tmp_dest = NULL;
	int			num, i = 0;
	nscd_cfg_group_info_t	*gi;
	nscd_rc_t		rc = NSCD_SUCCESS;
	nscd_cfg_param_desc_t	*desc;
	nscd_cfg_bitmap_t	bitmap;

	if (group_dest == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "input **group_dest == NULL\n");
		return (NSCD_INVALID_ARGUMENT);
	}

	if (group_cfg == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "input **group_cfg == NULL\n");
		return (NSCD_INVALID_ARGUMENT);
	}

	if (param_index != NULL && pdata == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "param_index != NULL but pdata == %p\n", pdata);
		return (NSCD_INVALID_ARGUMENT);
	}

	tmp_dest = calloc(1, gdesc->g_size);
	if (tmp_dest == NULL)
		return (NSCD_NO_MEMORY);

	if (group_src != NULL)
		gi = _nscd_cfg_get_gi(group_src);
	else {
		gi = _nscd_cfg_get_gi(group_cfg);
		bitmap = NSCD_CFG_BITMAP_ZERO;
	}

	num = gi->num_param;
	desc = gdesc;

	while (num-- > 0) {

		desc++;

		dest = (char *)tmp_dest + desc->p_offset;

		/*
		 * if member not selected by bitmap in group_src,
		 * get the member data in group_cfg
		 */
		if (_nscd_cfg_bitmap_is_not_set(gi->bitmap, i++) ||
		    group_src == NULL) {
			src = (char *)group_cfg + desc->p_offset;
		} else
			src = (char *)group_src + desc->p_offset;

		if (desc->id.index == param_index) {

			/* use the param data in pdata if provided */
			src = pdata;
			_nscd_cfg_bitmap_set_nth(bitmap, i);
		}

		/*
		 * if variable length data, get to the data
		 * instead of pointer to the data
		 */
		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_VLEN_DATA))
			src = *(char **)src;

		/*
		 * nscd_true asks _nscd_cfg_copy_param_data to
		 * set addr of the vlen data in 'dest' rather
		 * than copying the data content
		 */
		rc = _nscd_cfg_copy_param_data(desc, dest, src,
		    nscd_true, nscd_true);
		if (rc != NSCD_SUCCESS) {
			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "unable to copy param data for %s\n",
			    desc->id.name);

			_nscd_cfg_free_vlen_data_group(gdesc,
			    tmp_dest, nscd_true);

			free(tmp_dest);

			return (rc);
		}
	}

	*group_dest = tmp_dest;

	/*
	 * set bitmap: if input is group data, use the one
	 * given; if input is param data, use the one computed
	 * above
	 */
	if (group_src != NULL)
		(void) memcpy(*group_dest, group_src,
		    sizeof (nscd_cfg_group_info_t));
	else {
		gi = _nscd_cfg_get_gi(*group_dest);
		_nscd_cfg_bitmap_set(&gi->bitmap, bitmap);
	}

	return (rc);
}

/* ARGSUSED */
nscd_rc_t
_nscd_cfg_get(
	nscd_cfg_handle_t	*handle,
	void			**data,
	int			*data_len,
	nscd_cfg_error_t	**errorp)
{
	char			*me = "_nscd_cfg_get";
	int			dlen;
	nscd_rc_t		rc = NSCD_SUCCESS;
	nscd_cfg_id_t		*nswdb;
	nscd_cfg_param_desc_t	*desc;
	void			*cfg_data, *ptr = NULL;
	nscd_bool_t		get_group = nscd_false;
	nscd_bool_t		out = nscd_false;
	nscd_cfg_lock_t		*lock = NULL;

	if (data_len != NULL)
		*data_len = 0;

	if (data == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "input data = %p\n", data);
		return (NSCD_INVALID_ARGUMENT);
	}

	*data = NULL;

	if (handle == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "handle is NULL\n");
		return (NSCD_INVALID_ARGUMENT);
	}

	nswdb = handle->nswdb;
	desc = (nscd_cfg_param_desc_t *)handle->desc;

	if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP))
		get_group = nscd_true;

	/*
	 * locate the current value of the param or group
	 * and lock the config data for reading
	 */
	rc = _nscd_cfg_locate_cfg_data(&cfg_data, nscd_true, desc,
	    nswdb, get_group, NULL, &dlen, &lock);
	if (rc != NSCD_SUCCESS) {

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "unable to locate config data\n");
		return (rc);

	} else if (cfg_data == NULL) /* NULL vlen data */
		goto done;

	ptr = calloc(1, dlen);
	if (ptr == NULL) {
		rc = NSCD_NO_MEMORY;
		goto error_exit;
	}

	if (get_group == nscd_true) {

		rc = _nscd_cfg_copy_group_data_out(desc, ptr, cfg_data);
		if (rc != NSCD_SUCCESS) {
			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "unable to copy group data %p: "
			"error = %d\n", cfg_data, rc);

			goto error_exit;
		}
	} else {
		/*
		 * nscd_false asks _nscd_cfg_copy_param_data to
		 * copy the data content rather than just setting
		 * the addr of the vlen data in 'ptr'
		 */
		rc = _nscd_cfg_copy_param_data(desc, ptr, cfg_data,
		    out, nscd_false);

		if (rc != NSCD_SUCCESS) {
			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "unable to copy param data %p: "
			"error = %d\n", cfg_data, rc);

			goto error_exit;
		}
	}

	*data = ptr;

	done:

	if (data_len != NULL)
		*data_len = dlen;

	_nscd_cfg_unlock(lock);

	return (NSCD_SUCCESS);

	error_exit:

	_nscd_cfg_unlock(lock);
	if (ptr != NULL)
		free(ptr);

	return (rc);
}

/*
 * three type of data:
 * 1 - single param
 * 	desc is that of the param
 * 2 - single param to be sent in a group
 *	a single bit is set in the bitmap,
 * 	desc is that of the group
 * 3 - group data
 *	one of more bits are set in the bitmap,
 * 	desc is that of the group
 */
static nscd_rc_t
_nscd_cfg_notify_s(
	nscd_cfg_param_desc_t	*desc,
	nscd_cfg_id_t		*nswdb,
	void			*data,
	nscd_cfg_error_t	**errorp)
{
	int			i, num, is_group = 0;
	void			*cookie = NULL;
	void			*cdata;
	nscd_rc_t		rc;
	nscd_cfg_flag_t		dflag, dflag1;
	nscd_cfg_bitmap_t	bitmap_s, bitmap_in, *bitmap_addr = NULL;
	nscd_cfg_group_info_t	*gi;

	if (errorp != NULL)
		*errorp = NULL;

	/*
	 * Set data flag going with data to be sent to the
	 * verify/notify routines. To allow the config flag
	 * be exipandable, set the bits one by one.
	 */
	dflag = NSCD_CFG_FLAG_ZERO;
	dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_STATIC_DATA);
	if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) {
		dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_GROUP);
		is_group = 1;
	}
	if (nswdb != NULL &&
	    strcmp(NSCD_CFG_NSW_ALLDB, nswdb->name) == 0)
		dflag = _nscd_cfg_flag_set(dflag,
		    NSCD_CFG_DFLAG_SET_ALL_DB);

	/*
	 * the bitmap in the input data may be replaced before
	 * sending to the components, so save the bitmap for
	 * later use
	 */
	if (is_group == 1) {
		gi = _nscd_cfg_get_gi(data);
		bitmap_in = gi->bitmap;
		bitmap_addr = &(gi->bitmap);

		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP))
			/* send the entire group just once */
			num = 1;

		else { /* send individual members one by one */

			num = desc->p_fn;

			/*
			 * skip the first desc which is for the group
			 * and get to the desc for the first member
			 */
			desc++;

			dflag = _nscd_cfg_flag_unset(dflag,
			    NSCD_CFG_DFLAG_GROUP);
		}
	} else {
		/* not group data, send the member once */
			num = 1;
	}

	dflag1 = dflag;
	for (i = 0; i < num; i++, desc++) {

		dflag = dflag1;

		if (is_group == 0) {
			cdata = data;
			goto verify_data;
		}

		if (_nscd_cfg_flag_is_set(desc->pflag,
		    NSCD_CFG_PFLAG_SEND_BIT_SELECTED)) {

			/* set the bitmap to select just this member */
			bitmap_s = NSCD_CFG_BITMAP_ZERO;
			_nscd_cfg_bitmap_set_nth(bitmap_s, i);
			/* replace the bitmap in the input data */
			_nscd_cfg_bitmap_set(bitmap_addr, bitmap_s);

			/*
			 * send the whole group but with only one
			 * member selected
			 */
			cdata = data;

			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_GROUP);
			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_BIT_SELECTED);
		} else {
			/*
			 * send param data or group data:
			 * param data - non-xero desc->p_offset
			 * group data - zero desc->p_offset
			 */
			cdata = (char *)data + desc->p_offset;

			/*
			 * if variable length data, need to send pointer
			 * to the data (not the address of the pointer)
			 */
			if (_nscd_cfg_flag_is_set(desc->pflag,
			    NSCD_CFG_PFLAG_VLEN_DATA))
				cdata = *(char **)cdata;
		}

		verify_data:

		if (desc->verify != NULL) {
			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_VERIFY);
			rc = desc->verify(cdata, desc, nswdb,
			    dflag, errorp, &cookie);
			if (rc != NSCD_SUCCESS)
				goto error_exit;
		}

		if (desc->notify != NULL) {
			dflag = _nscd_cfg_flag_set(dflag,
			    NSCD_CFG_DFLAG_NOTIFY);

			rc = desc->notify(data, desc, nswdb,
			    dflag, errorp, cookie);
			if (rc != NSCD_SUCCESS)
				goto error_exit;
		}
	}

	rc = NSCD_SUCCESS;

	error_exit:

	/* restore the bitmap in the input data */
	if (bitmap_addr != NULL)
		_nscd_cfg_bitmap_set(bitmap_addr, bitmap_in);

	return (rc);
}

/*
 * Convert string 'str' to data based on the data type in 'desc'.
 * 'data' points to the buffer in which the converted data
 * is placed. '*data_p' points to the buffer, or in the case
 * of a string data type, points to the untoched string (i.e.,
 * 'str').
 */
nscd_rc_t
_nscd_cfg_str_to_data(
	nscd_cfg_param_desc_t	*desc,
	char			*str,
	void			*data,
	void			**data_p,
	nscd_cfg_error_t	**errorp)
{

	char			*me = "_nscd_cfg_str_to_data";
	char			*c;
	nscd_cfg_bitmap_t	bitmap;
	char			msg[NSCD_CFG_MAX_ERR_MSG_LEN];
	nscd_rc_t		rc = NSCD_CFG_DATA_CONVERSION_FAILED;

	if (desc == NULL || str == NULL || data == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "ERROR: one of the following is NULL "
		    "desc = %p, str = %p, data = %p, data_p = %p\n",
		    desc, str, data, data_p);

		return (NSCD_INVALID_ARGUMENT);
	}
	*data_p = data;

	/* if description is that of a group, return error */
	if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) {

		(void) snprintf(msg, sizeof (msg),
		gettext("single data specified for group %s"), desc->id.name);

		if (errorp != NULL)
			*errorp = _nscd_cfg_make_error(NSCD_INVALID_ARGUMENT,
			    msg);

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "ERROR: %s)\n", msg);

		return (NSCD_INVALID_ARGUMENT);

	}

	if (desc->type == NSCD_CFG_DATA_STRING) {
		if (strcmp(str, NSCD_NULL) == 0)
			*(char **)data_p = NULL;
		else {
			/* remove the " char if quoted string */
			if (str[0] == '"') {
				c = str + strlen(str) - 1;
				if (*c == '"')
					*c = '\0';
				*(char **)data_p = str + 1;
			} else
				*(char **)data_p = str;

		}
		return (NSCD_SUCCESS);
	}

	if (str == NULL) {

		(void) snprintf(msg, sizeof (msg),
		gettext("data must be specified for %s"), desc->id.name);

		if (errorp != NULL)
			*errorp = _nscd_cfg_make_error(NSCD_INVALID_ARGUMENT,
			    msg);

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "ERROR: %s\n", msg);

		return (NSCD_INVALID_ARGUMENT);

	}

	switch (desc->type) {

	case NSCD_CFG_DATA_BOOLEAN:

		if (strcasecmp(str, "yes") == 0)
			*(nscd_bool_t *)data = nscd_true;
		else if (strcasecmp(str, "no") == 0)
			*(nscd_bool_t *)data = nscd_false;
		else {

		(void) snprintf(msg, sizeof (msg),
		gettext("data (%s) must be 'yes' or 'no' for %s"),
		    str, desc->id.name);

		if (errorp != NULL)
			*errorp = _nscd_cfg_make_error(NSCD_INVALID_ARGUMENT,
			    msg);

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "ERROR: %s\n", msg);

			return (NSCD_INVALID_ARGUMENT);
		}

		break;

	case NSCD_CFG_DATA_INTEGER:

		errno = 0;
		*(int *)data = (int)strtol(str, (char **)NULL, 10);
		if (errno != NULL) {

			(void) snprintf(msg, sizeof (msg),
			gettext("unable to convert data (%s) for %s"),
			    str, desc->id.name);

			if (errorp != NULL)
				*errorp = _nscd_cfg_make_error(rc, msg);

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "ERROR: %s\n", msg);

			return (rc);
		}

		break;

	case NSCD_CFG_DATA_BITMAP:

		errno = 0;
		bitmap = (nscd_cfg_bitmap_t)strtol(str, (char **)NULL, 10);
		if (errno != NULL) {

			(void) snprintf(msg, sizeof (msg),
			gettext("unable to convert data (%s) for %s"),
			    str, desc->id.name);

			if (errorp != NULL)
				*errorp = _nscd_cfg_make_error(rc, msg);

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "ERROR: %s\n", msg);

			return (rc);
		}

		_nscd_cfg_bitmap_set(data, bitmap);

		break;

	}

	return (NSCD_SUCCESS);
}


nscd_rc_t
_nscd_cfg_set(
	nscd_cfg_handle_t	*handle,
	void			*data,
	nscd_cfg_error_t	**errorp)
{
	char			*me = "_nscd_cfg_set";
	int			dlen;
	nscd_cfg_id_t		*nswdb;
	nscd_cfg_param_desc_t	*desc, *gdesc;
	nscd_cfg_group_info_t	*gi;
	char			*nswdb_name, *param_name;
	void			*pdata = NULL;
	void			*cfg_data, *vdata_addr = NULL;
	nscd_bool_t		get_group = 0;
	nscd_bool_t		in = nscd_true;
	nscd_cfg_lock_t		*lock = NULL;
	nscd_rc_t		rc = NSCD_SUCCESS;

	if (handle == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "handle is NULL\n");
		return (NSCD_INVALID_ARGUMENT);
	}

	nswdb = handle->nswdb;
	desc = (nscd_cfg_param_desc_t *)handle->desc;
	if (nswdb == NULL)
		nswdb_name = "global";
	else
		nswdb_name = nswdb->name;
	param_name = desc->id.name;

	if (data == NULL && _nscd_cfg_flag_is_not_set(desc->pflag,
	    NSCD_CFG_PFLAG_VLEN_DATA)) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "data == NULL\n");
		return (NSCD_INVALID_ARGUMENT);
	}

	if (_nscd_cfg_flag_is_set(desc->pflag,
	    NSCD_CFG_PFLAG_UPDATE_SEND_WHOLE_GROUP) ||
	    _nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP))
		get_group = nscd_true;

	/*
	 * locate the current value of the param or group
	 * and lock the config data for writing
	 */
	rc = _nscd_cfg_locate_cfg_data(&cfg_data, nscd_false, desc,
	    nswdb, get_group, &vdata_addr, &dlen, &lock);
	if (rc != NSCD_SUCCESS) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "unable to locate config data (rc = %d)\n", rc);
		return (rc);
	}

	if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP) &&
	    ((nscd_cfg_group_info_t *)cfg_data)->num_param !=
	    ((nscd_cfg_group_info_t *)data)->num_param) {

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "number of parameters in group <%s : %s> not equal: "
		    "%d in input data, should be %d\n",
		    NSCD_STR_OR_GLOBAL(nswdb_name),
		    NSCD_STR_OR_NULL(param_name),
		    ((nscd_cfg_group_info_t *)data)->num_param,
		    ((nscd_cfg_group_info_t *)cfg_data)->num_param);

		rc = NSCD_INVALID_ARGUMENT;
		goto error_exit;
	}

	/*
	 * if variable length data, we want the address
	 * of the pointer pointing to the data
	 */
	if (vdata_addr != NULL)
		cfg_data = vdata_addr;

	/*
	 * just copy in the specified data, if no need
	 * to verify the data or notify the associated
	 * component
	 */
		if (get_group == nscd_true) {

			gdesc = &_nscd_cfg_param_desc[desc->g_index];

			rc = _nscd_cfg_copy_group_data_merge(
			    gdesc, &pdata, data, cfg_data,
			    desc->id.index, data);

			if (rc != NSCD_SUCCESS) {
				_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
				(me, "unable to copy group data <%s : %s>\n",
				    NSCD_STR_OR_GLOBAL(nswdb_name),
				    NSCD_STR_OR_NULL(param_name));

				goto error_exit;
			}

			rc = _nscd_cfg_notify_s(gdesc, nswdb,
			    pdata, errorp);

		} else
			rc = _nscd_cfg_notify_s(desc, nswdb, data,
			    errorp);

		if (rc != NSCD_SUCCESS) {

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "verifying/notifying  of new configuration "
			    "parameter <%s : %s> failed. %s\n",
			    NSCD_STR_OR_GLOBAL(nswdb_name),
			    param_name, (*errorp && (*errorp)->msg) ?
			    (*errorp)->msg : "");

			goto error_exit;
		}

	/*
	 * Move the new config into the config store
	 */
	rc = NSCD_CFG_SET_PARAM_FAILED;
	if (_nscd_cfg_flag_is_set(desc->pflag,
	    NSCD_CFG_PFLAG_GROUP)) {
		gi = _nscd_cfg_get_gi(pdata);
		rc = _nscd_cfg_copy_group_data_in(gdesc, gi,
		    cfg_data, pdata);
	} else {
		/*
		 * nscd_true asks _nscd_cfg_copy_param_data to
		 * set addr of the vlen data in 'cfg_data' rather
		 * than copying the data content
		 */
		if (pdata != NULL)
			_nscd_cfg_free_vlen_data_group(gdesc,
			    pdata, in);

		rc = _nscd_cfg_copy_param_data(desc,
		    cfg_data, data, in, nscd_true);
	}

	if (rc != NSCD_SUCCESS) {

		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "unable to make new param data <%s : %s> current\n",
		    NSCD_STR_OR_GLOBAL(nswdb_name),
		    NSCD_STR_OR_NULL(param_name));
	}

	error_exit:

	_nscd_cfg_unlock(lock);

	return (rc);
}

nscd_rc_t
_nscd_cfg_set_linked(
	nscd_cfg_handle_t		*handle,
	void				*data,
	nscd_cfg_error_t		**errorp)
{
	char				*me = "_nscd_cfg_set_linked";
	nscd_cfg_id_t			*nswdb;
	nscd_cfg_handle_t		*hl;
	nscd_cfg_param_desc_t		*desc;
	char				*nswdb_name, *param_name, *dbl;
	nscd_rc_t			rc = NSCD_SUCCESS;
	nscd_cfg_nsw_spc_default_t	*spc;
	int				i;
	char				msg[NSCD_CFG_MAX_ERR_MSG_LEN];

	if (handle == NULL) {
		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
		(me, "handle is NULL\n");
		return (NSCD_INVALID_ARGUMENT);
	}

	nswdb = handle->nswdb;
	desc = (nscd_cfg_param_desc_t *)handle->desc;

	/*
	 * no need to do the special linking thing,
	 * if a global param, or a group, or not a linked param
	 */
	if (nswdb == NULL || _nscd_cfg_flag_is_set(desc->pflag,
	    NSCD_CFG_PFLAG_GROUP) ||
	    _nscd_cfg_flag_is_not_set(desc->pflag,
	    NSCD_CFG_PFLAG_LINKED))
		return (_nscd_cfg_set(handle, data, errorp));
	else
		nswdb_name = nswdb->name;
	param_name = desc->id.name;

	/*
	 * if a param is linked to another, it can not be
	 * changed directly
	 */
	for (i = 0; i < _nscd_cfg_num_link_default; i++) {

		if (_nscd_cfg_nsw_link_default[i].data == NULL)
			continue;

		if (strcmp(_nscd_cfg_nsw_link_default[i].db,
		    nswdb_name) == 0 &&
		    _nscd_cfg_nsw_link_default[i].group_off ==
		    desc->g_offset &&
		    _nscd_cfg_nsw_link_default[i].param_off ==
		    desc->p_offset) {

			rc = NSCD_CFG_READ_ONLY;

			(void) snprintf(msg, sizeof (msg),
			    gettext("value of \'%s\' not changeable, "
			    "change that of \'%s\' instead"),
			    nswdb->name, "passwd");

			if (errorp != NULL)
				*errorp = _nscd_cfg_make_error(rc, msg);

			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
			(me, "ERROR: %s\n", msg);

			return (rc);
		}
	}

	/*
	 * if a param is linked from another, it should be verify
	 * and changed first
	 */
	for (i = 0; i < _nscd_cfg_num_link_default; i++) {

		if (_nscd_cfg_nsw_link_default[i].data == NULL)
			continue;

		spc = _nscd_cfg_nsw_link_default[i].data;

		if (strcmp(spc->db, nswdb_name) == 0 &&
		    spc->group_off == desc->g_offset &&
		    spc->param_off == desc->p_offset) {

			rc = _nscd_cfg_set(handle, data, errorp);
			if (rc != NSCD_SUCCESS)
				return (rc);
			break;
		}
	}

	/*
	 * then change all those linked to the one that has been changed
	 */
	for (i = 0; i < _nscd_cfg_num_link_default; i++) {

		if (_nscd_cfg_nsw_link_default[i].data == NULL)
			continue;

		spc = _nscd_cfg_nsw_link_default[i].data;

		if (strcmp(spc->db, nswdb_name) == 0 &&
		    spc->group_off == desc->g_offset &&
		    spc->param_off == desc->p_offset &&
		    _nscd_cfg_nsw_link_default[i].group_off ==
		    desc->g_offset &&
		    _nscd_cfg_nsw_link_default[i].param_off ==
		    desc->p_offset) {

			dbl = _nscd_cfg_nsw_link_default[i].db;

			rc = _nscd_cfg_get_handle(param_name, dbl,
			    &hl, errorp);
			if (rc != NSCD_SUCCESS)
				return (rc);
			rc = _nscd_cfg_set(hl, data, errorp);
			_nscd_cfg_free_handle(hl);
			if (rc != NSCD_SUCCESS)
				return (rc);
		}
	}

	return (_nscd_cfg_set(handle, data, errorp));
}

/*
 * Return a list of comma-separated database names that
 * have at least one of the input sources (the srcs array)
 * appears in their configured nsswitch policy string.
 * That is, if srcs contains "ldap" and "passwd: files ldap"
 * "group: files ldap" are in /etc/nsswitch.conf, then
 * "passwd,group" will be returned. The return string
 * should be freed by the caller.
 *
 * For compat nsswitch configuration, "group" and/or
 * "passwd,user_attr,shadow,audit_user" (not "group_compat"
 * or "passwd_compat") will be returned. Note that the
 * user_attr, shadow, and audit_user databases share the
 * same policy with the passwd database.
 *
 * For example, if srcs has "ldap" and in /etc/nsswitch.conf,
 * there are:
 *   passwd:		compat
 *   passwd_compat:	ldap
 *   group:		compat
 *   group_compat:	ldap
 *   netgroup:		ldap
 * then "netgroup,passwd,group,user_attr,shadow,audit_user"
 * will be returned.
 */
char *
_nscd_srcs_in_db_nsw_policy(
	int			num_src,
	char			**srcs)
{
	uint8_t			i, j, n = 0, nc = 0;
	uint8_t			compat_grp = 0, compat_pwd = 0;
	uint8_t			*db;
	uint8_t			*db_compat;
	int			dlen = 0;
	nscd_cfg_nsw_db_data_t	*dbcfg;
	nscd_cfg_switch_t	*sw;
	char			*outstr = NULL;
	char			*dbname;

	db = (uint8_t *)calloc(_nscd_cfg_num_nsw_db, sizeof (uint8_t));
	if (db == NULL)
		return (NULL);

	db_compat = (uint8_t *)calloc(_nscd_cfg_num_nsw_db,
	    sizeof (uint8_t));
	if (db_compat == NULL) {
		free(db);
		return (NULL);
	}

	for (i = 0; i < _nscd_cfg_num_nsw_db; i++) {

		(void) rw_rdlock(&nscd_cfg_nsw_db_data_rwlock[i]);

		dbcfg = &nscd_cfg_nsw_db_data_current[i];
		sw = &dbcfg->sw;
		if (sw->nsw_config_string == NULL) {
			(void) rw_unlock(&nscd_cfg_nsw_db_data_rwlock[i]);
			continue;
		}

		dbname = _nscd_cfg_nsw_db[i].name;
		for (j = 0; j < num_src; j++) {
			if (strstr(sw->nsw_config_string, srcs[j]) !=
			    NULL) {
				db[n++] = i;
				dlen += strlen(dbname) + 1;
			} else if (strcmp(sw->nsw_config_string,
			    "compat") == 0) {
				if (strcmp(dbname, "passwd") == 0) {
					compat_pwd = 1;
					dlen += 7;
				} else if (strcmp(dbname, "group") == 0) {
					compat_grp = 1;
					dlen += 6;
				} else {
					db_compat[nc++] = i;
					dlen += strlen(dbname) + 1;

				}
			}
		}
		(void) rw_unlock(&nscd_cfg_nsw_db_data_rwlock[i]);
	}

	if (dlen != 0)
		outstr = (char *)calloc(1, dlen);
	if (outstr == NULL) {
		free(db_compat);
		free(db);
		return (NULL);
	}

	for (j = 0; j < n; j++) {
		dbname = _nscd_cfg_nsw_db[db[j]].name;
		if (strstr(dbname, "group_compat") != NULL) {
			if (compat_grp == 1)
				dbname = "group";
			else
				continue;
		} else if (strstr(dbname, "passwd_compat") != NULL) {
			if (compat_pwd == 1)
				dbname = "passwd";
			else
				continue;
		}

		(void) strlcat(outstr, dbname, dlen);
		(void) strlcat(outstr, ",", dlen);
	}

	for (j = 0; j < nc; j++) {
		dbname = _nscd_cfg_nsw_db[db_compat[j]].name;
		if (compat_pwd == 1) {
			(void) strlcat(outstr, dbname, dlen);
			(void) strlcat(outstr, ",", dlen);
		}
	}

	/* remove the last comma */
	i = strlen(outstr) - 1;
	if (outstr[i] == ',')
		outstr[i] = '\0';

	free(db);
	free(db_compat);
	return (outstr);

}