1 /* $OpenBSD: main.c,v 1.63 2021/11/20 03:13:37 jcs 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
usage(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 = NULL;
135 FILE *cmdfp = NULL;
136
137 int
main(int argc,char * argv[])138 main(int argc, char *argv[])
139 {
140 char *p;
141 char *outfile = NULL;
142 int ch, eflag, uflag, fflag;
143 char dirbuffer[PATH_MAX];
144
145 if (pledge("stdio rpath wpath cpath flock proc exec", NULL) == -1)
146 err(1, "pledge");
147
148 pflag = eflag = uflag = fflag = 0;
149 while ((ch = getopt(argc, argv, "c:epfb:s:o:u")) != -1) {
150 switch (ch) {
151 case 'c':
152 cmdfile = optarg;
153 break;
154 case 'o':
155 outfile = optarg;
156 break;
157 case 'u':
158 uflag = 1;
159 break;
160 case 'f':
161 fflag = 1;
162 break;
163
164 case 'e':
165 eflag = 1;
166 if (!isatty(STDIN_FILENO))
167 verbose = 1;
168 break;
169
170 case 'p':
171 /*
172 * Essentially the same as makeoptions PROF="-pg",
173 * but also changes the path from ../../compile/FOO
174 * to ../../compile/FOO.PROF; i.e., compile a
175 * profiling kernel based on a typical "regular"
176 * kernel.
177 *
178 * Note that if you always want profiling, you
179 * can (and should) use a "makeoptions" line.
180 */
181 pflag = 1;
182 break;
183
184 case 'b':
185 bflag = optarg;
186 builddir = optarg;
187 break;
188
189 case 's':
190 sflag = optarg;
191 srcdir = optarg;
192 break;
193
194 default:
195 usage();
196 }
197 }
198
199 argc -= optind;
200 argv += optind;
201 if (argc > 1 || (eflag && argv[0] == NULL))
202 usage();
203 if (bflag) {
204 startdir = getcwd(dirbuffer, sizeof dirbuffer);
205 if (startdir == NULL)
206 warn("Use of -b and can't getcwd, no make config");
207 } else {
208 startdir = "../../conf";
209 }
210
211 if (eflag) {
212 #ifdef MAKE_BOOTSTRAP
213 errx(1, "UKC not available in this binary");
214 #else
215 if (cmdfile != NULL) {
216 cmdfp = fopen(cmdfile, "r");
217 if (cmdfp == NULL)
218 err(1, "open %s", cmdfile);
219 }
220 return (ukc(argv[0], outfile, uflag, fflag));
221 #endif
222 }
223
224 conffile = (argc == 1) ? argv[0] : "CONFIG";
225 if (firstfile(conffile))
226 err(2, "cannot read %s", conffile);
227
228 /*
229 * Init variables.
230 */
231 minmaxusers = 1;
232 maxmaxusers = 10000;
233 initintern();
234 initfiles();
235 initsem();
236 devbasetab = ht_new();
237 devatab = ht_new();
238 selecttab = ht_new();
239 needcnttab = ht_new();
240 opttab = ht_new();
241 mkopttab = ht_new();
242 defopttab = ht_new();
243 nextopt = &options;
244 nextmkopt = &mkoptions;
245 nextdefopt = &defoptions;
246
247 /*
248 * Handle profiling (must do this before we try to create any
249 * files).
250 */
251 last_component = strrchr(conffile, '/');
252 last_component = (last_component) ? last_component + 1 : conffile;
253 if (pflag) {
254 if (asprintf(&p, "../compile/%s.PROF", last_component) == -1)
255 err(1, NULL);
256 (void)addmkoption(intern("PROF"), "-pg");
257 (void)addoption(intern("GPROF"), NULL);
258 } else {
259 if (asprintf(&p, "../compile/%s", last_component) == -1)
260 err(1, NULL);
261 }
262 defbuilddir = (argc == 0) ? "." : p;
263
264 /*
265 * Parse config file (including machine definitions).
266 */
267 if (yyparse())
268 stop();
269
270 /*
271 * Fix (as in `set firmly in place') files.
272 */
273 if (fixfiles())
274 stop();
275
276 /*
277 * Fix objects and libraries.
278 */
279 if (fixobjects())
280 stop();
281
282 /*
283 * Perform cross-checking.
284 */
285 if (maxusers == 0) {
286 if (defmaxusers) {
287 (void)printf("maxusers not specified; %d assumed\n",
288 defmaxusers);
289 maxusers = defmaxusers;
290 } else {
291 warnx("need \"maxusers\" line");
292 errors++;
293 }
294 }
295 if (crosscheck() || errors)
296 stop();
297
298 /*
299 * Squeeze things down and finish cross-checks (STAR checks must
300 * run after packing).
301 */
302 pack();
303 if (badstar())
304 stop();
305
306 /*
307 * Ready to go. Build all the various files.
308 */
309 if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() ||
310 mkioconf())
311 stop();
312 optiondelta();
313 return (0);
314 }
315
316 static int
mksymlink(const char * value,const char * path)317 mksymlink(const char *value, const char *path)
318 {
319 int ret = 0;
320
321 if (remove(path) && errno != ENOENT) {
322 warn("remove(%s)", path);
323 ret = 1;
324 }
325 if (symlink(value, path)) {
326 warn("symlink(%s -> %s)", path, value);
327 ret = 1;
328 }
329 return (ret);
330 }
331
332
333 /*
334 * Make a symlink for "machine" so that "#include <machine/foo.h>" works,
335 * and for the machine's CPU architecture, so that works as well.
336 */
337 static int
mksymlinks(void)338 mksymlinks(void)
339 {
340 int ret;
341 char *p, buf[PATH_MAX];
342 const char *q;
343
344 snprintf(buf, sizeof buf, "arch/%s/include", machine);
345 p = sourcepath(buf);
346 ret = mksymlink(p, "machine");
347 if (machinearch != NULL) {
348 snprintf(buf, sizeof buf, "arch/%s/include", machinearch);
349 p = sourcepath(buf);
350 q = machinearch;
351 } else {
352 p = strdup("machine");
353 if (!p)
354 errx(1, "out of memory");
355 q = machine;
356 }
357 ret |= mksymlink(p, q);
358 free(p);
359
360 return (ret);
361 }
362
363 static __dead void
stop(void)364 stop(void)
365 {
366 (void)fprintf(stderr, "*** Stop.\n");
367 exit(1);
368 }
369
370 /*
371 * Define a standard option, for which a header file will be generated.
372 */
373 void
defoption(const char * name)374 defoption(const char *name)
375 {
376 char *p, *low, c;
377 const char *n;
378
379 /*
380 * Convert to lower case. The header file name will be
381 * in lower case, so we store the lower case version in
382 * the hash table to detect option name collisions. The
383 * original string will be stored in the nvlist for use
384 * in the header file.
385 */
386 low = emalloc(strlen(name) + 1);
387 for (n = name, p = low; (c = *n) != '\0'; n++)
388 *p++ = isupper((unsigned char)c) ?
389 tolower((unsigned char)c) : c;
390 *p = 0;
391
392 n = intern(low);
393 free(low);
394 (void)do_option(defopttab, &nextdefopt, n, name, "defopt");
395
396 /*
397 * Insert a verbatim copy of the option name, as well,
398 * to speed lookups when creating the Makefile.
399 */
400 (void)ht_insert(defopttab, name, (void *)name);
401 }
402
403 /*
404 * Remove an option.
405 */
406 void
removeoption(const char * name)407 removeoption(const char *name)
408 {
409 struct nvlist *nv, *nvt;
410 char *p, *low, c;
411 const char *n;
412
413 if ((nv = ht_lookup(opttab, name)) != NULL) {
414 if (options == nv) {
415 options = nv->nv_next;
416 nvfree(nv);
417 } else {
418 nvt = options;
419 while (nvt->nv_next != NULL) {
420 if (nvt->nv_next == nv) {
421 nvt->nv_next = nvt->nv_next->nv_next;
422 nvfree(nv);
423 break;
424 } else
425 nvt = nvt->nv_next;
426 }
427 }
428 }
429
430 (void)ht_remove(opttab, name);
431
432 low = emalloc(strlen(name) + 1);
433 /* make lowercase, then remove from select table */
434 for (n = name, p = low; (c = *n) != '\0'; n++)
435 *p++ = isupper((unsigned char)c) ?
436 tolower((unsigned char)c) : c;
437 *p = 0;
438 n = intern(low);
439 free(low);
440 (void)ht_remove(selecttab, n);
441 }
442
443 /*
444 * Add an option from "options FOO". Note that this selects things that
445 * are "optional foo".
446 */
447 void
addoption(const char * name,const char * value)448 addoption(const char *name, const char *value)
449 {
450 char *p, *low, c;
451 const char *n;
452
453 if (do_option(opttab, &nextopt, name, value, "options"))
454 return;
455
456 low = emalloc(strlen(name) + 1);
457 /* make lowercase, then add to select table */
458 for (n = name, p = low; (c = *n) != '\0'; n++)
459 *p++ = isupper((unsigned char)c) ?
460 tolower((unsigned char)c) : c;
461 *p = 0;
462 n = intern(low);
463 free(low);
464 (void)ht_insert(selecttab, n, (void *)n);
465 }
466
467 /*
468 * Add a "make" option.
469 */
470 void
addmkoption(const char * name,const char * value)471 addmkoption(const char *name, const char *value)
472 {
473
474 (void)do_option(mkopttab, &nextmkopt, name, value, "mkoptions");
475 }
476
477 /*
478 * Add a name=value pair to an option list. The value may be NULL.
479 */
480 static int
do_option(struct hashtab * ht,struct nvlist *** nppp,const char * name,const char * value,const char * type)481 do_option(struct hashtab *ht, struct nvlist ***nppp, const char *name,
482 const char *value, const char *type)
483 {
484 struct nvlist *nv;
485
486 /* assume it will work */
487 nv = newnv(name, value, NULL, 0, NULL);
488 if (ht_insert(ht, name, nv) == 0) {
489 **nppp = nv;
490 *nppp = &nv->nv_next;
491 return (0);
492 }
493
494 /* oops, already got that option */
495 nvfree(nv);
496 if ((nv = ht_lookup(ht, name)) == NULL)
497 panic("do_option");
498 if (nv->nv_str != NULL)
499 error("already have %s `%s=%s'", type, name, nv->nv_str);
500 else
501 error("already have %s `%s'", type, name);
502 return (1);
503 }
504
505 /*
506 * Return true if there is at least one instance of the given unit
507 * on the given device attachment (or any units, if unit == WILD).
508 */
509 int
deva_has_instances(struct deva * deva,int unit)510 deva_has_instances(struct deva *deva, int unit)
511 {
512 struct devi *i;
513
514 if (unit == WILD)
515 return (deva->d_ihead != NULL);
516 for (i = deva->d_ihead; i != NULL; i = i->i_asame)
517 if (unit == i->i_unit)
518 return (1);
519 return (0);
520 }
521
522 /*
523 * Return true if there is at least one instance of the given unit
524 * on the given base (or any units, if unit == WILD).
525 */
526 int
devbase_has_instances(struct devbase * dev,int unit)527 devbase_has_instances(struct devbase *dev, int unit)
528 {
529 struct deva *da;
530
531 for (da = dev->d_ahead; da != NULL; da = da->d_bsame)
532 if (deva_has_instances(da, unit))
533 return (1);
534 return (0);
535 }
536
537 static int
hasparent(struct devi * i)538 hasparent(struct devi *i)
539 {
540 struct nvlist *nv;
541 int atunit = i->i_atunit;
542
543 /*
544 * We determine whether or not a device has a parent in in one
545 * of two ways:
546 * (1) If a parent device was named in the config file,
547 * i.e. cases (2) and (3) in sem.c:adddev(), then
548 * we search its devbase for a matching unit number.
549 * (2) If the device was attach to an attribute, then we
550 * search all attributes the device can be attached to
551 * for parents (with appropriate unit numbers) that
552 * may be able to attach the device.
553 */
554
555 /*
556 * Case (1): A parent was named. Either it's configured, or not.
557 */
558 if (i->i_atdev != NULL)
559 return (devbase_has_instances(i->i_atdev, atunit));
560
561 /*
562 * Case (2): No parent was named. Look for devs that provide the attr.
563 */
564 if (i->i_atattr != NULL)
565 for (nv = i->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
566 if (devbase_has_instances(nv->nv_ptr, atunit))
567 return (1);
568 return (0);
569 }
570
571 static int
cfcrosscheck(struct config * cf,const char * what,struct nvlist * nv)572 cfcrosscheck(struct config *cf, const char *what, struct nvlist *nv)
573 {
574 struct devbase *dev;
575 struct devi *pd;
576 int errs, devminor;
577
578 if (maxpartitions <= 0)
579 panic("cfcrosscheck");
580
581 for (errs = 0; nv != NULL; nv = nv->nv_next) {
582 if (nv->nv_name == NULL)
583 continue;
584 dev = ht_lookup(devbasetab, nv->nv_name);
585 if (dev == NULL)
586 panic("cfcrosscheck(%s)", nv->nv_name);
587 devminor = minor(nv->nv_int) / maxpartitions;
588 if (devbase_has_instances(dev, devminor))
589 continue;
590 if (devbase_has_instances(dev, STAR) &&
591 devminor >= dev->d_umax)
592 continue;
593 for (pd = allpseudo; pd != NULL; pd = pd->i_next)
594 if (pd->i_base == dev && devminor < dev->d_umax &&
595 devminor >= 0)
596 goto loop;
597 (void)fprintf(stderr,
598 "%s:%d: %s says %s on %s, but there's no %s\n",
599 conffile, cf->cf_lineno,
600 cf->cf_name, what, nv->nv_str, nv->nv_str);
601 errs++;
602 loop:
603 ;
604 }
605 return (errs);
606 }
607
608 /*
609 * Cross-check the configuration: make sure that each target device
610 * or attribute (`at foo[0*?]') names at least one real device. Also
611 * see that the root, swap, and dump devices for all configurations
612 * are there.
613 */
614 int
crosscheck(void)615 crosscheck(void)
616 {
617 struct devi *i;
618 struct config *cf;
619 int errs;
620
621 errs = 0;
622 for (i = alldevi; i != NULL; i = i->i_next) {
623 if (i->i_at == NULL || hasparent(i))
624 continue;
625 xerror(conffile, i->i_lineno,
626 "%s at %s is orphaned", i->i_name, i->i_at);
627 (void)fprintf(stderr, " (%s %s declared)\n",
628 i->i_atunit == WILD ? "nothing matching" : "no",
629 i->i_at);
630 errs++;
631 }
632 if (allcf == NULL) {
633 (void)fprintf(stderr, "%s has no configurations!\n",
634 conffile);
635 errs++;
636 }
637 for (cf = allcf; cf != NULL; cf = cf->cf_next) {
638 if (cf->cf_root != NULL) { /* i.e., not swap generic */
639 errs += cfcrosscheck(cf, "root", cf->cf_root);
640 errs += cfcrosscheck(cf, "swap", cf->cf_swap);
641 errs += cfcrosscheck(cf, "dumps", cf->cf_dump);
642 }
643 }
644 return (errs);
645 }
646
647 /*
648 * Check to see if there is a *'d unit with a needs-count file.
649 */
650 int
badstar(void)651 badstar(void)
652 {
653 struct devbase *d;
654 struct deva *da;
655 struct devi *i;
656 int errs, n;
657
658 errs = 0;
659 for (d = allbases; d != NULL; d = d->d_next) {
660 for (da = d->d_ahead; da != NULL; da = da->d_bsame)
661 for (i = da->d_ihead; i != NULL; i = i->i_asame) {
662 if (i->i_unit == STAR)
663 goto foundstar;
664 }
665 continue;
666 foundstar:
667 if (ht_lookup(needcnttab, d->d_name)) {
668 warnx("%s's cannot be *'d until its driver is fixed",
669 d->d_name);
670 errs++;
671 continue;
672 }
673 for (n = 0; i != NULL; i = i->i_alias)
674 if (!i->i_collapsed)
675 n++;
676 if (n < 1)
677 panic("badstar() n<1");
678 }
679 return (errs);
680 }
681
682 /*
683 * Verify/create builddir if necessary, change to it, and verify srcdir.
684 * This will be called when we see the first include.
685 */
686 void
setupdirs(void)687 setupdirs(void)
688 {
689 struct stat st;
690 FILE *fp;
691
692 /* srcdir must be specified if builddir is not specified or if
693 * no configuration filename was specified. */
694 if ((builddir || strcmp(defbuilddir, ".") == 0) && !srcdir) {
695 error("source directory must be specified");
696 exit(1);
697 }
698
699 if (srcdir == NULL)
700 srcdir = "../../../..";
701 if (builddir == NULL)
702 builddir = defbuilddir;
703
704 if (stat(builddir, &st) != 0) {
705 if (mkdir(builddir, 0777))
706 err(2, "cannot create %s", builddir);
707 } else if (!S_ISDIR(st.st_mode))
708 errc(2, ENOTDIR, "%s", builddir);
709 if (chdir(builddir) != 0)
710 errx(2, "cannot change to %s", builddir);
711 if (stat(srcdir, &st) != 0 || !S_ISDIR(st.st_mode))
712 errc(2, ENOTDIR, "%s", srcdir);
713
714 if (bflag) {
715 if (pledge("stdio rpath wpath cpath flock", NULL) == -1)
716 err(1, "pledge");
717 return;
718 }
719
720 if (stat("obj", &st) == 0)
721 goto reconfig;
722
723 fp = fopen("Makefile", "w");
724 if (!fp)
725 errx(2, "cannot create Makefile");
726 if (fprintf(fp, ".include \"../Makefile.inc\"\n") < 0 ||
727 fclose(fp) == EOF)
728 errx(2, "cannot write Makefile");
729
730 reconfig:
731 if (system("make obj") != 0)
732 exit(2);
733 if (system("make config") != 0)
734 exit(2);
735 exit(0);
736 }
737
738 struct opt {
739 const char *name;
740 const char *val;
741 };
742
743 int
optcmp(const void * v1,const void * v2)744 optcmp(const void *v1, const void *v2)
745 {
746 const struct opt *sp1 = v1, *sp2 = v2;
747 int r;
748
749 r = strcmp(sp1->name, sp2->name);
750 if (r == 0) {
751 if (!sp1->val && !sp2->val)
752 r = 0;
753 else if (sp1->val && !sp2->val)
754 r = -1;
755 else if (sp2->val && !sp1->val)
756 r = 1;
757 else r = strcmp(sp1->val, sp2->val);
758 }
759 return (r);
760 }
761
762 void
optiondelta(void)763 optiondelta(void)
764 {
765 struct nvlist *nv;
766 char nbuf[BUFSIZ], obuf[BUFSIZ]; /* XXX size */
767 int nnewopts, ret = 0, i;
768 struct opt *newopts;
769 FILE *fp;
770
771 for (nnewopts = 0, nv = options; nv != NULL; nv = nv->nv_next)
772 nnewopts++;
773 newopts = ereallocarray(NULL, nnewopts, sizeof(struct opt));
774 if (newopts == NULL)
775 ret = 0;
776 for (i = 0, nv = options; nv != NULL; nv = nv->nv_next, i++) {
777 newopts[i].name = nv->nv_name;
778 newopts[i].val = nv->nv_str;
779 }
780 qsort(newopts, nnewopts, sizeof (struct opt), optcmp);
781
782 /* compare options against previous config */
783 if ((fp = fopen("options", "r"))) {
784 for (i = 0; !feof(fp) && i < nnewopts && ret == 0; i++) {
785 if (newopts[i].val)
786 snprintf(nbuf, sizeof nbuf, "%s=%s\n",
787 newopts[i].name, newopts[i].val);
788 else
789 snprintf(nbuf, sizeof nbuf, "%s\n",
790 newopts[i].name);
791 if (fgets(obuf, sizeof obuf, fp) == NULL ||
792 strcmp(nbuf, obuf))
793 ret = 1;
794 }
795 fclose(fp);
796 fp = NULL;
797 } else if (access("options", F_OK) == 0)
798 ret = 1;
799
800 /* replace with the new list of options */
801 if ((fp = fopen("options", "w+"))) {
802 rewind(fp);
803 for (i = 0; i < nnewopts; i++) {
804 if (newopts[i].val)
805 fprintf(fp, "%s=%s\n", newopts[i].name,
806 newopts[i].val);
807 else
808 fprintf(fp, "%s\n", newopts[i].name);
809 }
810 fclose(fp);
811 }
812 free(newopts);
813 if (ret == 0)
814 return;
815 (void)printf("Kernel options have changed -- you must run \"make clean\"\n");
816 }
817