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