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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "@(#)modsysfile.c 1.109 07/11/29 SMI" 27 28 #include <sys/types.h> 29 #include <sys/inttypes.h> 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/user.h> 33 #include <sys/disp.h> 34 #include <sys/conf.h> 35 #include <sys/bootconf.h> 36 #include <sys/sysconf.h> 37 #include <sys/sunddi.h> 38 #include <sys/esunddi.h> 39 #include <sys/ddi_impldefs.h> 40 #include <sys/kmem.h> 41 #include <sys/vmem.h> 42 #include <sys/fs/ufs_fsdir.h> 43 #include <sys/hwconf.h> 44 #include <sys/modctl.h> 45 #include <sys/cmn_err.h> 46 #include <sys/kobj.h> 47 #include <sys/kobj_lex.h> 48 #include <sys/errno.h> 49 #include <sys/debug.h> 50 #include <sys/autoconf.h> 51 #include <sys/callb.h> 52 #include <sys/sysmacros.h> 53 #include <sys/dacf.h> 54 #include <vm/seg_kmem.h> 55 56 struct hwc_class *hcl_head; /* head of list of classes */ 57 static kmutex_t hcl_lock; /* for accessing list of classes */ 58 59 #define DAFILE "/etc/driver_aliases" 60 #define CLASSFILE "/etc/driver_classes" 61 #define DACFFILE "/etc/dacf.conf" 62 63 static char class_file[] = CLASSFILE; 64 static char dafile[] = DAFILE; 65 static char dacffile[] = DACFFILE; 66 67 char *systemfile = "/etc/system"; /* name of ascii system file */ 68 69 static struct sysparam *sysparam_hd; /* head of parameters list */ 70 static struct sysparam *sysparam_tl; /* tail of parameters list */ 71 static vmem_t *mod_sysfile_arena; /* parser memory */ 72 73 char obp_bootpath[BO_MAXOBJNAME]; /* bootpath from obp */ 74 char svm_bootpath[BO_MAXOBJNAME]; /* bootpath redirected via rootdev */ 75 76 #if defined(_PSM_MODULES) 77 78 struct psm_mach { 79 struct psm_mach *m_next; 80 char *m_machname; 81 }; 82 83 static struct psm_mach *pmach_head; /* head of list of classes */ 84 85 #define MACHFILE "/etc/mach" 86 static char mach_file[] = MACHFILE; 87 88 #endif /* _PSM_MODULES */ 89 90 #if defined(_RTC_CONFIG) 91 static char rtc_config_file[] = "/etc/rtc_config"; 92 #endif 93 94 static void sys_set_var(int, struct sysparam *, void *); 95 96 static void setparams(void); 97 98 /* 99 * driver.conf parse thread control structure 100 */ 101 struct hwc_parse_mt { 102 ksema_t sema; 103 char *name; /* name of .conf files */ 104 struct par_list **pl; /* parsed parent list */ 105 ddi_prop_t **props; /* parsed properties */ 106 int rv; /* return value */ 107 }; 108 109 static int hwc_parse_now(char *, struct par_list **, ddi_prop_t **); 110 static void hwc_parse_thread(struct hwc_parse_mt *); 111 static struct hwc_parse_mt *hwc_parse_mtalloc(char *, struct par_list **, 112 ddi_prop_t **); 113 static void hwc_parse_mtfree(struct hwc_parse_mt *); 114 static void add_spec(struct hwc_spec *, struct par_list **); 115 static void add_props(struct hwc_spec *, ddi_prop_t **); 116 117 static void check_system_file(void); 118 static int sysparam_compare_entry(struct sysparam *, struct sysparam *); 119 static char *sysparam_type_to_str(int); 120 static void sysparam_count_entry(struct sysparam *, int *, u_longlong_t *); 121 static void sysparam_print_warning(struct sysparam *, u_longlong_t); 122 123 #ifdef DEBUG 124 static int parse_debug_on = 0; 125 126 /*VARARGS1*/ 127 static void 128 parse_debug(struct _buf *file, char *fmt, ...) 129 { 130 va_list adx; 131 132 if (parse_debug_on) { 133 va_start(adx, fmt); 134 vprintf(fmt, adx); 135 if (file) 136 printf(" on line %d of %s\n", kobj_linenum(file), 137 kobj_filename(file)); 138 va_end(adx); 139 } 140 } 141 #endif /* DEBUG */ 142 143 #define FE_BUFLEN 256 144 145 /*PRINTFLIKE3*/ 146 void 147 kobj_file_err(int type, struct _buf *file, char *fmt, ...) 148 { 149 va_list ap; 150 /* 151 * If we're in trouble, we might be short on stack... be paranoid 152 */ 153 char *buf = kmem_alloc(FE_BUFLEN, KM_SLEEP); 154 char *trailer = kmem_alloc(FE_BUFLEN, KM_SLEEP); 155 char *fmt_str = kmem_alloc(FE_BUFLEN, KM_SLEEP); 156 char prefix = '\0'; 157 158 va_start(ap, fmt); 159 if (strchr("^!?", fmt[0]) != NULL) { 160 prefix = fmt[0]; 161 fmt++; 162 } 163 (void) vsnprintf(buf, FE_BUFLEN, fmt, ap); 164 va_end(ap); 165 (void) snprintf(trailer, FE_BUFLEN, " on line %d of %s", 166 kobj_linenum(file), kobj_filename(file)); 167 168 /* 169 * If prefixed with !^?, prepend that character 170 */ 171 if (prefix != '\0') { 172 (void) snprintf(fmt_str, FE_BUFLEN, "%c%%s%%s", prefix); 173 } else { 174 (void) strncpy(fmt_str, "%s%s", FE_BUFLEN); 175 } 176 177 cmn_err(type, fmt_str, buf, trailer); 178 kmem_free(buf, FE_BUFLEN); 179 kmem_free(trailer, FE_BUFLEN); 180 kmem_free(fmt_str, FE_BUFLEN); 181 } 182 183 #ifdef DEBUG 184 char *tokennames[] = { 185 "UNEXPECTED", 186 "EQUALS", 187 "AMPERSAND", 188 "BIT_OR", 189 "STAR", 190 "POUND", 191 "COLON", 192 "SEMICOLON", 193 "COMMA", 194 "SLASH", 195 "WHITE_SPACE", 196 "NEWLINE", 197 "EOF", 198 "STRING", 199 "HEXVAL", 200 "DECVAL", 201 "NAME" 202 }; 203 #endif /* DEBUG */ 204 205 token_t 206 kobj_lex(struct _buf *file, char *val, size_t size) 207 { 208 char *cp; 209 int ch, oval, badquote; 210 size_t remain; 211 token_t token = UNEXPECTED; 212 213 if (size < 2) 214 return (token); /* this token is UNEXPECTED */ 215 216 cp = val; 217 while ((ch = kobj_getc(file)) == ' ' || ch == '\t') 218 ; 219 220 remain = size - 1; 221 *cp++ = (char)ch; 222 switch (ch) { 223 case '=': 224 token = EQUALS; 225 break; 226 case '&': 227 token = AMPERSAND; 228 break; 229 case '|': 230 token = BIT_OR; 231 break; 232 case '*': 233 token = STAR; 234 break; 235 case '#': 236 token = POUND; 237 break; 238 case ':': 239 token = COLON; 240 break; 241 case ';': 242 token = SEMICOLON; 243 break; 244 case ',': 245 token = COMMA; 246 break; 247 case '/': 248 token = SLASH; 249 break; 250 case ' ': 251 case '\t': 252 case '\f': 253 while ((ch = kobj_getc(file)) == ' ' || 254 ch == '\t' || ch == '\f') { 255 if (--remain == 0) { 256 token = UNEXPECTED; 257 goto out; 258 } 259 *cp++ = (char)ch; 260 } 261 (void) kobj_ungetc(file); 262 token = WHITE_SPACE; 263 break; 264 case '\n': 265 case '\r': 266 token = NEWLINE; 267 break; 268 case '"': 269 remain++; 270 cp--; 271 badquote = 0; 272 while (!badquote && (ch = kobj_getc(file)) != '"') { 273 switch (ch) { 274 case '\n': 275 case -1: 276 kobj_file_err(CE_WARN, file, "Missing \""); 277 remain = size - 1; 278 cp = val; 279 *cp++ = '\n'; 280 badquote = 1; 281 /* since we consumed the newline/EOF */ 282 (void) kobj_ungetc(file); 283 break; 284 285 case '\\': 286 if (--remain == 0) { 287 token = UNEXPECTED; 288 goto out; 289 } 290 ch = (char)kobj_getc(file); 291 if (!isdigit(ch)) { 292 /* escape the character */ 293 *cp++ = (char)ch; 294 break; 295 } 296 oval = 0; 297 while (ch >= '0' && ch <= '7') { 298 ch -= '0'; 299 oval = (oval << 3) + ch; 300 ch = (char)kobj_getc(file); 301 } 302 (void) kobj_ungetc(file); 303 /* check for character overflow? */ 304 if (oval > 127) { 305 cmn_err(CE_WARN, 306 "Character " 307 "overflow detected."); 308 } 309 *cp++ = (char)oval; 310 break; 311 default: 312 if (--remain == 0) { 313 token = UNEXPECTED; 314 goto out; 315 } 316 *cp++ = (char)ch; 317 break; 318 } 319 } 320 token = STRING; 321 break; 322 323 case -1: 324 token = EOF; 325 break; 326 327 default: 328 /* 329 * detect a lone '-' (including at the end of a line), and 330 * identify it as a 'name' 331 */ 332 if (ch == '-') { 333 if (--remain == 0) { 334 token = UNEXPECTED; 335 goto out; 336 } 337 *cp++ = (char)(ch = kobj_getc(file)); 338 if (iswhite(ch) || (ch == '\n')) { 339 (void) kobj_ungetc(file); 340 remain++; 341 cp--; 342 token = NAME; 343 break; 344 } 345 } else if (isunary(ch)) { 346 if (--remain == 0) { 347 token = UNEXPECTED; 348 goto out; 349 } 350 *cp++ = (char)(ch = kobj_getc(file)); 351 } 352 353 354 if (isdigit(ch)) { 355 if (ch == '0') { 356 if ((ch = kobj_getc(file)) == 'x') { 357 if (--remain == 0) { 358 token = UNEXPECTED; 359 goto out; 360 } 361 *cp++ = (char)ch; 362 ch = kobj_getc(file); 363 while (isxdigit(ch)) { 364 if (--remain == 0) { 365 token = UNEXPECTED; 366 goto out; 367 } 368 *cp++ = (char)ch; 369 ch = kobj_getc(file); 370 } 371 (void) kobj_ungetc(file); 372 token = HEXVAL; 373 } else { 374 goto digit; 375 } 376 } else { 377 ch = kobj_getc(file); 378 digit: 379 while (isdigit(ch)) { 380 if (--remain == 0) { 381 token = UNEXPECTED; 382 goto out; 383 } 384 *cp++ = (char)ch; 385 ch = kobj_getc(file); 386 } 387 (void) kobj_ungetc(file); 388 token = DECVAL; 389 } 390 } else if (isalpha(ch) || ch == '\\' || ch == '_') { 391 if (ch != '\\') { 392 ch = kobj_getc(file); 393 } else { 394 /* 395 * if the character was a backslash, 396 * back up so we can overwrite it with 397 * the next (i.e. escaped) character. 398 */ 399 remain++; 400 cp--; 401 } 402 while (isnamechar(ch) || ch == '\\') { 403 if (ch == '\\') 404 ch = kobj_getc(file); 405 if (--remain == 0) { 406 token = UNEXPECTED; 407 goto out; 408 } 409 *cp++ = (char)ch; 410 ch = kobj_getc(file); 411 } 412 (void) kobj_ungetc(file); 413 token = NAME; 414 } else { 415 token = UNEXPECTED; 416 } 417 break; 418 } 419 out: 420 *cp = '\0'; 421 422 #ifdef DEBUG 423 /* 424 * The UNEXPECTED token is the first element of the tokennames array, 425 * but its token value is -1. Adjust the value by adding one to it 426 * to change it to an index of the array. 427 */ 428 parse_debug(NULL, "kobj_lex: token %s value '%s'\n", 429 tokennames[token+1], val); 430 #endif 431 return (token); 432 } 433 434 /* 435 * Leave NEWLINE as the next character. 436 */ 437 438 void 439 kobj_find_eol(struct _buf *file) 440 { 441 int ch; 442 443 while ((ch = kobj_getc(file)) != -1) { 444 if (isnewline(ch)) { 445 (void) kobj_ungetc(file); 446 break; 447 } 448 } 449 } 450 451 /* 452 * The ascii system file is read and processed. 453 * 454 * The syntax of commands is as follows: 455 * 456 * '*' in column 1 is a comment line. 457 * <command> : <value> 458 * 459 * command is EXCLUDE, INCLUDE, FORCELOAD, ROOTDEV, ROOTFS, 460 * SWAPDEV, SWAPFS, MODDIR, SET 461 * 462 * value is an ascii string meaningful for the command. 463 */ 464 465 /* 466 * Table of commands 467 */ 468 static struct modcmd modcmd[] = { 469 { "EXCLUDE", MOD_EXCLUDE }, 470 { "exclude", MOD_EXCLUDE }, 471 { "INCLUDE", MOD_INCLUDE }, 472 { "include", MOD_INCLUDE }, 473 { "FORCELOAD", MOD_FORCELOAD }, 474 { "forceload", MOD_FORCELOAD }, 475 { "ROOTDEV", MOD_ROOTDEV }, 476 { "rootdev", MOD_ROOTDEV }, 477 { "ROOTFS", MOD_ROOTFS }, 478 { "rootfs", MOD_ROOTFS }, 479 { "SWAPDEV", MOD_SWAPDEV }, 480 { "swapdev", MOD_SWAPDEV }, 481 { "SWAPFS", MOD_SWAPFS }, 482 { "swapfs", MOD_SWAPFS }, 483 { "MODDIR", MOD_MODDIR }, 484 { "moddir", MOD_MODDIR }, 485 { "SET", MOD_SET }, 486 { "set", MOD_SET }, 487 { "SET32", MOD_SET32 }, 488 { "set32", MOD_SET32 }, 489 { "SET64", MOD_SET64 }, 490 { "set64", MOD_SET64 }, 491 { NULL, MOD_UNKNOWN } 492 }; 493 494 495 static char bad_op[] = "illegal operator '%s' used on a string"; 496 static char colon_err[] = "A colon (:) must follow the '%s' command"; 497 static char tok_err[] = "Unexpected token '%s'"; 498 static char extra_err[] = "extraneous input ignored starting at '%s'"; 499 static char oversize_err[] = "value too long"; 500 501 static struct sysparam * 502 do_sysfile_cmd(struct _buf *file, const char *cmd) 503 { 504 struct sysparam *sysp; 505 struct modcmd *mcp; 506 token_t token, op; 507 char *cp; 508 int ch; 509 char tok1[MOD_MAXPATH + 1]; /* used to read the path set by 'moddir' */ 510 char tok2[64]; 511 512 for (mcp = modcmd; mcp->mc_cmdname != NULL; mcp++) { 513 if (strcmp(mcp->mc_cmdname, cmd) == 0) 514 break; 515 } 516 sysp = vmem_alloc(mod_sysfile_arena, sizeof (struct sysparam), 517 VM_SLEEP); 518 bzero(sysp, sizeof (struct sysparam)); 519 sysp->sys_op = SETOP_NONE; /* set op to noop initially */ 520 521 switch (sysp->sys_type = mcp->mc_type) { 522 case MOD_INCLUDE: 523 case MOD_EXCLUDE: 524 case MOD_FORCELOAD: 525 /* 526 * Are followed by colon. 527 */ 528 case MOD_ROOTFS: 529 case MOD_SWAPFS: 530 if ((token = kobj_lex(file, tok1, sizeof (tok1))) == COLON) { 531 token = kobj_lex(file, tok1, sizeof (tok1)); 532 } else { 533 kobj_file_err(CE_WARN, file, colon_err, cmd); 534 } 535 if (token != NAME) { 536 kobj_file_err(CE_WARN, file, "value expected"); 537 goto bad; 538 } 539 540 cp = tok1 + strlen(tok1); 541 while ((ch = kobj_getc(file)) != -1 && !iswhite(ch) && 542 !isnewline(ch)) { 543 if (cp - tok1 >= sizeof (tok1) - 1) { 544 kobj_file_err(CE_WARN, file, oversize_err); 545 goto bad; 546 } 547 *cp++ = (char)ch; 548 } 549 *cp = '\0'; 550 551 if (ch != -1) 552 (void) kobj_ungetc(file); 553 if (sysp->sys_type == MOD_INCLUDE) 554 return (NULL); 555 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(tok1) + 1, 556 VM_SLEEP); 557 (void) strcpy(sysp->sys_ptr, tok1); 558 break; 559 case MOD_SET: 560 case MOD_SET64: 561 case MOD_SET32: 562 { 563 char *var; 564 token_t tok3; 565 566 if (kobj_lex(file, tok1, sizeof (tok1)) != NAME) { 567 kobj_file_err(CE_WARN, file, "value expected"); 568 goto bad; 569 } 570 571 /* 572 * If the next token is a colon (:), 573 * we have the <modname>:<variable> construct. 574 */ 575 if ((token = kobj_lex(file, tok2, sizeof (tok2))) == COLON) { 576 if ((token = kobj_lex(file, tok2, 577 sizeof (tok2))) == NAME) { 578 var = tok2; 579 /* 580 * Save the module name. 581 */ 582 sysp->sys_modnam = vmem_alloc(mod_sysfile_arena, 583 strlen(tok1) + 1, VM_SLEEP); 584 (void) strcpy(sysp->sys_modnam, tok1); 585 op = kobj_lex(file, tok1, sizeof (tok1)); 586 } else { 587 kobj_file_err(CE_WARN, file, "value expected"); 588 goto bad; 589 } 590 } else { 591 /* otherwise, it was the op */ 592 var = tok1; 593 op = token; 594 } 595 /* 596 * kernel param - place variable name in sys_ptr. 597 */ 598 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(var) + 1, 599 VM_SLEEP); 600 (void) strcpy(sysp->sys_ptr, var); 601 /* set operation */ 602 switch (op) { 603 case EQUALS: 604 /* simple assignment */ 605 sysp->sys_op = SETOP_ASSIGN; 606 break; 607 case AMPERSAND: 608 /* bitwise AND */ 609 sysp->sys_op = SETOP_AND; 610 break; 611 case BIT_OR: 612 /* bitwise OR */ 613 sysp->sys_op = SETOP_OR; 614 break; 615 default: 616 /* unsupported operation */ 617 kobj_file_err(CE_WARN, file, 618 "unsupported operator %s", tok2); 619 goto bad; 620 } 621 622 switch ((tok3 = kobj_lex(file, tok1, sizeof (tok1)))) { 623 case STRING: 624 /* string variable */ 625 if (sysp->sys_op != SETOP_ASSIGN) { 626 kobj_file_err(CE_WARN, file, bad_op, tok1); 627 goto bad; 628 } 629 if (kobj_get_string(&sysp->sys_info, tok1) == 0) { 630 kobj_file_err(CE_WARN, file, "string garbled"); 631 goto bad; 632 } 633 /* 634 * Set SYSPARAM_STR_TOKEN in sys_flags to notify 635 * sysparam_print_warning() that this is a string 636 * token. 637 */ 638 sysp->sys_flags |= SYSPARAM_STR_TOKEN; 639 break; 640 case HEXVAL: 641 case DECVAL: 642 if (kobj_getvalue(tok1, &sysp->sys_info) == -1) { 643 kobj_file_err(CE_WARN, file, 644 "invalid number '%s'", tok1); 645 goto bad; 646 } 647 648 /* 649 * Set the appropriate flag (hexadecimal or decimal) 650 * in sys_flags for sysparam_print_warning() to be 651 * able to print the number with the correct format. 652 */ 653 if (tok3 == HEXVAL) { 654 sysp->sys_flags |= SYSPARAM_HEX_TOKEN; 655 } else { 656 sysp->sys_flags |= SYSPARAM_DEC_TOKEN; 657 } 658 break; 659 default: 660 kobj_file_err(CE_WARN, file, "bad rvalue '%s'", tok1); 661 goto bad; 662 } /* end switch */ 663 664 /* 665 * Now that we've parsed it to check the syntax, consider 666 * discarding it (because it -doesn't- apply to this flavor 667 * of the kernel) 668 */ 669 #ifdef _LP64 670 if (sysp->sys_type == MOD_SET32) 671 return (NULL); 672 #else 673 if (sysp->sys_type == MOD_SET64) 674 return (NULL); 675 #endif 676 sysp->sys_type = MOD_SET; 677 break; 678 } 679 case MOD_MODDIR: 680 if ((token = kobj_lex(file, tok1, sizeof (tok1))) != COLON) { 681 kobj_file_err(CE_WARN, file, colon_err, cmd); 682 goto bad; 683 } 684 685 cp = tok1; 686 while ((token = kobj_lex(file, cp, 687 sizeof (tok1) - (cp - tok1))) != NEWLINE && token != EOF) { 688 if (token == -1) { 689 kobj_file_err(CE_WARN, file, oversize_err); 690 goto bad; 691 } 692 cp += strlen(cp); 693 while ((ch = kobj_getc(file)) != -1 && !iswhite(ch) && 694 !isnewline(ch) && ch != ':') { 695 if (cp - tok1 >= sizeof (tok1) - 1) { 696 kobj_file_err(CE_WARN, file, 697 oversize_err); 698 goto bad; 699 } 700 *cp++ = (char)ch; 701 } 702 *cp++ = ':'; 703 if (isnewline(ch)) { 704 cp--; 705 (void) kobj_ungetc(file); 706 } 707 } 708 (void) kobj_ungetc(file); 709 *cp = '\0'; 710 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(tok1) + 1, 711 VM_SLEEP); 712 (void) strcpy(sysp->sys_ptr, tok1); 713 break; 714 715 case MOD_SWAPDEV: 716 case MOD_ROOTDEV: 717 if ((token = kobj_lex(file, tok1, sizeof (tok1))) != COLON) { 718 kobj_file_err(CE_WARN, file, colon_err, cmd); 719 goto bad; 720 } 721 while ((ch = kobj_getc(file)) == ' ' || ch == '\t') 722 ; 723 cp = tok1; 724 while (!iswhite(ch) && !isnewline(ch) && ch != -1) { 725 if (cp - tok1 >= sizeof (tok1) - 1) { 726 kobj_file_err(CE_WARN, file, oversize_err); 727 goto bad; 728 } 729 730 *cp++ = (char)ch; 731 ch = kobj_getc(file); 732 } 733 if (ch != -1) 734 (void) kobj_ungetc(file); 735 *cp = '\0'; 736 737 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(tok1) + 1, 738 VM_SLEEP); 739 (void) strcpy(sysp->sys_ptr, tok1); 740 break; 741 742 case MOD_UNKNOWN: 743 default: 744 kobj_file_err(CE_WARN, file, "unknown command '%s'", cmd); 745 goto bad; 746 } 747 748 return (sysp); 749 750 bad: 751 kobj_find_eol(file); 752 return (NULL); 753 } 754 755 void 756 mod_read_system_file(int ask) 757 { 758 register struct sysparam *sp; 759 register struct _buf *file; 760 register token_t token, last_tok; 761 char tokval[MAXLINESIZE]; 762 763 mod_sysfile_arena = vmem_create("mod_sysfile", NULL, 0, 8, 764 segkmem_alloc, segkmem_free, heap_arena, 0, VM_SLEEP); 765 766 if (ask) 767 mod_askparams(); 768 769 if (systemfile != NULL) { 770 771 if ((file = kobj_open_file(systemfile)) == 772 (struct _buf *)-1) { 773 cmn_err(CE_WARN, "cannot open system file: %s", 774 systemfile); 775 } else { 776 sysparam_tl = (struct sysparam *)&sysparam_hd; 777 778 last_tok = NEWLINE; 779 while ((token = kobj_lex(file, tokval, 780 sizeof (tokval))) != EOF) { 781 switch (token) { 782 case STAR: 783 case POUND: 784 /* 785 * Skip comments. 786 */ 787 kobj_find_eol(file); 788 break; 789 case NEWLINE: 790 kobj_newline(file); 791 last_tok = NEWLINE; 792 break; 793 case NAME: 794 if (last_tok != NEWLINE) { 795 kobj_file_err(CE_WARN, file, 796 extra_err, tokval); 797 kobj_find_eol(file); 798 } else if ((sp = do_sysfile_cmd(file, 799 tokval)) != NULL) { 800 sp->sys_next = NULL; 801 sysparam_tl->sys_next = sp; 802 sysparam_tl = sp; 803 } 804 last_tok = NAME; 805 break; 806 default: 807 kobj_file_err(CE_WARN, 808 file, tok_err, tokval); 809 kobj_find_eol(file); 810 break; 811 } 812 } 813 kobj_close_file(file); 814 } 815 } 816 817 /* 818 * Sanity check of /etc/system. 819 */ 820 check_system_file(); 821 822 param_preset(); 823 (void) mod_sysctl(SYS_SET_KVAR, NULL); 824 param_check(); 825 826 if (ask == 0) 827 setparams(); 828 } 829 830 /* 831 * Search for a specific module variable assignment in /etc/system. If 832 * successful, 1 is returned and the value is stored in '*value'. 833 * Otherwise 0 is returned and '*value' isn't modified. If 'module' is 834 * NULL we look for global definitions. 835 * 836 * This is useful if the value of an assignment is needed before a 837 * module is loaded (e.g. to obtain a default privileged rctl limit). 838 */ 839 int 840 mod_sysvar(const char *module, const char *name, u_longlong_t *value) 841 { 842 struct sysparam *sysp; 843 int cnt = 0; /* dummy */ 844 845 ASSERT(name != NULL); 846 ASSERT(value != NULL); 847 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) { 848 849 if ((sysp->sys_type == MOD_SET) && 850 (((module == NULL) && (sysp->sys_modnam == NULL)) || 851 ((module != NULL) && (sysp->sys_modnam != NULL) && 852 (strcmp(module, sysp->sys_modnam) == 0)))) { 853 854 ASSERT(sysp->sys_ptr != NULL); 855 856 if (strcmp(name, sysp->sys_ptr) == 0) { 857 sysparam_count_entry(sysp, &cnt, value); 858 if ((sysp->sys_flags & SYSPARAM_TERM) != 0) 859 return (1); 860 continue; 861 } 862 } 863 } 864 ASSERT(cnt == 0); 865 return (0); 866 } 867 868 /* 869 * This function scans sysparam records, which are created from the 870 * contents of /etc/system, for entries which are logical duplicates, 871 * and prints warning messages as appropriate. When multiple "set" 872 * commands are encountered, the pileup of values with "&", "|" 873 * and "=" operators results in the final value. 874 */ 875 static void 876 check_system_file(void) 877 { 878 struct sysparam *sysp; 879 880 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) { 881 struct sysparam *entry, *final; 882 u_longlong_t value = 0; 883 int cnt = 1; 884 /* 885 * If the entry is already checked, skip it. 886 */ 887 if ((sysp->sys_flags & SYSPARAM_DUP) != 0) 888 continue; 889 /* 890 * Check if there is a duplicate entry by doing a linear 891 * search. 892 */ 893 final = sysp; 894 for (entry = sysp->sys_next; entry != NULL; 895 entry = entry->sys_next) { 896 /* 897 * Check the entry. if it's different, skip this. 898 */ 899 if (sysparam_compare_entry(sysp, entry) != 0) 900 continue; 901 /* 902 * Count the entry and put the mark. 903 */ 904 sysparam_count_entry(entry, &cnt, &value); 905 entry->sys_flags |= SYSPARAM_DUP; 906 final = entry; 907 } 908 final->sys_flags |= SYSPARAM_TERM; 909 /* 910 * Print the warning if it's duplicated. 911 */ 912 if (cnt >= 2) 913 sysparam_print_warning(final, value); 914 } 915 } 916 917 /* 918 * Compare the sysparam records. 919 * Return 0 if they are the same, return 1 if not. 920 */ 921 static int 922 sysparam_compare_entry(struct sysparam *sysp, struct sysparam *entry) 923 { 924 ASSERT(sysp->sys_ptr != NULL && entry->sys_ptr != NULL); 925 926 /* 927 * If the command is rootdev, rootfs, swapdev, swapfs or moddir, 928 * the record with the same type is treated as a duplicate record. 929 * In other cases, the record is treated as a duplicate record when 930 * its type, its module name (if it exists), and its variable name 931 * are the same. 932 */ 933 switch (sysp->sys_type) { 934 case MOD_ROOTDEV: 935 case MOD_ROOTFS: 936 case MOD_SWAPDEV: 937 case MOD_SWAPFS: 938 case MOD_MODDIR: 939 return (sysp->sys_type == entry->sys_type ? 0 : 1); 940 default: /* In other cases, just go through it. */ 941 break; 942 } 943 944 if (sysp->sys_type != entry->sys_type) 945 return (1); 946 947 if (sysp->sys_modnam != NULL && entry->sys_modnam == NULL) 948 return (1); 949 950 if (sysp->sys_modnam == NULL && entry->sys_modnam != NULL) 951 return (1); 952 953 if (sysp->sys_modnam != NULL && entry->sys_modnam != NULL && 954 strcmp(sysp->sys_modnam, entry->sys_modnam) != 0) 955 return (1); 956 957 return (strcmp(sysp->sys_ptr, entry->sys_ptr)); 958 } 959 960 /* 961 * Translate a sysparam type value to a string. 962 */ 963 static char * 964 sysparam_type_to_str(int type) 965 { 966 struct modcmd *mcp; 967 968 for (mcp = modcmd; mcp->mc_cmdname != NULL; mcp++) { 969 if (mcp->mc_type == type) 970 break; 971 } 972 ASSERT(mcp->mc_type == type); 973 974 if (type != MOD_UNKNOWN) 975 return ((++mcp)->mc_cmdname); /* lower case */ 976 else 977 return (""); /* MOD_UNKNOWN */ 978 } 979 980 /* 981 * Check the entry and accumulate the number of entries. 982 */ 983 static void 984 sysparam_count_entry(struct sysparam *sysp, int *cnt, u_longlong_t *value) 985 { 986 u_longlong_t ul = sysp->sys_info; 987 988 switch (sysp->sys_op) { 989 case SETOP_ASSIGN: 990 *value = ul; 991 (*cnt)++; 992 return; 993 case SETOP_AND: 994 *value &= ul; 995 return; 996 case SETOP_OR: 997 *value |= ul; 998 return; 999 default: /* Not MOD_SET */ 1000 (*cnt)++; 1001 return; 1002 } 1003 } 1004 1005 /* 1006 * Print out the warning if multiple entries are found in the system file. 1007 */ 1008 static void 1009 sysparam_print_warning(struct sysparam *sysp, u_longlong_t value) 1010 { 1011 char *modnam = sysp->sys_modnam; 1012 char *varnam = sysp->sys_ptr; 1013 int type = sysp->sys_type; 1014 char *typenam = sysparam_type_to_str(type); 1015 boolean_t str_token = ((sysp->sys_flags & SYSPARAM_STR_TOKEN) != 0); 1016 boolean_t hex_number = ((sysp->sys_flags & SYSPARAM_HEX_TOKEN) != 0); 1017 #define warn_format1 " is set more than once in /%s. " 1018 #define warn_format2 " applied as the current setting.\n" 1019 1020 ASSERT(varnam != NULL); 1021 1022 if (type == MOD_SET) { 1023 /* 1024 * If a string token is set, print out the string 1025 * instead of its pointer value. In other cases, 1026 * print out the value with the appropriate format 1027 * for a hexadecimal number or a decimal number. 1028 */ 1029 if (modnam == NULL) { 1030 if (str_token == B_TRUE) { 1031 cmn_err(CE_WARN, "%s" warn_format1 1032 "\"%s %s = %s\"" warn_format2, 1033 varnam, systemfile, typenam, 1034 varnam, (char *)(uintptr_t)value); 1035 } else if (hex_number == B_TRUE) { 1036 cmn_err(CE_WARN, "%s" warn_format1 1037 "\"%s %s = 0x%llx\"" warn_format2, 1038 varnam, systemfile, typenam, 1039 varnam, value); 1040 } else { 1041 cmn_err(CE_WARN, "%s" warn_format1 1042 "\"%s %s = %lld\"" warn_format2, 1043 varnam, systemfile, typenam, 1044 varnam, value); 1045 } 1046 } else { 1047 if (str_token == B_TRUE) { 1048 cmn_err(CE_WARN, "%s:%s" warn_format1 1049 "\"%s %s:%s = %s\"" warn_format2, 1050 modnam, varnam, systemfile, 1051 typenam, modnam, varnam, 1052 (char *)(uintptr_t)value); 1053 } else if (hex_number == B_TRUE) { 1054 cmn_err(CE_WARN, "%s:%s" warn_format1 1055 "\"%s %s:%s = 0x%llx\"" warn_format2, 1056 modnam, varnam, systemfile, 1057 typenam, modnam, varnam, value); 1058 } else { 1059 cmn_err(CE_WARN, "%s:%s" warn_format1 1060 "\"%s %s:%s = %lld\"" warn_format2, 1061 modnam, varnam, systemfile, 1062 typenam, modnam, varnam, value); 1063 } 1064 } 1065 } else { 1066 /* 1067 * If the type is MOD_ROOTDEV, MOD_ROOTFS, MOD_SWAPDEV, 1068 * MOD_SWAPFS or MOD_MODDIR, the entry is treated as 1069 * a duplicate one if it has the same type regardless 1070 * of its variable name. 1071 */ 1072 switch (type) { 1073 case MOD_ROOTDEV: 1074 case MOD_ROOTFS: 1075 case MOD_SWAPDEV: 1076 case MOD_SWAPFS: 1077 case MOD_MODDIR: 1078 cmn_err(CE_WARN, "\"%s\" appears more than once " 1079 "in /%s.", typenam, systemfile); 1080 break; 1081 default: 1082 cmn_err(CE_NOTE, "\"%s: %s\" appears more than once " 1083 "in /%s.", typenam, varnam, systemfile); 1084 break; 1085 } 1086 } 1087 } 1088 1089 /* 1090 * Process the system file commands. 1091 */ 1092 int 1093 mod_sysctl(int fcn, void *p) 1094 { 1095 static char wmesg[] = "forceload of %s failed"; 1096 struct sysparam *sysp; 1097 char *name; 1098 struct modctl *modp; 1099 1100 if (sysparam_hd == NULL) 1101 return (0); 1102 1103 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) { 1104 1105 switch (fcn) { 1106 1107 case SYS_FORCELOAD: 1108 if (sysp->sys_type == MOD_FORCELOAD) { 1109 name = sysp->sys_ptr; 1110 if (modload(NULL, name) == -1) 1111 cmn_err(CE_WARN, wmesg, name); 1112 /* 1113 * The following works because it 1114 * runs before autounloading is started!! 1115 */ 1116 modp = mod_find_by_filename(NULL, name); 1117 if (modp != NULL) 1118 modp->mod_loadflags |= MOD_NOAUTOUNLOAD; 1119 /* 1120 * For drivers, attempt to install it. 1121 */ 1122 if (strncmp(sysp->sys_ptr, "drv", 3) == 0) { 1123 (