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