xref: /openbsd-src/usr.sbin/config/main.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: main.c,v 1.33 2003/09/26 17:01:14 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 #ifndef lint
45 static char copyright[] =
46 "@(#) Copyright (c) 1992, 1993\n\
47 	The Regents of the University of California.  All rights reserved.\n";
48 #endif /* not lint */
49 
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <sys/param.h>
53 #include <ctype.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include "config.h"
60 
61 int	firstfile(const char *);
62 int	yyparse(void);
63 
64 extern char *optarg;
65 extern int optind;
66 
67 static struct hashtab *mkopttab;
68 static struct nvlist **nextopt;
69 static struct nvlist **nextdefopt;
70 static struct nvlist **nextmkopt;
71 
72 static __dead void stop(void);
73 static int do_option(struct hashtab *, struct nvlist ***,
74     const char *, const char *, const char *);
75 static int crosscheck(void);
76 static int badstar(void);
77 static int mksymlinks(void);
78 static int hasparent(struct devi *);
79 static int cfcrosscheck(struct config *, const char *, struct nvlist *);
80 static void optiondelta(void);
81 
82 int	madedir = 0;
83 
84 void
85 usage(void)
86 {
87 	extern char *__progname;
88 
89 	fprintf(stderr, "usage: %s [-p] [-s srcdir] [-b builddir] [sysname]\n",
90 	    __progname);
91 	fprintf(stderr, "       %s -e [-u] [-f | -o newkernel] kernel\n", __progname);
92 	exit(1);
93 }
94 
95 int
96 main(int argc, char *argv[])
97 {
98 	char *p;
99 	const char *last_component;
100 	char *outfile = NULL;
101 	int pflag, ch, eflag, uflag, fflag;
102 
103 	pflag = eflag = uflag = fflag = 0;
104 	while ((ch = getopt(argc, argv, "egpfb:s:o:u")) != -1) {
105 		switch (ch) {
106 
107 		case 'o':
108 			outfile = optarg;
109 			break;
110 		case 'u':
111 			uflag = 1;
112 			break;
113 		case 'f':
114 			fflag = 1;
115 			break;
116 
117 		case 'e':
118 			eflag = 1;
119 			break;
120 
121 		case 'g':
122 			/*
123 			 * In addition to DEBUG, you probably wanted to
124 			 * set "options KGDB" and maybe others.  We could
125 			 * do that for you, but you really should just
126 			 * put them in the config file.
127 			 */
128 			(void)fputs(
129 			    "-g is obsolete (use makeoptions DEBUG=\"-g\")\n",
130 			    stderr);
131 			usage();
132 
133 		case 'p':
134 			/*
135 			 * Essentially the same as makeoptions PROF="-pg",
136 			 * but also changes the path from ../../compile/FOO
137 			 * to ../../compile/FOO.PROF; i.e., compile a
138 			 * profiling kernel based on a typical "regular"
139 			 * kernel.
140 			 *
141 			 * Note that if you always want profiling, you
142 			 * can (and should) use a "makeoptions" line.
143 			 */
144 			pflag = 1;
145 			break;
146 
147 		case 'b':
148 			builddir = optarg;
149 			break;
150 
151 		case 's':
152 			srcdir = optarg;
153 			break;
154 
155 		case '?':
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 	const char *n;
327 	char *p, c;
328 	char low[500];
329 
330 	/*
331 	 * Convert to lower case.  The header file name will be
332 	 * in lower case, so we store the lower case version in
333 	 * the hash table to detect option name collisions.  The
334 	 * original string will be stored in the nvlist for use
335 	 * in the header file.
336 	 */
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 	(void)do_option(defopttab, &nextdefopt, n, name, "defopt");
343 
344 	/*
345 	 * Insert a verbatim copy of the option name, as well,
346 	 * to speed lookups when creating the Makefile.
347 	 */
348 	(void)ht_insert(defopttab, name, (void *)name);
349 }
350 
351 /*
352  * Remove an option.
353  */
354 void
355 removeoption(const char *name)
356 {
357 	struct nvlist *nv, *nvt;
358 	const char *n;
359 	char *p, c;
360 	char low[500];
361 
362 	if ((nv = ht_lookup(opttab, name)) != NULL) {
363 		if (options == nv)
364 		{
365 			options = nv->nv_next;
366 			nvfree(nv);
367 		} else {
368 			nvt = options;
369 			while (nvt->nv_next != NULL) {
370 				if (nvt->nv_next == nv) {
371 					nvt->nv_next = nvt->nv_next->nv_next;
372 					nvfree(nv);
373 					break;
374 				}
375 				else
376 					nvt = nvt->nv_next;
377 			}
378 		}
379 	}
380 
381 	(void)ht_remove(opttab, name);
382 
383 	/* make lowercase, then add to select table */
384 	for (n = name, p = low; (c = *n) != '\0'; n++)
385 		*p++ = isupper(c) ? tolower(c) : c;
386 	*p = 0;
387 	n = intern(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 	const char *n;
399 	char *p, c;
400 	char low[500];
401 
402 	if (do_option(opttab, &nextopt, name, value, "options"))
403 		return;
404 
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 	(void)ht_insert(selecttab, n, (void *)n);
411 }
412 
413 /*
414  * Add a "make" option.
415  */
416 void
417 addmkoption(const char *name, const char *value)
418 {
419 
420 	(void)do_option(mkopttab, &nextmkopt, name, value, "mkoptions");
421 }
422 
423 /*
424  * Add a name=value pair to an option list.  The value may be NULL.
425  */
426 static int
427 do_option(struct hashtab *ht, struct nvlist ***nppp, const char *name,
428     const char *value, const char *type)
429 {
430 	struct nvlist *nv;
431 
432 	/* assume it will work */
433 	nv = newnv(name, value, NULL, 0, NULL);
434 	if (ht_insert(ht, name, nv) == 0) {
435 		**nppp = nv;
436 		*nppp = &nv->nv_next;
437 		return (0);
438 	}
439 
440 	/* oops, already got that option */
441 	nvfree(nv);
442 	if ((nv = ht_lookup(ht, name)) == NULL)
443 		panic("do_option");
444 	if (nv->nv_str != NULL)
445 		error("already have %s `%s=%s'", type, name, nv->nv_str);
446 	else
447 		error("already have %s `%s'", type, name);
448 	return (1);
449 }
450 
451 /*
452  * Return true if there is at least one instance of the given unit
453  * on the given device attachment (or any units, if unit == WILD).
454  */
455 int
456 deva_has_instances(struct deva *deva, int unit)
457 {
458 	struct devi *i;
459 
460 	if (unit == WILD)
461 		return (deva->d_ihead != NULL);
462 	for (i = deva->d_ihead; i != NULL; i = i->i_asame)
463 		if (unit == i->i_unit)
464 			return (1);
465 	return (0);
466 }
467 
468 /*
469  * Return true if there is at least one instance of the given unit
470  * on the given base (or any units, if unit == WILD).
471  */
472 int
473 devbase_has_instances(struct devbase *dev, int unit)
474 {
475 	struct deva *da;
476 
477 	for (da = dev->d_ahead; da != NULL; da = da->d_bsame)
478 		if (deva_has_instances(da, unit))
479 			return (1);
480 	return (0);
481 }
482 
483 static int
484 hasparent(struct devi *i)
485 {
486 	struct nvlist *nv;
487 	int atunit = i->i_atunit;
488 
489 	/*
490 	 * We determine whether or not a device has a parent in in one
491 	 * of two ways:
492 	 *	(1) If a parent device was named in the config file,
493 	 *	    i.e. cases (2) and (3) in sem.c:adddev(), then
494 	 *	    we search its devbase for a matching unit number.
495 	 *	(2) If the device was attach to an attribute, then we
496 	 *	    search all attributes the device can be attached to
497 	 *	    for parents (with appropriate unit numbers) that
498 	 *	    may be able to attach the device.
499 	 */
500 
501 	/*
502 	 * Case (1): A parent was named.  Either it's configured, or not.
503 	 */
504 	if (i->i_atdev != NULL)
505 		return (devbase_has_instances(i->i_atdev, atunit));
506 
507 	/*
508 	 * Case (2): No parent was named.  Look for devs that provide the attr.
509 	 */
510 	if (i->i_atattr != NULL)
511 		for (nv = i->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
512 			if (devbase_has_instances(nv->nv_ptr, atunit))
513 				return (1);
514 	return (0);
515 }
516 
517 static int
518 cfcrosscheck(struct config *cf, const char *what, struct nvlist *nv)
519 {
520 	struct devbase *dev;
521 	struct devi *pd;
522 	int errs, devminor;
523 
524 	if (maxpartitions <= 0)
525 		panic("cfcrosscheck");
526 
527 	for (errs = 0; nv != NULL; nv = nv->nv_next) {
528 		if (nv->nv_name == NULL)
529 			continue;
530 		dev = ht_lookup(devbasetab, nv->nv_name);
531 		if (dev == NULL)
532 			panic("cfcrosscheck(%s)", nv->nv_name);
533 		devminor = minor(nv->nv_int) / maxpartitions;
534 		if (devbase_has_instances(dev, devminor))
535 			continue;
536 		if (devbase_has_instances(dev, STAR) &&
537 		    devminor >= dev->d_umax)
538 			continue;
539 		for (pd = allpseudo; pd != NULL; pd = pd->i_next)
540 			if (pd->i_base == dev && devminor < dev->d_umax &&
541 			    devminor >= 0)
542 				goto loop;
543 		(void)fprintf(stderr,
544 		    "%s:%d: %s says %s on %s, but there's no %s\n",
545 		    conffile, cf->cf_lineno,
546 		    cf->cf_name, what, nv->nv_str, nv->nv_str);
547 		errs++;
548 loop:
549 		;
550 	}
551 	return (errs);
552 }
553 
554 /*
555  * Cross-check the configuration: make sure that each target device
556  * or attribute (`at foo[0*?]') names at least one real device.  Also
557  * see that the root, swap, and dump devices for all configurations
558  * are there.
559  */
560 int
561 crosscheck(void)
562 {
563 	struct devi *i;
564 	struct config *cf;
565 	int errs;
566 
567 	errs = 0;
568 	for (i = alldevi; i != NULL; i = i->i_next) {
569 		if (i->i_at == NULL || hasparent(i))
570 			continue;
571 		xerror(conffile, i->i_lineno,
572 		    "%s at %s is orphaned", i->i_name, i->i_at);
573 		(void)fprintf(stderr, " (%s %s declared)\n",
574 		    i->i_atunit == WILD ? "nothing matching" : "no",
575 		    i->i_at);
576 		errs++;
577 	}
578 	if (allcf == NULL) {
579 		(void)fprintf(stderr, "%s has no configurations!\n",
580 		    conffile);
581 		errs++;
582 	}
583 	for (cf = allcf; cf != NULL; cf = cf->cf_next) {
584 		if (cf->cf_root != NULL) {	/* i.e., not swap generic */
585 			errs += cfcrosscheck(cf, "root", cf->cf_root);
586 			errs += cfcrosscheck(cf, "swap", cf->cf_swap);
587 			errs += cfcrosscheck(cf, "dumps", cf->cf_dump);
588 		}
589 	}
590 	return (errs);
591 }
592 
593 /*
594  * Check to see if there is a *'d unit with a needs-count file.
595  */
596 int
597 badstar(void)
598 {
599 	struct devbase *d;
600 	struct deva *da;
601 	struct devi *i;
602 	int errs, n;
603 
604 	errs = 0;
605 	for (d = allbases; d != NULL; d = d->d_next) {
606 		for (da = d->d_ahead; da != NULL; da = da->d_bsame)
607 			for (i = da->d_ihead; i != NULL; i = i->i_asame) {
608 				if (i->i_unit == STAR)
609 					goto foundstar;
610 			}
611 		continue;
612 	foundstar:
613 		if (ht_lookup(needcnttab, d->d_name)) {
614 			(void)fprintf(stderr,
615 		    "config: %s's cannot be *'d until its driver is fixed\n",
616 			    d->d_name);
617 			errs++;
618 			continue;
619 		}
620 		for (n = 0; i != NULL; i = i->i_alias)
621 			if (!i->i_collapsed)
622 				n++;
623 		if (n < 1)
624 			panic("badstar() n<1");
625 	}
626 	return (errs);
627 }
628 
629 /*
630  * Verify/create builddir if necessary, change to it, and verify srcdir.
631  * This will be called when we see the first include.
632  */
633 void
634 setupdirs(void)
635 {
636 	struct stat st;
637 
638 	/* srcdir must be specified if builddir is not specified or if
639 	 * no configuration filename was specified. */
640 	if ((builddir || strcmp(defbuilddir, ".") == 0) && !srcdir) {
641 		error("source directory must be specified");
642 		exit(1);
643 	}
644 
645 	if (srcdir == NULL)
646 		srcdir = "../../../..";
647 	if (builddir == NULL)
648 		builddir = defbuilddir;
649 
650 	if (stat(builddir, &st) != 0) {
651 		if (mkdir(builddir, 0777)) {
652 			(void)fprintf(stderr, "config: cannot create %s: %s\n",
653 			    builddir, strerror(errno));
654 			exit(2);
655 		}
656 		madedir = 1;
657 	} else if (!S_ISDIR(st.st_mode)) {
658 		(void)fprintf(stderr, "config: %s is not a directory\n",
659 			      builddir);
660 		exit(2);
661 	}
662 	if (chdir(builddir) != 0) {
663 		(void)fprintf(stderr, "config: cannot change to %s\n",
664 			      builddir);
665 		exit(2);
666 	}
667 	if (stat(srcdir, &st) != 0 || !S_ISDIR(st.st_mode)) {
668 		(void)fprintf(stderr, "config: %s is not a directory\n",
669 			      srcdir);
670 		exit(2);
671 	}
672 }
673 
674 struct opt {
675 	const char *name;
676 	const char *val;
677 };
678 
679 int
680 optcmp(const void *v1, const void *v2)
681 {
682 	const struct opt *sp1 = v1, *sp2 = v2;
683 	int r;
684 
685 	r = strcmp(sp1->name, sp2->name);
686 	if (r == 0) {
687 		if (!sp1->val && !sp2->val)
688 			r = 0;
689 		else if (sp1->val && !sp2->val)
690 			r = -1;
691 		else if (sp2->val && !sp1->val)
692 			r = 1;
693 		else r = strcmp(sp1->val, sp2->val);
694 	}
695 	return (r);
696 }
697 
698 void
699 optiondelta(void)
700 {
701 	struct nvlist *nv;
702 	char nbuf[BUFSIZ], obuf[BUFSIZ];	/* XXX size */
703 	int nnewopts, ret = 0, i;
704 	struct opt *newopts;
705 	FILE *fp;
706 
707 	for (nnewopts = 0, nv = options; nv != NULL; nv = nv->nv_next)
708 		nnewopts++;
709 	newopts = (struct opt *)emalloc(nnewopts * sizeof(struct opt));
710 	if (newopts == NULL)
711 		ret = 0;
712 	for (i = 0, nv = options; nv != NULL; nv = nv->nv_next, i++) {
713 		newopts[i].name = nv->nv_name;
714 		newopts[i].val = nv->nv_str;
715 	}
716 	qsort(newopts, nnewopts, sizeof (struct opt), optcmp);
717 
718 	/* compare options against previous config */
719 	if ((fp = fopen("options", "r"))) {
720 		for (i = 0; !feof(fp) && i < nnewopts && ret == 0; i++) {
721 			if (newopts[i].val)
722 				snprintf(nbuf, sizeof nbuf, "%s=%s\n",
723 				    newopts[i].name, newopts[i].val);
724 			else
725 				snprintf(nbuf, sizeof nbuf, "%s\n",
726 				    newopts[i].name);
727 			if (fgets(obuf, sizeof obuf, fp) == NULL ||
728 			    strcmp(nbuf, obuf))
729 				ret = 1;
730 		}
731 		fclose(fp);
732 		fp = NULL;
733 	} else
734 		ret = 1;
735 
736 	/* replace with the new list of options */
737 	if ((fp = fopen("options", "w+"))) {
738 		rewind(fp);
739 		for (i = 0; i < nnewopts; i++) {
740 			if (newopts[i].val)
741 				fprintf(fp, "%s=%s\n", newopts[i].name,
742 				    newopts[i].val);
743 			else
744 				fprintf(fp, "%s\n", newopts[i].name);
745 		}
746 		fclose(fp);
747 	}
748 	free(newopts);
749 	if (ret == 0 || madedir == 1)
750 		return;
751 	(void)printf("Kernel options have changed -- you must run \"make clean\"\n");
752 }
753