xref: /openbsd-src/usr.sbin/config/main.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
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