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