main.c revision 169647
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: head/usr.sbin/config/main.c 169647 2007-05-17 04:53:52Z imp $"; 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 (freopen("DEFAULTS", "r", stdin) != NULL) { 154 found_defaults = 1; 155 yyfile = "DEFAULTS"; 156 } else { 157 if (freopen(PREFIX, "r", stdin) == NULL) 158 err(2, "%s", PREFIX); 159 yyfile = PREFIX; 160 } 161 if (*destdir != '\0') { 162 len = strlen(destdir); 163 while (len > 1 && destdir[len - 1] == '/') 164 destdir[--len] = '\0'; 165 get_srcdir(); 166 } else { 167 strlcpy(destdir, CDIR, sizeof(destdir)); 168 strlcat(destdir, PREFIX, sizeof(destdir)); 169 } 170 171 p = path((char *)NULL); 172 if (stat(p, &buf)) { 173 if (mkdir(p, 0777)) 174 err(2, "%s", p); 175 } else if (!S_ISDIR(buf.st_mode)) 176 errx(2, "%s isn't a directory", p); 177 178 SLIST_INIT(&cputype); 179 SLIST_INIT(&mkopt); 180 SLIST_INIT(&opt); 181 SLIST_INIT(&rmopts); 182 STAILQ_INIT(&cfgfiles); 183 STAILQ_INIT(&dtab); 184 STAILQ_INIT(&fntab); 185 STAILQ_INIT(&ftab); 186 STAILQ_INIT(&hints); 187 if (yyparse()) 188 exit(3); 189 190 /* 191 * Ensure that required elements (machine, cpu, ident) are present. 192 */ 193 if (machinename == NULL) { 194 printf("Specify machine type, e.g. ``machine i386''\n"); 195 exit(1); 196 } 197 if (ident == NULL) { 198 printf("no ident line specified\n"); 199 exit(1); 200 } 201 if (SLIST_EMPTY(&cputype)) { 202 printf("cpu type must be specified\n"); 203 exit(1); 204 } 205 206 /* 207 * make symbolic links in compilation directory 208 * for "sys" (to make genassym.c work along with #include <sys/xxx>) 209 * and similarly for "machine". 210 */ 211 if (*srcdir == '\0') 212 (void)snprintf(xxx, sizeof(xxx), "../../include"); 213 else 214 (void)snprintf(xxx, sizeof(xxx), "%s/%s/include", 215 srcdir, machinename); 216 (void) unlink(path("machine")); 217 (void) symlink(xxx, path("machine")); 218 if (strcmp(machinename, machinearch) != 0) { 219 /* 220 * make symbolic links in compilation directory for 221 * machinearch, if it is different than machinename. 222 */ 223 if (*srcdir == '\0') 224 (void)snprintf(xxx, sizeof(xxx), "../../../%s/include", 225 machinearch); 226 else 227 (void)snprintf(xxx, sizeof(xxx), "%s/%s/include", 228 srcdir, machinearch); 229 (void) unlink(path(machinearch)); 230 (void) symlink(xxx, path(machinearch)); 231 } 232 configfile(); /* put config file into kernel*/ 233 options(); /* make options .h files */ 234 makefile(); /* build Makefile */ 235 makeenv(); /* build env.c */ 236 makehints(); /* build hints.c */ 237 headers(); /* make a lot of .h files */ 238 cleanheaders(p); 239 printf("Kernel build directory is %s\n", p); 240 printf("Don't forget to do ``make cleandepend && make depend''\n"); 241 exit(0); 242} 243 244/* 245 * get_srcdir 246 * determine the root of the kernel source tree 247 * and save that in srcdir. 248 */ 249static void 250get_srcdir(void) 251{ 252 253 if (realpath("../..", srcdir) == NULL) 254 errx(2, "Unable to find root of source tree"); 255} 256 257static void 258usage(void) 259{ 260 261 fprintf(stderr, "usage: config [-CgpV] [-d destdir] sysname\n"); 262 fprintf(stderr, " config -x kernel\n"); 263 exit(EX_USAGE); 264} 265 266/* 267 * get_word 268 * returns EOF on end of file 269 * NULL on end of line 270 * pointer to the word otherwise 271 */ 272char * 273get_word(FILE *fp) 274{ 275 static char line[80]; 276 int ch; 277 char *cp; 278 int escaped_nl = 0; 279 280begin: 281 while ((ch = getc(fp)) != EOF) 282 if (ch != ' ' && ch != '\t') 283 break; 284 if (ch == EOF) 285 return ((char *)EOF); 286 if (ch == '\\'){ 287 escaped_nl = 1; 288 goto begin; 289 } 290 if (ch == '\n') { 291 if (escaped_nl){ 292 escaped_nl = 0; 293 goto begin; 294 } 295 else 296 return (NULL); 297 } 298 cp = line; 299 *cp++ = ch; 300 while ((ch = getc(fp)) != EOF) { 301 if (isspace(ch)) 302 break; 303 *cp++ = ch; 304 } 305 *cp = 0; 306 if (ch == EOF) 307 return ((char *)EOF); 308 (void) ungetc(ch, fp); 309 return (line); 310} 311 312/* 313 * get_quoted_word 314 * like get_word but will accept something in double or single quotes 315 * (to allow embedded spaces). 316 */ 317char * 318get_quoted_word(FILE *fp) 319{ 320 static char line[256]; 321 int ch; 322 char *cp; 323 int escaped_nl = 0; 324 325begin: 326 while ((ch = getc(fp)) != EOF) 327 if (ch != ' ' && ch != '\t') 328 break; 329 if (ch == EOF) 330 return ((char *)EOF); 331 if (ch == '\\'){ 332 escaped_nl = 1; 333 goto begin; 334 } 335 if (ch == '\n') { 336 if (escaped_nl){ 337 escaped_nl = 0; 338 goto begin; 339 } 340 else 341 return (NULL); 342 } 343 cp = line; 344 if (ch == '"' || ch == '\'') { 345 int quote = ch; 346 347 while ((ch = getc(fp)) != EOF) { 348 if (ch == quote) 349 break; 350 if (ch == '\n') { 351 *cp = 0; 352 printf("config: missing quote reading `%s'\n", 353 line); 354 exit(2); 355 } 356 *cp++ = ch; 357 } 358 } else { 359 *cp++ = ch; 360 while ((ch = getc(fp)) != EOF) { 361 if (isspace(ch)) 362 break; 363 *cp++ = ch; 364 } 365 if (ch != EOF) 366 (void) ungetc(ch, fp); 367 } 368 *cp = 0; 369 if (ch == EOF) 370 return ((char *)EOF); 371 return (line); 372} 373 374/* 375 * prepend the path to a filename 376 */ 377char * 378path(const char *file) 379{ 380 char *cp = NULL; 381 382 if (file) 383 asprintf(&cp, "%s/%s", destdir, file); 384 else 385 cp = strdup(destdir); 386 return (cp); 387} 388 389/* 390 * Generate configuration file based on actual settings. With this mode, user 391 * will be able to obtain and build conifguration file with one command. 392 */ 393static void 394configfile_dynamic(struct sbuf *sb) 395{ 396 struct cputype *cput; 397 struct device *d; 398 struct opt *ol; 399 char *lend; 400 unsigned int i; 401 402 asprintf(&lend, "\\n\\\n"); 403 assert(lend != NULL); 404 sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend); 405 sbuf_printf(sb, "ident\t%s%s", ident, lend); 406 sbuf_printf(sb, "machine\t%s%s", machinename, lend); 407 SLIST_FOREACH(cput, &cputype, cpu_next) 408 sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend); 409 SLIST_FOREACH(ol, &mkopt, op_next) 410 sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name, 411 ol->op_value, lend); 412 SLIST_FOREACH(ol, &opt, op_next) { 413 if (strncmp(ol->op_name, "DEV_", 4) == 0) 414 continue; 415 sbuf_printf(sb, "options\t%s", ol->op_name); 416 if (ol->op_value != NULL) { 417 sbuf_putc(sb, '='); 418 for (i = 0; i < strlen(ol->op_value); i++) { 419 if (ol->op_value[i] == '"') 420 sbuf_printf(sb, "\\%c", 421 ol->op_value[i]); 422 else 423 sbuf_printf(sb, "%c", 424 ol->op_value[i]); 425 } 426 sbuf_printf(sb, "%s", lend); 427 } else { 428 sbuf_printf(sb, "%s", lend); 429 } 430 } 431 /* 432 * Mark this file as containing everything we need. 433 */ 434 STAILQ_FOREACH(d, &dtab, d_next) 435 sbuf_printf(sb, "device\t%s%s", d->d_name, lend); 436 free(lend); 437} 438 439/* 440 * Generate file from the configuration files. 441 */ 442static void 443configfile_filebased(struct sbuf *sb) 444{ 445 FILE *cff; 446 struct cfgfile *cf; 447 int i; 448 449 STAILQ_FOREACH(cf, &cfgfiles, cfg_next) { 450 cff = fopen(cf->cfg_path, "r"); 451 if (cff == NULL) { 452 warn("Couldn't open file %s", cf->cfg_path); 453 continue; 454 } 455 while ((i = getc(cff)) != EOF) { 456 if (i == '\n') 457 sbuf_printf(sb, "\\n\\\n"); 458 else if (i == '"' || i == '\'') 459 sbuf_printf(sb, "\\%c", i); 460 else 461 sbuf_putc(sb, i); 462 } 463 fclose(cff); 464 } 465} 466 467static void 468configfile(void) 469{ 470 FILE *fo; 471 struct sbuf *sb; 472 char *p; 473 474 /* Add main configuration file to the list of files to be included */ 475 cfgfile_add(PREFIX); 476 p = path("config.c.new"); 477 fo = fopen(p, "w"); 478 if (!fo) 479 err(2, "%s", p); 480 sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND); 481 assert(sb != NULL); 482 sbuf_clear(sb); 483 /* 484 * Try to read all configuration files. Since those will be present as 485 * C string in the macro, we have to slash their ends then the line 486 * wraps. 487 */ 488 if (filebased) { 489 /* Is needed, can be used for backward compatibility. */ 490 configfile_filebased(sb); 491 } else { 492 configfile_dynamic(sb); 493 } 494 sbuf_finish(sb); 495 /* 496 * We print first part of the tamplate, replace our tag with 497 * configuration files content and later continue writing our 498 * template. 499 */ 500 p = strstr(kernconfstr, KERNCONFTAG); 501 if (p == NULL) 502 errx(EXIT_FAILURE, "Something went terribly wrong!"); 503 *p = '\0'; 504 fprintf(fo, "%s", kernconfstr); 505 fprintf(fo, "%s", sbuf_data(sb)); 506 p += strlen(KERNCONFTAG); 507 fprintf(fo, "%s", p); 508 sbuf_delete(sb); 509 fclose(fo); 510 moveifchanged(path("config.c.new"), path("config.c")); 511 cfgfile_removeall(); 512} 513 514/* 515 * moveifchanged -- 516 * compare two files; rename if changed. 517 */ 518void 519moveifchanged(const char *from_name, const char *to_name) 520{ 521 char *p, *q; 522 int changed; 523 size_t tsize; 524 struct stat from_sb, to_sb; 525 int from_fd, to_fd; 526 527 changed = 0; 528 529 if ((from_fd = open(from_name, O_RDONLY)) < 0) 530 err(EX_OSERR, "moveifchanged open(%s)", from_name); 531 532 if ((to_fd = open(to_name, O_RDONLY)) < 0) 533 changed++; 534 535 if (!changed && fstat(from_fd, &from_sb) < 0) 536 err(EX_OSERR, "moveifchanged fstat(%s)", from_name); 537 538 if (!changed && fstat(to_fd, &to_sb) < 0) 539 err(EX_OSERR, "moveifchanged fstat(%s)", to_name); 540 541 if (!changed && from_sb.st_size != to_sb.st_size) 542 changed++; 543 544 tsize = (size_t)from_sb.st_size; 545 546 if (!changed) { 547 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0); 548#ifndef MAP_FAILED 549#define MAP_FAILED ((caddr_t) -1) 550#endif 551 if (p == MAP_FAILED) 552 err(EX_OSERR, "mmap %s", from_name); 553 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0); 554 if (q == MAP_FAILED) 555 err(EX_OSERR, "mmap %s", to_name); 556 557 changed = memcmp(p, q, tsize); 558 munmap(p, tsize); 559 munmap(q, tsize); 560 } 561 if (changed) { 562 if (rename(from_name, to_name) < 0) 563 err(EX_OSERR, "rename(%s, %s)", from_name, to_name); 564 } else { 565 if (unlink(from_name) < 0) 566 err(EX_OSERR, "unlink(%s)", from_name); 567 } 568} 569 570static void 571cleanheaders(char *p) 572{ 573 DIR *dirp; 574 struct dirent *dp; 575 struct file_list *fl; 576 struct hdr_list *hl; 577 int i; 578 579 remember("y.tab.h"); 580 remember("setdefs.h"); 581 STAILQ_FOREACH(fl, &ftab, f_next) 582 remember(fl->f_fn); 583 584 /* 585 * Scan the build directory and clean out stuff that looks like 586 * it might have been a leftover NFOO header, etc. 587 */ 588 if ((dirp = opendir(p)) == NULL) 589 err(EX_OSERR, "opendir %s", p); 590 while ((dp = readdir(dirp)) != NULL) { 591 i = dp->d_namlen - 2; 592 /* Skip non-headers */ 593 if (dp->d_name[i] != '.' || dp->d_name[i + 1] != 'h') 594 continue; 595 /* Skip special stuff, eg: bus_if.h, but check opt_*.h */ 596 if (index(dp->d_name, '_') && 597 strncmp(dp->d_name, "opt_", 4) != 0) 598 continue; 599 /* Check if it is a target file */ 600 for (hl = htab; hl != NULL; hl = hl->h_next) { 601 if (eq(dp->d_name, hl->h_name)) { 602 break; 603 } 604 } 605 if (hl) 606 continue; 607 printf("Removing stale header: %s\n", dp->d_name); 608 if (unlink(path(dp->d_name)) == -1) 609 warn("unlink %s", dp->d_name); 610 } 611 (void)closedir(dirp); 612} 613 614void 615remember(const char *file) 616{ 617 char *s; 618 struct hdr_list *hl; 619 620 if ((s = strrchr(file, '/')) != NULL) 621 s = ns(s + 1); 622 else 623 s = ns(file); 624 625 if (index(s, '_') && strncmp(s, "opt_", 4) != 0) { 626 free(s); 627 return; 628 } 629 for (hl = htab; hl != NULL; hl = hl->h_next) { 630 if (eq(s, hl->h_name)) { 631 free(s); 632 return; 633 } 634 } 635 hl = calloc(1, sizeof(*hl)); 636 hl->h_name = s; 637 hl->h_next = htab; 638 htab = hl; 639} 640 641/* 642 * This one is quick hack. Will be probably moved to elf(3) interface. 643 * It takes kernel configuration file name, passes it as an argument to 644 * elfdump -a, which output is parsed by some UNIX tools... 645 */ 646static void 647kernconfdump(const char *file) 648{ 649 struct stat st; 650 FILE *fp, *pp; 651 int error, len, osz, r; 652 unsigned int off, size; 653 char *cmd, *o; 654 655 r = open(file, O_RDONLY); 656 if (r == -1) 657 errx(EXIT_FAILURE, "Couldn't open file '%s'", file); 658 error = fstat(r, &st); 659 if (error == -1) 660 errx(EXIT_FAILURE, "fstat() failed"); 661 if (S_ISDIR(st.st_mode)) 662 errx(EXIT_FAILURE, "'%s' is a directory", file); 663 fp = fdopen(r, "r"); 664 if (fp == NULL) 665 errx(EXIT_FAILURE, "fdopen() failed"); 666 osz = 1024; 667 o = calloc(1, osz); 668 if (o == NULL) 669 errx(EXIT_FAILURE, "Couldn't allocate memory"); 670 /* ELF note section header. */ 671 asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 5 kern_conf" 672 "| tail -2 | cut -d ' ' -f 2 | paste - - -", file); 673 if (cmd == NULL) 674 errx(EXIT_FAILURE, "asprintf() failed"); 675 pp = popen(cmd, "r"); 676 if (pp == NULL) 677 errx(EXIT_FAILURE, "popen() failed"); 678 free(cmd); 679 len = fread(o, osz, 1, pp); 680 pclose(pp); 681 r = sscanf(o, "%d\t%d", &off, &size); 682 free(o); 683 if (r != 2) 684 errx(EXIT_FAILURE, "File %s doesn't contain configuration " 685 "file. Either unsupported, or not compiled with " 686 "INCLUDE_CONFIG_FILE", file); 687 r = fseek(fp, off, SEEK_CUR); 688 if (r != 0) 689 errx(EXIT_FAILURE, "fseek() failed"); 690 while ((r = fgetc(fp)) != EOF && size-- > 0) 691 fputc(r, stdout); 692 fclose(fp); 693} 694