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
set_bool(fmd_conf_param_t * pp,const char * s)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
get_bool(const fmd_conf_param_t * pp,void * ptr)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
set_i32x(fmd_conf_param_t * pp,const char * s,int64_t min,int64_t max)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
set_i8(fmd_conf_param_t * pp,const char * s)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
set_i16(fmd_conf_param_t * pp,const char * s)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
set_i32(fmd_conf_param_t * pp,const char * s)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
get_i32(const fmd_conf_param_t * pp,void * ptr)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
set_ui32x(fmd_conf_param_t * pp,const char * s,uint64_t max)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
set_ui8(fmd_conf_param_t * pp,const char * s)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
set_ui16(fmd_conf_param_t * pp,const char * s)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
set_ui32(fmd_conf_param_t * pp,const char * s)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
get_ui32(const fmd_conf_param_t * pp,void * ptr)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
set_i64(fmd_conf_param_t * pp,const char * s)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
get_i64(const fmd_conf_param_t * pp,void * ptr)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
set_ui64(fmd_conf_param_t * pp,const char * s)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
get_ui64(const fmd_conf_param_t * pp,void * ptr)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
set_str(fmd_conf_param_t * pp,const char * s)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
get_str(const fmd_conf_param_t * pp,void * ptr)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
free_str(fmd_conf_param_t * pp)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
set_path(fmd_conf_param_t * pp,const char * value)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
set_lst(fmd_conf_param_t * pp,const char * value)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
del_lst(fmd_conf_param_t * pp,const char * value)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
get_path(const fmd_conf_param_t * pp,void * ptr)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
free_path(fmd_conf_param_t * pp)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
set_time(fmd_conf_param_t * pp,const char * s)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
set_size(fmd_conf_param_t * pp,const char * s)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
set_sig(fmd_conf_param_t * pp,const char * s)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
get_par(const fmd_conf_param_t * pp,void * ptr)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
set_par(fmd_conf_param_t * pp,const char * s)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
fmd_conf_mode_set(const fmd_conf_mode_t * cma,fmd_conf_param_t * pp,const char * value)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
fmd_conf_mode_get(const fmd_conf_param_t * pp,void * ptr)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
fmd_conf_notsup(fmd_conf_param_t * pp,const char * value)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
fmd_conf_nop(fmd_conf_param_t * pp)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 *
fmd_conf_skipstr(char * s)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 *
fmd_conf_skipnws(char * s)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
fmd_conf_tokenize(char * s,char * tokv[])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
fmd_conf_exec_setprop(fmd_conf_t * cfp,int argc,char * argv[])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
fmd_conf_exec_subscribe(fmd_conf_t * cfp,int argc,char * argv[])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
fmd_conf_exec_dictionary(fmd_conf_t * cfp,int argc,char * argv[])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
fmd_conf_parse(fmd_conf_t * cfp,const char * file)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
fmd_conf_fill(fmd_conf_t * cfp,fmd_conf_param_t * ppbuf,int argc,const fmd_conf_formal_t * argv,int checkid)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 *
fmd_conf_open(const char * file,int argc,const fmd_conf_formal_t * argv,uint_t flag)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
fmd_conf_merge(fmd_conf_t * cfp,const char * file)836 fmd_conf_merge(fmd_conf_t *cfp, const char *file)
837 {
838 (void) fmd_conf_parse(cfp, file);
839 }
840
841 void
fmd_conf_propagate(fmd_conf_t * src,fmd_conf_t * dst,const char * scope)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
fmd_conf_close(fmd_conf_t * cfp)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 *
fmd_conf_getparam(fmd_conf_t * cfp,const char * name)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 *
fmd_conf_getnzstr(fmd_conf_t * cfp,const char * name)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 *
fmd_conf_gettype(fmd_conf_t * cfp,const char * name)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
fmd_conf_getprop(fmd_conf_t * cfp,const char * name,void * data)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
fmd_conf_setdefer(fmd_conf_t * cfp,const char * name,const char * value)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
fmd_conf_setprop(fmd_conf_t * cfp,const char * name,const char * value)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
fmd_conf_delprop(fmd_conf_t * cfp,const char * name,const char * value)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