/* * 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. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include /* gettext */ #include #include #include #include #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); } 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, USYNC_THREAD, 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], 0, 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, 0, 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 == 0) 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 != 0 && 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, NULL, 10); if (errno != 0) { (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, NULL, 10); if (errno != 0) { (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); }