xref: /freebsd/contrib/bmake/meta.c (revision 36d6566e5985030fd2f1100bd9c1387bbe0bd290)
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 */
1710