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