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