xref: /openbsd-src/usr.sbin/config/main.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: main.c,v 1.42 2011/04/15 02:48: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 #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 	optiondelta();
270 	exit(0);
271 }
272 
273 /*
274  * Make a symlink for "machine" so that "#include <machine/foo.h>" works,
275  * and for the machine's CPU architecture, so that works as well.
276  */
277 static int
278 mksymlinks(void)
279 {
280 	int ret;
281 	char *p, buf[MAXPATHLEN];
282 	const char *q;
283 
284 	snprintf(buf, sizeof buf, "arch/%s/include", machine);
285 	p = sourcepath(buf);
286 	(void)unlink("machine");
287 	ret = symlink(p, "machine");
288 	if (ret)
289 		(void)fprintf(stderr, "config: symlink(machine -> %s): %s\n",
290 		    p, strerror(errno));
291 
292 	if (machinearch != NULL) {
293 		snprintf(buf, sizeof buf, "arch/%s/include", machinearch);
294 		p = sourcepath(buf);
295 		q = machinearch;
296 	} else {
297 		p = strdup("machine");
298 		if (!p)
299 			errx(1, "out of memory");
300 		q = machine;
301 	}
302 	(void)unlink(q);
303 	ret = symlink(p, q);
304 	if (ret)
305 		(void)fprintf(stderr, "config: symlink(%s -> %s): %s\n",
306 		    q, p, strerror(errno));
307 	free(p);
308 
309 	return (ret);
310 }
311 
312 static __dead void
313 stop(void)
314 {
315 	(void)fprintf(stderr, "*** Stop.\n");
316 	exit(1);
317 }
318 
319 /*
320  * Define a standard option, for which a header file will be generated.
321  */
322 void
323 defoption(const char *name)
324 {
325 	char *p, *low, c;
326 	const char *n;
327 
328 	/*
329 	 * Convert to lower case.  The header file name will be
330 	 * in lower case, so we store the lower case version in
331 	 * the hash table to detect option name collisions.  The
332 	 * original string will be stored in the nvlist for use
333 	 * in the header file.
334 	 */
335 	low = emalloc(strlen(name) + 1);
336 	for (n = name, p = low; (c = *n) != '\0'; n++)
337 		*p++ = isupper(c) ? tolower(c) : c;
338 	*p = 0;
339 
340 	n = intern(low);
341 	free(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 	char *p, *low, c;
359 	const char *n;
360 
361 	if ((nv = ht_lookup(opttab, name)) != NULL) {
362 		if (options == nv) {
363 			options = nv->nv_next;
364 			nvfree(nv);
365 		} else {
366 			nvt = options;
367 			while (nvt->nv_next != NULL) {
368 				if (nvt->nv_next == nv) {
369 					nvt->nv_next = nvt->nv_next->nv_next;
370 					nvfree(nv);
371 					break;
372 				} else
373 					nvt = nvt->nv_next;
374 			}
375 		}
376 	}
377 
378 	(void)ht_remove(opttab, name);
379 
380 	low = emalloc(strlen(name) + 1);
381 	/* make lowercase, then remove from select table */
382 	for (n = name, p = low; (c = *n) != '\0'; n++)
383 		*p++ = isupper(c) ? tolower(c) : c;
384 	*p = 0;
385 	n = intern(low);
386 	free(low);
387 	(void)ht_remove(selecttab, n);
388 }
389 
390 /*
391  * Add an option from "options FOO".  Note that this selects things that
392  * are "optional foo".
393  */
394 void
395 addoption(const char *name, const char *value)
396 {
397 	char *p, *low, c;
398 	const char *n;
399 
400 	if (do_option(opttab, &nextopt, name, value, "options"))
401 		return;
402 
403 	low = emalloc(strlen(name) + 1);
404 	/* make lowercase, then add to select table */
405 	for (n = name, p = low; (c = *n) != '\0'; n++)
406 		*p++ = isupper(c) ? tolower(c) : c;
407 	*p = 0;
408 	n = intern(low);
409 	free(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