]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bmake/meta.c
Update to bmake-20201101
[FreeBSD/FreeBSD.git] / contrib / bmake / meta.c
1 /*      $NetBSD: meta.c,v 1.136 2020/10/31 12:04:24 rillig 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-2016, 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 #ifdef HAVE_LIBGEN_H
40 #include <libgen.h>
41 #elif !defined(HAVE_DIRNAME)
42 char * dirname(char *);
43 #endif
44 #include <errno.h>
45 #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H)
46 #include <err.h>
47 #endif
48
49 #include "make.h"
50 #include "dir.h"
51 #include "job.h"
52
53 #ifdef USE_FILEMON
54 #include "filemon/filemon.h"
55 #endif
56
57 static BuildMon Mybm;                   /* for compat */
58 static StringList *metaBailiwick;       /* our scope of control */
59 static char *metaBailiwickStr;          /* string storage for the list */
60 static StringList *metaIgnorePaths;     /* paths we deliberately ignore */
61 static char *metaIgnorePathsStr;        /* string storage for the list */
62
63 #ifndef MAKE_META_IGNORE_PATHS
64 #define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS"
65 #endif
66 #ifndef MAKE_META_IGNORE_PATTERNS
67 #define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS"
68 #endif
69 #ifndef MAKE_META_IGNORE_FILTER
70 #define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
71 #endif
72
73 Boolean useMeta = FALSE;
74 static Boolean useFilemon = FALSE;
75 static Boolean writeMeta = FALSE;
76 static Boolean metaMissing = FALSE;     /* oodate if missing */
77 static Boolean filemonMissing = FALSE;  /* oodate if missing */
78 static Boolean metaEnv = FALSE;         /* don't save env unless asked */
79 static Boolean metaVerbose = FALSE;
80 static Boolean metaIgnoreCMDs = FALSE;  /* ignore CMDs in .meta files */
81 static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */
82 static Boolean metaIgnoreFilter = FALSE;   /* do we have more complex filtering? */
83 static Boolean metaCurdirOk = FALSE;    /* write .meta in .CURDIR Ok? */
84 static Boolean metaSilent = FALSE;      /* if we have a .meta be SILENT */
85
86 extern Boolean forceJobs;
87 extern Boolean comatMake;
88 extern char    **environ;
89
90 #define MAKE_META_PREFIX        ".MAKE.META.PREFIX"
91
92 #ifndef N2U
93 # define N2U(n, u)   (((n) + ((u) - 1)) / (u))
94 #endif
95 #ifndef ROUNDUP
96 # define ROUNDUP(n, u)   (N2U((n), (u)) * (u))
97 #endif
98
99 #if !defined(HAVE_STRSEP)
100 # define strsep(s, d) stresep((s), (d), 0)
101 #endif
102
103 /*
104  * Filemon is a kernel module which snoops certain syscalls.
105  *
106  * C chdir
107  * E exec
108  * F [v]fork
109  * L [sym]link
110  * M rename
111  * R read
112  * W write
113  * S stat
114  *
115  * See meta_oodate below - we mainly care about 'E' and 'R'.
116  *
117  * We can still use meta mode without filemon, but
118  * the benefits are more limited.
119  */
120 #ifdef USE_FILEMON
121
122 /*
123  * Open the filemon device.
124  */
125 static void
126 meta_open_filemon(BuildMon *pbm)
127 {
128     int dupfd;
129
130     pbm->mon_fd = -1;
131     pbm->filemon = NULL;
132     if (!useFilemon || !pbm->mfp)
133         return;
134
135     pbm->filemon = filemon_open();
136     if (pbm->filemon == NULL) {
137         useFilemon = FALSE;
138         warn("Could not open filemon %s", filemon_path());
139         return;
140     }
141
142     /*
143      * We use a file outside of '.'
144      * to avoid a FreeBSD kernel bug where unlink invalidates
145      * cwd causing getcwd to do a lot more work.
146      * We only care about the descriptor.
147      */
148     pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL);
149     if ((dupfd = dup(pbm->mon_fd)) == -1) {
150         err(1, "Could not dup filemon output!");
151     }
152     (void)fcntl(dupfd, F_SETFD, FD_CLOEXEC);
153     if (filemon_setfd(pbm->filemon, dupfd) == -1) {
154         err(1, "Could not set filemon file descriptor!");
155     }
156     /* we don't need these once we exec */
157     (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
158 }
159
160 /*
161  * Read the build monitor output file and write records to the target's
162  * metadata file.
163  */
164 static int
165 filemon_read(FILE *mfp, int fd)
166 {
167     char buf[BUFSIZ];
168     int error;
169
170     /* Check if we're not writing to a meta data file.*/
171     if (mfp == NULL) {
172         if (fd >= 0)
173             close(fd);                  /* not interested */
174         return 0;
175     }
176     /* rewind */
177     if (lseek(fd, (off_t)0, SEEK_SET) < 0) {
178         error = errno;
179         warn("Could not rewind filemon");
180         fprintf(mfp, "\n");
181     } else {
182         ssize_t n;
183
184         error = 0;
185         fprintf(mfp, "\n-- filemon acquired metadata --\n");
186
187         while ((n = read(fd, buf, sizeof(buf))) > 0) {
188             if ((ssize_t)fwrite(buf, 1, (size_t)n, mfp) < n)
189                 error = EIO;
190         }
191     }
192     fflush(mfp);
193     if (close(fd) < 0)
194         error = errno;
195     return error;
196 }
197 #endif
198
199 /*
200  * when realpath() fails,
201  * we use this, to clean up ./ and ../
202  */
203 static void
204 eat_dots(char *buf, size_t bufsz, int dots)
205 {
206     char *cp;
207     char *cp2;
208     const char *eat;
209     size_t eatlen;
210
211     switch (dots) {
212     case 1:
213         eat = "/./";
214         eatlen = 2;
215         break;
216     case 2:
217         eat = "/../";
218         eatlen = 3;
219         break;
220     default:
221         return;
222     }
223
224     do {
225         cp = strstr(buf, eat);
226         if (cp) {
227             cp2 = cp + eatlen;
228             if (dots == 2 && cp > buf) {
229                 do {
230                     cp--;
231                 } while (cp > buf && *cp != '/');
232             }
233             if (*cp == '/') {
234                 strlcpy(cp, cp2, bufsz - (size_t)(cp - buf));
235             } else {
236                 return;                 /* can't happen? */
237             }
238         }
239     } while (cp);
240 }
241
242 static char *
243 meta_name(char *mname, size_t mnamelen,
244           const char *dname,
245           const char *tname,
246           const char *cwd)
247 {
248     char buf[MAXPATHLEN];
249     char *rp;
250     char *cp;
251     char *tp;
252     char *dtp;
253     size_t ldname;
254
255     /*
256      * Weed out relative paths from the target file name.
257      * We have to be careful though since if target is a
258      * symlink, the result will be unstable.
259      * So we use realpath() just to get the dirname, and leave the
260      * basename as given to us.
261      */
262     if ((cp = strrchr(tname, '/'))) {
263         if (cached_realpath(tname, buf)) {
264             if ((rp = strrchr(buf, '/'))) {
265                 rp++;
266                 cp++;
267                 if (strcmp(cp, rp) != 0)
268                     strlcpy(rp, cp, sizeof buf - (size_t)(rp - buf));
269             }
270             tname = buf;
271         } else {
272             /*
273              * We likely have a directory which is about to be made.
274              * We pretend realpath() succeeded, to have a chance
275              * of generating the same meta file name that we will
276              * next time through.
277              */
278             if (tname[0] == '/') {
279                 strlcpy(buf, tname, sizeof(buf));
280             } else {
281                 snprintf(buf, sizeof(buf), "%s/%s", cwd, tname);
282             }
283             eat_dots(buf, sizeof(buf), 1);      /* ./ */
284             eat_dots(buf, sizeof(buf), 2);      /* ../ */
285             tname = buf;
286         }
287     }
288     /* on some systems dirname may modify its arg */
289     tp = bmake_strdup(tname);
290     dtp = dirname(tp);
291     if (strcmp(dname, dtp) == 0)
292         snprintf(mname, mnamelen, "%s.meta", tname);
293     else {
294         ldname = strlen(dname);
295         if (strncmp(dname, dtp, ldname) == 0 && dtp[ldname] == '/')
296             snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]);
297         else
298             snprintf(mname, mnamelen, "%s/%s.meta", dname, tname);
299
300         /*
301          * Replace path separators in the file name after the
302          * current object directory path.
303          */
304         cp = mname + strlen(dname) + 1;
305
306         while (*cp != '\0') {
307             if (*cp == '/')
308                 *cp = '_';
309             cp++;
310         }
311     }
312     free(tp);
313     return mname;
314 }
315
316 /*
317  * Return true if running ${.MAKE}
318  * Bypassed if target is flagged .MAKE
319  */
320 static int
321 is_submake(void *cmdp, void *gnp)
322 {
323     static const char *p_make = NULL;
324     static size_t p_len;
325     char  *cmd = cmdp;
326     GNode *gn = gnp;
327     char *mp = NULL;
328     char *cp;
329     char *cp2;
330     int rc = 0;                         /* keep looking */
331
332     if (!p_make) {
333         void *dontFreeIt;
334         p_make = Var_Value(".MAKE", gn, &dontFreeIt);
335         p_len = strlen(p_make);
336     }
337     cp = strchr(cmd, '$');
338     if ((cp)) {
339         (void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
340         /* TODO: handle errors */
341         cmd = mp;
342     }
343     cp2 = strstr(cmd, p_make);
344     if ((cp2)) {
345         switch (cp2[p_len]) {
346         case '\0':
347         case ' ':
348         case '\t':
349         case '\n':
350             rc = 1;
351             break;
352         }
353         if (cp2 > cmd && rc > 0) {
354             switch (cp2[-1]) {
355             case ' ':
356             case '\t':
357             case '\n':
358                 break;
359             default:
360                 rc = 0;                 /* no match */
361                 break;
362             }
363         }
364     }
365     free(mp);
366     return rc;
367 }
368
369 typedef struct meta_file_s {
370     FILE *fp;
371     GNode *gn;
372 } meta_file_t;
373
374 static void
375 printCMD(const char *cmd, meta_file_t *mfp)
376 {
377     char *cmd_freeIt = NULL;
378
379     if (strchr(cmd, '$')) {
380         (void)Var_Subst(cmd, mfp->gn, VARE_WANTRES, &cmd_freeIt);
381         /* TODO: handle errors */
382         cmd = cmd_freeIt;
383     }
384     fprintf(mfp->fp, "CMD %s\n", cmd);
385     free(cmd_freeIt);
386 }
387
388 static void
389 printCMDs(GNode *gn, meta_file_t *mf)
390 {
391     GNodeListNode *ln;
392
393     for (ln = gn->commands->first; ln != NULL; ln = ln->next)
394         printCMD(ln->datum, mf);
395 }
396
397 /*
398  * Certain node types never get a .meta file
399  */
400 #define SKIP_META_TYPE(_type) do { \
401     if ((gn->type & __CONCAT(OP_, _type))) { \
402         if (verbose) { \
403             debug_printf("Skipping meta for %s: .%s\n", \
404                     gn->name, __STRING(_type)); \
405         } \
406         return FALSE; \
407     } \
408 } while (0)
409
410
411 /*
412  * Do we need/want a .meta file ?
413  */
414 static Boolean
415 meta_needed(GNode *gn, const char *dname,
416              char *objdir, int verbose)
417 {
418     struct make_stat mst;
419
420     if (verbose)
421         verbose = DEBUG(META);
422
423     /* This may be a phony node which we don't want meta data for... */
424     /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */
425     /* Or it may be explicitly flagged as .NOMETA */
426     SKIP_META_TYPE(NOMETA);
427     /* Unless it is explicitly flagged as .META */
428     if (!(gn->type & OP_META)) {
429         SKIP_META_TYPE(PHONY);
430         SKIP_META_TYPE(SPECIAL);
431         SKIP_META_TYPE(MAKE);
432     }
433
434     /* Check if there are no commands to execute. */
435     if (Lst_IsEmpty(gn->commands)) {
436         if (verbose)
437             debug_printf("Skipping meta for %s: no commands\n", gn->name);
438         return FALSE;
439     }
440     if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
441         /* OP_SUBMAKE is a bit too aggressive */
442         if (Lst_ForEachUntil(gn->commands, is_submake, gn)) {
443             DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
444             return FALSE;
445         }
446     }
447
448     /* The object directory may not exist. Check it.. */
449     if (cached_stat(dname, &mst) != 0) {
450         if (verbose)
451             debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name);
452         return FALSE;
453     }
454
455     /* make sure these are canonical */
456     if (cached_realpath(dname, objdir))
457         dname = objdir;
458
459     /* If we aren't in the object directory, don't create a meta file. */
460     if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
461         if (verbose)
462             debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n",
463                          gn->name);
464         return FALSE;
465     }
466     return TRUE;
467 }
468
469
470 static FILE *
471 meta_create(BuildMon *pbm, GNode *gn)
472 {
473     meta_file_t mf;
474     char buf[MAXPATHLEN];
475     char objdir[MAXPATHLEN];
476     char **ptr;
477     const char *dname;
478     const char *tname;
479     char *fname;
480     const char *cp;
481     void *objdir_freeIt;
482
483     mf.fp = NULL;
484
485     dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
486     tname = GNode_VarTarget(gn);
487
488     /* if this succeeds objdir is realpath of dname */
489     if (!meta_needed(gn, dname, objdir, TRUE))
490         goto out;
491     dname = objdir;
492
493     if (metaVerbose) {
494         char *mp;
495
496         /* Describe the target we are building */
497         (void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
498         /* TODO: handle errors */
499         if (*mp)
500             fprintf(stdout, "%s\n", mp);
501         free(mp);
502     }
503     /* Get the basename of the target */
504     if ((cp = strrchr(tname, '/')) == NULL) {
505         cp = tname;
506     } else {
507         cp++;
508     }
509
510     fflush(stdout);
511
512     if (!writeMeta)
513         /* Don't create meta data. */
514         goto out;
515
516     fname = meta_name(pbm->meta_fname, sizeof(pbm->meta_fname),
517                       dname, tname, objdir);
518
519 #ifdef DEBUG_META_MODE
520     DEBUG1(META, "meta_create: %s\n", fname);
521 #endif
522
523     if ((mf.fp = fopen(fname, "w")) == NULL)
524         err(1, "Could not open meta file '%s'", fname);
525
526     fprintf(mf.fp, "# Meta data file %s\n", fname);
527
528     mf.gn = gn;
529
530     printCMDs(gn, &mf);
531
532     fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf)));
533     fprintf(mf.fp, "TARGET %s\n", tname);
534     cp = GNode_VarOodate(gn);
535     if (cp && *cp) {
536             fprintf(mf.fp, "OODATE %s\n", cp);
537     }
538     if (metaEnv) {
539         for (ptr = environ; *ptr != NULL; ptr++)
540             fprintf(mf.fp, "ENV %s\n", *ptr);
541     }
542
543     fprintf(mf.fp, "-- command output --\n");
544     fflush(mf.fp);
545
546     Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
547     Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL);
548
549     gn->type |= OP_META;                /* in case anyone wants to know */
550     if (metaSilent) {
551             gn->type |= OP_SILENT;
552     }
553  out:
554     bmake_free(objdir_freeIt);
555
556     return mf.fp;
557 }
558
559 static Boolean
560 boolValue(char *s)
561 {
562     switch(*s) {
563     case '0':
564     case 'N':
565     case 'n':
566     case 'F':
567     case 'f':
568         return FALSE;
569     }
570     return TRUE;
571 }
572
573 /*
574  * Initialization we need before reading makefiles.
575  */
576 void
577 meta_init(void)
578 {
579 #ifdef USE_FILEMON
580         /* this allows makefiles to test if we have filemon support */
581         Var_Set(".MAKE.PATH_FILEMON", filemon_path(), VAR_GLOBAL);
582 #endif
583 }
584
585
586 #define get_mode_bf(bf, token) \
587     if ((cp = strstr(make_mode, token))) \
588         bf = boolValue(&cp[sizeof(token) - 1])
589
590 /*
591  * Initialization we need after reading makefiles.
592  */
593 void
594 meta_mode_init(const char *make_mode)
595 {
596     static int once = 0;
597     char *cp;
598     void *freeIt;
599
600     useMeta = TRUE;
601     useFilemon = TRUE;
602     writeMeta = TRUE;
603
604     if (make_mode) {
605         if (strstr(make_mode, "env"))
606             metaEnv = TRUE;
607         if (strstr(make_mode, "verb"))
608             metaVerbose = TRUE;
609         if (strstr(make_mode, "read"))
610             writeMeta = FALSE;
611         if (strstr(make_mode, "nofilemon"))
612             useFilemon = FALSE;
613         if (strstr(make_mode, "ignore-cmd"))
614             metaIgnoreCMDs = TRUE;
615         if (useFilemon)
616             get_mode_bf(filemonMissing, "missing-filemon=");
617         get_mode_bf(metaCurdirOk, "curdirok=");
618         get_mode_bf(metaMissing, "missing-meta=");
619         get_mode_bf(metaSilent, "silent=");
620     }
621     if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) {
622         /*
623          * The default value for MAKE_META_PREFIX
624          * prints the absolute path of the target.
625          * This works be cause :H will generate '.' if there is no /
626          * and :tA will resolve that to cwd.
627          */
628         Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL);
629     }
630     if (once)
631         return;
632     once = 1;
633     memset(&Mybm, 0, sizeof(Mybm));
634     /*
635      * We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
636      */
637     metaBailiwick = Lst_New();
638     (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
639                     VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
640     /* TODO: handle errors */
641     str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
642     /*
643      * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
644      */
645     metaIgnorePaths = Lst_New();
646     Var_Append(MAKE_META_IGNORE_PATHS,
647                "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
648     (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
649                     VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
650     /* TODO: handle errors */
651     str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
652
653     /*
654      * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
655      */
656     freeIt = NULL;
657     if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &freeIt)) {
658         metaIgnorePatterns = TRUE;
659         bmake_free(freeIt);
660     }
661     freeIt = NULL;
662     if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &freeIt)) {
663         metaIgnoreFilter = TRUE;
664         bmake_free(freeIt);
665     }
666 }
667
668 /*
669  * In each case below we allow for job==NULL
670  */
671 void
672 meta_job_start(Job *job, GNode *gn)
673 {
674     BuildMon *pbm;
675
676     if (job != NULL) {
677         pbm = &job->bm;
678     } else {
679         pbm = &Mybm;
680     }
681     pbm->mfp = meta_create(pbm, gn);
682 #ifdef USE_FILEMON_ONCE
683     /* compat mode we open the filemon dev once per command */
684     if (job == NULL)
685         return;
686 #endif
687 #ifdef USE_FILEMON
688     if (pbm->mfp != NULL && useFilemon) {
689         meta_open_filemon(pbm);
690     } else {
691         pbm->mon_fd = -1;
692         pbm->filemon = NULL;
693     }
694 #endif
695 }
696
697 /*
698  * The child calls this before doing anything.
699  * It does not disturb our state.
700  */
701 void
702 meta_job_child(Job *job)
703 {
704 #ifdef USE_FILEMON
705     BuildMon *pbm;
706
707     if (job != NULL) {
708         pbm = &job->bm;
709     } else {
710         pbm = &Mybm;
711     }
712     if (pbm->mfp != NULL) {
713         close(fileno(pbm->mfp));
714         if (useFilemon && pbm->filemon) {
715             pid_t pid;
716
717             pid = getpid();
718             if (filemon_setpid_child(pbm->filemon, pid) == -1) {
719                 err(1, "Could not set filemon pid!");
720             }
721         }
722     }
723 #endif
724 }
725
726 void
727 meta_job_parent(Job *job, pid_t pid)
728 {
729 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
730     BuildMon *pbm;
731
732     if (job != NULL) {
733         pbm = &job->bm;
734     } else {
735         pbm = &Mybm;
736     }
737     if (useFilemon && pbm->filemon) {
738         filemon_setpid_parent(pbm->filemon, pid);
739     }
740 #endif
741 }
742
743 int
744 meta_job_fd(Job *job)
745 {
746 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
747     BuildMon *pbm;
748
749     if (job != NULL) {
750         pbm = &job->bm;
751     } else {
752         pbm = &Mybm;
753     }
754     if (useFilemon && pbm->filemon) {
755         return filemon_readfd(pbm->filemon);
756     }
757 #endif
758     return -1;
759 }
760
761 int
762 meta_job_event(Job *job)
763 {
764 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
765     BuildMon *pbm;
766
767     if (job != NULL) {
768         pbm = &job->bm;
769     } else {
770         pbm = &Mybm;
771     }
772     if (useFilemon && pbm->filemon) {
773         return filemon_process(pbm->filemon);
774     }
775 #endif
776     return 0;
777 }
778
779 void
780 meta_job_error(Job *job, GNode *gn, int flags, int status)
781 {
782     char cwd[MAXPATHLEN];
783     BuildMon *pbm;
784
785     if (job != NULL) {
786         pbm = &job->bm;
787         if (!gn)
788             gn = job->node;
789     } else {
790         pbm = &Mybm;
791     }
792     if (pbm->mfp != NULL) {
793         fprintf(pbm->mfp, "\n*** Error code %d%s\n",
794                 status,
795                 (flags & JOB_IGNERR) ?
796                 "(ignored)" : "");
797     }
798     if (gn) {
799         Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
800     }
801     getcwd(cwd, sizeof(cwd));
802     Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL);
803     if (pbm->meta_fname[0]) {
804         Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL);
805     }
806     meta_job_finish(job);
807 }
808
809 void
810 meta_job_output(Job *job, char *cp, const char *nl)
811 {
812     BuildMon *pbm;
813
814     if (job != NULL) {
815         pbm = &job->bm;
816     } else {
817         pbm = &Mybm;
818     }
819     if (pbm->mfp != NULL) {
820         if (metaVerbose) {
821             static char *meta_prefix = NULL;
822             static size_t meta_prefix_len;
823
824             if (!meta_prefix) {
825                 char *cp2;
826
827                 (void)Var_Subst("${" MAKE_META_PREFIX "}",
828                                 VAR_GLOBAL, VARE_WANTRES, &meta_prefix);
829                 /* TODO: handle errors */
830                 if ((cp2 = strchr(meta_prefix, '$')))
831                     meta_prefix_len = (size_t)(cp2 - meta_prefix);
832                 else
833                     meta_prefix_len = strlen(meta_prefix);
834             }
835             if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
836                 cp = strchr(cp+1, '\n');
837                 if (!cp++)
838                     return;
839             }
840         }
841         fprintf(pbm->mfp, "%s%s", cp, nl);
842     }
843 }
844
845 int
846 meta_cmd_finish(void *pbmp)
847 {
848     int error = 0;
849     BuildMon *pbm = pbmp;
850 #ifdef USE_FILEMON
851     int x;
852 #endif
853
854     if (!pbm)
855         pbm = &Mybm;
856
857 #ifdef USE_FILEMON
858     if (pbm->filemon) {
859         while (filemon_process(pbm->filemon) > 0)
860             continue;
861         if (filemon_close(pbm->filemon) == -1)
862             error = errno;
863         x = filemon_read(pbm->mfp, pbm->mon_fd);
864         if (error == 0 && x != 0)
865             error = x;
866         pbm->mon_fd = -1;
867         pbm->filemon = NULL;
868     } else
869 #endif
870         fprintf(pbm->mfp, "\n");        /* ensure end with newline */
871     return error;
872 }
873
874 int
875 meta_job_finish(Job *job)
876 {
877     BuildMon *pbm;
878     int error = 0;
879     int x;
880
881     if (job != NULL) {
882         pbm = &job->bm;
883     } else {
884         pbm = &Mybm;
885     }
886     if (pbm->mfp != NULL) {
887         error = meta_cmd_finish(pbm);
888         x = fclose(pbm->mfp);
889         if (error == 0 && x != 0)
890             error = errno;
891         pbm->mfp = NULL;
892         pbm->meta_fname[0] = '\0';
893     }
894     return error;
895 }
896
897 void
898 meta_finish(void)
899 {
900     if (metaBailiwick != NULL)
901         Lst_Free(metaBailiwick);
902     free(metaBailiwickStr);
903     if (metaIgnorePaths != NULL)
904         Lst_Free(metaIgnorePaths);
905     free(metaIgnorePathsStr);
906 }
907
908 /*
909  * Fetch a full line from fp - growing bufp if needed
910  * Return length in bufp.
911  */
912 static int
913 fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
914 {
915     char *buf = *bufp;
916     size_t bufsz = *szp;
917     struct stat fs;
918     int x;
919
920     if (fgets(&buf[o], (int)bufsz - o, fp) != NULL) {
921     check_newline:
922         x = o + (int)strlen(&buf[o]);
923         if (buf[x - 1] == '\n')
924             return x;
925         /*
926          * We need to grow the buffer.
927          * The meta file can give us a clue.
928          */
929         if (fstat(fileno(fp), &fs) == 0) {
930             size_t newsz;
931             char *p;
932
933             newsz = ROUNDUP(((size_t)fs.st_size / 2), BUFSIZ);
934             if (newsz <= bufsz)
935                 newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ);
936             if (newsz <= bufsz)
937                 return x;               /* truncated */
938             DEBUG2(META, "growing buffer %zu -> %zu\n", bufsz, newsz);
939             p = bmake_realloc(buf, newsz);
940             if (p) {
941                 *bufp = buf = p;
942                 *szp = bufsz = newsz;
943                 /* fetch the rest */
944                 if (!fgets(&buf[x], (int)bufsz - x, fp))
945                     return x;           /* truncated! */
946                 goto check_newline;
947             }
948         }
949     }
950     return 0;
951 }
952
953 /* Lst_ForEachUntil wants 1 to stop search */
954 static int
955 prefix_match(void *p, void *q)
956 {
957     const char *prefix = p;
958     const char *path = q;
959     size_t n = strlen(prefix);
960
961     return strncmp(path, prefix, n) == 0;
962 }
963
964 /* See if the path equals prefix or starts with "prefix/". */
965 static Boolean
966 path_starts_with(const char *path, const char *prefix)
967 {
968     size_t n = strlen(prefix);
969
970     if (strncmp(path, prefix, n) != 0)
971         return FALSE;
972     return path[n] == '\0' || path[n] == '/';
973 }
974
975 static int
976 meta_ignore(GNode *gn, const char *p)
977 {
978     char fname[MAXPATHLEN];
979
980     if (p == NULL)
981         return TRUE;
982
983     if (*p == '/') {
984         cached_realpath(p, fname); /* clean it up */
985         if (Lst_ForEachUntil(metaIgnorePaths, prefix_match, fname)) {
986 #ifdef DEBUG_META_MODE
987             DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
988 #endif
989             return TRUE;
990         }
991     }
992
993     if (metaIgnorePatterns) {
994         const char *expr;
995         char *pm;
996
997         Var_Set(".p.", p, gn);
998         expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
999         (void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
1000         /* TODO: handle errors */
1001         if (*pm) {
1002 #ifdef DEBUG_META_MODE
1003             DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
1004 #endif
1005             free(pm);
1006             return TRUE;
1007         }
1008         free(pm);
1009     }
1010
1011     if (metaIgnoreFilter) {
1012         char *fm;
1013
1014         /* skip if filter result is empty */
1015         snprintf(fname, sizeof(fname),
1016                  "${%s:L:${%s:ts:}}",
1017                  p, MAKE_META_IGNORE_FILTER);
1018         (void)Var_Subst(fname, gn, VARE_WANTRES, &fm);
1019         /* TODO: handle errors */
1020         if (*fm == '\0') {
1021 #ifdef DEBUG_META_MODE
1022             DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p);
1023 #endif
1024             free(fm);
1025             return TRUE;
1026         }
1027         free(fm);
1028     }
1029     return FALSE;
1030 }
1031
1032 /*
1033  * When running with 'meta' functionality, a target can be out-of-date
1034  * if any of the references in its meta data file is more recent.
1035  * We have to track the latestdir on a per-process basis.
1036  */
1037 #define LCWD_VNAME_FMT ".meta.%d.lcwd"
1038 #define LDIR_VNAME_FMT ".meta.%d.ldir"
1039
1040 /*
1041  * It is possible that a .meta file is corrupted,
1042  * if we detect this we want to reproduce it.
1043  * Setting oodate TRUE will have that effect.
1044  */
1045 #define CHECK_VALID_META(p) if (!(p && *p)) { \
1046     warnx("%s: %d: malformed", fname, lineno); \
1047     oodate = TRUE; \
1048     continue; \
1049     }
1050
1051 #define DEQUOTE(p) if (*p == '\'') {    \
1052     char *ep; \
1053     p++; \
1054     if ((ep = strchr(p, '\''))) \
1055         *ep = '\0'; \
1056     }
1057
1058 static void
1059 append_if_new(StringList *list, const char *str)
1060 {
1061     StringListNode *ln;
1062
1063     for (ln = list->first; ln != NULL; ln = ln->next)
1064         if (strcmp(ln->datum, str) == 0)
1065             return;
1066     Lst_Append(list, bmake_strdup(str));
1067 }
1068
1069 Boolean
1070 meta_oodate(GNode *gn, Boolean oodate)
1071 {
1072     static char *tmpdir = NULL;
1073     static char cwd[MAXPATHLEN];
1074     char lcwd_vname[64];
1075     char ldir_vname[64];
1076     char lcwd[MAXPATHLEN];
1077     char latestdir[MAXPATHLEN];
1078     char fname[MAXPATHLEN];
1079     char fname1[MAXPATHLEN];
1080     char fname2[MAXPATHLEN];
1081     char fname3[MAXPATHLEN];
1082     const char *dname;
1083     const char *tname;
1084     char *p;
1085     char *cp;
1086     char *link_src;
1087     char *move_target;
1088     static size_t cwdlen = 0;
1089     static size_t tmplen = 0;
1090     FILE *fp;
1091     Boolean needOODATE = FALSE;
1092     StringList *missingFiles;
1093     int have_filemon = FALSE;
1094     void *objdir_freeIt;
1095
1096     if (oodate)
1097         return oodate;          /* we're done */
1098
1099     dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
1100     tname = GNode_VarTarget(gn);
1101
1102     /* if this succeeds fname3 is realpath of dname */
1103     if (!meta_needed(gn, dname, fname3, FALSE))
1104         goto oodate_out;
1105     dname = fname3;
1106
1107     missingFiles = Lst_New();
1108
1109     /*
1110      * We need to check if the target is out-of-date. This includes
1111      * checking if the expanded command has changed. This in turn
1112      * requires that all variables are set in the same way that they
1113      * would be if the target needs to be re-built.
1114      */
1115     Make_DoAllVar(gn);
1116
1117     meta_name(fname, sizeof(fname), dname, tname, dname);
1118
1119 #ifdef DEBUG_META_MODE
1120     DEBUG1(META, "meta_oodate: %s\n", fname);
1121 #endif
1122
1123     if ((fp = fopen(fname, "r")) != NULL) {
1124         static char *buf = NULL;
1125         static size_t bufsz;
1126         int lineno = 0;
1127         int lastpid = 0;
1128         int pid;
1129         int x;
1130         StringListNode *cmdNode;
1131         struct make_stat mst;
1132
1133         if (!buf) {
1134             bufsz = 8 * BUFSIZ;
1135             buf = bmake_malloc(bufsz);
1136         }
1137
1138         if (!cwdlen) {
1139             if (getcwd(cwd, sizeof(cwd)) == NULL)
1140                 err(1, "Could not get current working directory");
1141             cwdlen = strlen(cwd);
1142         }
1143         strlcpy(lcwd, cwd, sizeof(lcwd));
1144         strlcpy(latestdir, cwd, sizeof(latestdir));
1145
1146         if (!tmpdir) {
1147             tmpdir = getTmpdir();
1148             tmplen = strlen(tmpdir);
1149         }
1150
1151         /* we want to track all the .meta we read */
1152         Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
1153
1154         cmdNode = gn->commands->first;
1155         while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
1156             lineno++;
1157             if (buf[x - 1] == '\n')
1158                 buf[x - 1] = '\0';
1159             else {
1160                 warnx("%s: %d: line truncated at %u", fname, lineno, x);
1161                 oodate = TRUE;
1162                 break;
1163             }
1164             link_src = NULL;
1165             move_target = NULL;
1166             /* Find the start of the build monitor section. */
1167             if (!have_filemon) {
1168                 if (strncmp(buf, "-- filemon", 10) == 0) {
1169                     have_filemon = TRUE;
1170                     continue;
1171                 }
1172                 if (strncmp(buf, "# buildmon", 10) == 0) {
1173                     have_filemon = TRUE;
1174                     continue;
1175                 }
1176             }
1177
1178             /* Delimit the record type. */
1179             p = buf;
1180 #ifdef DEBUG_META_MODE
1181             DEBUG3(META, "%s: %d: %s\n", fname, lineno, buf);
1182 #endif
1183             strsep(&p, " ");
1184             if (have_filemon) {
1185                 /*
1186                  * We are in the 'filemon' output section.
1187                  * Each record from filemon follows the general form:
1188                  *
1189                  * <key> <pid> <data>
1190                  *
1191                  * Where:
1192                  * <key> is a single letter, denoting the syscall.
1193                  * <pid> is the process that made the syscall.
1194                  * <data> is the arguments (of interest).
1195                  */
1196                 switch(buf[0]) {
1197                 case '#':               /* comment */
1198                 case 'V':               /* version */
1199                     break;
1200                 default:
1201                     /*
1202                      * We need to track pathnames per-process.
1203                      *
1204                      * Each process run by make, starts off in the 'CWD'
1205                      * recorded in the .meta file, if it chdirs ('C')
1206                      * elsewhere we need to track that - but only for
1207                      * that process.  If it forks ('F'), we initialize
1208                      * the child to have the same cwd as its parent.
1209                      *
1210                      * We also need to track the 'latestdir' of
1211                      * interest.  This is usually the same as cwd, but
1212                      * not if a process is reading directories.
1213                      *
1214                      * Each time we spot a different process ('pid')
1215                      * we save the current value of 'latestdir' in a
1216                      * variable qualified by 'lastpid', and
1217                      * re-initialize 'latestdir' to any pre-saved
1218                      * value for the current 'pid' and 'CWD' if none.
1219                      */
1220                     CHECK_VALID_META(p);
1221                     pid = atoi(p);
1222                     if (pid > 0 && pid != lastpid) {
1223                         const char *ldir;
1224                         void *tp;
1225
1226                         if (lastpid > 0) {
1227                             /* We need to remember these. */
1228                             Var_Set(lcwd_vname, lcwd, VAR_GLOBAL);
1229                             Var_Set(ldir_vname, latestdir, VAR_GLOBAL);
1230                         }
1231                         snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid);
1232                         snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid);
1233                         lastpid = pid;
1234                         ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
1235                         if (ldir) {
1236                             strlcpy(latestdir, ldir, sizeof(latestdir));
1237                             bmake_free(tp);
1238                         }
1239                         ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
1240                         if (ldir) {
1241                             strlcpy(lcwd, ldir, sizeof(lcwd));
1242                             bmake_free(tp);
1243                         }
1244                     }
1245                     /* Skip past the pid. */
1246                     if (strsep(&p, " ") == NULL)
1247                         continue;
1248 #ifdef DEBUG_META_MODE
1249                     if (DEBUG(META))
1250                         debug_printf("%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
1251                                      fname, lineno,
1252                                      pid, buf[0], cwd, lcwd, latestdir);
1253 #endif
1254                     break;
1255                 }
1256
1257                 CHECK_VALID_META(p);
1258
1259                 /* Process according to record type. */
1260                 switch (buf[0]) {
1261                 case 'X':               /* eXit */
1262                     Var_Delete(lcwd_vname, VAR_GLOBAL);
1263                     Var_Delete(ldir_vname, VAR_GLOBAL);
1264                     lastpid = 0;        /* no need to save ldir_vname */
1265                     break;
1266
1267                 case 'F':               /* [v]Fork */
1268                     {
1269                         char cldir[64];
1270                         int child;
1271
1272                         child = atoi(p);
1273                         if (child > 0) {
1274                             snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child);
1275                             Var_Set(cldir, lcwd, VAR_GLOBAL);
1276                             snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child);
1277                             Var_Set(cldir, latestdir, VAR_GLOBAL);
1278 #ifdef DEBUG_META_MODE
1279                             if (DEBUG(META))
1280                                 debug_printf(
1281                                         "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
1282                                         fname, lineno,
1283                                         child, cwd, lcwd, latestdir);
1284 #endif
1285                         }
1286                     }
1287                     break;
1288
1289                 case 'C':               /* Chdir */
1290                     /* Update lcwd and latest directory. */
1291                     strlcpy(latestdir, p, sizeof(latestdir));
1292                     strlcpy(lcwd, p, sizeof(lcwd));
1293                     Var_Set(lcwd_vname, lcwd, VAR_GLOBAL);
1294                     Var_Set(ldir_vname, lcwd, VAR_GLOBAL);
1295 #ifdef DEBUG_META_MODE
1296                     DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
1297                            fname, lineno, cwd, lcwd);
1298 #endif
1299                     break;
1300
1301                 case 'M':               /* renaMe */
1302                     /*
1303                      * For 'M'oves we want to check
1304                      * the src as for 'R'ead
1305                      * and the target as for 'W'rite.
1306                      */
1307                     cp = p;             /* save this for a second */
1308                     /* now get target */
1309                     if (strsep(&p, " ") == NULL)
1310                         continue;
1311                     CHECK_VALID_META(p);
1312                     move_target = p;
1313                     p = cp;
1314                     /* 'L' and 'M' put single quotes around the args */
1315                     DEQUOTE(p);
1316                     DEQUOTE(move_target);
1317                     /* FALLTHROUGH */
1318                 case 'D':               /* unlink */
1319                     if (*p == '/') {
1320                         /* remove any missingFiles entries that match p */
1321                         StringListNode *ln = missingFiles->first;
1322                         while (ln != NULL) {
1323                             StringListNode *next = ln->next;
1324                             if (path_starts_with(ln->datum, p)) {
1325                                 free(ln->datum);
1326                                 Lst_Remove(missingFiles, ln);
1327                             }
1328                             ln = next;
1329                         }
1330                     }
1331                     if (buf[0] == 'M') {
1332                         /* the target of the mv is a file 'W'ritten */
1333 #ifdef DEBUG_META_MODE
1334                         DEBUG2(META, "meta_oodate: M %s -> %s\n",
1335                                p, move_target);
1336 #endif
1337                         p = move_target;
1338                         goto check_write;
1339                     }
1340                     break;
1341                 case 'L':               /* Link */
1342                     /*
1343                      * For 'L'inks check
1344                      * the src as for 'R'ead
1345                      * and the target as for 'W'rite.
1346                      */
1347                     link_src = p;
1348                     /* now get target */
1349                     if (strsep(&p, " ") == NULL)
1350                         continue;
1351                     CHECK_VALID_META(p);
1352                     /* 'L' and 'M' put single quotes around the args */
1353                     DEQUOTE(p);
1354                     DEQUOTE(link_src);
1355 #ifdef DEBUG_META_MODE
1356                     DEBUG2(META, "meta_oodate: L %s -> %s\n", link_src, p);
1357 #endif
1358                     /* FALLTHROUGH */
1359                 case 'W':               /* Write */
1360                 check_write:
1361                     /*
1362                      * If a file we generated within our bailiwick
1363                      * but outside of .OBJDIR is missing,
1364                      * we need to do it again.
1365                      */
1366                     /* ignore non-absolute paths */
1367                     if (*p != '/')
1368                         break;
1369
1370                     if (Lst_IsEmpty(metaBailiwick))
1371                         break;
1372
1373                     /* ignore cwd - normal dependencies handle those */
1374                     if (strncmp(p, cwd, cwdlen) == 0)
1375                         break;
1376
1377                     if (!Lst_ForEachUntil(metaBailiwick, prefix_match, p))
1378                         break;
1379
1380                     /* tmpdir might be within */
1381                     if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)
1382                         break;
1383
1384                     /* ignore anything containing the string "tmp" */
1385                     if ((strstr("tmp", p)))
1386                         break;
1387
1388                     if ((link_src != NULL && cached_lstat(p, &mst) < 0) ||
1389                         (link_src == NULL && cached_stat(p, &mst) < 0)) {
1390                         if (!meta_ignore(gn, p))
1391                             append_if_new(missingFiles, p);
1392                     }
1393                     break;
1394                 check_link_src:
1395                     p = link_src;
1396                     link_src = NULL;
1397 #ifdef DEBUG_META_MODE
1398                     DEBUG1(META, "meta_oodate: L src %s\n", p);
1399 #endif
1400                     /* FALLTHROUGH */
1401                 case 'R':               /* Read */
1402                 case 'E':               /* Exec */
1403                     /*
1404                      * Check for runtime files that can't
1405                      * be part of the dependencies because
1406                      * they are _expected_ to change.
1407                      */
1408                     if (meta_ignore(gn, p))
1409                         break;
1410
1411                     /*
1412                      * The rest of the record is the file name.
1413                      * Check if it's not an absolute path.
1414                      */
1415                     {
1416                         char *sdirs[4];
1417                         char **sdp;
1418                         int sdx = 0;
1419                         int found = 0;
1420
1421                         if (*p == '/') {
1422                             sdirs[sdx++] = p; /* done */
1423                         } else {
1424                             if (strcmp(".", p) == 0)
1425                                 continue;  /* no point */
1426
1427                             /* Check vs latestdir */
1428                             snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p);
1429                             sdirs[sdx++] = fname1;
1430
1431                             if (strcmp(latestdir, lcwd) != 0) {
1432                                 /* Check vs lcwd */
1433                                 snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p);
1434                                 sdirs[sdx++] = fname2;
1435                             }
1436                             if (strcmp(lcwd, cwd) != 0) {
1437                                 /* Check vs cwd */
1438                                 snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p);
1439                                 sdirs[sdx++] = fname3;
1440                             }
1441                         }
1442                         sdirs[sdx++] = NULL;
1443
1444                         for (sdp = sdirs; *sdp && !found; sdp++) {
1445 #ifdef DEBUG_META_MODE
1446                             DEBUG3(META, "%s: %d: looking for: %s\n",
1447                                    fname, lineno, *sdp);
1448 #endif
1449                             if (cached_stat(*sdp, &mst) == 0) {
1450                                 found = 1;
1451                                 p = *sdp;
1452                             }
1453                         }
1454                         if (found) {
1455 #ifdef DEBUG_META_MODE
1456                             DEBUG3(META, "%s: %d: found: %s\n",
1457                                    fname, lineno, p);
1458 #endif
1459                             if (!S_ISDIR(mst.mst_mode) &&
1460                                 mst.mst_mtime > gn->mtime) {
1461                                 DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
1462                                        fname, lineno, p);
1463                                 oodate = TRUE;
1464                             } else if (S_ISDIR(mst.mst_mode)) {
1465                                 /* Update the latest directory. */
1466                                 cached_realpath(p, latestdir);
1467                             }
1468                         } else if (errno == ENOENT && *p == '/' &&
1469                                    strncmp(p, cwd, cwdlen) != 0) {
1470                             /*
1471                              * A referenced file outside of CWD is missing.
1472                              * We cannot catch every eventuality here...
1473                              */
1474                             append_if_new(missingFiles, p);
1475                         }
1476                     }
1477                     if (buf[0] == 'E') {
1478                         /* previous latestdir is no longer relevant */
1479                         strlcpy(latestdir, lcwd, sizeof(latestdir));
1480                     }
1481                     break;
1482                 default:
1483                     break;
1484                 }
1485                 if (!oodate && buf[0] == 'L' && link_src != NULL)
1486                     goto check_link_src;
1487             } else if (strcmp(buf, "CMD") == 0) {
1488                 /*
1489                  * Compare the current command with the one in the
1490                  * meta data file.
1491                  */
1492                 if (cmdNode == NULL) {
1493                     DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
1494                            fname, lineno);
1495                     oodate = TRUE;
1496                 } else {
1497                     char *cmd = cmdNode->datum;
1498                     Boolean hasOODATE = FALSE;
1499
1500                     if (strstr(cmd, "$?"))
1501                         hasOODATE = TRUE;
1502                     else if ((cp = strstr(cmd, ".OODATE"))) {
1503                         /* check for $[{(].OODATE[:)}] */
1504                         if (cp > cmd + 2 && cp[-2] == '$')
1505                             hasOODATE = TRUE;
1506                     }
1507                     if (hasOODATE) {
1508                         needOODATE = TRUE;
1509                         DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
1510                                fname, lineno);
1511                     }
1512                     (void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
1513                     /* TODO: handle errors */
1514
1515                     if ((cp = strchr(cmd, '\n'))) {
1516                         int n;
1517
1518                         /*
1519                          * This command contains newlines, we need to
1520                          * fetch more from the .meta file before we
1521                          * attempt a comparison.
1522                          */
1523                         /* first put the newline back at buf[x - 1] */
1524                         buf[x - 1] = '\n';
1525                         do {
1526                             /* now fetch the next line */
1527                             if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0)
1528                                 break;
1529                             x = n;
1530                             lineno++;
1531                             if (buf[x - 1] != '\n') {
1532                                 warnx("%s: %d: line truncated at %u", fname, lineno, x);
1533                                 break;
1534                             }
1535                             cp = strchr(++cp, '\n');
1536                         } while (cp);
1537                         if (buf[x - 1] == '\n')
1538                             buf[x - 1] = '\0';
1539                     }
1540                     if (p &&
1541                         !hasOODATE &&
1542                         !(gn->type & OP_NOMETA_CMP) &&
1543                         strcmp(p, cmd) != 0) {
1544                         DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
1545                                fname, lineno, p, cmd);
1546                         if (!metaIgnoreCMDs)
1547                             oodate = TRUE;
1548                     }
1549                     free(cmd);
1550                     cmdNode = cmdNode->next;
1551                 }
1552             } else if (strcmp(buf, "CWD") == 0) {
1553                 /*
1554                  * Check if there are extra commands now
1555                  * that weren't in the meta data file.
1556                  */
1557                 if (!oodate && cmdNode != NULL) {
1558                     DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n",
1559                            fname, lineno);
1560                     oodate = TRUE;
1561                 }
1562                 CHECK_VALID_META(p);
1563                 if (strcmp(p, cwd) != 0) {
1564                     DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n",
1565                            fname, lineno, p, curdir);
1566                     oodate = TRUE;
1567                 }
1568             }
1569         }
1570
1571         fclose(fp);
1572         if (!Lst_IsEmpty(missingFiles)) {
1573             DEBUG2(META, "%s: missing files: %s...\n",
1574                    fname, (char *)missingFiles->first->datum);
1575             oodate = TRUE;
1576         }
1577         if (!oodate && !have_filemon && filemonMissing) {
1578             DEBUG1(META, "%s: missing filemon data\n", fname);
1579             oodate = TRUE;
1580         }
1581     } else {
1582         if (writeMeta && (metaMissing || (gn->type & OP_META))) {
1583             cp = NULL;
1584
1585             /* if target is in .CURDIR we do not need a meta file */
1586             if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) {
1587                 if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) {
1588                     cp = NULL;          /* not in .CURDIR */
1589                 }
1590             }
1591             if (!cp) {
1592                 DEBUG1(META, "%s: required but missing\n", fname);
1593                 oodate = TRUE;
1594                 needOODATE = TRUE;      /* assume the worst */
1595             }
1596         }
1597     }
1598
1599     Lst_Destroy(missingFiles, free);
1600
1601     if (oodate && needOODATE) {
1602         /*
1603          * Target uses .OODATE which is empty; or we wouldn't be here.
1604          * We have decided it is oodate, so .OODATE needs to be set.
1605          * All we can sanely do is set it to .ALLSRC.
1606          */
1607         Var_Delete(OODATE, gn);
1608         Var_Set(OODATE, GNode_VarAllsrc(gn), gn);
1609     }
1610
1611  oodate_out:
1612     bmake_free(objdir_freeIt);
1613     return oodate;
1614 }
1615
1616 /* support for compat mode */
1617
1618 static int childPipe[2];
1619
1620 void
1621 meta_compat_start(void)
1622 {
1623 #ifdef USE_FILEMON_ONCE
1624     /*
1625      * We need to re-open filemon for each cmd.
1626      */
1627     BuildMon *pbm = &Mybm;
1628
1629     if (pbm->mfp != NULL && useFilemon) {
1630         meta_open_filemon(pbm);
1631     } else {
1632         pbm->mon_fd = -1;
1633         pbm->filemon = NULL;
1634     }
1635 #endif
1636     if (pipe(childPipe) < 0)
1637         Punt("Cannot create pipe: %s", strerror(errno));
1638     /* Set close-on-exec flag for both */
1639     (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC);
1640     (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC);
1641 }
1642
1643 void
1644 meta_compat_child(void)
1645 {
1646     meta_job_child(NULL);
1647     if (dup2(childPipe[1], 1) < 0 || dup2(1, 2) < 0)
1648         execDie("dup2", "pipe");
1649 }
1650
1651 void
1652 meta_compat_parent(pid_t child)
1653 {
1654     int outfd, metafd, maxfd, nfds;
1655     char buf[BUFSIZ+1];
1656     fd_set readfds;
1657
1658     meta_job_parent(NULL, child);
1659     close(childPipe[1]);                        /* child side */
1660     outfd = childPipe[0];
1661 #ifdef USE_FILEMON
1662     metafd = Mybm.filemon ? filemon_readfd(Mybm.filemon) : -1;
1663 #else
1664     metafd = -1;
1665 #endif
1666     maxfd = -1;
1667     if (outfd > maxfd)
1668             maxfd = outfd;
1669     if (metafd > maxfd)
1670             maxfd = metafd;
1671
1672     while (outfd != -1 || metafd != -1) {
1673         FD_ZERO(&readfds);
1674         if (outfd != -1) {
1675             FD_SET(outfd, &readfds);
1676         }
1677         if (metafd != -1) {
1678             FD_SET(metafd, &readfds);
1679         }
1680         nfds = select(maxfd + 1, &readfds, NULL, NULL, NULL);
1681         if (nfds == -1) {
1682             if (errno == EINTR)
1683                 continue;
1684             err(1, "select");
1685         }
1686
1687         if (outfd != -1 && FD_ISSET(outfd, &readfds)) do {
1688             /* XXX this is not line-buffered */
1689             ssize_t nread = read(outfd, buf, sizeof(buf) - 1);
1690             if (nread == -1)
1691                 err(1, "read");
1692             if (nread == 0) {
1693                 close(outfd);
1694                 outfd = -1;
1695                 break;
1696             }
1697             fwrite(buf, 1, (size_t)nread, stdout);
1698             fflush(stdout);
1699             buf[nread] = '\0';
1700             meta_job_output(NULL, buf, "");
1701         } while (0);
1702         if (metafd != -1 && FD_ISSET(metafd, &readfds)) {
1703             if (meta_job_event(NULL) <= 0)
1704                 metafd = -1;
1705         }
1706     }
1707 }
1708
1709 #endif  /* USE_META */