1 /* $OpenBSD: main.c,v 1.62 2021/01/26 18:23:49 deraadt Exp $ */ 2 /* $NetBSD: main.c,v 1.22 1997/02/02 21:12:33 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This software was developed by the Computer Systems Engineering group 9 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 10 * contributed to Berkeley. 11 * 12 * All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by the University of 15 * California, Lawrence Berkeley Laboratories. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * from: @(#)main.c 8.1 (Berkeley) 6/6/93 42 */ 43 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 47 #include <ctype.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <limits.h> 55 56 #include "config.h" 57 58 int firstfile(const char *); 59 int yyparse(void); 60 61 static struct hashtab *mkopttab; 62 static struct nvlist **nextopt; 63 static struct nvlist **nextdefopt; 64 static struct nvlist **nextmkopt; 65 66 static __dead void stop(void); 67 static int do_option(struct hashtab *, struct nvlist ***, 68 const char *, const char *, const char *); 69 static int crosscheck(void); 70 static int badstar(void); 71 static int mksymlinks(void); 72 static int hasparent(struct devi *); 73 static int cfcrosscheck(struct config *, const char *, struct nvlist *); 74 static void optiondelta(void); 75 76 int verbose; 77 78 const char *conffile; /* source file, e.g., "GENERIC.sparc" */ 79 const char *last_component; 80 const char *machine; /* machine type, e.g., "sparc" or "sun3" */ 81 const char *machinearch; /* machine arch, e.g., "sparc" or "m68k" */ 82 const char *srcdir; /* path to source directory (rel. to build) */ 83 const char *builddir; /* path to build directory */ 84 const char *defbuilddir; /* default build directory */ 85 int errors; /* counts calls to error() */ 86 int minmaxusers; /* minimum "maxusers" parameter */ 87 int defmaxusers; /* default "maxusers" parameter */ 88 int maxmaxusers; /* default "maxusers" parameter */ 89 int maxusers; /* configuration's "maxusers" parameter */ 90 int maxpartitions; /* configuration's "maxpartitions" parameter */ 91 struct nvlist *options; /* options */ 92 struct nvlist *defoptions; /* "defopt"'d options */ 93 struct nvlist *mkoptions; /* makeoptions */ 94 struct hashtab *devbasetab; /* devbase lookup */ 95 struct hashtab *devatab; /* devbase attachment lookup */ 96 struct hashtab *selecttab; /* selects things that are "optional foo" */ 97 struct hashtab *needcnttab; /* retains names marked "needs-count" */ 98 struct hashtab *opttab; /* table of configured options */ 99 struct hashtab *defopttab; /* options that have been "defopt"'d */ 100 struct devbase *allbases; /* list of all devbase structures */ 101 struct deva *alldevas; /* list of all devbase attachment structures */ 102 struct config *allcf; /* list of configured kernels */ 103 struct devi *alldevi; /* list of all instances */ 104 struct devi *allpseudo; /* list of all pseudo-devices */ 105 int ndevi; /* number of devi's (before packing) */ 106 int npseudo; /* number of pseudo's */ 107 108 struct files *allfiles; /* list of all kernel source files */ 109 struct objects *allobjects; /* list of all kernel object and library files */ 110 111 struct devi **packed; /* arrayified table for packed devi's */ 112 int npacked; /* size of packed table, <= ndevi */ 113 114 struct parents parents; 115 struct locators locators; 116 117 __dead void 118 usage(void) 119 { 120 extern char *__progname; 121 122 fprintf(stderr, 123 "usage: %s [-p] [-b builddir] [-s srcdir] [config-file]\n" 124 " %s -e [-u] [-c cmdfile] [-f | -o outfile] infile\n", 125 __progname, __progname); 126 127 exit(1); 128 } 129 130 int pflag = 0; 131 char *sflag = NULL; 132 char *bflag = NULL; 133 char *startdir; 134 char *cmdfile; 135 136 int 137 main(int argc, char *argv[]) 138 { 139 char *p; 140 char *outfile = NULL; 141 int ch, eflag, uflag, fflag; 142 char dirbuffer[PATH_MAX]; 143 144 if (pledge("stdio rpath wpath cpath flock proc exec", NULL) == -1) 145 err(1, "pledge"); 146 147 pflag = eflag = uflag = fflag = 0; 148 while ((ch = getopt(argc, argv, "c:epfb:s:o:u")) != -1) { 149 switch (ch) { 150 case 'c': 151 cmdfile = optarg; 152 break; 153 case 'o': 154 outfile = optarg; 155 break; 156 case 'u': 157 uflag = 1; 158 break; 159 case 'f': 160 fflag = 1; 161 break; 162 163 case 'e': 164 eflag = 1; 165 if (!isatty(STDIN_FILENO)) 166 verbose = 1; 167 break; 168 169 case 'p': 170 /* 171 * Essentially the same as makeoptions PROF="-pg", 172 * but also changes the path from ../../compile/FOO 173 * to ../../compile/FOO.PROF; i.e., compile a 174 * profiling kernel based on a typical "regular" 175 * kernel. 176 * 177 * Note that if you always want profiling, you 178 * can (and should) use a "makeoptions" line. 179 */ 180 pflag = 1; 181 break; 182 183 case 'b': 184 bflag = optarg; 185 builddir = optarg; 186 break; 187 188 case 's': 189 sflag = optarg; 190 srcdir = optarg; 191 break; 192 193 default: 194 usage(); 195 } 196 } 197 198 argc -= optind; 199 argv += optind; 200 if (argc > 1 || (eflag && argv[0] == NULL)) 201 usage(); 202 if (bflag) { 203 startdir = getcwd(dirbuffer, sizeof dirbuffer); 204 if (startdir == NULL) 205 warn("Use of -b and can't getcwd, no make config"); 206 } else { 207 startdir = "../../conf"; 208 } 209 210 if (eflag) { 211 #ifdef MAKE_BOOTSTRAP 212 errx(1, "UKC not available in this binary"); 213 #else 214 return (ukc(argv[0], outfile, uflag, fflag)); 215 #endif 216 } 217 218 conffile = (argc == 1) ? argv[0] : "CONFIG"; 219 if (firstfile(conffile)) 220 err(2, "cannot read %s", conffile); 221 222 /* 223 * Init variables. 224 */ 225 minmaxusers = 1; 226 maxmaxusers = 10000; 227 initintern(); 228 initfiles(); 229 initsem(); 230 devbasetab = ht_new(); 231 devatab = ht_new(); 232 selecttab = ht_new(); 233 needcnttab = ht_new(); 234 opttab = ht_new(); 235 mkopttab = ht_new(); 236 defopttab = ht_new(); 237 nextopt = &options; 238 nextmkopt = &mkoptions; 239 nextdefopt = &defoptions; 240 241 /* 242 * Handle profiling (must do this before we try to create any 243 * files). 244 */ 245 last_component = strrchr(conffile, '/'); 246 last_component = (last_component) ? last_component + 1 : conffile; 247 if (pflag) { 248 if (asprintf(&p, "../compile/%s.PROF", last_component) == -1) 249 err(1, NULL); 250 (void)addmkoption(intern("PROF"), "-pg"); 251 (void)addoption(intern("GPROF"), NULL); 252 } else { 253 if (asprintf(&p, "../compile/%s", last_component) == -1) 254 err(1, NULL); 255 } 256 defbuilddir = (argc == 0) ? "." : p; 257 258 /* 259 * Parse config file (including machine definitions). 260 */ 261 if (yyparse()) 262 stop(); 263 264 /* 265 * Fix (as in `set firmly in place') files. 266 */ 267 if (fixfiles()) 268 stop(); 269 270 /* 271 * Fix objects and libraries. 272 */ 273 if (fixobjects()) 274 stop(); 275 276 /* 277 * Perform cross-checking. 278 */ 279 if (maxusers == 0) { 280 if (defmaxusers) { 281 (void)printf("maxusers not specified; %d assumed\n", 282 defmaxusers); 283 maxusers = defmaxusers; 284 } else { 285 warnx("need \"maxusers\" line"); 286 errors++; 287 } 288 } 289 if (crosscheck() || errors) 290 stop(); 291 292 /* 293 * Squeeze things down and finish cross-checks (STAR checks must 294 * run after packing). 295 */ 296 pack(); 297 if (badstar()) 298 stop(); 299 300 /* 301 * Ready to go. Build all the various files. 302 */ 303 if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() || 304 mkioconf()) 305 stop(); 306 optiondelta(); 307 return (0); 308 } 309 310 static int 311 mksymlink(const char *value, const char *path) 312 { 313 int ret = 0; 314 315 if (remove(path) && errno != ENOENT) { 316 warn("remove(%s)", path); 317 ret = 1; 318 } 319 if (symlink(value, path)) { 320 warn("symlink(%s -> %s)", path, value); 321 ret = 1; 322 } 323 return (ret); 324 } 325 326 327 /* 328 * Make a symlink for "machine" so that "#include <machine/foo.h>" works, 329 * and for the machine's CPU architecture, so that works as well. 330 */ 331 static int 332 mksymlinks(void) 333 { 334 int ret; 335 char *p, buf[PATH_MAX]; 336 const char *q; 337 338 snprintf(buf, sizeof buf, "arch/%s/include", machine); 339 p = sourcepath(buf); 340 ret = mksymlink(p, "machine"); 341 if (machinearch != NULL) { 342 snprintf(buf, sizeof buf, "arch/%s/include", machinearch); 343 p = sourcepath(buf); 344 q = machinearch; 345 } else { 346 p = strdup("machine"); 347 if (!p) 348 errx(1, "out of memory"); 349 q = machine; 350 } 351 ret |= mksymlink(p, q); 352 free(p); 353 354 return (ret); 355 } 356 357 static __dead void 358 stop(void) 359 { 360 (void)fprintf(stderr, "*** Stop.\n"); 361 exit(1); 362 } 363 364 /* 365 * Define a standard option, for which a header file will be generated. 366 */ 367 void 368 defoption(const char *name) 369 { 370 char *p, *low, c; 371 const char *n; 372 373 /* 374 * Convert to lower case. The header file name will be 375 * in lower case, so we store the lower case version in 376 * the hash table to detect option name collisions. The 377 * original string will be stored in the nvlist for use 378 * in the header file. 379 */ 380 low = emalloc(strlen(name) + 1); 381 for (n = name, p = low; (c = *n) != '\0'; n++) 382 *p++ = isupper((unsigned char)c) ? 383 tolower((unsigned char)c) : c; 384 *p = 0; 385 386 n = intern(low); 387 free(low); 388 (void)do_option(defopttab, &nextdefopt, n, name, "defopt"); 389 390 /* 391 * Insert a verbatim copy of the option name, as well, 392 * to speed lookups when creating the Makefile. 393 */ 394 (void)ht_insert(defopttab, name, (void *)name); 395 } 396 397 /* 398 * Remove an option. 399 */ 400 void 401 removeoption(const char *name) 402 { 403 struct nvlist *nv, *nvt; 404 char *p, *low, c; 405 const char *n; 406 407 if ((nv = ht_lookup(opttab, name)) != NULL) { 408 if (options == nv) { 409 options = nv->nv_next; 410 nvfree(nv); 411 } else { 412 nvt = options; 413 while (nvt->nv_next != NULL) { 414 if (nvt->nv_next == nv) { 415 nvt->nv_next = nvt->nv_next->nv_next; 416 nvfree(nv); 417 break; 418 } else 419 nvt = nvt->nv_next; 420 } 421 } 422 } 423 424 (void)ht_remove(opttab, name); 425 426 low = emalloc(strlen(name) + 1); 427 /* make lowercase, then remove from select table */ 428 for (n = name, p = low; (c = *n) != '\0'; n++) 429 *p++ = isupper((unsigned char)c) ? 430 tolower((unsigned char)c) : c; 431 *p = 0; 432 n = intern(low); 433 free(low); 434 (void)ht_remove(selecttab, n); 435 } 436 437 /* 438 * Add an option from "options FOO". Note that this selects things that 439 * are "optional foo". 440 */ 441 void 442 addoption(const char *name, const char *value) 443 { 444 char *p, *low, c; 445 const char *n; 446 447 if (do_option(opttab, &nextopt, name, value, "options")) 448 return; 449 450 low = emalloc(strlen(name) + 1); 451 /* make lowercase, then add to select table */ 452 for (n = name, p = low; (c = *n) != '\0'; n++) 453 *p++ = isupper((unsigned char)c) ? 454 tolower((unsigned char)c) : c; 455 *p = 0; 456 n = intern(low); 457 free(low); 458 (void)ht_insert(selecttab, n, (void *)n); 459 } 460 461 /* 462 * Add a "make" option. 463 */ 464 void 465 addmkoption(const char *name, const char *value) 466 { 467 468 (void)do_option(mkopttab, &nextmkopt, name, value, "mkoptions"); 469 } 470 471 /* 472 * Add a name=value pair to an option list. The value may be NULL. 473 */ 474 static int 475 do_option(struct hashtab *ht, struct nvlist ***nppp, const char *name, 476 const char *value, const char *type) 477 { 478 struct nvlist *nv; 479 480 /* assume it will work */ 481 nv = newnv(name, value, NULL, 0, NULL); 482 if (ht_insert(ht, name, nv) == 0) { 483 **nppp = nv; 484 *nppp = &nv->nv_next; 485 return (0); 486 } 487 488 /* oops, already got that option */ 489 nvfree(nv); 490 if ((nv = ht_lookup(ht, name)) == NULL) 491 panic("do_option"); 492 if (nv->nv_str != NULL) 493 error("already have %s `%s=%s'", type, name, nv->nv_str); 494 else 495 error("already have %s `%s'", type, name); 496 return (1); 497 } 498 499 /* 500 * Return true if there is at least one instance of the given unit 501 * on the given device attachment (or any units, if unit == WILD). 502 */ 503 int 504 deva_has_instances(struct deva *deva, int unit) 505 { 506 struct devi *i; 507 508 if (unit == WILD) 509 return (deva->d_ihead != NULL); 510 for (i = deva->d_ihead; i != NULL; i = i->i_asame) 511 if (unit == i->i_unit) 512 return (1); 513 return (0); 514 } 515 516 /* 517 * Return true if there is at least one instance of the given unit 518 * on the given base (or any units, if unit == WILD). 519 */ 520 int 521 devbase_has_instances(struct devbase *dev, int unit) 522 { 523 struct deva *da; 524 525 for (da = dev->d_ahead; da != NULL; da = da->d_bsame) 526 if (deva_has_instances(da, unit)) 527 return (1); 528 return (0); 529 } 530 531 static int 532 hasparent(struct devi *i) 533 { 534 struct nvlist *nv; 535 int atunit = i->i_atunit; 536 537 /* 538 * We determine whether or not a device has a parent in in one 539 * of two ways: 540 * (1) If a parent device was named in the config file, 541 * i.e. cases (2) and (3) in sem.c:adddev(), then 542 * we search its devbase for a matching unit number. 543 * (2) If the device was attach to an attribute, then we 544 * search all attributes the device can be attached to 545 * for parents (with appropriate unit numbers) that 546 * may be able to attach the device. 547 */ 548 549 /* 550 * Case (1): A parent was named. Either it's configured, or not. 551 */ 552 if (i->i_atdev != NULL) 553 return (devbase_has_instances(i->i_atdev, atunit)); 554 555 /* 556 * Case (2): No parent was named. Look for devs that provide the attr. 557 */ 558 if (i->i_atattr != NULL) 559 for (nv = i->i_atattr->a_refs; nv != NULL; nv = nv->nv_next) 560 if (devbase_has_instances(nv->nv_ptr, atunit)) 561 return (1); 562 return (0); 563 } 564 565 static int 566 cfcrosscheck(struct config *cf, const char *what, struct nvlist *nv) 567 { 568 struct devbase *dev; 569 struct devi *pd; 570 int errs, devminor; 571 572 if (maxpartitions <= 0) 573 panic("cfcrosscheck"); 574 575 for (errs = 0; nv != NULL; nv = nv->nv_next) { 576 if (nv->nv_name == NULL) 577 continue; 578 dev = ht_lookup(devbasetab, nv->nv_name); 579 if (dev == NULL) 580 panic("cfcrosscheck(%s)", nv->nv_name); 581 devminor = minor(nv->nv_int) / maxpartitions; 582 if (devbase_has_instances(dev, devminor)) 583 continue; 584 if (devbase_has_instances(dev, STAR) && 585 devminor >= dev->d_umax) 586 continue; 587 for (pd = allpseudo; pd != NULL; pd = pd->i_next) 588 if (pd->i_base == dev && devminor < dev->d_umax && 589 devminor >= 0) 590 goto loop; 591 (void)fprintf(stderr, 592 "%s:%d: %s says %s on %s, but there's no %s\n", 593 conffile, cf->cf_lineno, 594 cf->cf_name, what, nv->nv_str, nv->nv_str); 595 errs++; 596 loop: 597 ; 598 } 599 return (errs); 600 } 601 602 /* 603 * Cross-check the configuration: make sure that each target device 604 * or attribute (`at foo[0*?]') names at least one real device. Also 605 * see that the root, swap, and dump devices for all configurations 606 * are there. 607 */ 608 int 609 crosscheck(void) 610 { 611 struct devi *i; 612 struct config *cf; 613 int errs; 614 615 errs = 0; 616 for (i = alldevi; i != NULL; i = i->i_next) { 617 if (i->i_at == NULL || hasparent(i)) 618 continue; 619 xerror(conffile, i->i_lineno, 620 "%s at %s is orphaned", i->i_name, i->i_at); 621 (void)fprintf(stderr, " (%s %s declared)\n", 622 i->i_atunit == WILD ? "nothing matching" : "no", 623 i->i_at); 624 errs++; 625 } 626 if (allcf == NULL) { 627 (void)fprintf(stderr, "%s has no configurations!\n", 628 conffile); 629 errs++; 630 } 631 for (cf = allcf; cf != NULL; cf = cf->cf_next) { 632 if (cf->cf_root != NULL) { /* i.e., not swap generic */ 633 errs += cfcrosscheck(cf, "root", cf->cf_root); 634 errs += cfcrosscheck(cf, "swap", cf->cf_swap); 635 errs += cfcrosscheck(cf, "dumps", cf->cf_dump); 636 } 637 } 638 return (errs); 639 } 640 641 /* 642 * Check to see if there is a *'d unit with a needs-count file. 643 */ 644 int 645 badstar(void) 646 { 647 struct devbase *d; 648 struct deva *da; 649 struct devi *i; 650 int errs, n; 651 652 errs = 0; 653 for (d = allbases; d != NULL; d = d->d_next) { 654 for (da = d->d_ahead; da != NULL; da = da->d_bsame) 655 for (i = da->d_ihead; i != NULL; i = i->i_asame) { 656 if (i->i_unit == STAR) 657 goto foundstar; 658 } 659 continue; 660 foundstar: 661 if (ht_lookup(needcnttab, d->d_name)) { 662 warnx("%s's cannot be *'d until its driver is fixed", 663 d->d_name); 664 errs++; 665 continue; 666 } 667 for (n = 0; i != NULL; i = i->i_alias) 668 if (!i->i_collapsed) 669 n++; 670 if (n < 1) 671 panic("badstar() n<1"); 672 } 673 return (errs); 674 } 675 676 /* 677 * Verify/create builddir if necessary, change to it, and verify srcdir. 678 * This will be called when we see the first include. 679 */ 680 void 681 setupdirs(void) 682 { 683 struct stat st; 684 FILE *fp; 685 686 /* srcdir must be specified if builddir is not specified or if 687 * no configuration filename was specified. */ 688 if ((builddir || strcmp(defbuilddir, ".") == 0) && !srcdir) { 689 error("source directory must be specified"); 690 exit(1); 691 } 692 693 if (srcdir == NULL) 694 srcdir = "../../../.."; 695 if (builddir == NULL) 696 builddir = defbuilddir; 697 698 if (stat(builddir, &st) != 0) { 699 if (mkdir(builddir, 0777)) 700 err(2, "cannot create %s", builddir); 701 } else if (!S_ISDIR(st.st_mode)) 702 errc(2, ENOTDIR, "%s", builddir); 703 if (chdir(builddir) != 0) 704 errx(2, "cannot change to %s", builddir); 705 if (stat(srcdir, &st) != 0 || !S_ISDIR(st.st_mode)) 706 errc(2, ENOTDIR, "%s", srcdir); 707 708 if (bflag) { 709 if (pledge("stdio rpath wpath cpath flock", NULL) == -1) 710 err(1, "pledge"); 711 return; 712 } 713 714 if (stat("obj", &st) == 0) 715 goto reconfig; 716 717 fp = fopen("Makefile", "w"); 718 if (!fp) 719 errx(2, "cannot create Makefile"); 720 if (fprintf(fp, ".include \"../Makefile.inc\"\n") < 0 || 721 fclose(fp) == EOF) 722 errx(2, "cannot write Makefile"); 723 724 reconfig: 725 if (system("make obj") != 0) 726 exit(2); 727 if (system("make config") != 0) 728 exit(2); 729 exit(0); 730 } 731 732 struct opt { 733 const char *name; 734 const char *val; 735 }; 736 737 int 738 optcmp(const void *v1, const void *v2) 739 { 740 const struct opt *sp1 = v1, *sp2 = v2; 741 int r; 742 743 r = strcmp(sp1->name, sp2->name); 744 if (r == 0) { 745 if (!sp1->val && !sp2->val) 746 r = 0; 747 else if (sp1->val && !sp2->val) 748 r = -1; 749 else if (sp2->val && !sp1->val) 750 r = 1; 751 else r = strcmp(sp1->val, sp2->val); 752 } 753 return (r); 754 } 755 756 void 757 optiondelta(void) 758 { 759 struct nvlist *nv; 760 char nbuf[BUFSIZ], obuf[BUFSIZ]; /* XXX size */ 761 int nnewopts, ret = 0, i; 762 struct opt *newopts; 763 FILE *fp; 764 765 for (nnewopts = 0, nv = options; nv != NULL; nv = nv->nv_next) 766 nnewopts++; 767 newopts = ereallocarray(NULL, nnewopts, sizeof(struct opt)); 768 if (newopts == NULL) 769 ret = 0; 770 for (i = 0, nv = options; nv != NULL; nv = nv->nv_next, i++) { 771 newopts[i].name = nv->nv_name; 772 newopts[i].val = nv->nv_str; 773 } 774 qsort(newopts, nnewopts, sizeof (struct opt), optcmp); 775 776 /* compare options against previous config */ 777 if ((fp = fopen("options", "r"))) { 778 for (i = 0; !feof(fp) && i < nnewopts && ret == 0; i++) { 779 if (newopts[i].val) 780 snprintf(nbuf, sizeof nbuf, "%s=%s\n", 781 newopts[i].name, newopts[i].val); 782 else 783 snprintf(nbuf, sizeof nbuf, "%s\n", 784 newopts[i].name); 785 if (fgets(obuf, sizeof obuf, fp) == NULL || 786 strcmp(nbuf, obuf)) 787 ret = 1; 788 } 789 fclose(fp); 790 fp = NULL; 791 } else if (access("options", F_OK) == 0) 792 ret = 1; 793 794 /* replace with the new list of options */ 795 if ((fp = fopen("options", "w+"))) { 796 rewind(fp); 797 for (i = 0; i < nnewopts; i++) { 798 if (newopts[i].val) 799 fprintf(fp, "%s=%s\n", newopts[i].name, 800 newopts[i].val); 801 else 802 fprintf(fp, "%s\n", newopts[i].name); 803 } 804 fclose(fp); 805 } 806 free(newopts); 807 if (ret == 0) 808 return; 809 (void)printf("Kernel options have changed -- you must run \"make clean\"\n"); 810 } 811