8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / fm / fmd / common / fmd_conf.c
blobbbc24458de3a705b8eb30c90284c74d09e1d3c1a
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <pthread.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <inttypes.h>
31 #include <alloca.h>
32 #include <strings.h>
33 #include <stdlib.h>
34 #include <stdio.h>
36 #include <fmd_conf.h>
37 #include <fmd_alloc.h>
38 #include <fmd_error.h>
39 #include <fmd_subr.h>
40 #include <fmd_string.h>
41 #include <fmd.h>
43 const char FMD_PROP_SUBSCRIPTIONS[] = "_subscriptions";
44 const char FMD_PROP_DICTIONARIES[] = "_dictionaries";
47 * The property formals defined in _fmd_conf_defv[] are added to every config
48 * dictionary that is created. Here we define several special FMD_PROP_*
49 * properties that are used to implement the config file keyword actions, as
50 * well as properties that should be inherited by fmd_conf_t's from fmd.d_conf.
52 static const fmd_conf_formal_t _fmd_conf_defv[] = {
53 { FMD_PROP_SUBSCRIPTIONS, &fmd_conf_list, "" },
54 { FMD_PROP_DICTIONARIES, &fmd_conf_list, "" },
55 { "fmd.isaname", &fmd_conf_parent, "isaname" },
56 { "fmd.machine", &fmd_conf_parent, "machine" },
57 { "fmd.platform", &fmd_conf_parent, "platform" },
58 { "fmd.rootdir", &fmd_conf_parent, "rootdir" },
61 static const int _fmd_conf_defc =
62 sizeof (_fmd_conf_defv) / sizeof (_fmd_conf_defv[0]);
64 static int
65 set_bool(fmd_conf_param_t *pp, const char *s)
67 if (strcasecmp(s, "true") == 0)
68 pp->cp_value.cpv_num = 1;
69 else if (strcasecmp(s, "false") == 0)
70 pp->cp_value.cpv_num = 0;
71 else
72 return (fmd_set_errno(EFMD_CONF_INVAL));
74 return (0);
77 static void
78 get_bool(const fmd_conf_param_t *pp, void *ptr)
80 *((int *)ptr) = (int)pp->cp_value.cpv_num;
83 static int
84 set_i32x(fmd_conf_param_t *pp, const char *s, int64_t min, int64_t max)
86 int64_t val;
87 char *end;
89 errno = 0;
90 val = strtoll(s, &end, 0);
92 if (errno == EOVERFLOW || val < min || val > max)
93 return (fmd_set_errno(EFMD_CONF_OVERFLOW));
95 if (errno != 0 || end == s || *end != '\0')
96 return (fmd_set_errno(EFMD_CONF_INVAL));
98 pp->cp_value.cpv_num = val;
99 return (0);
102 static int
103 set_i8(fmd_conf_param_t *pp, const char *s)
105 return (set_i32x(pp, s, INT8_MIN, INT8_MAX));
108 static int
109 set_i16(fmd_conf_param_t *pp, const char *s)
111 return (set_i32x(pp, s, INT16_MIN, INT16_MAX));
114 static int
115 set_i32(fmd_conf_param_t *pp, const char *s)
117 return (set_i32x(pp, s, INT32_MIN, INT32_MAX));
120 static void
121 get_i32(const fmd_conf_param_t *pp, void *ptr)
123 *((int32_t *)ptr) = (int32_t)pp->cp_value.cpv_num;
126 static int
127 set_ui32x(fmd_conf_param_t *pp, const char *s, uint64_t max)
129 uint64_t val;
130 char *end;
132 errno = 0;
133 val = strtoull(s, &end, 0);
135 if (errno == EOVERFLOW || val > max)
136 return (fmd_set_errno(EFMD_CONF_OVERFLOW));
138 if (errno != 0 || end == s || *end != '\0')
139 return (fmd_set_errno(EFMD_CONF_INVAL));
141 pp->cp_value.cpv_num = val;
142 return (0);
145 static int
146 set_ui8(fmd_conf_param_t *pp, const char *s)
148 return (set_ui32x(pp, s, UINT8_MAX));
151 static int
152 set_ui16(fmd_conf_param_t *pp, const char *s)
154 return (set_ui32x(pp, s, UINT16_MAX));
157 static int
158 set_ui32(fmd_conf_param_t *pp, const char *s)
160 return (set_ui32x(pp, s, UINT32_MAX));
163 static void
164 get_ui32(const fmd_conf_param_t *pp, void *ptr)
166 *((uint32_t *)ptr) = (uint32_t)pp->cp_value.cpv_num;
169 static int
170 set_i64(fmd_conf_param_t *pp, const char *s)
172 int64_t val;
173 char *end;
175 errno = 0;
176 val = strtoll(s, &end, 0);
178 if (errno == EOVERFLOW)
179 return (fmd_set_errno(EFMD_CONF_OVERFLOW));
181 if (errno != 0 || end == s || *end != '\0')
182 return (fmd_set_errno(EFMD_CONF_INVAL));
184 pp->cp_value.cpv_num = val;
185 return (0);
188 static void
189 get_i64(const fmd_conf_param_t *pp, void *ptr)
191 *((int64_t *)ptr) = (int64_t)pp->cp_value.cpv_num;
194 static int
195 set_ui64(fmd_conf_param_t *pp, const char *s)
197 uint64_t val;
198 char *end;
200 errno = 0;
201 val = strtoull(s, &end, 0);
203 if (errno == EOVERFLOW)
204 return (fmd_set_errno(EFMD_CONF_OVERFLOW));
206 if (errno != 0 || end == s || *end != '\0')
207 return (fmd_set_errno(EFMD_CONF_INVAL));
209 pp->cp_value.cpv_num = val;
210 return (0);
213 static void
214 get_ui64(const fmd_conf_param_t *pp, void *ptr)
216 *((uint64_t *)ptr) = pp->cp_value.cpv_num;
219 static int
220 set_str(fmd_conf_param_t *pp, const char *s)
222 fmd_strfree(pp->cp_value.cpv_str);
223 pp->cp_value.cpv_str = fmd_strdup(s, FMD_SLEEP);
224 return (0);
227 static void
228 get_str(const fmd_conf_param_t *pp, void *ptr)
230 *((const char **)ptr) = pp->cp_value.cpv_str;
233 static void
234 free_str(fmd_conf_param_t *pp)
236 fmd_strfree(pp->cp_value.cpv_str);
237 pp->cp_value.cpv_str = NULL;
240 static int
241 set_path(fmd_conf_param_t *pp, const char *value)
243 size_t len = strlen(value);
244 char *s = alloca(len + 1);
246 char **patv = alloca(sizeof (char *) * len / 2);
247 int patc = 0;
249 static const char *const percent_sign = "%";
250 char *p, *q;
251 int c, i;
253 static const struct fmd_conf_token {
254 char tok_tag;
255 const char *const *tok_val;
256 } tokens[] = {
257 { 'i', &fmd.d_platform },
258 { 'm', &fmd.d_machine },
259 { 'p', &fmd.d_isaname },
260 { 'r', &fmd.d_rootdir },
261 { '%', &percent_sign },
262 { 0, NULL }
265 const struct fmd_conf_token *tok;
266 fmd_conf_path_t *pap;
268 pp->cp_formal->cf_ops->co_free(pp);
269 (void) strcpy(s, value);
271 for (p = strtok_r(s, ":", &q); p != NULL; p = strtok_r(NULL, ":", &q))
272 patv[patc++] = p;
274 pap = fmd_alloc(sizeof (fmd_conf_path_t), FMD_SLEEP);
275 pap->cpa_argv = fmd_alloc(sizeof (char *) * patc, FMD_SLEEP);
276 pap->cpa_argc = patc;
278 for (i = 0; i < patc; i++) {
279 for (len = 0, p = patv[i]; (c = *p) != '\0'; p++, len++) {
280 if (c != '%' || (c = p[1]) == '\0')
281 continue;
283 for (tok = tokens; tok->tok_tag != 0; tok++) {
284 if (c == tok->tok_tag) {
285 len += strlen(*tok->tok_val) - 1;
286 p++;
287 break;
292 pap->cpa_argv[i] = q = fmd_alloc(len + 1, FMD_SLEEP);
293 q[len] = '\0';
295 for (p = patv[i]; (c = *p) != '\0'; p++) {
296 if (c != '%' || (c = p[1]) == '\0') {
297 *q++ = c;
298 continue;
301 for (tok = tokens; tok->tok_tag != 0; tok++) {
302 if (c == tok->tok_tag) {
303 (void) strcpy(q, *tok->tok_val);
304 q += strlen(q);
305 p++;
306 break;
310 if (tok->tok_tag == 0)
311 *q++ = c;
315 pp->cp_value.cpv_ptr = pap;
316 return (0);
319 static int
320 set_lst(fmd_conf_param_t *pp, const char *value)
322 fmd_conf_path_t *old;
324 old = pp->cp_value.cpv_ptr;
325 pp->cp_value.cpv_ptr = NULL;
327 if (set_path(pp, value) != 0) {
328 pp->cp_value.cpv_ptr = old;
329 return (-1); /* errno is set for us */
332 if (old != NULL) {
333 fmd_conf_path_t *new = pp->cp_value.cpv_ptr;
334 int i, totc = old->cpa_argc + new->cpa_argc;
336 int new_argc = new->cpa_argc;
337 const char **new_argv = new->cpa_argv;
339 new->cpa_argc = 0;
340 new->cpa_argv = fmd_alloc(sizeof (char *) * totc, FMD_SLEEP);
342 for (i = 0; i < old->cpa_argc; i++)
343 new->cpa_argv[new->cpa_argc++] = old->cpa_argv[i];
345 for (i = 0; i < new_argc; i++)
346 new->cpa_argv[new->cpa_argc++] = new_argv[i];
348 ASSERT(new->cpa_argc == totc);
350 fmd_free(new_argv, sizeof (char *) * new_argc);
351 fmd_free(old->cpa_argv, sizeof (char *) * old->cpa_argc);
352 fmd_free(old, sizeof (fmd_conf_path_t));
355 return (0);
358 static int
359 del_lst(fmd_conf_param_t *pp, const char *value)
361 fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
362 const char **new_argv;
363 int i, new_argc;
365 for (i = 0; i < pap->cpa_argc; i++) {
366 if (strcmp(pap->cpa_argv[i], value) == 0)
367 break;
370 if (i == pap->cpa_argc)
371 return (fmd_set_errno(ENOENT));
373 fmd_strfree((char *)pap->cpa_argv[i]);
374 pap->cpa_argv[i] = NULL;
376 new_argc = 0;
377 new_argv = fmd_alloc(sizeof (char *) * (pap->cpa_argc - 1), FMD_SLEEP);
379 for (i = 0; i < pap->cpa_argc; i++) {
380 if (pap->cpa_argv[i] != NULL)
381 new_argv[new_argc++] = pap->cpa_argv[i];
384 fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc);
385 pap->cpa_argv = new_argv;
386 pap->cpa_argc = new_argc;
388 return (0);
391 static void
392 get_path(const fmd_conf_param_t *pp, void *ptr)
394 *((fmd_conf_path_t **)ptr) = (fmd_conf_path_t *)pp->cp_value.cpv_ptr;
397 static void
398 free_path(fmd_conf_param_t *pp)
400 fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
401 int i;
403 if (pap == NULL)
404 return; /* no value was ever set */
406 for (i = 0; i < pap->cpa_argc; i++)
407 fmd_strfree((char *)pap->cpa_argv[i]);
409 fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc);
410 fmd_free(pap, sizeof (fmd_conf_path_t));
411 pp->cp_value.cpv_ptr = NULL;
414 static int
415 set_time(fmd_conf_param_t *pp, const char *s)
417 static const struct {
418 const char *name;
419 hrtime_t mul;
420 } suffix[] = {
421 { "ns", NANOSEC / NANOSEC },
422 { "nsec", NANOSEC / NANOSEC },
423 { "us", NANOSEC / MICROSEC },
424 { "usec", NANOSEC / MICROSEC },
425 { "ms", NANOSEC / MILLISEC },
426 { "msec", NANOSEC / MILLISEC },
427 { "s", NANOSEC / SEC },
428 { "sec", NANOSEC / SEC },
429 { "m", NANOSEC * (hrtime_t)60 },
430 { "min", NANOSEC * (hrtime_t)60 },
431 { "h", NANOSEC * (hrtime_t)(60 * 60) },
432 { "hour", NANOSEC * (hrtime_t)(60 * 60) },
433 { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
434 { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
435 { "hz", 0 },
436 { NULL }
439 hrtime_t val, mul = 1;
440 char *end;
441 int i;
443 errno = 0;
444 val = strtoull(s, &end, 0);
446 if (errno == EOVERFLOW)
447 return (fmd_set_errno(EFMD_CONF_OVERFLOW));
449 if (errno != 0 || end == s)
450 return (fmd_set_errno(EFMD_CONF_INVAL));
452 for (i = 0; suffix[i].name != NULL; i++) {
453 if (strcasecmp(suffix[i].name, end) == 0) {
454 mul = suffix[i].mul;
455 break;
459 if (suffix[i].name == NULL && *end != '\0')
460 return (fmd_set_errno(EFMD_CONF_INVAL));
462 if (mul == 0) {
463 if (val != 0)
464 val = NANOSEC / val; /* compute val as value per sec */
465 } else
466 val *= mul;
468 pp->cp_value.cpv_num = val;
469 return (0);
472 static int
473 set_size(fmd_conf_param_t *pp, const char *s)
475 size_t len = strlen(s);
476 uint64_t val, mul = 1;
477 char *end;
479 switch (s[len - 1]) {
480 case 't':
481 case 'T':
482 mul *= 1024;
483 /*FALLTHRU*/
484 case 'g':
485 case 'G':
486 mul *= 1024;
487 /*FALLTHRU*/
488 case 'm':
489 case 'M':
490 mul *= 1024;
491 /*FALLTHRU*/
492 case 'k':
493 case 'K':
494 mul *= 1024;
495 /*FALLTHRU*/
496 default:
497 break;
500 errno = 0;
501 val = strtoull(s, &end, 0) * mul;
503 if (errno == EOVERFLOW)
504 return (fmd_set_errno(EFMD_CONF_OVERFLOW));
506 if ((mul != 1 && end != &s[len - 1]) ||
507 (mul == 1 && *end != '\0') || errno != 0)
508 return (fmd_set_errno(EFMD_CONF_INVAL));
510 pp->cp_value.cpv_num = val;
511 return (0);
514 static int
515 set_sig(fmd_conf_param_t *pp, const char *s)
517 int sig;
519 if (strncasecmp(s, "SIG", 3) == 0)
520 s += 3; /* be friendlier than strsig() and permit the prefix */
522 if (str2sig(s, &sig) != 0)
523 return (fmd_set_errno(EFMD_CONF_INVAL));
525 pp->cp_value.cpv_num = sig;
526 return (0);
529 static void
530 get_par(const fmd_conf_param_t *pp, void *ptr)
532 if (fmd_conf_getprop(fmd.d_conf, pp->cp_formal->cf_default, ptr) != 0) {
533 fmd_panic("fmd.d_conf does not define '%s' (inherited as %s)\n",
534 (char *)pp->cp_formal->cf_default, pp->cp_formal->cf_name);
538 /*ARGSUSED*/
539 static int
540 set_par(fmd_conf_param_t *pp, const char *s)
542 return (fmd_set_errno(EFMD_CONF_RDONLY));
546 * Utility routine for callers who define custom ops where a list of string
547 * tokens are translated into a bitmask. 'cmp' should be set to point to an
548 * array of fmd_conf_mode_t's where the final element has cm_name == NULL.
551 fmd_conf_mode_set(const fmd_conf_mode_t *cma,
552 fmd_conf_param_t *pp, const char *value)
554 const fmd_conf_mode_t *cmp;
555 char *p, *q, *s = fmd_strdup(value, FMD_SLEEP);
556 size_t len = value ? strlen(value) + 1 : 0;
557 uint_t mode = 0;
559 if (s == NULL) {
560 pp->cp_value.cpv_num = 0;
561 return (0);
564 for (p = strtok_r(s, ",", &q); p != NULL; p = strtok_r(NULL, ",", &q)) {
565 for (cmp = cma; cmp->cm_name != NULL; cmp++) {
566 if (strcmp(cmp->cm_name, p) == 0) {
567 mode |= cmp->cm_bits;
568 break;
572 if (cmp->cm_name == NULL) {
573 fmd_free(s, len);
574 return (fmd_set_errno(EFMD_CONF_INVAL));
578 pp->cp_value.cpv_num = mode;
579 fmd_free(s, len);
580 return (0);
583 void
584 fmd_conf_mode_get(const fmd_conf_param_t *pp, void *ptr)
586 *((uint_t *)ptr) = (uint_t)pp->cp_value.cpv_num;
589 /*ARGSUSED*/
591 fmd_conf_notsup(fmd_conf_param_t *pp, const char *value)
593 return (fmd_set_errno(ENOTSUP));
596 /*ARGSUSED*/
597 void
598 fmd_conf_nop(fmd_conf_param_t *pp)
600 /* no free required for integer-type parameters */
603 #define CONF_DEFINE(name, a, b, c, d) \
604 const fmd_conf_ops_t name = { a, b, c, d }
606 CONF_DEFINE(fmd_conf_bool, set_bool, get_bool, fmd_conf_notsup, fmd_conf_nop);
607 CONF_DEFINE(fmd_conf_int8, set_i8, get_i32, fmd_conf_notsup, fmd_conf_nop);
608 CONF_DEFINE(fmd_conf_uint8, set_ui8, get_ui32, fmd_conf_notsup, fmd_conf_nop);
609 CONF_DEFINE(fmd_conf_int16, set_i16, get_i32, fmd_conf_notsup, fmd_conf_nop);
610 CONF_DEFINE(fmd_conf_uint16, set_ui16, get_ui32, fmd_conf_notsup, fmd_conf_nop);
611 CONF_DEFINE(fmd_conf_int32, set_i32, get_i32, fmd_conf_notsup, fmd_conf_nop);
612 CONF_DEFINE(fmd_conf_uint32, set_ui32, get_ui32, fmd_conf_notsup, fmd_conf_nop);
613 CONF_DEFINE(fmd_conf_int64, set_i64, get_i64, fmd_conf_notsup, fmd_conf_nop);
614 CONF_DEFINE(fmd_conf_uint64, set_ui64, get_ui64, fmd_conf_notsup, fmd_conf_nop);
615 CONF_DEFINE(fmd_conf_string, set_str, get_str, fmd_conf_notsup, free_str);
616 CONF_DEFINE(fmd_conf_path, set_path, get_path, fmd_conf_notsup, free_path);
617 CONF_DEFINE(fmd_conf_list, set_lst, get_path, del_lst, free_path);
618 CONF_DEFINE(fmd_conf_time, set_time, get_ui64, fmd_conf_notsup, fmd_conf_nop);
619 CONF_DEFINE(fmd_conf_size, set_size, get_ui64, fmd_conf_notsup, fmd_conf_nop);
620 CONF_DEFINE(fmd_conf_signal, set_sig, get_i32, fmd_conf_notsup, fmd_conf_nop);
621 CONF_DEFINE(fmd_conf_parent, set_par, get_par, fmd_conf_notsup, fmd_conf_nop);
623 static char *
624 fmd_conf_skipstr(char *s)
626 int c;
628 while ((c = *s) != '\0') {
629 if (c == '\\')
630 s++;
631 else if (c == '"')
632 break;
633 s++;
636 return (s);
639 static char *
640 fmd_conf_skipnws(char *s)
642 while (strchr("\f\n\r\t\v ", *s) == NULL)
643 s++;
645 return (s);
648 static int
649 fmd_conf_tokenize(char *s, char *tokv[])
651 int c, tokc = 0;
653 while ((c = *s) != '\0') {
654 switch (c) {
655 case '"':
656 tokv[tokc] = s + 1;
657 s = fmd_conf_skipstr(s + 1);
658 *s++ = '\0';
659 (void) fmd_stresc2chr(tokv[tokc++]);
660 continue;
661 case '\f': case '\n': case '\r':
662 case '\t': case '\v': case ' ':
663 s++;
664 continue;
665 default:
666 tokv[tokc++] = s;
667 s = fmd_conf_skipnws(s);
668 *s++ = '\0';
672 return (tokc);
675 static int
676 fmd_conf_exec_setprop(fmd_conf_t *cfp, int argc, char *argv[])
678 if (argc != 2)
679 return (fmd_set_errno(EFMD_CONF_USAGE));
681 return (fmd_conf_setprop(cfp, argv[0], argv[1]));
684 static int
685 fmd_conf_exec_subscribe(fmd_conf_t *cfp, int argc, char *argv[])
687 if (argc != 1)
688 return (fmd_set_errno(EFMD_CONF_USAGE));
690 return (fmd_conf_setprop(cfp, FMD_PROP_SUBSCRIPTIONS, argv[0]));
693 static int
694 fmd_conf_exec_dictionary(fmd_conf_t *cfp, int argc, char *argv[])
696 if (argc != 1)
697 return (fmd_set_errno(EFMD_CONF_USAGE));
699 return (fmd_conf_setprop(cfp, FMD_PROP_DICTIONARIES, argv[0]));
702 static int
703 fmd_conf_parse(fmd_conf_t *cfp, const char *file)
705 static const fmd_conf_verb_t verbs[] = {
706 { "setprop", fmd_conf_exec_setprop },
707 { "subscribe", fmd_conf_exec_subscribe },
708 { "dictionary", fmd_conf_exec_dictionary },
709 { NULL, NULL }
712 int line, errs = 0;
713 char buf[BUFSIZ];
714 FILE *fp;
716 if ((fp = fopen(file, "r")) == NULL) {
717 if (errno == EMFILE)
718 fmd_error(EFMD_EXIT, "failed to open %s: %s\n",
719 file, fmd_strerror(errno));
720 else
721 fmd_error(EFMD_CONF_OPEN, "failed to open %s: %s\n",
722 file, fmd_strerror(errno));
723 return (fmd_set_errno(EFMD_CONF_OPEN));
726 for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
727 char *tokv[sizeof (buf) / 2 + 1];
728 int tokc = fmd_conf_tokenize(buf, tokv);
729 const fmd_conf_verb_t *vp;
731 if (tokc == 0 || tokv[0][0] == '#')
732 continue; /* skip blank lines and comment lines */
734 for (vp = verbs; vp->cv_name != NULL; vp++) {
735 if (strcmp(tokv[0], vp->cv_name) == 0)
736 break;
739 if (vp->cv_name == NULL) {
740 fmd_error(EFMD_CONF_KEYWORD, "\"%s\", line %d: "
741 "invalid configuration file keyword: %s\n",
742 file, line, tokv[0]);
743 errs++;
744 continue;
747 if (vp->cv_exec(cfp, tokc - 1, tokv + 1) != 0) {
748 fmd_error(errno, "\"%s\", line %d", file, line);
749 errs++;
750 continue;
754 if (ferror(fp) != 0 || fclose(fp) != 0)
755 return (fmd_set_errno(EFMD_CONF_IO));
757 if (errs != 0)
758 return (fmd_set_errno(EFMD_CONF_ERRS));
760 return (0);
763 static void
764 fmd_conf_fill(fmd_conf_t *cfp, fmd_conf_param_t *ppbuf,
765 int argc, const fmd_conf_formal_t *argv, int checkid)
767 int i;
769 for (i = 0; i < argc; i++, argv++) {
770 fmd_conf_param_t *op, *pp = ppbuf + i;
771 const char *name = argv->cf_name;
772 ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen;
774 if (fmd_strbadid(name, checkid) != NULL) {
775 fmd_error(EFMD_CONF_PROPNAME, "ignoring invalid formal "
776 "property %s\n", name);
777 continue;
780 for (op = cfp->cf_parhash[h]; op != NULL; op = op->cp_next) {
781 if (strcmp(op->cp_formal->cf_name, name) == 0) {
782 fmd_error(EFMD_CONF_PROPDUP, "ignoring "
783 "duplicate formal property %s\n", name);
784 break;
788 if (op != NULL)
789 continue;
791 pp->cp_formal = argv;
792 pp->cp_next = cfp->cf_parhash[h];
793 cfp->cf_parhash[h] = pp;
795 if (argv->cf_default && argv->cf_ops != &fmd_conf_parent &&
796 fmd_conf_setprop(cfp, name, argv->cf_default) != 0) {
797 fmd_error(EFMD_CONF_DEFAULT, "ignoring invalid default "
798 "<%s> for property %s: %s\n", argv->cf_default,
799 name, fmd_strerror(errno));
804 fmd_conf_t *
805 fmd_conf_open(const char *file, int argc,
806 const fmd_conf_formal_t *argv, uint_t flag)
808 fmd_conf_t *cfp = fmd_alloc(sizeof (fmd_conf_t), FMD_SLEEP);
810 (void) pthread_rwlock_init(&cfp->cf_lock, NULL);
811 cfp->cf_argv = argv;
812 cfp->cf_argc = argc;
813 cfp->cf_flag = flag;
815 cfp->cf_params = fmd_zalloc(
816 sizeof (fmd_conf_param_t) * (_fmd_conf_defc + argc), FMD_SLEEP);
818 cfp->cf_parhashlen = fmd.d_str_buckets;
819 cfp->cf_parhash = fmd_zalloc(
820 sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen, FMD_SLEEP);
822 cfp->cf_defer = NULL;
824 fmd_conf_fill(cfp, cfp->cf_params, _fmd_conf_defc, _fmd_conf_defv, 0);
825 fmd_conf_fill(cfp, cfp->cf_params + _fmd_conf_defc, argc, argv, 1);
827 if (file != NULL && fmd_conf_parse(cfp, file) != 0) {
828 fmd_conf_close(cfp);
829 return (NULL);
832 return (cfp);
835 void
836 fmd_conf_merge(fmd_conf_t *cfp, const char *file)
838 (void) fmd_conf_parse(cfp, file);
841 void
842 fmd_conf_propagate(fmd_conf_t *src, fmd_conf_t *dst, const char *scope)
844 size_t len = strlen(scope);
845 fmd_conf_defer_t *cdp;
847 (void) pthread_rwlock_rdlock(&src->cf_lock);
849 for (cdp = src->cf_defer; cdp != NULL; cdp = cdp->cd_next) {
850 if (len == (size_t)(strchr(cdp->cd_name, ':') - cdp->cd_name) &&
851 strncmp(cdp->cd_name, scope, len) == 0 && fmd_conf_setprop(
852 dst, cdp->cd_name + len + 1, cdp->cd_value) != 0) {
853 fmd_error(EFMD_CONF_DEFER,
854 "failed to apply deferred property %s to %s: %s\n",
855 cdp->cd_name, scope, fmd_strerror(errno));
859 (void) pthread_rwlock_unlock(&src->cf_lock);
862 void
863 fmd_conf_close(fmd_conf_t *cfp)
865 fmd_conf_param_t *pp = cfp->cf_params;
866 int i, nparams = _fmd_conf_defc + cfp->cf_argc;
867 fmd_conf_defer_t *cdp, *ndp;
869 for (cdp = cfp->cf_defer; cdp != NULL; cdp = ndp) {
870 ndp = cdp->cd_next;
871 fmd_strfree(cdp->cd_name);
872 fmd_strfree(cdp->cd_value);
873 fmd_free(cdp, sizeof (fmd_conf_defer_t));
876 fmd_free(cfp->cf_parhash,
877 sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen);
879 for (i = 0; i < nparams; i++, pp++) {
880 if (pp->cp_formal != NULL)
881 pp->cp_formal->cf_ops->co_free(pp);
884 fmd_free(cfp->cf_params, sizeof (fmd_conf_param_t) * nparams);
885 fmd_free(cfp, sizeof (fmd_conf_t));
888 static fmd_conf_param_t *
889 fmd_conf_getparam(fmd_conf_t *cfp, const char *name)
891 ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen;
892 fmd_conf_param_t *pp = cfp->cf_parhash[h];
894 ASSERT(RW_LOCK_HELD(&cfp->cf_lock));
896 for (; pp != NULL; pp = pp->cp_next) {
897 if (strcmp(name, pp->cp_formal->cf_name) == 0)
898 return (pp);
901 return (NULL);
905 * String-friendly version of fmd_conf_getprop(): return the string as our
906 * return value, and return NULL if the string is the empty string.
908 const char *
909 fmd_conf_getnzstr(fmd_conf_t *cfp, const char *name)
911 const fmd_conf_param_t *pp;
912 char *s = NULL;
914 (void) pthread_rwlock_rdlock(&cfp->cf_lock);
916 if ((pp = fmd_conf_getparam(cfp, name)) != NULL) {
917 ASSERT(pp->cp_formal->cf_ops == &fmd_conf_string);
918 pp->cp_formal->cf_ops->co_get(pp, &s);
919 } else
920 (void) fmd_set_errno(EFMD_CONF_NOPROP);
922 (void) pthread_rwlock_unlock(&cfp->cf_lock);
924 if (s != NULL && s[0] == '\0') {
925 (void) fmd_set_errno(EFMD_CONF_UNDEF);
926 s = NULL;
929 return (s);
932 const fmd_conf_ops_t *
933 fmd_conf_gettype(fmd_conf_t *cfp, const char *name)
935 const fmd_conf_param_t *pp;
936 const fmd_conf_ops_t *ops = NULL;
938 (void) pthread_rwlock_rdlock(&cfp->cf_lock);
940 if ((pp = fmd_conf_getparam(cfp, name)) != NULL) {
941 if ((ops = pp->cp_formal->cf_ops) == &fmd_conf_parent) {
942 ops = fmd_conf_gettype(fmd.d_conf,
943 pp->cp_formal->cf_default);
945 } else
946 (void) fmd_set_errno(EFMD_CONF_NOPROP);
948 (void) pthread_rwlock_unlock(&cfp->cf_lock);
949 return (ops);
953 fmd_conf_getprop(fmd_conf_t *cfp, const char *name, void *data)
955 const fmd_conf_param_t *pp;
956 int err = 0;
958 (void) pthread_rwlock_rdlock(&cfp->cf_lock);
960 if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
961 pp->cp_formal->cf_ops->co_get(pp, data);
962 else
963 err = fmd_set_errno(EFMD_CONF_NOPROP);
965 (void) pthread_rwlock_unlock(&cfp->cf_lock);
966 return (err);
969 static int
970 fmd_conf_setdefer(fmd_conf_t *cfp, const char *name, const char *value)
972 fmd_conf_defer_t *cdp;
974 if (!(cfp->cf_flag & FMD_CONF_DEFER))
975 return (fmd_set_errno(EFMD_CONF_NODEFER));
977 (void) pthread_rwlock_wrlock(&cfp->cf_lock);
979 for (cdp = cfp->cf_defer; cdp != NULL; cdp = cdp->cd_next) {
980 if (strcmp(name, cdp->cd_name) == 0) {
981 fmd_strfree(cdp->cd_value);
982 cdp->cd_value = fmd_strdup(value, FMD_SLEEP);
983 goto out;
987 cdp = fmd_alloc(sizeof (fmd_conf_defer_t), FMD_SLEEP);
989 cdp->cd_name = fmd_strdup(name, FMD_SLEEP);
990 cdp->cd_value = fmd_strdup(value, FMD_SLEEP);
991 cdp->cd_next = cfp->cf_defer;
993 cfp->cf_defer = cdp;
994 out:
995 (void) pthread_rwlock_unlock(&cfp->cf_lock);
996 return (0);
1000 fmd_conf_setprop(fmd_conf_t *cfp, const char *name, const char *value)
1002 fmd_conf_param_t *pp;
1003 int err;
1005 if (strchr(name, ':') != NULL)
1006 return (fmd_conf_setdefer(cfp, name, value));
1008 (void) pthread_rwlock_wrlock(&cfp->cf_lock);
1010 if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
1011 err = pp->cp_formal->cf_ops->co_set(pp, value);
1012 else
1013 err = fmd_set_errno(EFMD_CONF_NOPROP);
1015 (void) pthread_rwlock_unlock(&cfp->cf_lock);
1016 return (err);
1020 fmd_conf_delprop(fmd_conf_t *cfp, const char *name, const char *value)
1022 fmd_conf_param_t *pp;
1023 int err;
1025 (void) pthread_rwlock_wrlock(&cfp->cf_lock);
1027 if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
1028 err = pp->cp_formal->cf_ops->co_del(pp, value);
1029 else
1030 err = fmd_set_errno(EFMD_CONF_NOPROP);
1032 (void) pthread_rwlock_unlock(&cfp->cf_lock);
1033 return (err);