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