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