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