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