xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_conf.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <pthread.h>
30 #include <unistd.h>
31 #include <signal.h>
32 #include <inttypes.h>
33 #include <alloca.h>
34 #include <strings.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 
38 #include <fmd_conf.h>
39 #include <fmd_alloc.h>
40 #include <fmd_error.h>
41 #include <fmd_subr.h>
42 #include <fmd_string.h>
43 #include <fmd.h>
44 
45 const char FMD_PROP_SUBSCRIPTIONS[] = "_subscriptions";
46 const char FMD_PROP_DICTIONARIES[] = "_dictionaries";
47 
48 /*
49  * The property formals defined in _fmd_conf_defv[] are added to every config
50  * dictionary that is created.  Here we define several special FMD_PROP_*
51  * properties that are used to implement the config file keyword actions, as
52  * well as properties that should be inherited by fmd_conf_t's from fmd.d_conf.
53  */
54 static const fmd_conf_formal_t _fmd_conf_defv[] = {
55 	{ FMD_PROP_SUBSCRIPTIONS, &fmd_conf_list, "" },
56 	{ FMD_PROP_DICTIONARIES, &fmd_conf_list, "" },
57 	{ "fmd.isaname", &fmd_conf_parent, "isaname" },
58 	{ "fmd.machine", &fmd_conf_parent, "machine" },
59 	{ "fmd.platform", &fmd_conf_parent, "platform" },
60 	{ "fmd.rootdir", &fmd_conf_parent, "rootdir" },
61 };
62 
63 static const int _fmd_conf_defc =
64     sizeof (_fmd_conf_defv) / sizeof (_fmd_conf_defv[0]);
65 
66 static int
67 set_bool(fmd_conf_param_t *pp, const char *s)
68 {
69 	if (strcasecmp(s, "true") == 0)
70 		pp->cp_value.cpv_num = 1;
71 	else if (strcasecmp(s, "false") == 0)
72 		pp->cp_value.cpv_num = 0;
73 	else
74 		return (fmd_set_errno(EFMD_CONF_INVAL));
75 
76 	return (0);
77 }
78 
79 static void
80 get_bool(const fmd_conf_param_t *pp, void *ptr)
81 {
82 	*((int *)ptr) = (int)pp->cp_value.cpv_num;
83 }
84 
85 static int
86 set_i32(fmd_conf_param_t *pp, const char *s)
87 {
88 	int64_t val;
89 	char *end;
90 
91 	errno = 0;
92 	val = strtoll(s, &end, 0);
93 
94 	if (errno == EOVERFLOW || val < INT32_MIN || val > INT32_MAX)
95 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
96 
97 	if (errno != 0 || end == s || *end != '\0')
98 		return (fmd_set_errno(EFMD_CONF_INVAL));
99 
100 	pp->cp_value.cpv_num = val;
101 	return (0);
102 }
103 
104 static void
105 get_i32(const fmd_conf_param_t *pp, void *ptr)
106 {
107 	*((int32_t *)ptr) = (int32_t)pp->cp_value.cpv_num;
108 }
109 
110 static int
111 set_ui32(fmd_conf_param_t *pp, const char *s)
112 {
113 	uint64_t val;
114 	char *end;
115 
116 	errno = 0;
117 	val = strtoull(s, &end, 0);
118 
119 	if (errno == EOVERFLOW || val > UINT32_MAX)
120 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
121 
122 	if (errno != 0 || end == s || *end != '\0')
123 		return (fmd_set_errno(EFMD_CONF_INVAL));
124 
125 	pp->cp_value.cpv_num = val;
126 	return (0);
127 }
128 
129 static void
130 get_ui32(const fmd_conf_param_t *pp, void *ptr)
131 {
132 	*((uint32_t *)ptr) = (uint32_t)pp->cp_value.cpv_num;
133 }
134 
135 static int
136 set_i64(fmd_conf_param_t *pp, const char *s)
137 {
138 	int64_t val;
139 	char *end;
140 
141 	errno = 0;
142 	val = strtoll(s, &end, 0);
143 
144 	if (errno == EOVERFLOW)
145 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
146 
147 	if (errno != 0 || end == s || *end != '\0')
148 		return (fmd_set_errno(EFMD_CONF_INVAL));
149 
150 	pp->cp_value.cpv_num = val;
151 	return (0);
152 }
153 
154 static void
155 get_i64(const fmd_conf_param_t *pp, void *ptr)
156 {
157 	*((int64_t *)ptr) = (int64_t)pp->cp_value.cpv_num;
158 }
159 
160 static int
161 set_ui64(fmd_conf_param_t *pp, const char *s)
162 {
163 	uint64_t val;
164 	char *end;
165 
166 	errno = 0;
167 	val = strtoull(s, &end, 0);
168 
169 	if (errno == EOVERFLOW)
170 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
171 
172 	if (errno != 0 || end == s || *end != '\0')
173 		return (fmd_set_errno(EFMD_CONF_INVAL));
174 
175 	pp->cp_value.cpv_num = val;
176 	return (0);
177 }
178 
179 static void
180 get_ui64(const fmd_conf_param_t *pp, void *ptr)
181 {
182 	*((uint64_t *)ptr) = pp->cp_value.cpv_num;
183 }
184 
185 static int
186 set_str(fmd_conf_param_t *pp, const char *s)
187 {
188 	fmd_strfree(pp->cp_value.cpv_str);
189 	pp->cp_value.cpv_str = fmd_strdup(s, FMD_SLEEP);
190 	return (0);
191 }
192 
193 static void
194 get_str(const fmd_conf_param_t *pp, void *ptr)
195 {
196 	*((const char **)ptr) = pp->cp_value.cpv_str;
197 }
198 
199 static void
200 free_str(fmd_conf_param_t *pp)
201 {
202 	fmd_strfree(pp->cp_value.cpv_str);
203 	pp->cp_value.cpv_str = NULL;
204 }
205 
206 static int
207 set_path(fmd_conf_param_t *pp, const char *value)
208 {
209 	size_t len = strlen(value);
210 	char *s = alloca(len + 1);
211 
212 	char **patv = alloca(sizeof (char *) * len / 2);
213 	int patc = 0;
214 
215 	static const char *const percent_sign = "%";
216 	char *p, *q;
217 	int c, i;
218 
219 	static const struct fmd_conf_token {
220 		char tok_tag;
221 		const char *const *tok_val;
222 	} tokens[] = {
223 		{ 'i', &fmd.d_platform },
224 		{ 'm', &fmd.d_machine },
225 		{ 'p', &fmd.d_isaname },
226 		{ 'r', &fmd.d_rootdir },
227 		{ '%', &percent_sign },
228 		{ 0, NULL }
229 	};
230 
231 	const struct fmd_conf_token *tok;
232 	fmd_conf_path_t *pap;
233 
234 	pp->cp_formal->cf_ops->co_free(pp);
235 	(void) strcpy(s, value);
236 
237 	for (p = strtok_r(s, ":", &q); p != NULL; p = strtok_r(NULL, ":", &q))
238 		patv[patc++] = p;
239 
240 	pap = fmd_alloc(sizeof (fmd_conf_path_t), FMD_SLEEP);
241 	pap->cpa_argv = fmd_alloc(sizeof (char *) * patc, FMD_SLEEP);
242 	pap->cpa_argc = patc;
243 
244 	for (i = 0; i < patc; i++) {
245 		for (len = 0, p = patv[i]; (c = *p) != '\0'; p++, len++) {
246 			if (c != '%' || (c = p[1]) == '\0')
247 				continue;
248 
249 			for (tok = tokens; tok->tok_tag != 0; tok++) {
250 				if (c == tok->tok_tag) {
251 					len += strlen(*tok->tok_val) - 1;
252 					p++;
253 					break;
254 				}
255 			}
256 		}
257 
258 		pap->cpa_argv[i] = q = fmd_alloc(len + 1, FMD_SLEEP);
259 		q[len] = '\0';
260 
261 		for (p = patv[i]; (c = *p) != '\0'; p++) {
262 			if (c != '%' || (c = p[1]) == '\0') {
263 				*q++ = c;
264 				continue;
265 			}
266 
267 			for (tok = tokens; tok->tok_tag != 0; tok++) {
268 				if (c == tok->tok_tag) {
269 					(void) strcpy(q, *tok->tok_val);
270 					q += strlen(q);
271 					p++;
272 					break;
273 				}
274 			}
275 
276 			if (tok->tok_tag == 0)
277 				*q++ = c;
278 		}
279 	}
280 
281 	pp->cp_value.cpv_ptr = pap;
282 	return (0);
283 }
284 
285 static int
286 set_lst(fmd_conf_param_t *pp, const char *value)
287 {
288 	fmd_conf_path_t *old;
289 
290 	old = pp->cp_value.cpv_ptr;
291 	pp->cp_value.cpv_ptr = NULL;
292 
293 	if (set_path(pp, value) != 0) {
294 		pp->cp_value.cpv_ptr = old;
295 		return (-1); /* errno is set for us */
296 	}
297 
298 	if (old != NULL) {
299 		fmd_conf_path_t *new = pp->cp_value.cpv_ptr;
300 		int i, totc = old->cpa_argc + new->cpa_argc;
301 
302 		int new_argc = new->cpa_argc;
303 		const char **new_argv = new->cpa_argv;
304 
305 		new->cpa_argc = 0;
306 		new->cpa_argv = fmd_alloc(sizeof (char *) * totc, FMD_SLEEP);
307 
308 		for (i = 0; i < old->cpa_argc; i++)
309 			new->cpa_argv[new->cpa_argc++] = old->cpa_argv[i];
310 
311 		for (i = 0; i < new_argc; i++)
312 			new->cpa_argv[new->cpa_argc++] = new_argv[i];
313 
314 		ASSERT(new->cpa_argc == totc);
315 
316 		fmd_free(new_argv, sizeof (char *) * new_argc);
317 		fmd_free(old->cpa_argv, sizeof (char *) * old->cpa_argc);
318 		fmd_free(old, sizeof (fmd_conf_path_t));
319 	}
320 
321 	return (0);
322 }
323 
324 static int
325 del_lst(fmd_conf_param_t *pp, const char *value)
326 {
327 	fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
328 	const char **new_argv;
329 	int i, new_argc;
330 
331 	for (i = 0; i < pap->cpa_argc; i++) {
332 		if (strcmp(pap->cpa_argv[i], value) == 0)
333 			break;
334 	}
335 
336 	if (i == pap->cpa_argc)
337 		return (fmd_set_errno(ENOENT));
338 
339 	fmd_strfree((char *)pap->cpa_argv[i]);
340 	pap->cpa_argv[i] = NULL;
341 
342 	new_argc = 0;
343 	new_argv = fmd_alloc(sizeof (char *) * (pap->cpa_argc - 1), FMD_SLEEP);
344 
345 	for (i = 0; i < pap->cpa_argc; i++) {
346 		if (pap->cpa_argv[i] != NULL)
347 			new_argv[new_argc++] = pap->cpa_argv[i];
348 	}
349 
350 	fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc);
351 	pap->cpa_argv = new_argv;
352 	pap->cpa_argc = new_argc;
353 
354 	return (0);
355 }
356 
357 static void
358 get_path(const fmd_conf_param_t *pp, void *ptr)
359 {
360 	*((fmd_conf_path_t **)ptr) = (fmd_conf_path_t *)pp->cp_value.cpv_ptr;
361 }
362 
363 static void
364 free_path(fmd_conf_param_t *pp)
365 {
366 	fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
367 	int i;
368 
369 	if (pap == NULL)
370 		return; /* no value was ever set */
371 
372 	for (i = 0; i < pap->cpa_argc; i++)
373 		fmd_strfree((char *)pap->cpa_argv[i]);
374 
375 	fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc);
376 	fmd_free(pap, sizeof (fmd_conf_path_t));
377 	pp->cp_value.cpv_ptr = NULL;
378 }
379 
380 static int
381 set_time(fmd_conf_param_t *pp, const char *s)
382 {
383 	static const struct {
384 		const char *name;
385 		hrtime_t mul;
386 	} suffix[] = {
387 		{ "ns", 	NANOSEC / NANOSEC },
388 		{ "nsec",	NANOSEC / NANOSEC },
389 		{ "us",		NANOSEC / MICROSEC },
390 		{ "usec",	NANOSEC / MICROSEC },
391 		{ "ms",		NANOSEC / MILLISEC },
392 		{ "msec",	NANOSEC / MILLISEC },
393 		{ "s",		NANOSEC / SEC },
394 		{ "sec",	NANOSEC / SEC },
395 		{ "m",		NANOSEC * (hrtime_t)60 },
396 		{ "min",	NANOSEC * (hrtime_t)60 },
397 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
398 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
399 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
400 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
401 		{ "hz",		0 },
402 		{ NULL }
403 	};
404 
405 	hrtime_t val, mul = 1;
406 	char *end;
407 	int i;
408 
409 	errno = 0;
410 	val = strtoull(s, &end, 0);
411 
412 	if (errno == EOVERFLOW)
413 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
414 
415 	if (errno != 0 || end == s)
416 		return (fmd_set_errno(EFMD_CONF_INVAL));
417 
418 	for (i = 0; suffix[i].name != NULL; i++) {
419 		if (strcasecmp(suffix[i].name, end) == 0) {
420 			mul = suffix[i].mul;
421 			break;
422 		}
423 	}
424 
425 	if (suffix[i].name == NULL && *end != '\0')
426 		return (fmd_set_errno(EFMD_CONF_INVAL));
427 
428 	if (mul == 0) {
429 		if (val != 0)
430 			val = NANOSEC / val; /* compute val as value per sec */
431 	} else
432 		val *= mul;
433 
434 	pp->cp_value.cpv_num = val;
435 	return (0);
436 }
437 
438 static int
439 set_size(fmd_conf_param_t *pp, const char *s)
440 {
441 	size_t len = strlen(s);
442 	uint64_t val, mul = 1;
443 	char *end;
444 
445 	switch (s[len - 1]) {
446 	case 't':
447 	case 'T':
448 		mul *= 1024;
449 		/*FALLTHRU*/
450 	case 'g':
451 	case 'G':
452 		mul *= 1024;
453 		/*FALLTHRU*/
454 	case 'm':
455 	case 'M':
456 		mul *= 1024;
457 		/*FALLTHRU*/
458 	case 'k':
459 	case 'K':
460 		mul *= 1024;
461 		/*FALLTHRU*/
462 	default:
463 		break;
464 	}
465 
466 	errno = 0;
467 	val = strtoull(s, &end, 0) * mul;
468 
469 	if (errno == EOVERFLOW)
470 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
471 
472 	if ((mul != 1 && end != &s[len - 1]) ||
473 	    (mul == 1 && *end != '\0') || errno != 0)
474 		return (fmd_set_errno(EFMD_CONF_INVAL));
475 
476 	pp->cp_value.cpv_num = val;
477 	return (0);
478 }
479 
480 static int
481 set_sig(fmd_conf_param_t *pp, const char *s)
482 {
483 	int sig;
484 
485 	if (strncasecmp(s, "SIG", 3) == 0)
486 		s += 3; /* be friendlier than strsig() and permit the prefix */
487 
488 	if (str2sig(s, &sig) != 0)
489 		return (fmd_set_errno(EFMD_CONF_INVAL));
490 
491 	pp->cp_value.cpv_num = sig;
492 	return (0);
493 }
494 
495 static void
496 get_par(const fmd_conf_param_t *pp, void *ptr)
497 {
498 	if (fmd_conf_getprop(fmd.d_conf, pp->cp_formal->cf_default, ptr) != 0) {
499 		fmd_panic("fmd.d_conf does not define '%s' (inherited as %s)\n",
500 		    (char *)pp->cp_formal->cf_default, pp->cp_formal->cf_name);
501 	}
502 }
503 
504 /*ARGSUSED*/
505 static int
506 set_par(fmd_conf_param_t *pp, const char *s)
507 {
508 	return (fmd_set_errno(EFMD_CONF_RDONLY));
509 }
510 
511 /*
512  * Utility routine for callers who define custom ops where a list of string
513  * tokens are translated into a bitmask.  'cmp' should be set to point to an
514  * array of fmd_conf_mode_t's where the final element has cm_name == NULL.
515  */
516 int
517 fmd_conf_mode_set(const fmd_conf_mode_t *cmp,
518     fmd_conf_param_t *pp, const char *value)
519 {
520 	char *p, *q, *s = fmd_strdup(value, FMD_SLEEP);
521 	size_t len = value ? strlen(value) + 1 : 0;
522 	uint_t mode = 0;
523 
524 	if (s == NULL) {
525 		pp->cp_value.cpv_num = 0;
526 		return (0);
527 	}
528 
529 	for (p = strtok_r(s, ",", &q); p != NULL; p = strtok_r(NULL, ",", &q)) {
530 		for (; cmp->cm_name != NULL; cmp++) {
531 			if (strcmp(cmp->cm_name, p) == 0) {
532 				mode |= cmp->cm_bits;
533 				break;
534 			}
535 		}
536 
537 		if (cmp->cm_name == NULL) {
538 			fmd_free(s, len);
539 			return (fmd_set_errno(EFMD_CONF_INVAL));
540 		}
541 	}
542 
543 	pp->cp_value.cpv_num = mode;
544 	fmd_free(s, len);
545 	return (0);
546 }
547 
548 void
549 fmd_conf_mode_get(const fmd_conf_param_t *pp, void *ptr)
550 {
551 	*((uint_t *)ptr) = (uint_t)pp->cp_value.cpv_num;
552 }
553 
554 /*ARGSUSED*/
555 int
556 fmd_conf_notsup(fmd_conf_param_t *pp, const char *value)
557 {
558 	return (fmd_set_errno(ENOTSUP));
559 }
560 
561 /*ARGSUSED*/
562 void
563 fmd_conf_nop(fmd_conf_param_t *pp)
564 {
565 	/* no free required for integer-type parameters */
566 }
567 
568 #define	CONF_DEFINE(name, a, b, c, d) \
569 	const fmd_conf_ops_t name = { a, b, c, d }
570 
571 CONF_DEFINE(fmd_conf_bool, set_bool, get_bool, fmd_conf_notsup, fmd_conf_nop);
572 CONF_DEFINE(fmd_conf_int32, set_i32, get_i32, fmd_conf_notsup, fmd_conf_nop);
573 CONF_DEFINE(fmd_conf_uint32, set_ui32, get_ui32, fmd_conf_notsup, fmd_conf_nop);
574 CONF_DEFINE(fmd_conf_int64, set_i64, get_i64, fmd_conf_notsup, fmd_conf_nop);
575 CONF_DEFINE(fmd_conf_uint64, set_ui64, get_ui64, fmd_conf_notsup, fmd_conf_nop);
576 CONF_DEFINE(fmd_conf_string, set_str, get_str, fmd_conf_notsup, free_str);
577 CONF_DEFINE(fmd_conf_path, set_path, get_path, fmd_conf_notsup, free_path);
578 CONF_DEFINE(fmd_conf_list, set_lst, get_path, del_lst, free_path);
579 CONF_DEFINE(fmd_conf_time, set_time, get_ui64, fmd_conf_notsup, fmd_conf_nop);
580 CONF_DEFINE(fmd_conf_size, set_size, get_ui64, fmd_conf_notsup, fmd_conf_nop);
581 CONF_DEFINE(fmd_conf_signal, set_sig, get_i32, fmd_conf_notsup, fmd_conf_nop);
582 CONF_DEFINE(fmd_conf_parent, set_par, get_par, fmd_conf_notsup, fmd_conf_nop);
583 
584 static char *
585 fmd_conf_skipstr(char *s)
586 {
587 	int c;
588 
589 	while ((c = *s) != '\0') {
590 		if (c == '\\')
591 			s++;
592 		else if (c == '"')
593 			break;
594 		s++;
595 	}
596 
597 	return (s);
598 }
599 
600 static char *
601 fmd_conf_skipnws(char *s)
602 {
603 	while (strchr("\f\n\r\t\v ", *s) == NULL)
604 		s++;
605 
606 	return (s);
607 }
608 
609 static int
610 fmd_conf_tokenize(char *s, char *tokv[])
611 {
612 	int c, tokc = 0;
613 
614 	while ((c = *s) != '\0') {
615 		switch (c) {
616 		case '"':
617 			tokv[tokc] = s + 1;
618 			s = fmd_conf_skipstr(s + 1);
619 			*s++ = '\0';
620 			(void) fmd_stresc2chr(tokv[tokc++]);
621 			continue;
622 		case '\f': case '\n': case '\r':
623 		case '\t': case '\v': case ' ':
624 			s++;
625 			continue;
626 		default:
627 			tokv[tokc++] = s;
628 			s = fmd_conf_skipnws(s);
629 			*s++ = '\0';
630 		}
631 	}
632 
633 	return (tokc);
634 }
635 
636 static int
637 fmd_conf_exec_setprop(fmd_conf_t *cfp, int argc, char *argv[])
638 {
639 	if (argc != 2)
640 		return (fmd_set_errno(EFMD_CONF_USAGE));
641 
642 	return (fmd_conf_setprop(cfp, argv[0], argv[1]));
643 }
644 
645 static int
646 fmd_conf_exec_subscribe(fmd_conf_t *cfp, int argc, char *argv[])
647 {
648 	if (argc != 1)
649 		return (fmd_set_errno(EFMD_CONF_USAGE));
650 
651 	return (fmd_conf_setprop(cfp, FMD_PROP_SUBSCRIPTIONS, argv[0]));
652 }
653 
654 static int
655 fmd_conf_exec_dictionary(fmd_conf_t *cfp, int argc, char *argv[])
656 {
657 	if (argc != 1)
658 		return (fmd_set_errno(EFMD_CONF_USAGE));
659 
660 	return (fmd_conf_setprop(cfp, FMD_PROP_DICTIONARIES, argv[0]));
661 }
662 
663 static int
664 fmd_conf_parse(fmd_conf_t *cfp, const char *file)
665 {
666 	static const fmd_conf_verb_t verbs[] = {
667 		{ "setprop", fmd_conf_exec_setprop },
668 		{ "subscribe", fmd_conf_exec_subscribe },
669 		{ "dictionary", fmd_conf_exec_dictionary },
670 		{ NULL, NULL }
671 	};
672 
673 	int line, errs = 0;
674 	char buf[BUFSIZ];
675 	FILE *fp;
676 
677 	if ((fp = fopen(file, "r")) == NULL) {
678 		fmd_error(EFMD_CONF_OPEN, "failed to open %s: %s\n",
679 		    file, fmd_strerror(errno));
680 		return (fmd_set_errno(EFMD_CONF_OPEN));
681 	}
682 
683 	for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
684 		char *tokv[sizeof (buf) / 2 + 1];
685 		int tokc = fmd_conf_tokenize(buf, tokv);
686 		const fmd_conf_verb_t *vp;
687 
688 		if (tokc == 0 || tokv[0][0] == '#')
689 			continue; /* skip blank lines and comment lines */
690 
691 		for (vp = verbs; vp->cv_name != NULL; vp++) {
692 			if (strcmp(tokv[0], vp->cv_name) == 0)
693 				break;
694 		}
695 
696 		if (vp->cv_name == NULL) {
697 			fmd_error(EFMD_CONF_KEYWORD, "\"%s\", line %d: "
698 			    "invalid configuration file keyword: %s\n",
699 			    file, line, tokv[0]);
700 			errs++;
701 			continue;
702 		}
703 
704 		if (vp->cv_exec(cfp, tokc - 1, tokv + 1) != 0) {
705 			fmd_error(errno, "\"%s\", line %d", file, line);
706 			errs++;
707 			continue;
708 		}
709 	}
710 
711 	if (ferror(fp) != 0 || fclose(fp) != 0)
712 		return (fmd_set_errno(EFMD_CONF_IO));
713 
714 	if (errs != 0)
715 		return (fmd_set_errno(EFMD_CONF_ERRS));
716 
717 	return (0);
718 }
719 
720 static void
721 fmd_conf_fill(fmd_conf_t *cfp, fmd_conf_param_t *ppbuf,
722     int argc, const fmd_conf_formal_t *argv, int checkid)
723 {
724 	int i;
725 
726 	for (i = 0; i < argc; i++, argv++) {
727 		fmd_conf_param_t *op, *pp = ppbuf + i;
728 		const char *name = argv->cf_name;
729 		ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen;
730 
731 		if (fmd_strbadid(name, checkid) != NULL) {
732 			fmd_error(EFMD_CONF_PROPNAME, "ignoring invalid formal "
733 			    "property %s\n", name);
734 			continue;
735 		}
736 
737 		for (op = cfp->cf_parhash[h]; op != NULL; op = op->cp_next) {
738 			if (strcmp(op->cp_formal->cf_name, name) == 0) {
739 				fmd_error(EFMD_CONF_PROPDUP, "ignoring "
740 				    "duplicate formal property %s\n", name);
741 				break;
742 			}
743 		}
744 
745 		if (op != NULL)
746 			continue;
747 
748 		pp->cp_formal = argv;
749 		pp->cp_next = cfp->cf_parhash[h];
750 		cfp->cf_parhash[h] = pp;
751 
752 		if (argv->cf_default && argv->cf_ops != &fmd_conf_parent &&
753 		    fmd_conf_setprop(cfp, name, argv->cf_default) != 0) {
754 			fmd_error(EFMD_CONF_DEFAULT, "ignoring invalid default "
755 			    "<%s> for property %s: %s\n", argv->cf_default,
756 			    name, fmd_strerror(errno));
757 		}
758 	}
759 }
760 
761 fmd_conf_t *
762 fmd_conf_open(const char *file, int argc, const fmd_conf_formal_t *argv)
763 {
764 	fmd_conf_t *cfp = fmd_alloc(sizeof (fmd_conf_t), FMD_SLEEP);
765 
766 	(void) pthread_rwlock_init(&cfp->cf_lock, NULL);
767 	cfp->cf_argv = argv;
768 	cfp->cf_argc = argc;
769 
770 	cfp->cf_params = fmd_zalloc(
771 	    sizeof (fmd_conf_param_t) * (_fmd_conf_defc + argc), FMD_SLEEP);
772 
773 	cfp->cf_parhashlen = fmd.d_str_buckets;
774 	cfp->cf_parhash = fmd_zalloc(
775 	    sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen, FMD_SLEEP);
776 
777 	fmd_conf_fill(cfp, cfp->cf_params, _fmd_conf_defc, _fmd_conf_defv, 0);
778 	fmd_conf_fill(cfp, cfp->cf_params + _fmd_conf_defc, argc, argv, 1);
779 
780 	if (file != NULL && fmd_conf_parse(cfp, file) != 0) {
781 		fmd_conf_close(cfp);
782 		return (NULL);
783 	}
784 
785 	return (cfp);
786 }
787 
788 void
789 fmd_conf_merge(fmd_conf_t *cfp, const char *file)
790 {
791 	(void) fmd_conf_parse(cfp, file);
792 }
793 
794 void
795 fmd_conf_close(fmd_conf_t *cfp)
796 {
797 	fmd_conf_param_t *pp = cfp->cf_params;
798 	int i, nparams = _fmd_conf_defc + cfp->cf_argc;
799 
800 	fmd_free(cfp->cf_parhash,
801 	    sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen);
802 
803 	for (i = 0; i < nparams; i++, pp++) {
804 		if (pp->cp_formal != NULL)
805 			pp->cp_formal->cf_ops->co_free(pp);
806 	}
807 
808 	fmd_free(cfp->cf_params, sizeof (fmd_conf_param_t) * nparams);
809 	fmd_free(cfp, sizeof (fmd_conf_t));
810 }
811 
812 static fmd_conf_param_t *
813 fmd_conf_getparam(fmd_conf_t *cfp, const char *name)
814 {
815 	ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen;
816 	fmd_conf_param_t *pp = cfp->cf_parhash[h];
817 
818 	ASSERT(RW_LOCK_HELD(&cfp->cf_lock));
819 
820 	for (; pp != NULL; pp = pp->cp_next) {
821 		if (strcmp(name, pp->cp_formal->cf_name) == 0)
822 			return (pp);
823 	}
824 
825 	return (NULL);
826 }
827 
828 const fmd_conf_ops_t *
829 fmd_conf_gettype(fmd_conf_t *cfp, const char *name)
830 {
831 	const fmd_conf_param_t *pp;
832 	const fmd_conf_ops_t *ops = NULL;
833 
834 	(void) pthread_rwlock_rdlock(&cfp->cf_lock);
835 
836 	if ((pp = fmd_conf_getparam(cfp, name)) != NULL) {
837 		if ((ops = pp->cp_formal->cf_ops) == &fmd_conf_parent) {
838 			ops = fmd_conf_gettype(fmd.d_conf,
839 			    pp->cp_formal->cf_default);
840 		}
841 	} else
842 		(void) fmd_set_errno(EFMD_CONF_NOPROP);
843 
844 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
845 	return (ops);
846 }
847 
848 int
849 fmd_conf_getprop(fmd_conf_t *cfp, const char *name, void *data)
850 {
851 	const fmd_conf_param_t *pp;
852 	int err = 0;
853 
854 	(void) pthread_rwlock_rdlock(&cfp->cf_lock);
855 
856 	if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
857 		pp->cp_formal->cf_ops->co_get(pp, data);
858 	else
859 		err = fmd_set_errno(EFMD_CONF_NOPROP);
860 
861 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
862 	return (err);
863 }
864 
865 int
866 fmd_conf_setprop(fmd_conf_t *cfp, const char *name, const char *value)
867 {
868 	fmd_conf_param_t *pp;
869 	int err;
870 
871 	(void) pthread_rwlock_wrlock(&cfp->cf_lock);
872 
873 	if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
874 		err = pp->cp_formal->cf_ops->co_set(pp, value);
875 	else
876 		err = fmd_set_errno(EFMD_CONF_NOPROP);
877 
878 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
879 	return (err);
880 }
881 
882 int
883 fmd_conf_delprop(fmd_conf_t *cfp, const char *name, const char *value)
884 {
885 	fmd_conf_param_t *pp;
886 	int err;
887 
888 	(void) pthread_rwlock_wrlock(&cfp->cf_lock);
889 
890 	if ((pp = fmd_conf_getparam(cfp, name)) != NULL)
891 		err = pp->cp_formal->cf_ops->co_del(pp, value);
892 	else
893 		err = fmd_set_errno(EFMD_CONF_NOPROP);
894 
895 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
896 	return (err);
897 }
898