xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_conf.c (revision 5f8171005a0c33f3c67f7da52d41c2362c3fd891)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
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>
35 
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>
42 
43 const char FMD_PROP_SUBSCRIPTIONS[] = "_subscriptions";
44 const char FMD_PROP_DICTIONARIES[] = "_dictionaries";
45 
46 /*
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.
51  */
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" },
59 };
60 
61 static const int _fmd_conf_defc =
62     sizeof (_fmd_conf_defv) / sizeof (_fmd_conf_defv[0]);
63 
64 static int
65 set_bool(fmd_conf_param_t *pp, const char *s)
66 {
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));
73 
74 	return (0);
75 }
76 
77 static void
78 get_bool(const fmd_conf_param_t *pp, void *ptr)
79 {
80 	*((int *)ptr) = (int)pp->cp_value.cpv_num;
81 }
82 
83 static int
84 set_i32x(fmd_conf_param_t *pp, const char *s, int64_t min, int64_t max)
85 {
86 	int64_t val;
87 	char *end;
88 
89 	errno = 0;
90 	val = strtoll(s, &end, 0);
91 
92 	if (errno == EOVERFLOW || val < min || val > max)
93 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
94 
95 	if (errno != 0 || end == s || *end != '\0')
96 		return (fmd_set_errno(EFMD_CONF_INVAL));
97 
98 	pp->cp_value.cpv_num = val;
99 	return (0);
100 }
101 
102 static int
103 set_i8(fmd_conf_param_t *pp, const char *s)
104 {
105 	return (set_i32x(pp, s, INT8_MIN, INT8_MAX));
106 }
107 
108 static int
109 set_i16(fmd_conf_param_t *pp, const char *s)
110 {
111 	return (set_i32x(pp, s, INT16_MIN, INT16_MAX));
112 }
113 
114 static int
115 set_i32(fmd_conf_param_t *pp, const char *s)
116 {
117 	return (set_i32x(pp, s, INT32_MIN, INT32_MAX));
118 }
119 
120 static void
121 get_i32(const fmd_conf_param_t *pp, void *ptr)
122 {
123 	*((int32_t *)ptr) = (int32_t)pp->cp_value.cpv_num;
124 }
125 
126 static int
127 set_ui32x(fmd_conf_param_t *pp, const char *s, uint64_t max)
128 {
129 	uint64_t val;
130 	char *end;
131 
132 	errno = 0;
133 	val = strtoull(s, &end, 0);
134 
135 	if (errno == EOVERFLOW || val > max)
136 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
137 
138 	if (errno != 0 || end == s || *end != '\0')
139 		return (fmd_set_errno(EFMD_CONF_INVAL));
140 
141 	pp->cp_value.cpv_num = val;
142 	return (0);
143 }
144 
145 static int
146 set_ui8(fmd_conf_param_t *pp, const char *s)
147 {
148 	return (set_ui32x(pp, s, UINT8_MAX));
149 }
150 
151 static int
152 set_ui16(fmd_conf_param_t *pp, const char *s)
153 {
154 	return (set_ui32x(pp, s, UINT16_MAX));
155 }
156 
157 static int
158 set_ui32(fmd_conf_param_t *pp, const char *s)
159 {
160 	return (set_ui32x(pp, s, UINT32_MAX));
161 }
162 
163 static void
164 get_ui32(const fmd_conf_param_t *pp, void *ptr)
165 {
166 	*((uint32_t *)ptr) = (uint32_t)pp->cp_value.cpv_num;
167 }
168 
169 static int
170 set_i64(fmd_conf_param_t *pp, const char *s)
171 {
172 	int64_t val;
173 	char *end;
174 
175 	errno = 0;
176 	val = strtoll(s, &end, 0);
177 
178 	if (errno == EOVERFLOW)
179 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
180 
181 	if (errno != 0 || end == s || *end != '\0')
182 		return (fmd_set_errno(EFMD_CONF_INVAL));
183 
184 	pp->cp_value.cpv_num = val;
185 	return (0);
186 }
187 
188 static void
189 get_i64(const fmd_conf_param_t *pp, void *ptr)
190 {
191 	*((int64_t *)ptr) = (int64_t)pp->cp_value.cpv_num;
192 }
193 
194 static int
195 set_ui64(fmd_conf_param_t *pp, const char *s)
196 {
197 	uint64_t val;
198 	char *end;
199 
200 	errno = 0;
201 	val = strtoull(s, &end, 0);
202 
203 	if (errno == EOVERFLOW)
204 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
205 
206 	if (errno != 0 || end == s || *end != '\0')
207 		return (fmd_set_errno(EFMD_CONF_INVAL));
208 
209 	pp->cp_value.cpv_num = val;
210 	return (0);
211 }
212 
213 static void
214 get_ui64(const fmd_conf_param_t *pp, void *ptr)
215 {
216 	*((uint64_t *)ptr) = pp->cp_value.cpv_num;
217 }
218 
219 static int
220 set_str(fmd_conf_param_t *pp, const char *s)
221 {
222 	fmd_strfree(pp->cp_value.cpv_str);
223 	pp->cp_value.cpv_str = fmd_strdup(s, FMD_SLEEP);
224 	return (0);
225 }
226 
227 static void
228 get_str(const fmd_conf_param_t *pp, void *ptr)
229 {
230 	*((const char **)ptr) = pp->cp_value.cpv_str;
231 }
232 
233 static void
234 free_str(fmd_conf_param_t *pp)
235 {
236 	fmd_strfree(pp->cp_value.cpv_str);
237 	pp->cp_value.cpv_str = NULL;
238 }
239 
240 static int
241 set_path(fmd_conf_param_t *pp, const char *value)
242 {
243 	size_t len = strlen(value);
244 	char *s = alloca(len + 1);
245 
246 	char **patv = alloca(sizeof (char *) * len / 2);
247 	int patc = 0;
248 
249 	static const char *const percent_sign = "%";
250 	char *p, *q;
251 	int c, i;
252 
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 }
263 	};
264 
265 	const struct fmd_conf_token *tok;
266 	fmd_conf_path_t *pap;
267 
268 	pp->cp_formal->cf_ops->co_free(pp);
269 	(void) strcpy(s, value);
270 
271 	for (p = strtok_r(s, ":", &q); p != NULL; p = strtok_r(NULL, ":", &q))
272 		patv[patc++] = p;
273 
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;
277 
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;
282 
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;
288 				}
289 			}
290 		}
291 
292 		pap->cpa_argv[i] = q = fmd_alloc(len + 1, FMD_SLEEP);
293 		q[len] = '\0';
294 
295 		for (p = patv[i]; (c = *p) != '\0'; p++) {
296 			if (c != '%' || (c = p[1]) == '\0') {
297 				*q++ = c;
298 				continue;
299 			}
300 
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;
307 				}
308 			}
309 
310 			if (tok->tok_tag == 0)
311 				*q++ = c;
312 		}
313 	}
314 
315 	pp->cp_value.cpv_ptr = pap;
316 	return (0);
317 }
318 
319 static int
320 set_lst(fmd_conf_param_t *pp, const char *value)
321 {
322 	fmd_conf_path_t *old;
323 
324 	old = pp->cp_value.cpv_ptr;
325 	pp->cp_value.cpv_ptr = NULL;
326 
327 	if (set_path(pp, value) != 0) {
328 		pp->cp_value.cpv_ptr = old;
329 		return (-1); /* errno is set for us */
330 	}
331 
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;
335 
336 		int new_argc = new->cpa_argc;
337 		const char **new_argv = new->cpa_argv;
338 
339 		new->cpa_argc = 0;
340 		new->cpa_argv = fmd_alloc(sizeof (char *) * totc, FMD_SLEEP);
341 
342 		for (i = 0; i < old->cpa_argc; i++)
343 			new->cpa_argv[new->cpa_argc++] = old->cpa_argv[i];
344 
345 		for (i = 0; i < new_argc; i++)
346 			new->cpa_argv[new->cpa_argc++] = new_argv[i];
347 
348 		ASSERT(new->cpa_argc == totc);
349 
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));
353 	}
354 
355 	return (0);
356 }
357 
358 static int
359 del_lst(fmd_conf_param_t *pp, const char *value)
360 {
361 	fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
362 	const char **new_argv;
363 	int i, new_argc;
364 
365 	for (i = 0; i < pap->cpa_argc; i++) {
366 		if (strcmp(pap->cpa_argv[i], value) == 0)
367 			break;
368 	}
369 
370 	if (i == pap->cpa_argc)
371 		return (fmd_set_errno(ENOENT));
372 
373 	fmd_strfree((char *)pap->cpa_argv[i]);
374 	pap->cpa_argv[i] = NULL;
375 
376 	new_argc = 0;
377 	new_argv = fmd_alloc(sizeof (char *) * (pap->cpa_argc - 1), FMD_SLEEP);
378 
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];
382 	}
383 
384 	fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc);
385 	pap->cpa_argv = new_argv;
386 	pap->cpa_argc = new_argc;
387 
388 	return (0);
389 }
390 
391 static void
392 get_path(const fmd_conf_param_t *pp, void *ptr)
393 {
394 	*((fmd_conf_path_t **)ptr) = (fmd_conf_path_t *)pp->cp_value.cpv_ptr;
395 }
396 
397 static void
398 free_path(fmd_conf_param_t *pp)
399 {
400 	fmd_conf_path_t *pap = pp->cp_value.cpv_ptr;
401 	int i;
402 
403 	if (pap == NULL)
404 		return; /* no value was ever set */
405 
406 	for (i = 0; i < pap->cpa_argc; i++)
407 		fmd_strfree((char *)pap->cpa_argv[i]);
408 
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;
412 }
413 
414 static int
415 set_time(fmd_conf_param_t *pp, const char *s)
416 {
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 }
437 	};
438 
439 	hrtime_t val, mul = 1;
440 	char *end;
441 	int i;
442 
443 	errno = 0;
444 	val = strtoull(s, &end, 0);
445 
446 	if (errno == EOVERFLOW)
447 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
448 
449 	if (errno != 0 || end == s)
450 		return (fmd_set_errno(EFMD_CONF_INVAL));
451 
452 	for (i = 0; suffix[i].name != NULL; i++) {
453 		if (strcasecmp(suffix[i].name, end) == 0) {
454 			mul = suffix[i].mul;
455 			break;
456 		}
457 	}
458 
459 	if (suffix[i].name == NULL && *end != '\0')
460 		return (fmd_set_errno(EFMD_CONF_INVAL));
461 
462 	if (mul == 0) {
463 		if (val != 0)
464 			val = NANOSEC / val; /* compute val as value per sec */
465 	} else
466 		val *= mul;
467 
468 	pp->cp_value.cpv_num = val;
469 	return (0);
470 }
471 
472 static int
473 set_size(fmd_conf_param_t *pp, const char *s)
474 {
475 	size_t len = strlen(s);
476 	uint64_t val, mul = 1;
477 	char *end;
478 
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;
498 	}
499 
500 	errno = 0;
501 	val = strtoull(s, &end, 0) * mul;
502 
503 	if (errno == EOVERFLOW)
504 		return (fmd_set_errno(EFMD_CONF_OVERFLOW));
505 
506 	if ((mul != 1 && end != &s[len - 1]) ||
507 	    (mul == 1 && *end != '\0') || errno != 0)
508 		return (fmd_set_errno(EFMD_CONF_INVAL));
509 
510 	pp->cp_value.cpv_num = val;
511 	return (0);
512 }
513 
514 static int
515 set_sig(fmd_conf_param_t *pp, const char *s)
516 {
517 	int sig;
518 
519 	if (strncasecmp(s, "SIG", 3) == 0)
520 		s += 3; /* be friendlier than strsig() and permit the prefix */
521 
522 	if (str2sig(s, &sig) != 0)
523 		return (fmd_set_errno(EFMD_CONF_INVAL));
524 
525 	pp->cp_value.cpv_num = sig;
526 	return (0);
527 }
528 
529 static void
530 get_par(const fmd_conf_param_t *pp, void *ptr)
531 {
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);
535 	}
536 }
537 
538 /*ARGSUSED*/
539 static int
540 set_par(fmd_conf_param_t *pp, const char *s)
541 {
542 	return (fmd_set_errno(EFMD_CONF_RDONLY));
543 }
544 
545 /*
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.
549  */
550 int
551 fmd_conf_mode_set(const fmd_conf_mode_t *cma,
552     fmd_conf_param_t *pp, const char *value)
553 {
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;
558 
559 	if (s == NULL) {
560 		pp->cp_value.cpv_num = 0;
561 		return (0);
562 	}
563 
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;
569 			}
570 		}
571 
572 		if (cmp->cm_name == NULL) {
573 			fmd_free(s, len);
574 			return (fmd_set_errno(EFMD_CONF_INVAL));
575 		}
576 	}
577 
578 	pp->cp_value.cpv_num = mode;
579 	fmd_free(s, len);
580 	return (0);
581 }
582 
583 void
584 fmd_conf_mode_get(const fmd_conf_param_t *pp, void *ptr)
585 {
586 	*((uint_t *)ptr) = (uint_t)pp->cp_value.cpv_num;
587 }
588 
589 /*ARGSUSED*/
590 int
591 fmd_conf_notsup(fmd_conf_param_t *pp, const char *value)
592 {
593 	return (fmd_set_errno(ENOTSUP));
594 }
595 
596 /*ARGSUSED*/
597 void
598 fmd_conf_nop(fmd_conf_param_t *pp)
599 {
600 	/* no free required for integer-type parameters */
601 }
602 
603 #define	CONF_DEFINE(name, a, b, c, d) \
604 	const fmd_conf_ops_t name = { a, b, c, d }
605 
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);
622 
623 static char *
624 fmd_conf_skipstr(char *s)
625 {
626 	int c;
627 
628 	while ((c = *s) != '\0') {
629 		if (c == '\\')
630 			s++;
631 		else if (c == '"')
632 			break;
633 		s++;
634 	}
635 
636 	return (s);
637 }
638 
639 static char *
640 fmd_conf_skipnws(char *s)
641 {
642 	while (strchr("\f\n\r\t\v ", *s) == NULL)
643 		s++;
644 
645 	return (s);
646 }
647 
648 static int
649 fmd_conf_tokenize(char *s, char *tokv[])
650 {
651 	int c, tokc = 0;
652 
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';
669 		}
670 	}
671 
672 	return (tokc);
673 }
674 
675 static int
676 fmd_conf_exec_setprop(fmd_conf_t *cfp, int argc, char *argv[])
677 {
678 	if (argc != 2)
679 		return (fmd_set_errno(EFMD_CONF_USAGE));
680 
681 	return (fmd_conf_setprop(cfp, argv[0], argv[1]));
682 }
683 
684 static int
685 fmd_conf_exec_subscribe(fmd_conf_t *cfp, int argc, char *argv[])
686 {
687 	if (argc != 1)
688 		return (fmd_set_errno(EFMD_CONF_USAGE));
689 
690 	return (fmd_conf_setprop(cfp, FMD_PROP_SUBSCRIPTIONS, argv[0]));
691 }
692 
693 static int
694 fmd_conf_exec_dictionary(fmd_conf_t *cfp, int argc, char *argv[])
695 {
696 	if (argc != 1)
697 		return (fmd_set_errno(EFMD_CONF_USAGE));
698 
699 	return (fmd_conf_setprop(cfp, FMD_PROP_DICTIONARIES, argv[0]));
700 }
701 
702 static int
703 fmd_conf_parse(fmd_conf_t *cfp, const char *file)
704 {
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 }
710 	};
711 
712 	int line, errs = 0;
713 	char buf[BUFSIZ];
714 	FILE *fp;
715 
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));
724 	}
725 
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;
730 
731 		if (tokc == 0 || tokv[0][0] == '#')
732 			continue; /* skip blank lines and comment lines */
733 
734 		for (vp = verbs; vp->cv_name != NULL; vp++) {
735 			if (strcmp(tokv[0], vp->cv_name) == 0)
736 				break;
737 		}
738 
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;
745 		}
746 
747 		if (vp->cv_exec(cfp, tokc - 1, tokv + 1) != 0) {
748 			fmd_error(errno, "\"%s\", line %d", file, line);
749 			errs++;
750 			continue;
751 		}
752 	}
753 
754 	if (ferror(fp) != 0 || fclose(fp) != 0)
755 		return (fmd_set_errno(EFMD_CONF_IO));
756 
757 	if (errs != 0)
758 		return (fmd_set_errno(EFMD_CONF_ERRS));
759 
760 	return (0);
761 }
762 
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)
766 {
767 	int i;
768 
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;
773 
774 		if (fmd_strbadid(name, checkid) != NULL) {
775 			fmd_error(EFMD_CONF_PROPNAME, "ignoring invalid formal "
776 			    "property %s\n", name);
777 			continue;
778 		}
779 
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;
785 			}
786 		}
787 
788 		if (op != NULL)
789 			continue;
790 
791 		pp->cp_formal = argv;
792 		pp->cp_next = cfp->cf_parhash[h];
793 		cfp->cf_parhash[h] = pp;
794 
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));
800 		}
801 	}
802 }
803 
804 fmd_conf_t *
805 fmd_conf_open(const char *file, int argc,
806     const fmd_conf_formal_t *argv, uint_t flag)
807 {
808 	fmd_conf_t *cfp = fmd_alloc(sizeof (fmd_conf_t), FMD_SLEEP);
809 
810 	(void) pthread_rwlock_init(&cfp->cf_lock, NULL);
811 	cfp->cf_argv = argv;
812 	cfp->cf_argc = argc;
813 	cfp->cf_flag = flag;
814 
815 	cfp->cf_params = fmd_zalloc(
816 	    sizeof (fmd_conf_param_t) * (_fmd_conf_defc + argc), FMD_SLEEP);
817 
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);
821 
822 	cfp->cf_defer = NULL;
823 
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);
826 
827 	if (file != NULL && fmd_conf_parse(cfp, file) != 0) {
828 		fmd_conf_close(cfp);
829 		return (NULL);
830 	}
831 
832 	return (cfp);
833 }
834 
835 void
836 fmd_conf_merge(fmd_conf_t *cfp, const char *file)
837 {
838 	(void) fmd_conf_parse(cfp, file);
839 }
840 
841 void
842 fmd_conf_propagate(fmd_conf_t *src, fmd_conf_t *dst, const char *scope)
843 {
844 	size_t len = strlen(scope);
845 	fmd_conf_defer_t *cdp;
846 
847 	(void) pthread_rwlock_rdlock(&src->cf_lock);
848 
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));
856 		}
857 	}
858 
859 	(void) pthread_rwlock_unlock(&src->cf_lock);
860 }
861 
862 void
863 fmd_conf_close(fmd_conf_t *cfp)
864 {
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;
868 
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));
874 	}
875 
876 	fmd_free(cfp->cf_parhash,
877 	    sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen);
878 
879 	for (i = 0; i < nparams; i++, pp++) {
880 		if (pp->cp_formal != NULL)
881 			pp->cp_formal->cf_ops->co_free(pp);
882 	}
883 
884 	fmd_free(cfp->cf_params, sizeof (fmd_conf_param_t) * nparams);
885 	fmd_free(cfp, sizeof (fmd_conf_t));
886 }
887 
888 static fmd_conf_param_t *
889 fmd_conf_getparam(fmd_conf_t *cfp, const char *name)
890 {
891 	ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen;
892 	fmd_conf_param_t *pp = cfp->cf_parhash[h];
893 
894 	ASSERT(RW_LOCK_HELD(&cfp->cf_lock));
895 
896 	for (; pp != NULL; pp = pp->cp_next) {
897 		if (strcmp(name, pp->cp_formal->cf_name) == 0)
898 			return (pp);
899 	}
900 
901 	return (NULL);
902 }
903 
904 /*
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.
907  */
908 const char *
909 fmd_conf_getnzstr(fmd_conf_t *cfp, const char *name)
910 {
911 	const fmd_conf_param_t *pp;
912 	char *s = NULL;
913 
914 	(void) pthread_rwlock_rdlock(&cfp->cf_lock);
915 
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);
921 
922 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
923 
924 	if (s != NULL && s[0] == '\0') {
925 		(void) fmd_set_errno(EFMD_CONF_UNDEF);
926 		s = NULL;
927 	}
928 
929 	return (s);
930 }
931 
932 const fmd_conf_ops_t *
933 fmd_conf_gettype(fmd_conf_t *cfp, const char *name)
934 {
935 	const fmd_conf_param_t *pp;
936 	const fmd_conf_ops_t *ops = NULL;
937 
938 	(void) pthread_rwlock_rdlock(&cfp->cf_lock);
939 
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);
944 		}
945 	} else
946 		(void) fmd_set_errno(EFMD_CONF_NOPROP);
947 
948 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
949 	return (ops);
950 }
951 
952 int
953 fmd_conf_getprop(fmd_conf_t *cfp, const char *name, void *data)
954 {
955 	const fmd_conf_param_t *pp;
956 	int err = 0;
957 
958 	(void) pthread_rwlock_rdlock(&cfp->cf_lock);
959 
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);
964 
965 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
966 	return (err);
967 }
968 
969 static int
970 fmd_conf_setdefer(fmd_conf_t *cfp, const char *name, const char *value)
971 {
972 	fmd_conf_defer_t *cdp;
973 
974 	if (!(cfp->cf_flag & FMD_CONF_DEFER))
975 		return (fmd_set_errno(EFMD_CONF_NODEFER));
976 
977 	(void) pthread_rwlock_wrlock(&cfp->cf_lock);
978 
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;
984 		}
985 	}
986 
987 	cdp = fmd_alloc(sizeof (fmd_conf_defer_t), FMD_SLEEP);
988 
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;
992 
993 	cfp->cf_defer = cdp;
994 out:
995 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
996 	return (0);
997 }
998 
999 int
1000 fmd_conf_setprop(fmd_conf_t *cfp, const char *name, const char *value)
1001 {
1002 	fmd_conf_param_t *pp;
1003 	int err;
1004 
1005 	if (strchr(name, ':') != NULL)
1006 		return (fmd_conf_setdefer(cfp, name, value));
1007 
1008 	(void) pthread_rwlock_wrlock(&cfp->cf_lock);
1009 
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);
1014 
1015 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
1016 	return (err);
1017 }
1018 
1019 int
1020 fmd_conf_delprop(fmd_conf_t *cfp, const char *name, const char *value)
1021 {
1022 	fmd_conf_param_t *pp;
1023 	int err;
1024 
1025 	(void) pthread_rwlock_wrlock(&cfp->cf_lock);
1026 
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);
1031 
1032 	(void) pthread_rwlock_unlock(&cfp->cf_lock);
1033 	return (err);
1034 }
1035