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