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$"; 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); 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 escaped_nl = 0; 355 while ((ch = getc(fp)) != EOF) { 356 if (ch == quote && !escaped_nl) 357 break; 358 if (ch == '\n' && !escaped_nl) { 359 *cp = 0; 360 printf("config: missing quote reading `%s'\n", 361 line); 362 exit(2); 363 } 364 if (ch == '\\' && !escaped_nl) { 365 escaped_nl = 1; 366 continue; 367 } 368 if (ch != quote && escaped_nl) 369 *cp++ = '\\'; 370 *cp++ = ch; 371 escaped_nl = 0; 372 } 373 } else { 374 *cp++ = ch; 375 while ((ch = getc(fp)) != EOF) { 376 if (isspace(ch)) 377 break; 378 *cp++ = ch; 379 } 380 if (ch != EOF) 381 (void) ungetc(ch, fp); 382 } 383 *cp = 0; 384 if (ch == EOF) 385 return ((char *)EOF); 386 return (line); 387} 388 389/* 390 * prepend the path to a filename 391 */ 392char * 393path(const char *file) 394{ 395 char *cp = NULL; 396 397 if (file) 398 asprintf(&cp, "%s/%s", destdir, file); 399 else 400 cp = strdup(destdir); 401 return (cp); 402} 403 404/* 405 * Generate configuration file based on actual settings. With this mode, user 406 * will be able to obtain and build conifguration file with one command. 407 */ 408static void 409configfile_dynamic(struct sbuf *sb) 410{ 411 struct cputype *cput; 412 struct device *d; 413 struct opt *ol; 414 char *lend; 415 unsigned int i; 416 417 asprintf(&lend, "\\n\\\n"); 418 assert(lend != NULL); 419 sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend); 420 sbuf_printf(sb, "ident\t%s%s", ident, lend); 421 sbuf_printf(sb, "machine\t%s%s", machinename, lend); 422 SLIST_FOREACH(cput, &cputype, cpu_next) 423 sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend); 424 SLIST_FOREACH(ol, &mkopt, op_next) 425 sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name, 426 ol->op_value, lend); 427 SLIST_FOREACH(ol, &opt, op_next) { 428 if (strncmp(ol->op_name, "DEV_", 4) == 0) 429 continue; 430 sbuf_printf(sb, "options\t%s", ol->op_name); 431 if (ol->op_value != NULL) { 432 sbuf_putc(sb, '='); 433 for (i = 0; i < strlen(ol->op_value); i++) { 434 if (ol->op_value[i] == '"') 435 sbuf_printf(sb, "\\%c", 436 ol->op_value[i]); 437 else 438 sbuf_printf(sb, "%c", 439 ol->op_value[i]); 440 } 441 sbuf_printf(sb, "%s", lend); 442 } else { 443 sbuf_printf(sb, "%s", lend); 444 } 445 } 446 /* 447 * Mark this file as containing everything we need. 448 */ 449 STAILQ_FOREACH(d, &dtab, d_next) 450 sbuf_printf(sb, "device\t%s%s", d->d_name, lend); 451 free(lend); 452} 453 454/* 455 * Generate file from the configuration files. 456 */ 457static void 458configfile_filebased(struct sbuf *sb) 459{ 460 FILE *cff; 461 struct cfgfile *cf; 462 int i; 463 464 /* 465 * Try to read all configuration files. Since those will be present as 466 * C string in the macro, we have to slash their ends then the line 467 * wraps. 468 */ 469 STAILQ_FOREACH(cf, &cfgfiles, cfg_next) { 470 cff = fopen(cf->cfg_path, "r"); 471 if (cff == NULL) { 472 warn("Couldn't open file %s", cf->cfg_path); 473 continue; 474 } 475 while ((i = getc(cff)) != EOF) { 476 if (i == '\n') 477 sbuf_printf(sb, "\\n\\\n"); 478 else if (i == '"' || i == '\'') 479 sbuf_printf(sb, "\\%c", i); 480 else 481 sbuf_putc(sb, i); 482 } 483 fclose(cff); 484 } 485} 486 487static void 488configfile(void) 489{ 490 FILE *fo; 491 struct sbuf *sb; 492 char *p; 493 494 /* Add main configuration file to the list of files to be included */ 495 cfgfile_add(PREFIX); 496 p = path("config.c.new"); 497 fo = fopen(p, "w"); 498 if (!fo) 499 err(2, "%s", p); 500 sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND); 501 assert(sb != NULL); 502 sbuf_clear(sb); 503 if (filebased) { 504 /* Is needed, can be used for backward compatibility. */ 505 configfile_filebased(sb); 506 } else { 507 configfile_dynamic(sb); 508 } 509 sbuf_finish(sb); 510 /* 511 * We print first part of the template, replace our tag with 512 * configuration files content and later continue writing our 513 * template. 514 */ 515 p = strstr(kernconfstr, KERNCONFTAG); 516 if (p == NULL) 517 errx(EXIT_FAILURE, "Something went terribly wrong!"); 518 *p = '\0'; 519 fprintf(fo, "%s", kernconfstr); 520 fprintf(fo, "%s", sbuf_data(sb)); 521 p += strlen(KERNCONFTAG); 522 fprintf(fo, "%s", p); 523 sbuf_delete(sb); 524 fclose(fo); 525 moveifchanged(path("config.c.new"), path("config.c")); 526 cfgfile_removeall(); 527} 528 529/* 530 * moveifchanged -- 531 * compare two files; rename if changed. 532 */ 533void 534moveifchanged(const char *from_name, const char *to_name) 535{ 536 char *p, *q; 537 int changed; 538 size_t tsize; 539 struct stat from_sb, to_sb; 540 int from_fd, to_fd; 541 542 changed = 0; 543 544 if ((from_fd = open(from_name, O_RDONLY)) < 0) 545 err(EX_OSERR, "moveifchanged open(%s)", from_name); 546 547 if ((to_fd = open(to_name, O_RDONLY)) < 0) 548 changed++; 549 550 if (!changed && fstat(from_fd, &from_sb) < 0) 551 err(EX_OSERR, "moveifchanged fstat(%s)", from_name); 552 553 if (!changed && fstat(to_fd, &to_sb) < 0) 554 err(EX_OSERR, "moveifchanged fstat(%s)", to_name); 555 556 if (!changed && from_sb.st_size != to_sb.st_size) 557 changed++; 558 559 tsize = (size_t)from_sb.st_size; 560 561 if (!changed) { 562 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0); 563 if (p == MAP_FAILED) 564 err(EX_OSERR, "mmap %s", from_name); 565 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0); 566 if (q == MAP_FAILED) 567 err(EX_OSERR, "mmap %s", to_name); 568 569 changed = memcmp(p, q, tsize); 570 munmap(p, tsize); 571 munmap(q, tsize); 572 } 573 if (changed) { 574 if (rename(from_name, to_name) < 0) 575 err(EX_OSERR, "rename(%s, %s)", from_name, to_name); 576 } else { 577 if (unlink(from_name) < 0) 578 err(EX_OSERR, "unlink(%s)", from_name); 579 } 580} 581 582static void 583cleanheaders(char *p) 584{ 585 DIR *dirp; 586 struct dirent *dp; 587 struct file_list *fl; 588 struct hdr_list *hl; 589 size_t len; 590 591 remember("y.tab.h"); 592 remember("setdefs.h"); 593 STAILQ_FOREACH(fl, &ftab, f_next) 594 remember(fl->f_fn); 595 596 /* 597 * Scan the build directory and clean out stuff that looks like 598 * it might have been a leftover NFOO header, etc. 599 */ 600 if ((dirp = opendir(p)) == NULL) 601 err(EX_OSERR, "opendir %s", p); 602 while ((dp = readdir(dirp)) != NULL) { 603 len = strlen(dp->d_name); 604 /* Skip non-headers */ 605 if (len < 2 || dp->d_name[len - 2] != '.' || 606 dp->d_name[len - 1] != 'h') 607 continue; 608 /* Skip special stuff, eg: bus_if.h, but check opt_*.h */ 609 if (strchr(dp->d_name, '_') && 610 strncmp(dp->d_name, "opt_", 4) != 0) 611 continue; 612 /* Check if it is a target file */ 613 for (hl = htab; hl != NULL; hl = hl->h_next) { 614 if (eq(dp->d_name, hl->h_name)) { 615 break; 616 } 617 } 618 if (hl) 619 continue; 620 printf("Removing stale header: %s\n", dp->d_name); 621 if (unlink(path(dp->d_name)) == -1) 622 warn("unlink %s", dp->d_name); 623 } 624 (void)closedir(dirp); 625} 626 627void 628remember(const char *file) 629{ 630 char *s; 631 struct hdr_list *hl; 632 633 if ((s = strrchr(file, '/')) != NULL) 634 s = ns(s + 1); 635 else 636 s = ns(file); 637 638 if (strchr(s, '_') && strncmp(s, "opt_", 4) != 0) { 639 free(s); 640 return; 641 } 642 for (hl = htab; hl != NULL; hl = hl->h_next) { 643 if (eq(s, hl->h_name)) { 644 free(s); 645 return; 646 } 647 } 648 hl = calloc(1, sizeof(*hl)); 649 if (hl == NULL) 650 err(EXIT_FAILURE, "calloc"); 651 hl->h_name = s; 652 hl->h_next = htab; 653 htab = hl; 654} 655 656/* 657 * This one is quick hack. Will be probably moved to elf(3) interface. 658 * It takes kernel configuration file name, passes it as an argument to 659 * elfdump -a, which output is parsed by some UNIX tools... 660 */ 661static void 662kernconfdump(const char *file) 663{ 664 struct stat st; 665 FILE *fp, *pp; 666 int error, len, osz, r; 667 unsigned int i, off, size, t1, t2, align; 668 char *cmd, *o; 669 670 r = open(file, O_RDONLY); 671 if (r == -1) 672 err(EXIT_FAILURE, "Couldn't open file '%s'", file); 673 error = fstat(r, &st); 674 if (error == -1) 675 err(EXIT_FAILURE, "fstat() failed"); 676 if (S_ISDIR(st.st_mode)) 677 errx(EXIT_FAILURE, "'%s' is a directory", file); 678 fp = fdopen(r, "r"); 679 if (fp == NULL) 680 err(EXIT_FAILURE, "fdopen() failed"); 681 osz = 1024; 682 o = calloc(1, osz); 683 if (o == NULL) 684 err(EXIT_FAILURE, "Couldn't allocate memory"); 685 /* ELF note section header. */ 686 asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 8 kern_conf" 687 "| tail -5 | cut -d ' ' -f 2 | paste - - - - -", file); 688 if (cmd == NULL) 689 errx(EXIT_FAILURE, "asprintf() failed"); 690 pp = popen(cmd, "r"); 691 if (pp == NULL) 692 errx(EXIT_FAILURE, "popen() failed"); 693 free(cmd); 694 len = fread(o, osz, 1, pp); 695 pclose(pp); 696 r = sscanf(o, "%d%d%d%d%d", &off, &size, &t1, &t2, &align); 697 free(o); 698 if (r != 5) 699 errx(EXIT_FAILURE, "File %s doesn't contain configuration " 700 "file. Either unsupported, or not compiled with " 701 "INCLUDE_CONFIG_FILE", file); 702 r = fseek(fp, off, SEEK_CUR); 703 if (r != 0) 704 err(EXIT_FAILURE, "fseek() failed"); 705 for (i = 0; i < size; i++) { 706 r = fgetc(fp); 707 if (r == EOF) 708 break; 709 if (r == '\0') { 710 assert(i == size - 1 && 711 ("\\0 found in the middle of a file")); 712 break; 713 } 714 fputc(r, stdout); 715 } 716 fclose(fp); 717} 718 719static void 720badversion(int versreq) 721{ 722 fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n"); 723 fprintf(stderr, "config version = %d, ", CONFIGVERS); 724 fprintf(stderr, "version required = %d\n\n", versreq); 725 fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n"); 726 fprintf(stderr, "with your /usr/src/sys and install a new config binary\n"); 727 fprintf(stderr, "before trying this again.\n\n"); 728 fprintf(stderr, "If running the new config fails check your config\n"); 729 fprintf(stderr, "file against the GENERIC or LINT config files for\n"); 730 fprintf(stderr, "changes in config syntax, or option/device naming\n"); 731 fprintf(stderr, "conventions\n\n"); 732 exit(1); 733} 734 735static void 736checkversion(void) 737{ 738 FILE *ifp; 739 char line[BUFSIZ]; 740 int versreq; 741 742 ifp = open_makefile_template(); 743 while (fgets(line, BUFSIZ, ifp) != 0) { 744 if (*line != '%') 745 continue; 746 if (strncmp(line, "%VERSREQ=", 9) != 0) 747 continue; 748 versreq = atoi(line + 9); 749 if (MAJOR_VERS(versreq) == MAJOR_VERS(CONFIGVERS) && 750 versreq <= CONFIGVERS) 751 continue; 752 badversion(versreq); 753 } 754 fclose(ifp); 755} 756