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