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