fmd_conf.c revision 7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fe
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <inttypes.h>
#include <alloca.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <fmd_conf.h>
#include <fmd_alloc.h>
#include <fmd_error.h>
#include <fmd_subr.h>
#include <fmd_string.h>
#include <fmd.h>
const char FMD_PROP_SUBSCRIPTIONS[] = "_subscriptions";
const char FMD_PROP_DICTIONARIES[] = "_dictionaries";
/*
* The property formals defined in _fmd_conf_defv[] are added to every config
* dictionary that is created. Here we define several special FMD_PROP_*
* properties that are used to implement the config file keyword actions, as
* well as properties that should be inherited by fmd_conf_t's from fmd.d_conf.
*/
static const fmd_conf_formal_t _fmd_conf_defv[] = {
{ FMD_PROP_SUBSCRIPTIONS, &fmd_conf_list, "" },
{ FMD_PROP_DICTIONARIES, &fmd_conf_list, "" },
{ "fmd.isaname", &fmd_conf_parent, "isaname" },
{ "fmd.machine", &fmd_conf_parent, "machine" },
{ "fmd.platform", &fmd_conf_parent, "platform" },
{ "fmd.rootdir", &fmd_conf_parent, "rootdir" },
};
static const int _fmd_conf_defc =
sizeof (_fmd_conf_defv) / sizeof (_fmd_conf_defv[0]);
static int
set_bool(fmd_conf_param_t *pp, const char *s)
{
if (strcasecmp(s, "true") == 0)
pp->cp_value.cpv_num = 1;
else if (strcasecmp(s, "false") == 0)
pp->cp_value.cpv_num = 0;
else
return (fmd_set_errno(EFMD_CONF_INVAL));
return (0);
}
static void
get_bool(const fmd_conf_param_t *pp, void *ptr)
{
*((int *)ptr) = (int)pp->cp_value.cpv_num;
}
static int
set_i32x(fmd_conf_param_t *pp, const char *s, int64_t min, int64_t max)
{
int64_t val;
char *end;
errno = 0;
val = strtoll(s, &end, 0);
if (errno == EOVERFLOW || val < min || val > max)
return (fmd_set_errno(EFMD_CONF_OVERFLOW));
if (errno != 0 || end == s || *end != '\0')
return (fmd_set_errno(EFMD_CONF_INVAL));
pp->cp_value.cpv_num = val;
return (0);
}
static int
set_i8(fmd_conf_param_t *pp, const char *s)
{
return (set_i32x(pp, s, INT8_MIN, INT8_MAX));
}
static int
set_i16(fmd_conf_param_t *pp, const char *s)
{
return (set_i32x(pp, s, INT16_MIN, INT16_MAX));
}
static int
set_i32(fmd_conf_param_t *pp, const char *s)
{
return (set_i32x(pp, s, INT32_MIN, INT32_MAX));
}
static void
get_i32(const fmd_conf_param_t *pp, void *ptr)
{
*((int32_t *)ptr) = (int32_t)pp->cp_value.cpv_num;
}
static int
set_ui32x(fmd_conf_param_t *pp, const char *s, uint64_t max)
{
uint64_t val;
char *end;
errno = 0;
val = strtoull(s, &end, 0);
if (errno == EOVERFLOW || val > max)
return (fmd_set_errno(EFMD_CONF_OVERFLOW));
if (errno != 0 || end == s || *end != '\0')
return (fmd_set_errno(EFMD_CONF_INVAL));
pp->cp_value.cpv_num = val;
return (0);
}
static int
set_ui8(fmd_conf_param_t *pp, const char *s)
{
return (set_ui32x(pp, s, UINT8_MAX));
}
static int
set_ui16(fmd_conf_param_t *pp, const char *s)
{
return (set_ui32x(pp, s, UINT16_MAX));
}
static int
set_ui32(fmd_conf_param_t *pp, const char *s)
{
return (set_ui32x(pp, s, UINT32_MAX));
}
static void
get_ui32(const fmd_conf_param_t *pp, void *ptr)
{
*((uint32_t *)ptr) = (uint32_t)pp->cp_value.cpv_num;
}
static int
set_i64(fmd_conf_param_t *pp, const char *s)
{
int64_t val;
char *end;
errno = 0;
val = strtoll(s, &end, 0);
if (errno == EOVERFLOW)
return (fmd_set_errno(EFMD_CONF_OVERFLOW));
if (errno != 0 || end == s || *end != '\0')
return (fmd_set_errno(EFMD_CONF_INVAL));
pp->cp_value.cpv_num = val;
return (0);
}
static void
get_i64(const fmd_conf_param_t *pp, void *ptr)
{
*((int64_t *)ptr) = (int64_t)pp->cp_value.cpv_num;
}
static int
set_ui64(fmd_conf_param_t *pp, const char *s)
{
uint64_t val;
char *end;
errno = 0;
val = strtoull(s, &end, 0);
if (errno == EOVERFLOW)
return (fmd_set_errno(EFMD_CONF_OVERFLOW));
if (errno != 0 || end == s || *end != '\0')
return (fmd_set_errno(EFMD_CONF_INVAL));
pp->cp_value.cpv_num = val;
return (0);
}
static void
get_ui64(const fmd_conf_param_t *pp, void *ptr)
{
*((uint64_t *)ptr) = pp->cp_value.cpv_num;
}
static int
set_str(fmd_conf_param_t *pp, const char *s)
{
fmd_strfree(pp->cp_value.cpv_str);
pp->cp_value.cpv_str = fmd_strdup(s, FMD_SLEEP);
return (0);
}
static void
get_str(const fmd_conf_param_t *pp, void *ptr)
{
*((const char **)ptr) = pp->cp_value.cpv_str;
}
static void
free_str(fmd_conf_param_t *pp)
{
fmd_strfree(pp->cp_value.cpv_str);
pp->cp_value.cpv_str = NULL;
}
static int
set_path(fmd_conf_param_t *pp, const char *value)
{
size_t len = strlen(value);
char *s = alloca(len + 1);
char **patv = alloca(sizeof (char *) * len / 2);
int patc = 0;
static const char *const percent_sign = "%";
char *p, *q;
int c, i;
static const struct fmd_conf_token {
char tok_tag;
const char *const *tok_val;
} tokens[] = {
{ 'i', &fmd.d_platform },
{ 'm', &fmd.d_machine },
{ 'p', &fmd.d_isaname },
{ 'r', &fmd.d_rootdir },
{ '%', &percent_sign },
{ 0, NULL }
};
const struct fmd_conf_token *tok;
fmd_conf_path_t *pap;
pp->cp_formal->cf_ops->co_free(pp);
(void) strcpy(s, value);
for (p = strtok_r(s, ":", &q); p != NULL; p = strtok_r(NULL, ":", &q))
patv[patc++] = p;
pap = fmd_alloc(sizeof (fmd_conf_path_t), FMD_SLEEP);
pap->cpa_argv = fmd_alloc(sizeof (char *) * patc, FMD_SLEEP);
pap->cpa_argc = patc;
for (i = 0; i < patc; i++) {
for (len = 0, p = patv[i]; (c = *p) != '\0'; p++, len++) {
if (c != '%' || (c = p[1]) == '\0')
continue;
for (tok = tokens; tok->tok_tag != 0; tok++) {
if (c == tok->tok_tag) {
len += strlen(*tok->tok_val) - 1;
p++;
break;
}
}
}
pap->cpa_argv[i] = q = fmd_alloc(len + 1, FMD_SLEEP);
q[len] = '\0';
for (p = patv[i]; (c = *p) != '\0'; p++) {
if (c != '%' || (c = p[1]) == '\0') {
*q++ = c;
continue;
}
for (tok = tokens; tok->tok_tag != 0; tok++) {
if (c == tok->tok_tag) {
(void) strcpy(q, *tok->tok_val);
q += strlen(q);
p++;
break;
}
}
if (tok->tok_tag == 0)
*q++ = c;
}
}
pp->cp_value.cpv_ptr = pap;
return (0);
}
static int
set_lst(fmd_conf_param_t *pp, const char *value)
{
fmd_conf_path_t *old;
old = pp->cp_value.cpv_ptr;
pp->cp_value.cpv_ptr = NULL;
if (set_path(pp, value) != 0) {
pp->cp_value.cpv_ptr = old;
return (-1); /* errno is set for us */
}
if (old != NULL) {
fmd_conf_path_t *new = pp->cp_value.cpv_ptr;
int i, totc = old->cpa_argc + new->cpa_argc;
int new_argc = new->cpa_argc;
const char **new_argv = new->cpa_argv;
new->cpa_argc = 0;
new->cpa_argv = fmd_alloc(sizeof (char *) * totc, FMD_SLEEP);
for (i = 0; i < old->cpa_argc; i++)
new->cpa_argv[new->cpa_argc++] = old->cpa_argv[i];
for (i = 0; i < new_argc; i++)
new->cpa_argv[new->cpa_argc++] = new_argv[i];
ASSERT(new->cpa_argc == totc);
fmd_free(new_argv, sizeof (char *) * new_argc);
fmd_free(old->cpa_argv, sizeof (char *) * old->cpa_argc);
fmd_free(old, sizeof (fmd_conf_path_t));
}
return (0);
}
static int
del_lst(fmd_conf_param_t *pp, const char *value)
{
fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
const char **new_argv;
int i, new_argc;
for (i = 0; i < pap->cpa_argc; i++) {
if (strcmp(pap->cpa_argv[i], value) == 0)
break;
}
if (i == pap->cpa_argc)
return (fmd_set_errno(ENOENT));
fmd_strfree((char *)pap->cpa_argv[i]);
pap->cpa_argv[i] = NULL;
new_argc = 0;
new_argv = fmd_alloc(sizeof (char *) * (pap->cpa_argc - 1), FMD_SLEEP);
for (i = 0; i < pap->cpa_argc; i++) {
if (pap->cpa_argv[i] != NULL)
new_argv[new_argc++] = pap->cpa_argv[i];
}
fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc);
pap->cpa_argv = new_argv;
pap->cpa_argc = new_argc;
return (0);
}
static void
get_path(const fmd_conf_param_t *pp, void *ptr)
{
*((fmd_conf_path_t **)ptr) = (fmd_conf_path_t *)pp->cp_value.cpv_ptr;
}
static void
free_path(fmd_conf_param_t *pp)
{
fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
int i;
if (pap == NULL)
return; /* no value was ever set */
for (i = 0; i < pap->cpa_argc; i++)
fmd_strfree((char *)pap->cpa_argv[i]);
fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc);
fmd_free(pap, sizeof (fmd_conf_path_t));
pp->cp_value.cpv_ptr = NULL;
}
static int
set_time(fmd_conf_param_t *pp, const char *s)
{
static const struct {
const char *name;
hrtime_t mul;
} suffix[] = {
{ "ns", NANOSEC / NANOSEC },
{ "nsec", NANOSEC / NANOSEC },
{ "us", NANOSEC / MICROSEC },
{ "usec", NANOSEC / MICROSEC },
{ "ms", NANOSEC / MILLISEC },
{ "msec", NANOSEC / MILLISEC },
{ "s", NANOSEC / SEC },
{ "sec", NANOSEC / SEC },
{ "m", NANOSEC * (hrtime_t)60 },
{ "min", NANOSEC * (hrtime_t)60 },
{ "h", NANOSEC * (hrtime_t)(60 * 60) },
{ "hour", NANOSEC * (hrtime_t)(60 * 60) },
{ "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
{ "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
{ "hz", 0 },
{ NULL }
};
hrtime_t val, mul = 1;
char *end;
int i;
errno = 0;
val = strtoull(s, &end, 0);
if (errno == EOVERFLOW)
return (fmd_set_errno(EFMD_CONF_OVERFLOW));
if (errno != 0 || end == s)
return (fmd_set_errno(EFMD_CONF_INVAL));
for (i = 0; suffix[i].name != NULL; i++) {
if (strcasecmp(suffix[i].name, end) == 0) {
mul = suffix[i].mul;
break;
}
}
if (suffix[i].name == NULL && *end != '\0')
return (fmd_set_errno(EFMD_CONF_INVAL));
if (mul == 0) {
if (val != 0)
val = NANOSEC / val; /* compute val as value per sec */
} else
val *= mul;
pp->cp_value.cpv_num = val;
return (0);
}
static int
set_size(fmd_conf_param_t *pp, const char *s)
{
size_t len = strlen(s);
uint64_t val, mul = 1;
char *end;
switch (s[len - 1]) {
case 't':
case 'T':
mul *= 1024;
/*FALLTHRU*/
case 'g':
case 'G':
mul *= 1024;
/*FALLTHRU*/
case 'm':
case 'M':
mul *= 1024;
/*FALLTHRU*/
case 'k':
case 'K':
mul *= 1024;
/*FALLTHRU*/
default:
break;
}
errno = 0;
val = strtoull(s, &end, 0) * mul;
if (errno == EOVERFLOW)
return (fmd_set_errno(EFMD_CONF_OVERFLOW));
if ((mul != 1 && end != &s[len - 1]) ||
(mul == 1 && *end != '\0') || errno != 0)
return (fmd_set_errno(EFMD_CONF_INVAL));
pp->cp_value.cpv_num = val;
return (0);
}
static int
set_sig(fmd_conf_param_t *pp, const char *s)
{
int sig;
if (strncasecmp(s, "SIG", 3) == 0)
s += 3; /* be friendlier than strsig() and permit the prefix */
if (str2sig(s, &sig) != 0)
return (fmd_set_errno(EFMD_CONF_INVAL));
pp->cp_value.cpv_num = sig;
return (0);
}
static void
get_par(const fmd_conf_param_t *pp, void *ptr)
{
if (fmd_conf_getprop(fmd.d_conf, pp->cp_formal->cf_default, ptr) != 0) {
fmd_panic("fmd.d_conf does not define '%s' (inherited as %s)\n",
(char *)pp->cp_formal->cf_default, pp->cp_formal->cf_name);
}
}
/*ARGSUSED*/
static int
set_par(fmd_conf_param_t *pp, const char *s)
{
return (fmd_set_errno(EFMD_CONF_RDONLY));
}
/*
* Utility routine for callers who define custom ops where a list of string
* tokens are translated into a bitmask. 'cmp' should be set to point to an
* array of fmd_conf_mode_t's where the final element has cm_name == NULL.
*/
int
fmd_conf_mode_set(const fmd_conf_mode_t *cma,
fmd_conf_param_t *pp, const char *value)
{
const fmd_conf_mode_t *cmp;
char *p, *q, *s = fmd_strdup(value, FMD_SLEEP);
size_t len = value ? strlen(value) + 1 : 0;
uint_t mode = 0;
if (s == NULL) {
pp->cp_value.cpv_num = 0;
return (0);
}
for (p = strtok_r(s, ",", &q); p != NULL; p = strtok_r(NULL, ",", &q)) {
for (cmp = cma; cmp->cm_name != NULL; cmp++) {
if (strcmp(cmp->cm_name, p) == 0) {
mode |= cmp->cm_bits;
break;
}
}
if (cmp->cm_name == NULL) {
fmd_free(s, len);
return (fmd_set_errno(EFMD_CONF_INVAL));
}
}
pp->cp_value.cpv_num = mode;
fmd_free(s, len);
return (0);
}
void
fmd_conf_mode_get(const fmd_conf_param_t *pp, void *ptr)
{
*((uint_t *)ptr) = (uint_t)pp->cp_value.cpv_num;
}
/*ARGSUSED*/
int
fmd_conf_notsup(fmd_conf_param_t *pp, const char *value)
{
return (fmd_set_errno(ENOTSUP));
}
/*ARGSUSED*/
void
fmd_conf_nop(fmd_conf_param_t *pp)
{
/* no free required for integer-type parameters */
}
#define CONF_DEFINE(name, a, b, c, d) \
const fmd_conf_ops_t name = { a, b, c, d }
CONF_DEFINE(fmd_conf_bool, set_bool, get_bool, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_int8, set_i8, get_i32, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_uint8, set_ui8, get_ui32, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_int16, set_i16, get_i32, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_uint16, set_ui16, get_ui32, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_int32, set_i32, get_i32, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_uint32, set_ui32, get_ui32, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_int64, set_i64, get_i64, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_uint64, set_ui64, get_ui64, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_string, set_str, get_str, fmd_conf_notsup, free_str);
CONF_DEFINE(fmd_conf_path, set_path, get_path, fmd_conf_notsup, free_path);
CONF_DEFINE(fmd_conf_list, set_lst, get_path, del_lst, free_path);
CONF_DEFINE(fmd_conf_time, set_time, get_ui64, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_size, set_size, get_ui64, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_signal, set_sig, get_i32, fmd_conf_notsup, fmd_conf_nop);
CONF_DEFINE(fmd_conf_parent, set_par, get_par, fmd_conf_notsup, fmd_conf_nop);
static char *
fmd_conf_skipstr(char *s)
{
int c;
while ((c = *s) != '\0') {
if (c == '\\')
s++;
else if (c == '"')
break;
s++;
}
return (s);
}
static char *
fmd_conf_skipnws(char *s)
{
while (strchr("\f\n\r\t\v ", *s) == NULL)
s++;
return (s);
}
static int
fmd_conf_tokenize(char *s, char *tokv[])
{
int c, tokc = 0;
while ((c = *s) != '\0') {
switch (c) {
case '"':
tokv[tokc] = s + 1;
s = fmd_conf_skipstr(s + 1);
*s++ = '\0';
(void) fmd_stresc2chr(tokv[tokc++]);
continue;
case '\f': case '\n': case '\r':
case '\t': case '\v': case ' ':
s++;
continue;
default:
tokv[tokc++] = s;
s = fmd_conf_skipnws(s);
*s++ = '\0';
}
}
return (tokc);
}
static int
fmd_conf_exec_setprop(fmd_conf_t *cfp, int argc, char *argv[])
{
if (argc != 2)
return (fmd_set_errno(EFMD_CONF_USAGE));
return (fmd_conf_setprop(cfp, argv[0], argv[1]));
}
static int
fmd_conf_exec_subscribe(fmd_conf_t *cfp, int argc, char *argv[])
{
if (argc != 1)
return (fmd_set_errno(EFMD_CONF_USAGE));
return (fmd_conf_setprop(cfp, FMD_PROP_SUBSCRIPTIONS, argv[0]));
}
static int
fmd_conf_exec_dictionary(fmd_conf_t *cfp, int argc, char *argv[])
{
if (argc != 1)
return (fmd_set_errno(EFMD_CONF_USAGE));
return (fmd_conf_setprop(cfp, FMD_PROP_DICTIONARIES, argv[0]));
}
static int
fmd_conf_parse(fmd_conf_t *cfp, const char *file)
{
static const fmd_conf_verb_t verbs[] = {
{ "setprop", fmd_conf_exec_setprop },
{ "subscribe", fmd_conf_exec_subscribe },
{ "dictionary", fmd_conf_exec_dictionary },
{ NULL, NULL }
};
int line, errs = 0;
char buf[BUFSIZ];
FILE *fp;
if ((fp = fopen(file, "r")) == NULL) {
fmd_error(EFMD_CONF_OPEN, "failed to open %s: %s\n",
file, fmd_strerror(errno));
return (fmd_set_errno(EFMD_CONF_OPEN));
}
for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
char *tokv[sizeof (buf) / 2 + 1];
int tokc = fmd_conf_tokenize(buf, tokv);
const fmd_conf_verb_t *vp;
if (tokc == 0 || tokv[0][0] == '#')
continue; /* skip blank lines and comment lines */
for (vp = verbs; vp->cv_name != NULL; vp++) {
if (strcmp(tokv[0], vp->cv_name) == 0)
break;
}
if (vp->cv_name == NULL) {
fmd_error(EFMD_CONF_KEYWORD, "\"%s\", line %d: "
"invalid configuration file keyword: %s\n",
file, line, tokv[0]);
errs++;
continue;
}
if (vp->cv_exec(cfp, tokc - 1, tokv + 1) != 0) {
fmd_error(errno, "\"%s\", line %d", file, line);
errs++;
continue;
}
}
if (ferror(fp) != 0 || fclose(fp) != 0)
return (fmd_set_errno(EFMD_CONF_IO));
if (errs != 0)
return (fmd_set_errno(EFMD_CONF_ERRS));
return (0);
}
static void
fmd_conf_fill(fmd_conf_t *cfp, fmd_conf_param_t *ppbuf,
int argc, const fmd_conf_formal_t *argv, int checkid)
{
int i;
for (i = 0; i < argc; i++, argv++) {
fmd_conf_param_t *op, *pp = ppbuf + i;
const char *name = argv->cf_name;
ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen;
if (fmd_strbadid(name, checkid) != NULL) {
fmd_error(EFMD_CONF_PROPNAME, "ignoring invalid formal "
"property %s\n", name);
continue;
}
for (op = cfp->cf_parhash[h]; op != NULL; op = op->cp_next) {
if (strcmp(op->cp_formal->cf_name, name) == 0) {
fmd_error(EFMD_CONF_PROPDUP, "ignoring "
"duplicate formal property %s\n", name);
break;
}
}
if (op != NULL)
continue;
pp->cp_formal = argv;
pp->cp_next = cfp->cf_parhash[h];
cfp->cf_parhash[h] = pp;
if (argv->cf_default && argv->cf_ops != &fmd_conf_parent &&
fmd_conf_setprop(cfp, name, argv->cf_default) != 0) {
fmd_error(EFMD_CONF_DEFAULT, "ignoring invalid default "
"<%s> for property %s: %s\n", argv->cf_default,
name, fmd_strerror(errno));
}
}
}
fmd_conf_t *
fmd_conf_open(const char *file, int argc,
const fmd_conf_formal_t *argv, uint_t flag)
{
fmd_conf_t *cfp = fmd_alloc(sizeof (fmd_conf_t), FMD_SLEEP);
(void) pthread_rwlock_init(&cfp->cf_lock, NULL);
cfp->cf_argv = argv;
cfp->cf_argc = argc;
cfp->cf_flag = flag;
cfp->cf_params = fmd_zalloc(
sizeof (fmd_conf_param_t) * (_fmd_conf_defc + argc), FMD_SLEEP);
cfp->cf_parhashlen = fmd.d_str_buckets;
cfp->cf_parhash = fmd_zalloc(
sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen, FMD_SLEEP);
cfp->cf_defer = NULL;
fmd_conf_fill(cfp, cfp->cf_params, _fmd_conf_defc, _fmd_conf_defv, 0);
fmd_conf_fill(cfp, cfp->cf_params + _fmd_conf_defc, argc, argv, 1);
if (file != NULL && fmd_conf_parse(cfp, file) != 0) {
fmd_conf_close(cfp);
return (NULL);
}
return (cfp);
}
void
fmd_conf_merge(fmd_conf_t *cfp, const char *file)
{
(void) fmd_conf_parse(cfp, file);
}
void
fmd_conf_propagate(fmd_conf_t *src, fmd_conf_t *dst, const char *scope)
{
size_t len = strlen(scope);
fmd_conf_defer_t *cdp;
(void) pthread_rwlock_rdlock(&src->cf_lock);
for (cdp = src->cf_defer; cdp != NULL; cdp = cdp->cd_next) {
if (len == (size_t)(strchr(cdp->cd_name, ':') - cdp->cd_name) &&
strncmp(cdp->cd_name, scope, len) == 0 && fmd_conf_setprop(
dst, cdp->cd_name + len + 1, cdp->cd_value) != 0) {
fmd_error(EFMD_CONF_DEFER,
"failed to apply deferred property %s to %s: %s\n",
cdp->cd_name, scope, fmd_strerror(errno));
}
}
(void) pthread_rwlock_unlock(&src->cf_lock);
}
void
fmd_conf_close(fmd_conf_t *cfp)
{
fmd_conf_param_t *pp = cfp->cf_params;
int i, nparams = _fmd_conf_defc + cfp->cf_argc;
fmd_conf_defer_t *cdp, *ndp;
for (cdp = cfp->cf_defer; cdp != NULL; cdp = ndp) {
ndp = cdp->cd_next;
fmd_strfree(cdp->cd_name);
fmd_strfree(cdp->cd_value);
fmd_free(cdp, sizeof (fmd_conf_defer_t));
}
fmd_free(cfp->cf_parhash,
sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen);
for (i = 0; i < nparams; i++, pp++) {
if (pp->cp_formal != NULL)
pp->cp_formal->cf_ops->co_free(pp);
}
fmd_free(cfp->cf_params, sizeof (fmd_conf_param_t) * nparams);
fmd_free(cfp, sizeof (fmd_conf_t));
}
static fmd_conf_param_t *
fmd_conf_getparam(fmd_conf_t *cfp, const char *name)
{
ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen;
fmd_conf_param_t *pp = cfp->cf_parhash[h];
ASSERT(RW_LOCK_HELD(&cfp->cf_lock));
for (; pp != NULL; pp = pp->cp_next) {
if (strcmp(name, pp->cp_formal->cf_name) == 0)
return (pp);
}
return (NULL);
}
/*
* String-friendly version of fmd_conf_getprop(): return the string as our
* return value, and return NULL if the string is the empty string.
*/
const char *
fmd_conf_getnzstr(fmd_conf_t *cfp, const char *name)
{
const fmd_conf_param_t *pp;
char *s = NULL;
(void) pthread_rwlock_rdlock(&cfp->cf_lock);
if ((pp = fmd_conf_getparam(cfp, name)) != NULL) {
ASSERT(pp->cp_formal->cf_ops == &fmd_conf_string);
pp->cp_formal->cf_ops->co_get(pp, &s);
} else
(void) fmd_set_errno(EFMD_CONF_NOPROP);
(void) pthread_rwlock_unlock(&cfp->cf_lock);
if (s != NULL && s[0] == '\0') {
(void) fmd_set_errno(EFMD_CONF_UNDEF);
s = NULL;
}
return (s);
}
const fmd_conf_ops_t *
fmd_conf_gettype(fmd_conf_t *cfp, const char *name)
{
const fmd_conf_param_t *pp;
const fmd_conf_ops_t *ops = NULL;
(void) pthread_rwlock_rdlock(&cfp->cf_lock);
if ((pp = fmd_conf_getparam(cfp, name)) != NULL) {
if ((ops = pp->cp_formal->cf_ops) == &fmd_conf_parent) {
ops = fmd_conf_gettype(fmd.d_conf,
pp->cp_formal->cf_default);
}
} else
(void) fmd_set_errno(EFMD_CONF_NOPROP);
(void) pthread_rwlock_unlock(&cfp->cf_lock);
return (ops);
}
int
fmd_conf_getprop(fmd_conf_t *cfp, const char *name, void *data)
{
const fmd_conf_param_t *pp;
int err = 0;
(void) pthread_rwlock_rdlock(&cfp->cf_lock);
if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
pp->cp_formal->cf_ops->co_get(pp, data);
else
err = fmd_set_errno(EFMD_CONF_NOPROP);
(void) pthread_rwlock_unlock(&cfp->cf_lock);
return (err);
}
static int
fmd_conf_setdefer(fmd_conf_t *cfp, const char *name, const char *value)
{
fmd_conf_defer_t *cdp;
if (!(cfp->cf_flag & FMD_CONF_DEFER))
return (fmd_set_errno(EFMD_CONF_NODEFER));
(void) pthread_rwlock_wrlock(&cfp->cf_lock);
for (cdp = cfp->cf_defer; cdp != NULL; cdp = cdp->cd_next) {
if (strcmp(name, cdp->cd_name) == 0) {
fmd_strfree(cdp->cd_value);
cdp->cd_value = fmd_strdup(value, FMD_SLEEP);
goto out;
}
}
cdp = fmd_alloc(sizeof (fmd_conf_defer_t), FMD_SLEEP);
cdp->cd_name = fmd_strdup(name, FMD_SLEEP);
cdp->cd_value = fmd_strdup(value, FMD_SLEEP);
cdp->cd_next = cfp->cf_defer;
cfp->cf_defer = cdp;
out:
(void) pthread_rwlock_unlock(&cfp->cf_lock);
return (0);
}
int
fmd_conf_setprop(fmd_conf_t *cfp, const char *name, const char *value)
{
fmd_conf_param_t *pp;
int err;
if (strchr(name, ':') != NULL)
return (fmd_conf_setdefer(cfp, name, value));
(void) pthread_rwlock_wrlock(&cfp->cf_lock);
if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
err = pp->cp_formal->cf_ops->co_set(pp, value);
else
err = fmd_set_errno(EFMD_CONF_NOPROP);
(void) pthread_rwlock_unlock(&cfp->cf_lock);
return (err);
}
int
fmd_conf_delprop(fmd_conf_t *cfp, const char *name, const char *value)
{
fmd_conf_param_t *pp;
int err;
(void) pthread_rwlock_wrlock(&cfp->cf_lock);
if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
err = pp->cp_formal->cf_ops->co_del(pp, value);
else
err = fmd_set_errno(EFMD_CONF_NOPROP);
(void) pthread_rwlock_unlock(&cfp->cf_lock);
return (err);
}