Deleted Added
full compact
main.c (229403) main.c (235789)
1/*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1980, 1993\n\
33 The Regents of the University of California. All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
39#endif
40static const char rcsid[] =
1/*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1980, 1993\n\
33 The Regents of the University of California. All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
39#endif
40static const char rcsid[] =
41 "$FreeBSD: head/usr.sbin/config/main.c 229403 2012-01-03 18:51:58Z ed $";
41 "$FreeBSD: head/usr.sbin/config/main.c 235789 2012-05-22 16:33:10Z bapt $";
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <sys/sbuf.h>
47#include <sys/file.h>
48#include <sys/mman.h>
49#include <sys/param.h>
50
51#include <assert.h>
52#include <ctype.h>
53#include <err.h>
54#include <stdio.h>
55#include <string.h>
56#include <sysexits.h>
57#include <unistd.h>
58#include <dirent.h>
59#include "y.tab.h"
60#include "config.h"
61#include "configvers.h"
62
63#ifndef TRUE
64#define TRUE (1)
65#endif
66
67#ifndef FALSE
68#define FALSE (0)
69#endif
70
71#define CDIR "../compile/"
72
73char * PREFIX;
74char destdir[MAXPATHLEN];
75char srcdir[MAXPATHLEN];
76
77int debugging;
78int profiling;
79int found_defaults;
80int incignore;
81
82/*
83 * Preserve old behaviour in INCLUDE_CONFIG_FILE handling (files are included
84 * literally).
85 */
86int filebased = 0;
87
88static void configfile(void);
89static void get_srcdir(void);
90static void usage(void);
91static void cleanheaders(char *);
92static void kernconfdump(const char *);
93static void checkversion(void);
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <sys/sbuf.h>
47#include <sys/file.h>
48#include <sys/mman.h>
49#include <sys/param.h>
50
51#include <assert.h>
52#include <ctype.h>
53#include <err.h>
54#include <stdio.h>
55#include <string.h>
56#include <sysexits.h>
57#include <unistd.h>
58#include <dirent.h>
59#include "y.tab.h"
60#include "config.h"
61#include "configvers.h"
62
63#ifndef TRUE
64#define TRUE (1)
65#endif
66
67#ifndef FALSE
68#define FALSE (0)
69#endif
70
71#define CDIR "../compile/"
72
73char * PREFIX;
74char destdir[MAXPATHLEN];
75char srcdir[MAXPATHLEN];
76
77int debugging;
78int profiling;
79int found_defaults;
80int incignore;
81
82/*
83 * Preserve old behaviour in INCLUDE_CONFIG_FILE handling (files are included
84 * literally).
85 */
86int filebased = 0;
87
88static void configfile(void);
89static void get_srcdir(void);
90static void usage(void);
91static void cleanheaders(char *);
92static void kernconfdump(const char *);
93static void checkversion(void);
94extern int yyparse(void);
94
95struct hdr_list {
96 char *h_name;
97 struct hdr_list *h_next;
98} *htab;
99
100/*
101 * Config builds a set of files for building a UNIX
102 * system given a description of the desired system.
103 */
104int
105main(int argc, char **argv)
106{
107
108 struct stat buf;
109 int ch, len;
110 char *p;
111 char *kernfile;
112 int printmachine;
113
114 printmachine = 0;
115 kernfile = NULL;
116 while ((ch = getopt(argc, argv, "Cd:gmpVx:")) != -1)
117 switch (ch) {
118 case 'C':
119 filebased = 1;
120 break;
121 case 'm':
122 printmachine = 1;
123 break;
124 case 'd':
125 if (*destdir == '\0')
126 strlcpy(destdir, optarg, sizeof(destdir));
127 else
128 errx(EXIT_FAILURE, "directory already set");
129 break;
130 case 'g':
131 debugging++;
132 break;
133 case 'p':
134 profiling++;
135 break;
136 case 'V':
137 printf("%d\n", CONFIGVERS);
138 exit(0);
139 case 'x':
140 kernfile = optarg;
141 break;
142 case '?':
143 default:
144 usage();
145 }
146 argc -= optind;
147 argv += optind;
148
149 if (kernfile != NULL) {
150 kernconfdump(kernfile);
151 exit(EXIT_SUCCESS);
152 }
153
154 if (argc != 1)
155 usage();
156
157 PREFIX = *argv;
158 if (stat(PREFIX, &buf) != 0 || !S_ISREG(buf.st_mode))
159 err(2, "%s", PREFIX);
160 if (freopen("DEFAULTS", "r", stdin) != NULL) {
161 found_defaults = 1;
162 yyfile = "DEFAULTS";
163 } else {
164 if (freopen(PREFIX, "r", stdin) == NULL)
165 err(2, "%s", PREFIX);
166 yyfile = PREFIX;
167 }
168 if (*destdir != '\0') {
169 len = strlen(destdir);
170 while (len > 1 && destdir[len - 1] == '/')
171 destdir[--len] = '\0';
172 get_srcdir();
173 } else {
174 strlcpy(destdir, CDIR, sizeof(destdir));
175 strlcat(destdir, PREFIX, sizeof(destdir));
176 }
177
178 SLIST_INIT(&cputype);
179 SLIST_INIT(&mkopt);
180 SLIST_INIT(&opt);
181 SLIST_INIT(&rmopts);
182 STAILQ_INIT(&cfgfiles);
183 STAILQ_INIT(&dtab);
184 STAILQ_INIT(&fntab);
185 STAILQ_INIT(&ftab);
186 STAILQ_INIT(&hints);
187 if (yyparse())
188 exit(3);
189
190 /*
191 * Ensure that required elements (machine, cpu, ident) are present.
192 */
193 if (machinename == NULL) {
194 printf("Specify machine type, e.g. ``machine i386''\n");
195 exit(1);
196 }
197 if (ident == NULL) {
198 printf("no ident line specified\n");
199 exit(1);
200 }
201 if (SLIST_EMPTY(&cputype)) {
202 printf("cpu type must be specified\n");
203 exit(1);
204 }
205 checkversion();
206
207 if (printmachine) {
208 printf("%s\t%s\n",machinename,machinearch);
209 exit(0);
210 }
211
212 /* Make compile directory */
213 p = path((char *)NULL);
214 if (stat(p, &buf)) {
215 if (mkdir(p, 0777))
216 err(2, "%s", p);
217 } else if (!S_ISDIR(buf.st_mode))
218 errx(EXIT_FAILURE, "%s isn't a directory", p);
219
220 configfile(); /* put config file into kernel*/
221 options(); /* make options .h files */
222 makefile(); /* build Makefile */
223 makeenv(); /* build env.c */
224 makehints(); /* build hints.c */
225 headers(); /* make a lot of .h files */
226 cleanheaders(p);
227 printf("Kernel build directory is %s\n", p);
228 printf("Don't forget to do ``make cleandepend && make depend''\n");
229 exit(0);
230}
231
232/*
233 * get_srcdir
234 * determine the root of the kernel source tree
235 * and save that in srcdir.
236 */
237static void
238get_srcdir(void)
239{
240 struct stat lg, phy;
241 char *p, *pwd;
242 int i;
243
244 if (realpath("../..", srcdir) == NULL)
245 err(EXIT_FAILURE, "Unable to find root of source tree");
246 if ((pwd = getenv("PWD")) != NULL && *pwd == '/' &&
247 (pwd = strdup(pwd)) != NULL) {
248 /* Remove the last two path components. */
249 for (i = 0; i < 2; i++) {
250 if ((p = strrchr(pwd, '/')) == NULL) {
251 free(pwd);
252 return;
253 }
254 *p = '\0';
255 }
256 if (stat(pwd, &lg) != -1 && stat(srcdir, &phy) != -1 &&
257 lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino)
258 strlcpy(srcdir, pwd, MAXPATHLEN);
259 free(pwd);
260 }
261}
262
263static void
264usage(void)
265{
266
267 fprintf(stderr, "usage: config [-CgmpV] [-d destdir] sysname\n");
268 fprintf(stderr, " config -x kernel\n");
269 exit(EX_USAGE);
270}
271
272/*
273 * get_word
274 * returns EOF on end of file
275 * NULL on end of line
276 * pointer to the word otherwise
277 */
278char *
279get_word(FILE *fp)
280{
281 static char line[80];
282 int ch;
283 char *cp;
284 int escaped_nl = 0;
285
286begin:
287 while ((ch = getc(fp)) != EOF)
288 if (ch != ' ' && ch != '\t')
289 break;
290 if (ch == EOF)
291 return ((char *)EOF);
292 if (ch == '\\'){
293 escaped_nl = 1;
294 goto begin;
295 }
296 if (ch == '\n') {
297 if (escaped_nl){
298 escaped_nl = 0;
299 goto begin;
300 }
301 else
302 return (NULL);
303 }
304 cp = line;
305 *cp++ = ch;
306 while ((ch = getc(fp)) != EOF) {
307 if (isspace(ch))
308 break;
309 *cp++ = ch;
310 }
311 *cp = 0;
312 if (ch == EOF)
313 return ((char *)EOF);
314 (void) ungetc(ch, fp);
315 return (line);
316}
317
318/*
319 * get_quoted_word
320 * like get_word but will accept something in double or single quotes
321 * (to allow embedded spaces).
322 */
323char *
324get_quoted_word(FILE *fp)
325{
326 static char line[256];
327 int ch;
328 char *cp;
329 int escaped_nl = 0;
330
331begin:
332 while ((ch = getc(fp)) != EOF)
333 if (ch != ' ' && ch != '\t')
334 break;
335 if (ch == EOF)
336 return ((char *)EOF);
337 if (ch == '\\'){
338 escaped_nl = 1;
339 goto begin;
340 }
341 if (ch == '\n') {
342 if (escaped_nl){
343 escaped_nl = 0;
344 goto begin;
345 }
346 else
347 return (NULL);
348 }
349 cp = line;
350 if (ch == '"' || ch == '\'') {
351 int quote = ch;
352
353 while ((ch = getc(fp)) != EOF) {
354 if (ch == quote)
355 break;
356 if (ch == '\n') {
357 *cp = 0;
358 printf("config: missing quote reading `%s'\n",
359 line);
360 exit(2);
361 }
362 *cp++ = ch;
363 }
364 } else {
365 *cp++ = ch;
366 while ((ch = getc(fp)) != EOF) {
367 if (isspace(ch))
368 break;
369 *cp++ = ch;
370 }
371 if (ch != EOF)
372 (void) ungetc(ch, fp);
373 }
374 *cp = 0;
375 if (ch == EOF)
376 return ((char *)EOF);
377 return (line);
378}
379
380/*
381 * prepend the path to a filename
382 */
383char *
384path(const char *file)
385{
386 char *cp = NULL;
387
388 if (file)
389 asprintf(&cp, "%s/%s", destdir, file);
390 else
391 cp = strdup(destdir);
392 return (cp);
393}
394
395/*
396 * Generate configuration file based on actual settings. With this mode, user
397 * will be able to obtain and build conifguration file with one command.
398 */
399static void
400configfile_dynamic(struct sbuf *sb)
401{
402 struct cputype *cput;
403 struct device *d;
404 struct opt *ol;
405 char *lend;
406 unsigned int i;
407
408 asprintf(&lend, "\\n\\\n");
409 assert(lend != NULL);
410 sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend);
411 sbuf_printf(sb, "ident\t%s%s", ident, lend);
412 sbuf_printf(sb, "machine\t%s%s", machinename, lend);
413 SLIST_FOREACH(cput, &cputype, cpu_next)
414 sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend);
415 SLIST_FOREACH(ol, &mkopt, op_next)
416 sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name,
417 ol->op_value, lend);
418 SLIST_FOREACH(ol, &opt, op_next) {
419 if (strncmp(ol->op_name, "DEV_", 4) == 0)
420 continue;
421 sbuf_printf(sb, "options\t%s", ol->op_name);
422 if (ol->op_value != NULL) {
423 sbuf_putc(sb, '=');
424 for (i = 0; i < strlen(ol->op_value); i++) {
425 if (ol->op_value[i] == '"')
426 sbuf_printf(sb, "\\%c",
427 ol->op_value[i]);
428 else
429 sbuf_printf(sb, "%c",
430 ol->op_value[i]);
431 }
432 sbuf_printf(sb, "%s", lend);
433 } else {
434 sbuf_printf(sb, "%s", lend);
435 }
436 }
437 /*
438 * Mark this file as containing everything we need.
439 */
440 STAILQ_FOREACH(d, &dtab, d_next)
441 sbuf_printf(sb, "device\t%s%s", d->d_name, lend);
442 free(lend);
443}
444
445/*
446 * Generate file from the configuration files.
447 */
448static void
449configfile_filebased(struct sbuf *sb)
450{
451 FILE *cff;
452 struct cfgfile *cf;
453 int i;
454
455 /*
456 * Try to read all configuration files. Since those will be present as
457 * C string in the macro, we have to slash their ends then the line
458 * wraps.
459 */
460 STAILQ_FOREACH(cf, &cfgfiles, cfg_next) {
461 cff = fopen(cf->cfg_path, "r");
462 if (cff == NULL) {
463 warn("Couldn't open file %s", cf->cfg_path);
464 continue;
465 }
466 while ((i = getc(cff)) != EOF) {
467 if (i == '\n')
468 sbuf_printf(sb, "\\n\\\n");
469 else if (i == '"' || i == '\'')
470 sbuf_printf(sb, "\\%c", i);
471 else
472 sbuf_putc(sb, i);
473 }
474 fclose(cff);
475 }
476}
477
478static void
479configfile(void)
480{
481 FILE *fo;
482 struct sbuf *sb;
483 char *p;
484
485 /* Add main configuration file to the list of files to be included */
486 cfgfile_add(PREFIX);
487 p = path("config.c.new");
488 fo = fopen(p, "w");
489 if (!fo)
490 err(2, "%s", p);
491 sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND);
492 assert(sb != NULL);
493 sbuf_clear(sb);
494 if (filebased) {
495 /* Is needed, can be used for backward compatibility. */
496 configfile_filebased(sb);
497 } else {
498 configfile_dynamic(sb);
499 }
500 sbuf_finish(sb);
501 /*
502 * We print first part of the template, replace our tag with
503 * configuration files content and later continue writing our
504 * template.
505 */
506 p = strstr(kernconfstr, KERNCONFTAG);
507 if (p == NULL)
508 errx(EXIT_FAILURE, "Something went terribly wrong!");
509 *p = '\0';
510 fprintf(fo, "%s", kernconfstr);
511 fprintf(fo, "%s", sbuf_data(sb));
512 p += strlen(KERNCONFTAG);
513 fprintf(fo, "%s", p);
514 sbuf_delete(sb);
515 fclose(fo);
516 moveifchanged(path("config.c.new"), path("config.c"));
517 cfgfile_removeall();
518}
519
520/*
521 * moveifchanged --
522 * compare two files; rename if changed.
523 */
524void
525moveifchanged(const char *from_name, const char *to_name)
526{
527 char *p, *q;
528 int changed;
529 size_t tsize;
530 struct stat from_sb, to_sb;
531 int from_fd, to_fd;
532
533 changed = 0;
534
535 if ((from_fd = open(from_name, O_RDONLY)) < 0)
536 err(EX_OSERR, "moveifchanged open(%s)", from_name);
537
538 if ((to_fd = open(to_name, O_RDONLY)) < 0)
539 changed++;
540
541 if (!changed && fstat(from_fd, &from_sb) < 0)
542 err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
543
544 if (!changed && fstat(to_fd, &to_sb) < 0)
545 err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
546
547 if (!changed && from_sb.st_size != to_sb.st_size)
548 changed++;
549
550 tsize = (size_t)from_sb.st_size;
551
552 if (!changed) {
553 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
554 if (p == MAP_FAILED)
555 err(EX_OSERR, "mmap %s", from_name);
556 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
557 if (q == MAP_FAILED)
558 err(EX_OSERR, "mmap %s", to_name);
559
560 changed = memcmp(p, q, tsize);
561 munmap(p, tsize);
562 munmap(q, tsize);
563 }
564 if (changed) {
565 if (rename(from_name, to_name) < 0)
566 err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
567 } else {
568 if (unlink(from_name) < 0)
569 err(EX_OSERR, "unlink(%s)", from_name);
570 }
571}
572
573static void
574cleanheaders(char *p)
575{
576 DIR *dirp;
577 struct dirent *dp;
578 struct file_list *fl;
579 struct hdr_list *hl;
580 size_t len;
581
582 remember("y.tab.h");
583 remember("setdefs.h");
584 STAILQ_FOREACH(fl, &ftab, f_next)
585 remember(fl->f_fn);
586
587 /*
588 * Scan the build directory and clean out stuff that looks like
589 * it might have been a leftover NFOO header, etc.
590 */
591 if ((dirp = opendir(p)) == NULL)
592 err(EX_OSERR, "opendir %s", p);
593 while ((dp = readdir(dirp)) != NULL) {
594 len = strlen(dp->d_name);
595 /* Skip non-headers */
596 if (len < 2 || dp->d_name[len - 2] != '.' ||
597 dp->d_name[len - 1] != 'h')
598 continue;
599 /* Skip special stuff, eg: bus_if.h, but check opt_*.h */
600 if (strchr(dp->d_name, '_') &&
601 strncmp(dp->d_name, "opt_", 4) != 0)
602 continue;
603 /* Check if it is a target file */
604 for (hl = htab; hl != NULL; hl = hl->h_next) {
605 if (eq(dp->d_name, hl->h_name)) {
606 break;
607 }
608 }
609 if (hl)
610 continue;
611 printf("Removing stale header: %s\n", dp->d_name);
612 if (unlink(path(dp->d_name)) == -1)
613 warn("unlink %s", dp->d_name);
614 }
615 (void)closedir(dirp);
616}
617
618void
619remember(const char *file)
620{
621 char *s;
622 struct hdr_list *hl;
623
624 if ((s = strrchr(file, '/')) != NULL)
625 s = ns(s + 1);
626 else
627 s = ns(file);
628
629 if (strchr(s, '_') && strncmp(s, "opt_", 4) != 0) {
630 free(s);
631 return;
632 }
633 for (hl = htab; hl != NULL; hl = hl->h_next) {
634 if (eq(s, hl->h_name)) {
635 free(s);
636 return;
637 }
638 }
639 hl = calloc(1, sizeof(*hl));
640 if (hl == NULL)
641 err(EXIT_FAILURE, "calloc");
642 hl->h_name = s;
643 hl->h_next = htab;
644 htab = hl;
645}
646
647/*
648 * This one is quick hack. Will be probably moved to elf(3) interface.
649 * It takes kernel configuration file name, passes it as an argument to
650 * elfdump -a, which output is parsed by some UNIX tools...
651 */
652static void
653kernconfdump(const char *file)
654{
655 struct stat st;
656 FILE *fp, *pp;
657 int error, len, osz, r;
658 unsigned int i, off, size, t1, t2, align;
659 char *cmd, *o;
660
661 r = open(file, O_RDONLY);
662 if (r == -1)
663 err(EXIT_FAILURE, "Couldn't open file '%s'", file);
664 error = fstat(r, &st);
665 if (error == -1)
666 err(EXIT_FAILURE, "fstat() failed");
667 if (S_ISDIR(st.st_mode))
668 errx(EXIT_FAILURE, "'%s' is a directory", file);
669 fp = fdopen(r, "r");
670 if (fp == NULL)
671 err(EXIT_FAILURE, "fdopen() failed");
672 osz = 1024;
673 o = calloc(1, osz);
674 if (o == NULL)
675 err(EXIT_FAILURE, "Couldn't allocate memory");
676 /* ELF note section header. */
677 asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 8 kern_conf"
678 "| tail -5 | cut -d ' ' -f 2 | paste - - - - -", file);
679 if (cmd == NULL)
680 errx(EXIT_FAILURE, "asprintf() failed");
681 pp = popen(cmd, "r");
682 if (pp == NULL)
683 errx(EXIT_FAILURE, "popen() failed");
684 free(cmd);
685 len = fread(o, osz, 1, pp);
686 pclose(pp);
687 r = sscanf(o, "%d%d%d%d%d", &off, &size, &t1, &t2, &align);
688 free(o);
689 if (r != 5)
690 errx(EXIT_FAILURE, "File %s doesn't contain configuration "
691 "file. Either unsupported, or not compiled with "
692 "INCLUDE_CONFIG_FILE", file);
693 r = fseek(fp, off, SEEK_CUR);
694 if (r != 0)
695 err(EXIT_FAILURE, "fseek() failed");
696 for (i = 0; i < size; i++) {
697 r = fgetc(fp);
698 if (r == EOF)
699 break;
700 /*
701 * If '\0' is present in the middle of the configuration
702 * string, this means something very weird is happening.
703 * Make such case very visible. However, some architectures
704 * pad the length of the section with NULs to a multiple of
705 * sh_addralign, allow a NUL in that part of the section.
706 */
707 if (r == '\0' && (size - i) < align)
708 break;
709 assert(r != '\0' && ("Char present in the configuration "
710 "string mustn't be equal to 0"));
711 fputc(r, stdout);
712 }
713 fclose(fp);
714}
715
716static void
717badversion(int versreq)
718{
719 fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n");
720 fprintf(stderr, "config version = %d, ", CONFIGVERS);
721 fprintf(stderr, "version required = %d\n\n", versreq);
722 fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n");
723 fprintf(stderr, "with your /usr/src/sys and install a new config binary\n");
724 fprintf(stderr, "before trying this again.\n\n");
725 fprintf(stderr, "If running the new config fails check your config\n");
726 fprintf(stderr, "file against the GENERIC or LINT config files for\n");
727 fprintf(stderr, "changes in config syntax, or option/device naming\n");
728 fprintf(stderr, "conventions\n\n");
729 exit(1);
730}
731
732static void
733checkversion(void)
734{
735 FILE *ifp;
736 char line[BUFSIZ];
737 int versreq;
738
739 ifp = open_makefile_template();
740 while (fgets(line, BUFSIZ, ifp) != 0) {
741 if (*line != '%')
742 continue;
743 if (strncmp(line, "%VERSREQ=", 9) != 0)
744 continue;
745 versreq = atoi(line + 9);
746 if (MAJOR_VERS(versreq) == MAJOR_VERS(CONFIGVERS) &&
747 versreq <= CONFIGVERS)
748 continue;
749 badversion(versreq);
750 }
751 fclose(ifp);
752}
95
96struct hdr_list {
97 char *h_name;
98 struct hdr_list *h_next;
99} *htab;
100
101/*
102 * Config builds a set of files for building a UNIX
103 * system given a description of the desired system.
104 */
105int
106main(int argc, char **argv)
107{
108
109 struct stat buf;
110 int ch, len;
111 char *p;
112 char *kernfile;
113 int printmachine;
114
115 printmachine = 0;
116 kernfile = NULL;
117 while ((ch = getopt(argc, argv, "Cd:gmpVx:")) != -1)
118 switch (ch) {
119 case 'C':
120 filebased = 1;
121 break;
122 case 'm':
123 printmachine = 1;
124 break;
125 case 'd':
126 if (*destdir == '\0')
127 strlcpy(destdir, optarg, sizeof(destdir));
128 else
129 errx(EXIT_FAILURE, "directory already set");
130 break;
131 case 'g':
132 debugging++;
133 break;
134 case 'p':
135 profiling++;
136 break;
137 case 'V':
138 printf("%d\n", CONFIGVERS);
139 exit(0);
140 case 'x':
141 kernfile = optarg;
142 break;
143 case '?':
144 default:
145 usage();
146 }
147 argc -= optind;
148 argv += optind;
149
150 if (kernfile != NULL) {
151 kernconfdump(kernfile);
152 exit(EXIT_SUCCESS);
153 }
154
155 if (argc != 1)
156 usage();
157
158 PREFIX = *argv;
159 if (stat(PREFIX, &buf) != 0 || !S_ISREG(buf.st_mode))
160 err(2, "%s", PREFIX);
161 if (freopen("DEFAULTS", "r", stdin) != NULL) {
162 found_defaults = 1;
163 yyfile = "DEFAULTS";
164 } else {
165 if (freopen(PREFIX, "r", stdin) == NULL)
166 err(2, "%s", PREFIX);
167 yyfile = PREFIX;
168 }
169 if (*destdir != '\0') {
170 len = strlen(destdir);
171 while (len > 1 && destdir[len - 1] == '/')
172 destdir[--len] = '\0';
173 get_srcdir();
174 } else {
175 strlcpy(destdir, CDIR, sizeof(destdir));
176 strlcat(destdir, PREFIX, sizeof(destdir));
177 }
178
179 SLIST_INIT(&cputype);
180 SLIST_INIT(&mkopt);
181 SLIST_INIT(&opt);
182 SLIST_INIT(&rmopts);
183 STAILQ_INIT(&cfgfiles);
184 STAILQ_INIT(&dtab);
185 STAILQ_INIT(&fntab);
186 STAILQ_INIT(&ftab);
187 STAILQ_INIT(&hints);
188 if (yyparse())
189 exit(3);
190
191 /*
192 * Ensure that required elements (machine, cpu, ident) are present.
193 */
194 if (machinename == NULL) {
195 printf("Specify machine type, e.g. ``machine i386''\n");
196 exit(1);
197 }
198 if (ident == NULL) {
199 printf("no ident line specified\n");
200 exit(1);
201 }
202 if (SLIST_EMPTY(&cputype)) {
203 printf("cpu type must be specified\n");
204 exit(1);
205 }
206 checkversion();
207
208 if (printmachine) {
209 printf("%s\t%s\n",machinename,machinearch);
210 exit(0);
211 }
212
213 /* Make compile directory */
214 p = path((char *)NULL);
215 if (stat(p, &buf)) {
216 if (mkdir(p, 0777))
217 err(2, "%s", p);
218 } else if (!S_ISDIR(buf.st_mode))
219 errx(EXIT_FAILURE, "%s isn't a directory", p);
220
221 configfile(); /* put config file into kernel*/
222 options(); /* make options .h files */
223 makefile(); /* build Makefile */
224 makeenv(); /* build env.c */
225 makehints(); /* build hints.c */
226 headers(); /* make a lot of .h files */
227 cleanheaders(p);
228 printf("Kernel build directory is %s\n", p);
229 printf("Don't forget to do ``make cleandepend && make depend''\n");
230 exit(0);
231}
232
233/*
234 * get_srcdir
235 * determine the root of the kernel source tree
236 * and save that in srcdir.
237 */
238static void
239get_srcdir(void)
240{
241 struct stat lg, phy;
242 char *p, *pwd;
243 int i;
244
245 if (realpath("../..", srcdir) == NULL)
246 err(EXIT_FAILURE, "Unable to find root of source tree");
247 if ((pwd = getenv("PWD")) != NULL && *pwd == '/' &&
248 (pwd = strdup(pwd)) != NULL) {
249 /* Remove the last two path components. */
250 for (i = 0; i < 2; i++) {
251 if ((p = strrchr(pwd, '/')) == NULL) {
252 free(pwd);
253 return;
254 }
255 *p = '\0';
256 }
257 if (stat(pwd, &lg) != -1 && stat(srcdir, &phy) != -1 &&
258 lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino)
259 strlcpy(srcdir, pwd, MAXPATHLEN);
260 free(pwd);
261 }
262}
263
264static void
265usage(void)
266{
267
268 fprintf(stderr, "usage: config [-CgmpV] [-d destdir] sysname\n");
269 fprintf(stderr, " config -x kernel\n");
270 exit(EX_USAGE);
271}
272
273/*
274 * get_word
275 * returns EOF on end of file
276 * NULL on end of line
277 * pointer to the word otherwise
278 */
279char *
280get_word(FILE *fp)
281{
282 static char line[80];
283 int ch;
284 char *cp;
285 int escaped_nl = 0;
286
287begin:
288 while ((ch = getc(fp)) != EOF)
289 if (ch != ' ' && ch != '\t')
290 break;
291 if (ch == EOF)
292 return ((char *)EOF);
293 if (ch == '\\'){
294 escaped_nl = 1;
295 goto begin;
296 }
297 if (ch == '\n') {
298 if (escaped_nl){
299 escaped_nl = 0;
300 goto begin;
301 }
302 else
303 return (NULL);
304 }
305 cp = line;
306 *cp++ = ch;
307 while ((ch = getc(fp)) != EOF) {
308 if (isspace(ch))
309 break;
310 *cp++ = ch;
311 }
312 *cp = 0;
313 if (ch == EOF)
314 return ((char *)EOF);
315 (void) ungetc(ch, fp);
316 return (line);
317}
318
319/*
320 * get_quoted_word
321 * like get_word but will accept something in double or single quotes
322 * (to allow embedded spaces).
323 */
324char *
325get_quoted_word(FILE *fp)
326{
327 static char line[256];
328 int ch;
329 char *cp;
330 int escaped_nl = 0;
331
332begin:
333 while ((ch = getc(fp)) != EOF)
334 if (ch != ' ' && ch != '\t')
335 break;
336 if (ch == EOF)
337 return ((char *)EOF);
338 if (ch == '\\'){
339 escaped_nl = 1;
340 goto begin;
341 }
342 if (ch == '\n') {
343 if (escaped_nl){
344 escaped_nl = 0;
345 goto begin;
346 }
347 else
348 return (NULL);
349 }
350 cp = line;
351 if (ch == '"' || ch == '\'') {
352 int quote = ch;
353
354 while ((ch = getc(fp)) != EOF) {
355 if (ch == quote)
356 break;
357 if (ch == '\n') {
358 *cp = 0;
359 printf("config: missing quote reading `%s'\n",
360 line);
361 exit(2);
362 }
363 *cp++ = ch;
364 }
365 } else {
366 *cp++ = ch;
367 while ((ch = getc(fp)) != EOF) {
368 if (isspace(ch))
369 break;
370 *cp++ = ch;
371 }
372 if (ch != EOF)
373 (void) ungetc(ch, fp);
374 }
375 *cp = 0;
376 if (ch == EOF)
377 return ((char *)EOF);
378 return (line);
379}
380
381/*
382 * prepend the path to a filename
383 */
384char *
385path(const char *file)
386{
387 char *cp = NULL;
388
389 if (file)
390 asprintf(&cp, "%s/%s", destdir, file);
391 else
392 cp = strdup(destdir);
393 return (cp);
394}
395
396/*
397 * Generate configuration file based on actual settings. With this mode, user
398 * will be able to obtain and build conifguration file with one command.
399 */
400static void
401configfile_dynamic(struct sbuf *sb)
402{
403 struct cputype *cput;
404 struct device *d;
405 struct opt *ol;
406 char *lend;
407 unsigned int i;
408
409 asprintf(&lend, "\\n\\\n");
410 assert(lend != NULL);
411 sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend);
412 sbuf_printf(sb, "ident\t%s%s", ident, lend);
413 sbuf_printf(sb, "machine\t%s%s", machinename, lend);
414 SLIST_FOREACH(cput, &cputype, cpu_next)
415 sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend);
416 SLIST_FOREACH(ol, &mkopt, op_next)
417 sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name,
418 ol->op_value, lend);
419 SLIST_FOREACH(ol, &opt, op_next) {
420 if (strncmp(ol->op_name, "DEV_", 4) == 0)
421 continue;
422 sbuf_printf(sb, "options\t%s", ol->op_name);
423 if (ol->op_value != NULL) {
424 sbuf_putc(sb, '=');
425 for (i = 0; i < strlen(ol->op_value); i++) {
426 if (ol->op_value[i] == '"')
427 sbuf_printf(sb, "\\%c",
428 ol->op_value[i]);
429 else
430 sbuf_printf(sb, "%c",
431 ol->op_value[i]);
432 }
433 sbuf_printf(sb, "%s", lend);
434 } else {
435 sbuf_printf(sb, "%s", lend);
436 }
437 }
438 /*
439 * Mark this file as containing everything we need.
440 */
441 STAILQ_FOREACH(d, &dtab, d_next)
442 sbuf_printf(sb, "device\t%s%s", d->d_name, lend);
443 free(lend);
444}
445
446/*
447 * Generate file from the configuration files.
448 */
449static void
450configfile_filebased(struct sbuf *sb)
451{
452 FILE *cff;
453 struct cfgfile *cf;
454 int i;
455
456 /*
457 * Try to read all configuration files. Since those will be present as
458 * C string in the macro, we have to slash their ends then the line
459 * wraps.
460 */
461 STAILQ_FOREACH(cf, &cfgfiles, cfg_next) {
462 cff = fopen(cf->cfg_path, "r");
463 if (cff == NULL) {
464 warn("Couldn't open file %s", cf->cfg_path);
465 continue;
466 }
467 while ((i = getc(cff)) != EOF) {
468 if (i == '\n')
469 sbuf_printf(sb, "\\n\\\n");
470 else if (i == '"' || i == '\'')
471 sbuf_printf(sb, "\\%c", i);
472 else
473 sbuf_putc(sb, i);
474 }
475 fclose(cff);
476 }
477}
478
479static void
480configfile(void)
481{
482 FILE *fo;
483 struct sbuf *sb;
484 char *p;
485
486 /* Add main configuration file to the list of files to be included */
487 cfgfile_add(PREFIX);
488 p = path("config.c.new");
489 fo = fopen(p, "w");
490 if (!fo)
491 err(2, "%s", p);
492 sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND);
493 assert(sb != NULL);
494 sbuf_clear(sb);
495 if (filebased) {
496 /* Is needed, can be used for backward compatibility. */
497 configfile_filebased(sb);
498 } else {
499 configfile_dynamic(sb);
500 }
501 sbuf_finish(sb);
502 /*
503 * We print first part of the template, replace our tag with
504 * configuration files content and later continue writing our
505 * template.
506 */
507 p = strstr(kernconfstr, KERNCONFTAG);
508 if (p == NULL)
509 errx(EXIT_FAILURE, "Something went terribly wrong!");
510 *p = '\0';
511 fprintf(fo, "%s", kernconfstr);
512 fprintf(fo, "%s", sbuf_data(sb));
513 p += strlen(KERNCONFTAG);
514 fprintf(fo, "%s", p);
515 sbuf_delete(sb);
516 fclose(fo);
517 moveifchanged(path("config.c.new"), path("config.c"));
518 cfgfile_removeall();
519}
520
521/*
522 * moveifchanged --
523 * compare two files; rename if changed.
524 */
525void
526moveifchanged(const char *from_name, const char *to_name)
527{
528 char *p, *q;
529 int changed;
530 size_t tsize;
531 struct stat from_sb, to_sb;
532 int from_fd, to_fd;
533
534 changed = 0;
535
536 if ((from_fd = open(from_name, O_RDONLY)) < 0)
537 err(EX_OSERR, "moveifchanged open(%s)", from_name);
538
539 if ((to_fd = open(to_name, O_RDONLY)) < 0)
540 changed++;
541
542 if (!changed && fstat(from_fd, &from_sb) < 0)
543 err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
544
545 if (!changed && fstat(to_fd, &to_sb) < 0)
546 err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
547
548 if (!changed && from_sb.st_size != to_sb.st_size)
549 changed++;
550
551 tsize = (size_t)from_sb.st_size;
552
553 if (!changed) {
554 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
555 if (p == MAP_FAILED)
556 err(EX_OSERR, "mmap %s", from_name);
557 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
558 if (q == MAP_FAILED)
559 err(EX_OSERR, "mmap %s", to_name);
560
561 changed = memcmp(p, q, tsize);
562 munmap(p, tsize);
563 munmap(q, tsize);
564 }
565 if (changed) {
566 if (rename(from_name, to_name) < 0)
567 err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
568 } else {
569 if (unlink(from_name) < 0)
570 err(EX_OSERR, "unlink(%s)", from_name);
571 }
572}
573
574static void
575cleanheaders(char *p)
576{
577 DIR *dirp;
578 struct dirent *dp;
579 struct file_list *fl;
580 struct hdr_list *hl;
581 size_t len;
582
583 remember("y.tab.h");
584 remember("setdefs.h");
585 STAILQ_FOREACH(fl, &ftab, f_next)
586 remember(fl->f_fn);
587
588 /*
589 * Scan the build directory and clean out stuff that looks like
590 * it might have been a leftover NFOO header, etc.
591 */
592 if ((dirp = opendir(p)) == NULL)
593 err(EX_OSERR, "opendir %s", p);
594 while ((dp = readdir(dirp)) != NULL) {
595 len = strlen(dp->d_name);
596 /* Skip non-headers */
597 if (len < 2 || dp->d_name[len - 2] != '.' ||
598 dp->d_name[len - 1] != 'h')
599 continue;
600 /* Skip special stuff, eg: bus_if.h, but check opt_*.h */
601 if (strchr(dp->d_name, '_') &&
602 strncmp(dp->d_name, "opt_", 4) != 0)
603 continue;
604 /* Check if it is a target file */
605 for (hl = htab; hl != NULL; hl = hl->h_next) {
606 if (eq(dp->d_name, hl->h_name)) {
607 break;
608 }
609 }
610 if (hl)
611 continue;
612 printf("Removing stale header: %s\n", dp->d_name);
613 if (unlink(path(dp->d_name)) == -1)
614 warn("unlink %s", dp->d_name);
615 }
616 (void)closedir(dirp);
617}
618
619void
620remember(const char *file)
621{
622 char *s;
623 struct hdr_list *hl;
624
625 if ((s = strrchr(file, '/')) != NULL)
626 s = ns(s + 1);
627 else
628 s = ns(file);
629
630 if (strchr(s, '_') && strncmp(s, "opt_", 4) != 0) {
631 free(s);
632 return;
633 }
634 for (hl = htab; hl != NULL; hl = hl->h_next) {
635 if (eq(s, hl->h_name)) {
636 free(s);
637 return;
638 }
639 }
640 hl = calloc(1, sizeof(*hl));
641 if (hl == NULL)
642 err(EXIT_FAILURE, "calloc");
643 hl->h_name = s;
644 hl->h_next = htab;
645 htab = hl;
646}
647
648/*
649 * This one is quick hack. Will be probably moved to elf(3) interface.
650 * It takes kernel configuration file name, passes it as an argument to
651 * elfdump -a, which output is parsed by some UNIX tools...
652 */
653static void
654kernconfdump(const char *file)
655{
656 struct stat st;
657 FILE *fp, *pp;
658 int error, len, osz, r;
659 unsigned int i, off, size, t1, t2, align;
660 char *cmd, *o;
661
662 r = open(file, O_RDONLY);
663 if (r == -1)
664 err(EXIT_FAILURE, "Couldn't open file '%s'", file);
665 error = fstat(r, &st);
666 if (error == -1)
667 err(EXIT_FAILURE, "fstat() failed");
668 if (S_ISDIR(st.st_mode))
669 errx(EXIT_FAILURE, "'%s' is a directory", file);
670 fp = fdopen(r, "r");
671 if (fp == NULL)
672 err(EXIT_FAILURE, "fdopen() failed");
673 osz = 1024;
674 o = calloc(1, osz);
675 if (o == NULL)
676 err(EXIT_FAILURE, "Couldn't allocate memory");
677 /* ELF note section header. */
678 asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 8 kern_conf"
679 "| tail -5 | cut -d ' ' -f 2 | paste - - - - -", file);
680 if (cmd == NULL)
681 errx(EXIT_FAILURE, "asprintf() failed");
682 pp = popen(cmd, "r");
683 if (pp == NULL)
684 errx(EXIT_FAILURE, "popen() failed");
685 free(cmd);
686 len = fread(o, osz, 1, pp);
687 pclose(pp);
688 r = sscanf(o, "%d%d%d%d%d", &off, &size, &t1, &t2, &align);
689 free(o);
690 if (r != 5)
691 errx(EXIT_FAILURE, "File %s doesn't contain configuration "
692 "file. Either unsupported, or not compiled with "
693 "INCLUDE_CONFIG_FILE", file);
694 r = fseek(fp, off, SEEK_CUR);
695 if (r != 0)
696 err(EXIT_FAILURE, "fseek() failed");
697 for (i = 0; i < size; i++) {
698 r = fgetc(fp);
699 if (r == EOF)
700 break;
701 /*
702 * If '\0' is present in the middle of the configuration
703 * string, this means something very weird is happening.
704 * Make such case very visible. However, some architectures
705 * pad the length of the section with NULs to a multiple of
706 * sh_addralign, allow a NUL in that part of the section.
707 */
708 if (r == '\0' && (size - i) < align)
709 break;
710 assert(r != '\0' && ("Char present in the configuration "
711 "string mustn't be equal to 0"));
712 fputc(r, stdout);
713 }
714 fclose(fp);
715}
716
717static void
718badversion(int versreq)
719{
720 fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n");
721 fprintf(stderr, "config version = %d, ", CONFIGVERS);
722 fprintf(stderr, "version required = %d\n\n", versreq);
723 fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n");
724 fprintf(stderr, "with your /usr/src/sys and install a new config binary\n");
725 fprintf(stderr, "before trying this again.\n\n");
726 fprintf(stderr, "If running the new config fails check your config\n");
727 fprintf(stderr, "file against the GENERIC or LINT config files for\n");
728 fprintf(stderr, "changes in config syntax, or option/device naming\n");
729 fprintf(stderr, "conventions\n\n");
730 exit(1);
731}
732
733static void
734checkversion(void)
735{
736 FILE *ifp;
737 char line[BUFSIZ];
738 int versreq;
739
740 ifp = open_makefile_template();
741 while (fgets(line, BUFSIZ, ifp) != 0) {
742 if (*line != '%')
743 continue;
744 if (strncmp(line, "%VERSREQ=", 9) != 0)
745 continue;
746 versreq = atoi(line + 9);
747 if (MAJOR_VERS(versreq) == MAJOR_VERS(CONFIGVERS) &&
748 versreq <= CONFIGVERS)
749 continue;
750 badversion(versreq);
751 }
752 fclose(ifp);
753}