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