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