1 /* $NetBSD: meta.c,v 1.17 2011/05/04 20:38:32 sjg Exp $ */ 2 3 /* 4 * Implement 'meta' mode. 5 * Adapted from John Birrell's patches to FreeBSD make. 6 * --sjg 7 */ 8 /* 9 * Copyright (c) 2009-2010, Juniper Networks, Inc. 10 * Portions Copyright (c) 2009, John Birrell. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #if defined(USE_META) 34 35 #ifdef HAVE_CONFIG_H 36 # include "config.h" 37 #endif 38 #include <sys/stat.h> 39 #include <sys/ioctl.h> 40 #include <fcntl.h> 41 #include <libgen.h> 42 #include <errno.h> 43 #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) 44 #include <err.h> 45 #endif 46 47 #include "make.h" 48 #include "job.h" 49 50 #ifdef HAVE_FILEMON_H 51 # include <filemon.h> 52 #endif 53 #if !defined(USE_FILEMON) && defined(FILEMON_SET_FD) 54 # define USE_FILEMON 55 #endif 56 57 static BuildMon Mybm; /* for compat */ 58 static Lst metaBailiwick; /* our scope of control */ 59 60 Boolean useMeta = FALSE; 61 static Boolean useFilemon = FALSE; 62 static Boolean writeMeta = FALSE; 63 static Boolean metaEnv = FALSE; /* don't save env unless asked */ 64 static Boolean metaVerbose = FALSE; 65 static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ 66 static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ 67 68 extern Boolean forceJobs; 69 extern Boolean comatMake; 70 71 #define MAKE_META_PREFIX ".MAKE.META.PREFIX" 72 73 #ifndef N2U 74 # define N2U(n, u) (((n) + ((u) - 1)) / (u)) 75 #endif 76 #ifndef ROUNDUP 77 # define ROUNDUP(n, u) (N2U((n), (u)) * (u)) 78 #endif 79 80 #if !defined(HAVE_STRSEP) 81 # define strsep(s, d) stresep((s), (d), 0) 82 #endif 83 84 /* 85 * Filemon is a kernel module which snoops certain syscalls. 86 * 87 * C chdir 88 * E exec 89 * F [v]fork 90 * L [sym]link 91 * M rename 92 * R read 93 * W write 94 * S stat 95 * 96 * See meta_oodate below - we mainly care about 'E' and 'R'. 97 * 98 * We can still use meta mode without filemon, but 99 * the benefits are more limited. 100 */ 101 #ifdef USE_FILEMON 102 # ifndef _PATH_FILEMON 103 # define _PATH_FILEMON "/dev/filemon" 104 # endif 105 106 /* 107 * Open the filemon device. 108 */ 109 static void 110 filemon_open(BuildMon *pbm) 111 { 112 int retry; 113 114 pbm->mon_fd = pbm->filemon_fd = -1; 115 if (!useFilemon) 116 return; 117 118 for (retry = 5; retry >= 0; retry--) { 119 if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0) 120 break; 121 } 122 123 if (pbm->filemon_fd < 0) { 124 useFilemon = FALSE; 125 warn("Could not open %s", _PATH_FILEMON); 126 return; 127 } 128 129 /* 130 * We use a file outside of '.' 131 * to avoid a FreeBSD kernel bug where unlink invalidates 132 * cwd causing getcwd to do a lot more work. 133 * We only care about the descriptor. 134 */ 135 pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); 136 if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) { 137 err(1, "Could not set filemon file descriptor!"); 138 } 139 /* we don't need these once we exec */ 140 (void)fcntl(pbm->mon_fd, F_SETFD, 1); 141 (void)fcntl(pbm->filemon_fd, F_SETFD, 1); 142 } 143 144 /* 145 * Read the build monitor output file and write records to the target's 146 * metadata file. 147 */ 148 static void 149 filemon_read(FILE *mfp, int fd) 150 { 151 FILE *fp; 152 char buf[BUFSIZ]; 153 154 /* Check if we're not writing to a meta data file.*/ 155 if (mfp == NULL) { 156 if (fd >= 0) 157 close(fd); /* not interested */ 158 return; 159 } 160 /* rewind */ 161 lseek(fd, SEEK_SET, 0); 162 if ((fp = fdopen(fd, "r")) == NULL) 163 err(1, "Could not read build monitor file '%d'", fd); 164 165 fprintf(mfp, "-- filemon acquired metadata --\n"); 166 167 while (fgets(buf, sizeof(buf), fp)) { 168 fprintf(mfp, "%s", buf); 169 } 170 fflush(mfp); 171 clearerr(fp); 172 fclose(fp); 173 } 174 #endif 175 176 /* 177 * when realpath() fails, 178 * we use this, to clean up ./ and ../ 179 */ 180 static void 181 eat_dots(char *buf, size_t bufsz, int dots) 182 { 183 char *cp; 184 char *cp2; 185 const char *eat; 186 size_t eatlen; 187 188 switch (dots) { 189 case 1: 190 eat = "/./"; 191 eatlen = 2; 192 break; 193 case 2: 194 eat = "/../"; 195 eatlen = 3; 196 break; 197 default: 198 return; 199 } 200 201 do { 202 cp = strstr(buf, eat); 203 if (cp) { 204 cp2 = cp + eatlen; 205 if (dots == 2 && cp > buf) { 206 do { 207 cp--; 208 } while (cp > buf && *cp != '/'); 209 } 210 if (*cp == '/') { 211 strlcpy(cp, cp2, bufsz - (cp - buf)); 212 } else { 213 return; /* can't happen? */ 214 } 215 } 216 } while (cp); 217 } 218 219 static char * 220 meta_name(struct GNode *gn, char *mname, size_t mnamelen, 221 const char *dname, 222 const char *tname) 223 { 224 char buf[MAXPATHLEN]; 225 char cwd[MAXPATHLEN]; 226 char *rp; 227 char *cp; 228 char *tp; 229 char *p[4]; /* >= number of possible uses */ 230 int i; 231 232 i = 0; 233 if (!dname) 234 dname = Var_Value(".OBJDIR", gn, &p[i++]); 235 if (!tname) 236 tname = Var_Value(TARGET, gn, &p[i++]); 237 238 if (realpath(dname, cwd)) 239 dname = cwd; 240 241 /* 242 * Weed out relative paths from the target file name. 243 * We have to be careful though since if target is a 244 * symlink, the result will be unstable. 245 * So we use realpath() just to get the dirname, and leave the 246 * basename as given to us. 247 */ 248 if ((cp = strrchr(tname, '/'))) { 249 if (realpath(tname, buf)) { 250 if ((rp = strrchr(buf, '/'))) { 251 rp++; 252 cp++; 253 if (strcmp(cp, rp) != 0) 254 strlcpy(rp, cp, sizeof(buf) - (rp - buf)); 255 } 256 tname = buf; 257 } else { 258 /* 259 * We likely have a directory which is about to be made. 260 * We pretend realpath() succeeded, to have a chance 261 * of generating the same meta file name that we will 262 * next time through. 263 */ 264 if (tname[0] == '/') { 265 strlcpy(buf, tname, sizeof(buf)); 266 } else { 267 snprintf(buf, sizeof(buf), "%s/%s", cwd, tname); 268 } 269 eat_dots(buf, sizeof(buf), 1); /* ./ */ 270 eat_dots(buf, sizeof(buf), 2); /* ../ */ 271 tname = buf; 272 } 273 } 274 /* on some systems dirname may modify its arg */ 275 tp = bmake_strdup(tname); 276 if (strcmp(dname, dirname(tp)) == 0) 277 snprintf(mname, mnamelen, "%s.meta", tname); 278 else { 279 snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); 280 281 /* 282 * Replace path separators in the file name after the 283 * current object directory path. 284 */ 285 cp = mname + strlen(dname) + 1; 286 287 while (*cp != '\0') { 288 if (*cp == '/') 289 *cp = '_'; 290 cp++; 291 } 292 } 293 free(tp); 294 for (i--; i >= 0; i--) { 295 if (p[i]) 296 free(p[i]); 297 } 298 return (mname); 299 } 300 301 /* 302 * Return true if running ${.MAKE} 303 * Bypassed if target is flagged .MAKE 304 */ 305 static int 306 is_submake(void *cmdp, void *gnp) 307 { 308 static char *p_make = NULL; 309 static int p_len; 310 char *cmd = cmdp; 311 GNode *gn = gnp; 312 char *mp = NULL; 313 char *cp; 314 char *cp2; 315 int rc = 0; /* keep looking */ 316 317 if (!p_make) { 318 p_make = Var_Value(".MAKE", gn, &cp); 319 p_len = strlen(p_make); 320 } 321 cp = strchr(cmd, '$'); 322 if ((cp)) { 323 mp = Var_Subst(NULL, cmd, gn, FALSE); 324 cmd = mp; 325 } 326 cp2 = strstr(cmd, p_make); 327 if ((cp2)) { 328 switch (cp2[p_len]) { 329 case '\0': 330 case ' ': 331 case '\t': 332 case '\n': 333 rc = 1; 334 break; 335 } 336 if (cp2 > cmd && rc > 0) { 337 switch (cp2[-1]) { 338 case ' ': 339 case '\t': 340 case '\n': 341 break; 342 default: 343 rc = 0; /* no match */ 344 break; 345 } 346 } 347 } 348 if (mp) 349 free(mp); 350 return (rc); 351 } 352 353 typedef struct meta_file_s { 354 FILE *fp; 355 GNode *gn; 356 } meta_file_t; 357 358 static int 359 printCMD(void *cmdp, void *mfpp) 360 { 361 meta_file_t *mfp = mfpp; 362 char *cmd = cmdp; 363 char *cp = NULL; 364 365 if (strchr(cmd, '$')) { 366 cmd = cp = Var_Subst(NULL, cmd, mfp->gn, FALSE); 367 } 368 fprintf(mfp->fp, "CMD %s\n", cmd); 369 if (cp) 370 free(cp); 371 return 0; 372 } 373 374 /* 375 * Certain node types never get a .meta file 376 */ 377 #define SKIP_META_TYPE(_type) do { \ 378 if ((gn->type & __CONCAT(OP_, _type))) { \ 379 if (DEBUG(META)) { \ 380 fprintf(debug_file, "Skipping meta for %s: .%s\n", \ 381 gn->name, __STRING(_type)); \ 382 } \ 383 return (NULL); \ 384 } \ 385 } while (0) 386 387 static FILE * 388 meta_create(BuildMon *pbm, GNode *gn) 389 { 390 extern char **environ; 391 meta_file_t mf; 392 char buf[MAXPATHLEN]; 393 char objdir[MAXPATHLEN]; 394 char **ptr; 395 const char *dname; 396 const char *tname; 397 char *fname; 398 const char *cp; 399 char *p[4]; /* >= possible uses */ 400 int i; 401 struct stat fs; 402 403 404 /* This may be a phony node which we don't want meta data for... */ 405 /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ 406 /* Or it may be explicitly flagged as .NOMETA */ 407 SKIP_META_TYPE(NOMETA); 408 /* Unless it is explicitly flagged as .META */ 409 if (!(gn->type & OP_META)) { 410 SKIP_META_TYPE(PHONY); 411 SKIP_META_TYPE(SPECIAL); 412 SKIP_META_TYPE(MAKE); 413 } 414 415 mf.fp = NULL; 416 417 i = 0; 418 419 dname = Var_Value(".OBJDIR", gn, &p[i++]); 420 tname = Var_Value(TARGET, gn, &p[i++]); 421 422 /* The object directory may not exist. Check it.. */ 423 if (stat(dname, &fs) != 0) { 424 if (DEBUG(META)) 425 fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", 426 gn->name); 427 goto out; 428 } 429 /* Check if there are no commands to execute. */ 430 if (Lst_IsEmpty(gn->commands)) { 431 if (DEBUG(META)) 432 fprintf(debug_file, "Skipping meta for %s: no commands\n", 433 gn->name); 434 goto out; 435 } 436 437 /* make sure these are canonical */ 438 if (realpath(dname, objdir)) 439 dname = objdir; 440 441 /* If we aren't in the object directory, don't create a meta file. */ 442 if (!metaCurdirOk && strcmp(curdir, dname) == 0) { 443 if (DEBUG(META)) 444 fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", 445 gn->name); 446 goto out; 447 } 448 if (!(gn->type & OP_META)) { 449 /* We do not generate .meta files for sub-makes */ 450 if (Lst_ForEach(gn->commands, is_submake, gn)) { 451 if (DEBUG(META)) 452 fprintf(debug_file, "Skipping meta for %s: .MAKE\n", 453 gn->name); 454 goto out; 455 } 456 } 457 458 if (metaVerbose) { 459 char *mp; 460 461 /* Describe the target we are building */ 462 mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, 0); 463 if (*mp) 464 fprintf(stdout, "%s\n", mp); 465 free(mp); 466 } 467 /* Get the basename of the target */ 468 if ((cp = strrchr(tname, '/')) == NULL) { 469 cp = tname; 470 } else { 471 cp++; 472 } 473 474 fflush(stdout); 475 476 if (strcmp(cp, makeDependfile) == 0) 477 goto out; 478 479 if (!writeMeta) 480 /* Don't create meta data. */ 481 goto out; 482 483 fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), 484 dname, tname); 485 486 #ifdef DEBUG_META_MODE 487 if (DEBUG(META)) 488 fprintf(debug_file, "meta_create: %s\n", fname); 489 #endif 490 491 if ((mf.fp = fopen(fname, "w")) == NULL) 492 err(1, "Could not open meta file '%s'", fname); 493 494 fprintf(mf.fp, "# Meta data file %s\n", fname); 495 496 mf.gn = gn; 497 498 Lst_ForEach(gn->commands, printCMD, &mf); 499 500 fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf))); 501 fprintf(mf.fp, "TARGET %s\n", tname); 502 503 if (metaEnv) { 504 for (ptr = environ; *ptr != NULL; ptr++) 505 fprintf(mf.fp, "ENV %s\n", *ptr); 506 } 507 508 fprintf(mf.fp, "-- command output --\n"); 509 fflush(mf.fp); 510 511 Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 512 Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); 513 514 out: 515 for (i--; i >= 0; i--) { 516 if (p[i]) 517 free(p[i]); 518 } 519 520 return (mf.fp); 521 } 522 523 static Boolean 524 boolValue(char *s) 525 { 526 switch(*s) { 527 case '0': 528 case 'N': 529 case 'n': 530 case 'F': 531 case 'f': 532 return FALSE; 533 } 534 return TRUE; 535 } 536 537 void 538 meta_init(const char *make_mode) 539 { 540 static int once = 0; 541 char *cp; 542 543 useMeta = TRUE; 544 useFilemon = TRUE; 545 writeMeta = TRUE; 546 547 if (make_mode) { 548 if (strstr(make_mode, "env")) 549 metaEnv = TRUE; 550 if (strstr(make_mode, "verb")) 551 metaVerbose = TRUE; 552 if (strstr(make_mode, "read")) 553 writeMeta = FALSE; 554 if (strstr(make_mode, "nofilemon")) 555 useFilemon = FALSE; 556 if ((cp = strstr(make_mode, "curdirok="))) { 557 metaCurdirOk = boolValue(&cp[9]); 558 } 559 if (strstr(make_mode, "ignore-cmd")) 560 metaIgnoreCMDs = TRUE; 561 /* for backwards compatability */ 562 Var_Set(".MAKE.META_CREATED", "${.MAKE.META.CREATED}", VAR_GLOBAL, 0); 563 Var_Set(".MAKE.META_FILES", "${.MAKE.META.FILES}", VAR_GLOBAL, 0); 564 } 565 if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { 566 /* 567 * The default value for MAKE_META_PREFIX 568 * prints the absolute path of the target. 569 * This works be cause :H will generate '.' if there is no / 570 * and :tA will resolve that to cwd. 571 */ 572 Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0); 573 } 574 if (once) 575 return; 576 once = 1; 577 memset(&Mybm, 0, sizeof(Mybm)); 578 /* 579 * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} 580 */ 581 metaBailiwick = Lst_Init(FALSE); 582 cp = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", VAR_GLOBAL, 0); 583 if (cp) { 584 str2Lst_Append(metaBailiwick, cp, NULL); 585 } 586 } 587 588 /* 589 * In each case below we allow for job==NULL 590 */ 591 void 592 meta_job_start(Job *job, GNode *gn) 593 { 594 BuildMon *pbm; 595 596 if (job != NULL) { 597 pbm = &job->bm; 598 } else { 599 pbm = &Mybm; 600 } 601 pbm->mfp = meta_create(pbm, gn); 602 #ifdef USE_FILEMON_ONCE 603 /* compat mode we open the filemon dev once per command */ 604 if (job == NULL) 605 return; 606 #endif 607 #ifdef USE_FILEMON 608 if (pbm->mfp != NULL && useFilemon) { 609 filemon_open(pbm); 610 } else { 611 pbm->mon_fd = pbm->filemon_fd = -1; 612 } 613 #endif 614 } 615 616 /* 617 * The child calls this before doing anything. 618 * It does not disturb our state. 619 */ 620 void 621 meta_job_child(Job *job) 622 { 623 #ifdef USE_FILEMON 624 BuildMon *pbm; 625 pid_t pid; 626 627 if (job != NULL) { 628 pbm = &job->bm; 629 } else { 630 pbm = &Mybm; 631 } 632 pid = getpid(); 633 if (pbm->mfp != NULL && useFilemon) { 634 if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) { 635 err(1, "Could not set filemon pid!"); 636 } 637 } 638 #endif 639 } 640 641 void 642 meta_job_error(Job *job, GNode *gn, int flags, int status) 643 { 644 char cwd[MAXPATHLEN]; 645 BuildMon *pbm; 646 647 if (job != NULL) { 648 pbm = &job->bm; 649 } else { 650 if (!gn) 651 gn = job->node; 652 pbm = &Mybm; 653 } 654 if (pbm->mfp != NULL) { 655 fprintf(pbm->mfp, "*** Error code %d%s\n", 656 status, 657 (flags & JOB_IGNERR) ? 658 "(ignored)" : ""); 659 } 660 if (gn) { 661 Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0); 662 } 663 getcwd(cwd, sizeof(cwd)); 664 Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0); 665 if (pbm && pbm->meta_fname[0]) { 666 Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0); 667 } 668 meta_job_finish(job); 669 } 670 671 void 672 meta_job_output(Job *job, char *cp, const char *nl) 673 { 674 BuildMon *pbm; 675 676 if (job != NULL) { 677 pbm = &job->bm; 678 } else { 679 pbm = &Mybm; 680 } 681 if (pbm->mfp != NULL) { 682 if (metaVerbose) { 683 static char *meta_prefix = NULL; 684 static int meta_prefix_len; 685 686 if (!meta_prefix) { 687 char *cp2; 688 689 meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", VAR_GLOBAL, 0); 690 if ((cp2 = strchr(meta_prefix, '$'))) 691 meta_prefix_len = cp2 - meta_prefix; 692 else 693 meta_prefix_len = strlen(meta_prefix); 694 } 695 if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { 696 cp = strchr(cp+1, '\n'); 697 if (!cp++) 698 return; 699 } 700 } 701 fprintf(pbm->mfp, "%s%s", cp, nl); 702 } 703 } 704 705 void 706 meta_cmd_finish(void *pbmp) 707 { 708 #ifdef USE_FILEMON 709 BuildMon *pbm = pbmp; 710 711 if (!pbm) 712 pbm = &Mybm; 713 714 if (pbm->filemon_fd >= 0) { 715 close(pbm->filemon_fd); 716 filemon_read(pbm->mfp, pbm->mon_fd); 717 pbm->filemon_fd = pbm->mon_fd = -1; 718 } 719 #endif 720 } 721 722 void 723 meta_job_finish(Job *job) 724 { 725 BuildMon *pbm; 726 727 if (job != NULL) { 728 pbm = &job->bm; 729 } else { 730 pbm = &Mybm; 731 } 732 if (pbm->mfp != NULL) { 733 meta_cmd_finish(pbm); 734 fclose(pbm->mfp); 735 pbm->mfp = NULL; 736 pbm->meta_fname[0] = '\0'; 737 } 738 } 739 740 /* 741 * Fetch a full line from fp - growing bufp if needed 742 * Return length in bufp. 743 */ 744 static int 745 fgetLine(char **bufp, size_t *szp, int o, FILE *fp) 746 { 747 char *buf = *bufp; 748 size_t bufsz = *szp; 749 struct stat fs; 750 int x; 751 752 if (fgets(&buf[o], bufsz - o, fp) != NULL) { 753 check_newline: 754 x = o + strlen(&buf[o]); 755 if (buf[x - 1] == '\n') 756 return x; 757 /* 758 * We need to grow the buffer. 759 * The meta file can give us a clue. 760 */ 761 if (fstat(fileno(fp), &fs) == 0) { 762 size_t newsz; 763 char *p; 764 765 newsz = ROUNDUP((fs.st_size / 2), BUFSIZ); 766 if (newsz <= bufsz) 767 newsz = ROUNDUP(fs.st_size, BUFSIZ); 768 if (DEBUG(META)) 769 fprintf(debug_file, "growing buffer %u -> %u\n", 770 bufsz, newsz); 771 p = bmake_realloc(buf, newsz); 772 if (p) { 773 *bufp = buf = p; 774 *szp = bufsz = newsz; 775 /* fetch the rest */ 776 if (!fgets(&buf[x], bufsz - x, fp)) 777 return x; /* truncated! */ 778 goto check_newline; 779 } 780 } 781 } 782 return 0; 783 } 784 785 static int 786 prefix_match(void *p, void *q) 787 { 788 const char *prefix = p; 789 const char *path = q; 790 size_t n = strlen(prefix); 791 792 return (0 == strncmp(path, prefix, n)); 793 } 794 795 static int 796 string_match(const void *p, const void *q) 797 { 798 const char *p1 = p; 799 const char *p2 = q; 800 801 return strcmp(p1, p2); 802 } 803 804 805 /* 806 * When running with 'meta' functionality, a target can be out-of-date 807 * if any of the references in it's meta data file is more recent. 808 * We have to track the latestdir on a per-process basis. 809 */ 810 #define LDIR_VNAME_FMT ".meta.%d.ldir" 811 812 Boolean 813 meta_oodate(GNode *gn, Boolean oodate) 814 { 815 static char *tmpdir = NULL; 816 static char cwd[MAXPATHLEN]; 817 char ldir_vname[64]; 818 char latestdir[MAXPATHLEN]; 819 char fname[MAXPATHLEN]; 820 char fname1[MAXPATHLEN]; 821 char fname2[MAXPATHLEN]; 822 char *p; 823 char *cp; 824 static size_t cwdlen = 0; 825 static size_t tmplen = 0; 826 FILE *fp; 827 Boolean ignoreOODATE = FALSE; 828 Lst missingFiles; 829 830 if (oodate) 831 return oodate; /* we're done */ 832 833 missingFiles = Lst_Init(FALSE); 834 835 /* 836 * We need to check if the target is out-of-date. This includes 837 * checking if the expanded command has changed. This in turn 838 * requires that all variables are set in the same way that they 839 * would be if the target needs to be re-built. 840 */ 841 Make_DoAllVar(gn); 842 843 meta_name(gn, fname, sizeof(fname), NULL, NULL); 844 845 #ifdef DEBUG_META_MODE 846 if (DEBUG(META)) 847 fprintf(debug_file, "meta_oodate: %s\n", fname); 848 #endif 849 850 if ((fp = fopen(fname, "r")) != NULL) { 851 static char *buf = NULL; 852 static size_t bufsz; 853 int lineno = 0; 854 int lastpid = 0; 855 int pid; 856 int f = 0; 857 int x; 858 LstNode ln; 859 struct stat fs; 860 861 if (!buf) { 862 bufsz = 8 * BUFSIZ; 863 buf = bmake_malloc(bufsz); 864 } 865 866 if (!cwdlen) { 867 if (getcwd(cwd, sizeof(cwd)) == NULL) 868 err(1, "Could not get current working directory"); 869 cwdlen = strlen(cwd); 870 } 871 872 if (!tmpdir) { 873 tmpdir = getTmpdir(); 874 tmplen = strlen(tmpdir); 875 } 876 877 /* we want to track all the .meta we read */ 878 Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 879 880 ln = Lst_First(gn->commands); 881 while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { 882 lineno++; 883 if (buf[x - 1] == '\n') 884 buf[x - 1] = '\0'; 885 else 886 warnx("%s: %d: line truncated at %u", fname, lineno, x); 887 888 /* Find the start of the build monitor section. */ 889 if (!f) { 890 if (strncmp(buf, "-- filemon", 10) == 0) { 891 f = 1; 892 continue; 893 } 894 if (strncmp(buf, "# buildmon", 10) == 0) { 895 f = 1; 896 continue; 897 } 898 } 899 900 /* Delimit the record type. */ 901 p = buf; 902 #ifdef DEBUG_META_MODE 903 if (DEBUG(META)) 904 fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); 905 #endif 906 strsep(&p, " "); 907 if (f) { 908 /* 909 * We are in the 'filemon' output section. 910 * Each record from filemon follows the general form: 911 * 912 * <key> <pid> <data> 913 * 914 * Where: 915 * <key> is a single letter, denoting the syscall. 916 * <pid> is the process that made the syscall. 917 * <data> is the arguments (of interest). 918 */ 919 switch(buf[0]) { 920 case '#': /* comment */ 921 case 'V': /* version */ 922 break; 923 default: 924 /* 925 * We need to track pathnames per-process. 926 * 927 * Each process run by make, starts off in the 'CWD' 928 * recorded in the .meta file, if it chdirs ('C') 929 * elsewhere we need to track that - but only for 930 * that process. If it forks ('F'), we initialize 931 * the child to have the same cwd as its parent. 932 * 933 * We also need to track the 'latestdir' of 934 * interest. This is usually the same as cwd, but 935 * not if a process is reading directories. 936 * 937 * Each time we spot a different process ('pid') 938 * we save the current value of 'latestdir' in a 939 * variable qualified by 'lastpid', and 940 * re-initialize 'latestdir' to any pre-saved 941 * value for the current 'pid' and 'CWD' if none. 942 */ 943 pid = atoi(p); 944 if (pid > 0 && pid != lastpid) { 945 char *ldir; 946 char *tp; 947 948 if (lastpid > 0) { 949 /* We need to remember this. */ 950 Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); 951 } 952 snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); 953 lastpid = pid; 954 ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); 955 if (ldir) { 956 strlcpy(latestdir, ldir, sizeof(latestdir)); 957 if (tp) 958 free(tp); 959 } else 960 strlcpy(latestdir, cwd, sizeof(latestdir)); 961 } 962 /* Skip past the pid. */ 963 if (strsep(&p, " ") == NULL) 964 continue; 965 #ifdef DEBUG_META_MODE 966 if (DEBUG(META)) 967 fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, latestdir); 968 #endif 969 break; 970 } 971 972 /* Process according to record type. */ 973 switch (buf[0]) { 974 case 'X': /* eXit */ 975 Var_Delete(ldir_vname, VAR_GLOBAL); 976 lastpid = 0; /* no need to save ldir_vname */ 977 break; 978 979 case 'F': /* [v]Fork */ 980 { 981 char cldir[64]; 982 int child; 983 984 child = atoi(p); 985 if (child > 0) { 986 snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); 987 Var_Set(cldir, latestdir, VAR_GLOBAL, 0); 988 } 989 } 990 break; 991 992 case 'C': /* Chdir */ 993 /* Update the latest directory. */ 994 strlcpy(latestdir, p, sizeof(latestdir)); 995 break; 996 997 case 'M': /* renaMe */ 998 if (Lst_IsEmpty(missingFiles)) 999 break; 1000 /* 'L' and 'M' put single quotes around the args */ 1001 if (*p == '\'') { 1002 char *ep; 1003 1004 p++; 1005 if ((ep = strchr(p, '\''))) 1006 *ep = '\0'; 1007 } 1008 /* FALLTHROUGH */ 1009 case 'D': /* unlink */ 1010 if (*p == '/' && !Lst_IsEmpty(missingFiles)) { 1011 /* remove p from the missingFiles list if present */ 1012 if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) { 1013 char *tp = Lst_Datum(ln); 1014 Lst_Remove(missingFiles, ln); 1015 free(tp); 1016 } 1017 } 1018 break; 1019 case 'L': /* Link */ 1020 /* we want the target */ 1021 if (strsep(&p, " ") == NULL) 1022 continue; 1023 /* 'L' and 'M' put single quotes around the args */ 1024 if (*p == '\'') { 1025 char *ep; 1026 1027 p++; 1028 if ((ep = strchr(p, '\''))) 1029 *ep = '\0'; 1030 } 1031 /* FALLTHROUGH */ 1032 case 'W': /* Write */ 1033 /* 1034 * If a file we generated within our bailiwick 1035 * but outside of .OBJDIR is missing, 1036 * we need to do it again. 1037 */ 1038 /* ignore non-absolute paths */ 1039 if (*p != '/') 1040 break; 1041 1042 if (Lst_IsEmpty(metaBailiwick)) 1043 break; 1044 1045 /* ignore cwd - normal dependencies handle those */ 1046 if (strncmp(p, cwd, cwdlen) == 0) 1047 break; 1048 1049 if (!Lst_ForEach(metaBailiwick, prefix_match, p)) 1050 break; 1051 1052 /* tmpdir might be within */ 1053 if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) 1054 break; 1055 1056 /* ignore anything containing the string "tmp" */ 1057 if ((strstr("tmp", p))) 1058 break; 1059 1060 if (stat(p, &fs) < 0) { 1061 Lst_AtEnd(missingFiles, bmake_strdup(p)); 1062 } 1063 break; 1064 case 'R': /* Read */ 1065 case 'E': /* Exec */ 1066 /* 1067 * Check for runtime files that can't 1068 * be part of the dependencies because 1069 * they are _expected_ to change. 1070 */ 1071 if (strncmp(p, "/tmp/", 5) == 0 || 1072 (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)) 1073 break; 1074 1075 if (strncmp(p, "/var/", 5) == 0) 1076 break; 1077 1078 /* Ignore device files. */ 1079 if (strncmp(p, "/dev/", 5) == 0) 1080 break; 1081 1082 /* Ignore /etc/ files. */ 1083 if (strncmp(p, "/etc/", 5) == 0) 1084 break; 1085 1086 /* 1087 * The rest of the record is the file name. 1088 * Check if it's not an absolute path. 1089 */ 1090 { 1091 char *sdirs[4]; 1092 char **sdp; 1093 int sdx = 0; 1094 int found = 0; 1095 1096 if (*p == '/') { 1097 sdirs[sdx++] = p; /* done */ 1098 } else { 1099 if (strcmp(".", p) == 0) 1100 continue; /* no point */ 1101 1102 /* Check vs latestdir */ 1103 snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); 1104 sdirs[sdx++] = fname1; 1105 1106 if (strcmp(latestdir, cwd) != 0) { 1107 /* Check vs cwd */ 1108 snprintf(fname2, sizeof(fname2), "%s/%s", cwd, p); 1109 sdirs[sdx++] = fname2; 1110 } 1111 } 1112 sdirs[sdx++] = NULL; 1113 1114 for (sdp = sdirs; *sdp && !found; sdp++) { 1115 #ifdef DEBUG_META_MODE 1116 if (DEBUG(META)) 1117 fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); 1118 #endif 1119 if (stat(*sdp, &fs) == 0) { 1120 found = 1; 1121 p = *sdp; 1122 } 1123 } 1124 if (found) { 1125 #ifdef DEBUG_META_MODE 1126 if (DEBUG(META)) 1127 fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); 1128 #endif 1129 if (!S_ISDIR(fs.st_mode) && 1130 fs.st_mtime > gn->mtime) { 1131 if (DEBUG(META)) 1132 fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); 1133 oodate = TRUE; 1134 } else if (S_ISDIR(fs.st_mode)) { 1135 /* Update the latest directory. */ 1136 realpath(p, latestdir); 1137 } 1138 } else if (errno == ENOENT && *p == '/' && 1139 strncmp(p, cwd, cwdlen) != 0) { 1140 /* 1141 * A referenced file outside of CWD is missing. 1142 * We cannot catch every eventuality here... 1143 */ 1144 if (DEBUG(META)) 1145 fprintf(debug_file, "%s: %d: file '%s' may have moved?...\n", fname, lineno, p); 1146 oodate = TRUE; 1147 } 1148 } 1149 break; 1150 default: 1151 break; 1152 } 1153 } else if (strcmp(buf, "CMD") == 0) { 1154 /* 1155 * Compare the current command with the one in the 1156 * meta data file. 1157 */ 1158 if (ln == NULL) { 1159 if (DEBUG(META)) 1160 fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); 1161 oodate = TRUE; 1162 } else { 1163 char *cmd = (char *)Lst_Datum(ln); 1164 1165 if (!ignoreOODATE) { 1166 if (strstr(cmd, "$?")) 1167 ignoreOODATE = TRUE; 1168 else if ((cp = strstr(cmd, ".OODATE"))) { 1169 /* check for $[{(].OODATE[)}] */ 1170 if (cp > cmd + 2 && cp[-2] == '$') 1171 ignoreOODATE = TRUE; 1172 } 1173 if (ignoreOODATE && DEBUG(META)) 1174 fprintf(debug_file, "%s: %d: cannot compare commands using .OODATE\n", fname, lineno); 1175 } 1176 cmd = Var_Subst(NULL, cmd, gn, TRUE); 1177 1178 if ((cp = strchr(cmd, '\n'))) { 1179 int n; 1180 1181 /* 1182 * This command contains newlines, we need to 1183 * fetch more from the .meta file before we 1184 * attempt a comparison. 1185 */ 1186 /* first put the newline back at buf[x - 1] */ 1187 buf[x - 1] = '\n'; 1188 do { 1189 /* now fetch the next line */ 1190 if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) 1191 break; 1192 x = n; 1193 lineno++; 1194 if (buf[x - 1] != '\n') { 1195 warnx("%s: %d: line truncated at %u", fname, lineno, x); 1196 break; 1197 } 1198 cp = strchr(++cp, '\n'); 1199 } while (cp); 1200 if (buf[x - 1] == '\n') 1201 buf[x - 1] = '\0'; 1202 } 1203 if (!ignoreOODATE && 1204 !(gn->type & OP_NOMETA_CMP) && 1205 strcmp(p, cmd) != 0) { 1206 if (DEBUG(META)) 1207 fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); 1208 if (!metaIgnoreCMDs) 1209 oodate = TRUE; 1210 } 1211 free(cmd); 1212 ln = Lst_Succ(ln); 1213 } 1214 } else if (strcmp(buf, "CWD") == 0) { 1215 /* 1216 * Check if there are extra commands now 1217 * that weren't in the meta data file. 1218 */ 1219 if (!oodate && ln != NULL) { 1220 if (DEBUG(META)) 1221 fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); 1222 oodate = TRUE; 1223 } 1224 if (strcmp(p, cwd) != 0) { 1225 if (DEBUG(META)) 1226 fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); 1227 oodate = TRUE; 1228 } 1229 } 1230 } 1231 1232 fclose(fp); 1233 if (!Lst_IsEmpty(missingFiles)) { 1234 if (DEBUG(META)) 1235 fprintf(debug_file, "%s: missing files: %s...\n", 1236 fname, (char *)Lst_Datum(Lst_First(missingFiles))); 1237 oodate = TRUE; 1238 Lst_Destroy(missingFiles, (FreeProc *)free); 1239 } 1240 } 1241 if (oodate && ignoreOODATE) { 1242 /* 1243 * Target uses .OODATE, so we need to re-compute it. 1244 * We need to clean up what Make_DoAllVar() did. 1245 */ 1246 Var_Delete(ALLSRC, gn); 1247 Var_Delete(OODATE, gn); 1248 gn->flags &= ~DONE_ALLSRC; 1249 } 1250 return oodate; 1251 } 1252 1253 /* support for compat mode */ 1254 1255 static int childPipe[2]; 1256 1257 void 1258 meta_compat_start(void) 1259 { 1260 #ifdef USE_FILEMON_ONCE 1261 /* 1262 * We need to re-open filemon for each cmd. 1263 */ 1264 BuildMon *pbm = &Mybm; 1265 1266 if (pbm->mfp != NULL && useFilemon) { 1267 filemon_open(pbm); 1268 } else { 1269 pbm->mon_fd = pbm->filemon_fd = -1; 1270 } 1271 #endif 1272 if (pipe(childPipe) < 0) 1273 Punt("Cannot create pipe: %s", strerror(errno)); 1274 /* Set close-on-exec flag for both */ 1275 (void)fcntl(childPipe[0], F_SETFD, 1); 1276 (void)fcntl(childPipe[1], F_SETFD, 1); 1277 } 1278 1279 void 1280 meta_compat_child(void) 1281 { 1282 meta_job_child(NULL); 1283 if (dup2(childPipe[1], 1) < 0 || 1284 dup2(1, 2) < 0) { 1285 execError("dup2", "pipe"); 1286 _exit(1); 1287 } 1288 } 1289 1290 void 1291 meta_compat_parent(void) 1292 { 1293 FILE *fp; 1294 char buf[BUFSIZ]; 1295 1296 close(childPipe[1]); /* child side */ 1297 fp = fdopen(childPipe[0], "r"); 1298 while (fgets(buf, sizeof(buf), fp)) { 1299 meta_job_output(NULL, buf, ""); 1300 printf("%s", buf); 1301 } 1302 fclose(fp); 1303 } 1304 1305 #endif /* USE_META */ 1306