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