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