Module Name:    src
Committed By:   riastradh
Date:           Sun Jan 19 19:42:32 UTC 2020

Modified Files:
        src/usr.bin/make: Makefile compat.c job.c meta.c meta.h
Added Files:
        src/usr.bin/make: filemon.c filemon.h

Log Message:
Reimplement make(1) meta mode without filemon(4).


To generate a diff of this commit:
cvs rdiff -u -r1.65 -r1.66 src/usr.bin/make/Makefile
cvs rdiff -u -r1.109 -r1.110 src/usr.bin/make/compat.c
cvs rdiff -u -r0 -r1.1 src/usr.bin/make/filemon.c src/usr.bin/make/filemon.h
cvs rdiff -u -r1.195 -r1.196 src/usr.bin/make/job.c
cvs rdiff -u -r1.73 -r1.74 src/usr.bin/make/meta.c
cvs rdiff -u -r1.5 -r1.6 src/usr.bin/make/meta.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/make/Makefile
diff -u src/usr.bin/make/Makefile:1.65 src/usr.bin/make/Makefile:1.66
--- src/usr.bin/make/Makefile:1.65	Thu Dec 19 07:14:07 2019
+++ src/usr.bin/make/Makefile	Sun Jan 19 19:42:32 2020
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.65 2019/12/19 07:14:07 maxv Exp $
+#	$NetBSD: Makefile,v 1.66 2020/01/19 19:42:32 riastradh Exp $
 #	@(#)Makefile	5.2 (Berkeley) 12/28/90
 
 PROG=	make
@@ -17,9 +17,10 @@ USE_META ?= yes
 .if ${USE_META:tl} != "no"
 SRCS+=	meta.c
 CPPFLAGS+= -DUSE_META
-FILEMON_H ?= ${.CURDIR:H:H}/sys/dev/filemon/filemon.h
-.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h"
-COPTS.meta.c += -DHAVE_FILEMON_H -I${FILEMON_H:H}
+USE_FILEMON ?= yes
+.if ${USE_FILEMON:tl} != "no"
+SRCS+=	filemon.c
+CPPFLAGS+= -DUSE_FILEMON
 .endif
 .endif
 

Index: src/usr.bin/make/compat.c
diff -u src/usr.bin/make/compat.c:1.109 src/usr.bin/make/compat.c:1.110
--- src/usr.bin/make/compat.c:1.109	Thu Dec 19 07:14:07 2019
+++ src/usr.bin/make/compat.c	Sun Jan 19 19:42:32 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: compat.c,v 1.109 2019/12/19 07:14:07 maxv Exp $	*/
+/*	$NetBSD: compat.c,v 1.110 2020/01/19 19:42:32 riastradh Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,14 +70,14 @@
  */
 
 #ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: compat.c,v 1.109 2019/12/19 07:14:07 maxv Exp $";
+static char rcsid[] = "$NetBSD: compat.c,v 1.110 2020/01/19 19:42:32 riastradh Exp $";
 #else
 #include <sys/cdefs.h>
 #ifndef lint
 #if 0
 static char sccsid[] = "@(#)compat.c	8.2 (Berkeley) 3/19/94";
 #else
-__RCSID("$NetBSD: compat.c,v 1.109 2019/12/19 07:14:07 maxv Exp $");
+__RCSID("$NetBSD: compat.c,v 1.110 2020/01/19 19:42:32 riastradh Exp $");
 #endif
 #endif /* not lint */
 #endif
@@ -404,7 +404,7 @@ again:
 
 #ifdef USE_META
     if (useMeta) {
-	meta_compat_parent();
+	meta_compat_parent(cpid);
     }
 #endif
 

Index: src/usr.bin/make/job.c
diff -u src/usr.bin/make/job.c:1.195 src/usr.bin/make/job.c:1.196
--- src/usr.bin/make/job.c:1.195	Sun May 13 22:13:28 2018
+++ src/usr.bin/make/job.c	Sun Jan 19 19:42:32 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: job.c,v 1.195 2018/05/13 22:13:28 sjg Exp $	*/
+/*	$NetBSD: job.c,v 1.196 2020/01/19 19:42:32 riastradh Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,14 +70,14 @@
  */
 
 #ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: job.c,v 1.195 2018/05/13 22:13:28 sjg Exp $";
+static char rcsid[] = "$NetBSD: job.c,v 1.196 2020/01/19 19:42:32 riastradh Exp $";
 #else
 #include <sys/cdefs.h>
 #ifndef lint
 #if 0
 static char sccsid[] = "@(#)job.c	8.2 (Berkeley) 3/19/94";
 #else
-__RCSID("$NetBSD: job.c,v 1.195 2018/05/13 22:13:28 sjg Exp $");
+__RCSID("$NetBSD: job.c,v 1.196 2020/01/19 19:42:32 riastradh Exp $");
 #endif
 #endif /* not lint */
 #endif
@@ -329,6 +329,8 @@ static Job childExitJob;	/* child exit p
 #define	CHILD_EXIT	"."
 #define	DO_JOB_RESUME	"R"
 
+static const int npseudojobs = 2; /* number of pseudo-jobs */
+
 #define TARG_FMT  "%s %s ---\n" /* Default format */
 #define MESSAGE(fp, gn) \
 	if (maxJobs != 1 && targPrefix && *targPrefix) \
@@ -357,6 +359,16 @@ static void JobSigReset(void);
 
 const char *malloc_options="A";
 
+static unsigned
+nfds_per_job(void)
+{
+#ifdef USE_META
+    if (useMeta)
+	return 2;
+#endif
+    return 1;
+}
+
 static void
 job_table_dump(const char *where)
 {
@@ -1439,6 +1451,12 @@ JobExec(Job *job, char **argv)
 
     Trace_Log(JOBSTART, job);
 
+#ifdef USE_META
+    if (useMeta) {
+	meta_job_parent(job, cpid);
+    }
+#endif
+
     /*
      * Set the current position in the buffer to the beginning
      * and mark another stream to watch in the outputs mask
@@ -2121,12 +2139,24 @@ Job_CatchOutput(void)
     if (nready == 0)
 	    return;
 
-    for (i = 2; i < nfds; i++) {
+    for (i = npseudojobs*nfds_per_job(); i < nfds; i++) {
 	if (!fds[i].revents)
 	    continue;
 	job = jobfds[i];
 	if (job->job_state == JOB_ST_RUNNING)
 	    JobDoOutput(job, FALSE);
+#ifdef USE_META
+	/*
+	 * With meta mode, we may have activity on the job's filemon
+	 * descriptor too, which at the moment is any pollfd other than
+	 * job->inPollfd.
+	 */
+	if (useMeta && job->inPollfd != &fds[i]) {
+	    if (meta_job_event(job) <= 0) {
+		fds[i].events = 0; /* never mind */
+	    }
+	}
+#endif
 	if (--nready == 0)
 		return;
     }
@@ -2271,9 +2301,11 @@ Job_Init(void)
 
     JobCreatePipe(&childExitJob, 3);
 
-    /* We can only need to wait for tokens, children and output from each job */
-    fds = bmake_malloc(sizeof (*fds) * (2 + maxJobs));
-    jobfds = bmake_malloc(sizeof (*jobfds) * (2 + maxJobs));
+    /* Preallocate enough for the maximum number of jobs.  */
+    fds = bmake_malloc(sizeof(*fds) *
+	(npseudojobs + maxJobs) * nfds_per_job());
+    jobfds = bmake_malloc(sizeof(*jobfds) *
+	(npseudojobs + maxJobs) * nfds_per_job());
 
     /* These are permanent entries and take slots 0 and 1 */
     watchfd(&tokenWaitJob);
@@ -2792,6 +2824,14 @@ watchfd(Job *job)
     jobfds[nfds] = job;
     job->inPollfd = &fds[nfds];
     nfds++;
+#ifdef USE_META
+    if (useMeta) {
+	fds[nfds].fd = meta_job_fd(job);
+	fds[nfds].events = fds[nfds].fd == -1 ? 0 : POLLIN;
+	jobfds[nfds] = job;
+	nfds++;
+    }
+#endif
 }
 
 static void
@@ -2802,6 +2842,18 @@ clearfd(Job *job)
 	Punt("Unwatching unwatched job");
     i = job->inPollfd - fds;
     nfds--;
+#ifdef USE_META
+    if (useMeta) {
+	/*
+	 * Sanity check: there should be two fds per job, so the job's
+	 * pollfd number should be even.
+	 */
+	assert(nfds_per_job() == 2);
+	if (i % 2)
+	    Punt("odd-numbered fd with meta");
+	nfds--;
+    }
+#endif
     /*
      * Move last job in table into hole made by dead job.
      */
@@ -2809,6 +2861,12 @@ clearfd(Job *job)
 	fds[i] = fds[nfds];
 	jobfds[i] = jobfds[nfds];
 	jobfds[i]->inPollfd = &fds[i];
+#ifdef USE_META
+	if (useMeta) {
+	    fds[i + 1] = fds[nfds + 1];
+	    jobfds[i + 1] = jobfds[nfds + 1];
+	}
+#endif
     }
     job->inPollfd = NULL;
 }

Index: src/usr.bin/make/meta.c
diff -u src/usr.bin/make/meta.c:1.73 src/usr.bin/make/meta.c:1.74
--- src/usr.bin/make/meta.c:1.73	Thu Dec 19 07:14:07 2019
+++ src/usr.bin/make/meta.c	Sun Jan 19 19:42:32 2020
@@ -1,4 +1,4 @@
-/*      $NetBSD: meta.c,v 1.73 2019/12/19 07:14:07 maxv Exp $ */
+/*      $NetBSD: meta.c,v 1.74 2020/01/19 19:42:32 riastradh Exp $ */
 
 /*
  * Implement 'meta' mode.
@@ -36,7 +36,6 @@
 # include "config.h"
 #endif
 #include <sys/stat.h>
-#include <sys/ioctl.h>
 #include <libgen.h>
 #include <errno.h>
 #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H)
@@ -46,11 +45,8 @@
 #include "make.h"
 #include "job.h"
 
-#ifdef HAVE_FILEMON_H
-# include <filemon.h>
-#endif
-#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD)
-# define USE_FILEMON
+#ifdef USE_FILEMON
+#include "filemon.h"
 #endif
 
 static BuildMon Mybm;			/* for compat */
@@ -117,30 +113,24 @@ extern char    **environ;
  * the benefits are more limited.
  */
 #ifdef USE_FILEMON
-# ifndef _PATH_FILEMON
-#   define _PATH_FILEMON "/dev/filemon"
-# endif
 
 /*
  * Open the filemon device.
  */
 static void
-filemon_open(BuildMon *pbm)
+meta_open_filemon(BuildMon *pbm)
 {
-    int retry;
-    
-    pbm->mon_fd = pbm->filemon_fd = -1;
+    int dupfd;
+
+    pbm->mon_fd = -1;
+    pbm->filemon = NULL;
     if (!useFilemon)
 	return;
 
-    for (retry = 5; retry >= 0; retry--) {
-	if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0)
-	    break;
-    }
-
-    if (pbm->filemon_fd < 0) {
+    pbm->filemon = filemon_open();
+    if (pbm->filemon == NULL) {
 	useFilemon = FALSE;
-	warn("Could not open %s", _PATH_FILEMON);
+	warn("Could not open filemon");
 	return;
     }
 
@@ -151,12 +141,15 @@ filemon_open(BuildMon *pbm)
      * We only care about the descriptor.
      */
     pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL);
-    if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) {
+    if ((dupfd = dup(pbm->mon_fd)) == -1) {
+	err(1, "Could not dup filemon output!");
+    }
+    (void)fcntl(dupfd, F_SETFD, FD_CLOEXEC);
+    if (filemon_setfd(pbm->filemon, dupfd) == -1) {
 	err(1, "Could not set filemon file descriptor!");
     }
     /* we don't need these once we exec */
     (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
-    (void)fcntl(pbm->filemon_fd, F_SETFD, FD_CLOEXEC);
 }
 
 /*
@@ -570,7 +563,7 @@ meta_init(void)
 {
 #ifdef USE_FILEMON
 	/* this allows makefiles to test if we have filemon support */
-	Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0);
+	Var_Set(".MAKE.PATH_FILEMON", "ktrace", VAR_GLOBAL, 0); /* XXX */
 #endif
 }
 
@@ -680,9 +673,10 @@ meta_job_start(Job *job, GNode *gn)
 #endif
 #ifdef USE_FILEMON
     if (pbm->mfp != NULL && useFilemon) {
-	filemon_open(pbm);
+	meta_open_filemon(pbm);
     } else {
-	pbm->mon_fd = pbm->filemon_fd = -1;
+	pbm->mon_fd = -1;
+	pbm->filemon = NULL;
     }
 #endif
 }
@@ -708,7 +702,7 @@ meta_job_child(Job *job)
 	    pid_t pid;
 
 	    pid = getpid();
-	    if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) {
+	    if (filemon_setpid_child(pbm->filemon, pid) == -1) {
 		err(1, "Could not set filemon pid!");
 	    }
 	}
@@ -717,6 +711,59 @@ meta_job_child(Job *job)
 }
 
 void
+meta_job_parent(Job *job, pid_t pid)
+{
+#ifdef USE_FILEMON
+    BuildMon *pbm;
+
+    if (job != NULL) {
+	pbm = &job->bm;
+    } else {
+	pbm = &Mybm;
+    }
+    if (useFilemon) {
+	filemon_setpid_parent(pbm->filemon, pid);
+    }
+#endif
+}
+
+int
+meta_job_fd(Job *job)
+{
+#ifdef USE_FILEMON
+    BuildMon *pbm;
+
+    if (job != NULL) {
+	pbm = &job->bm;
+    } else {
+	pbm = &Mybm;
+    }
+    if (useFilemon && pbm->filemon) {
+	return filemon_readfd(pbm->filemon);
+    }
+#endif
+    return -1;
+}
+
+int
+meta_job_event(Job *job)
+{
+#ifdef USE_FILEMON
+    BuildMon *pbm;
+
+    if (job != NULL) {
+	pbm = &job->bm;
+    } else {
+	pbm = &Mybm;
+    }
+    if (useFilemon && pbm->filemon) {
+	return filemon_process(pbm->filemon);
+    }
+#endif
+    return 0;
+}
+
+void
 meta_job_error(Job *job, GNode *gn, int flags, int status)
 {
     char cwd[MAXPATHLEN];
@@ -794,13 +841,16 @@ meta_cmd_finish(void *pbmp)
 	pbm = &Mybm;
 
 #ifdef USE_FILEMON
-    if (pbm->filemon_fd >= 0) {
-	if (close(pbm->filemon_fd) < 0)
+    if (pbm->filemon) {
+	while (filemon_process(pbm->filemon) > 0)
+	    continue;
+	if (filemon_close(pbm->filemon) == -1)
 	    error = errno;
 	x = filemon_read(pbm->mfp, pbm->mon_fd);
 	if (error == 0 && x != 0)
 	    error = x;
-	pbm->filemon_fd = pbm->mon_fd = -1;
+	pbm->mon_fd = -1;
+	pbm->filemon = NULL;
     } else
 #endif
 	fprintf(pbm->mfp, "\n");	/* ensure end with newline */
@@ -1599,9 +1649,10 @@ meta_compat_start(void)
     BuildMon *pbm = &Mybm;
     
     if (pbm->mfp != NULL && useFilemon) {
-	filemon_open(pbm);
+	meta_open_filemon(pbm);
     } else {
-	pbm->mon_fd = pbm->filemon_fd = -1;
+	pbm->mon_fd = -1;
+	pbm->filemon = NULL;
     }
 #endif
     if (pipe(childPipe) < 0)
@@ -1623,19 +1674,56 @@ meta_compat_child(void)
 }
 
 void
-meta_compat_parent(void)
+meta_compat_parent(pid_t child)
 {
-    FILE *fp;
+    int outfd, metafd, maxfd, nfds;
     char buf[BUFSIZ];
-    
+    fd_set readfds;
+
+    meta_job_parent(NULL, child);
     close(childPipe[1]);			/* child side */
-    fp = fdopen(childPipe[0], "r");
-    while (fgets(buf, sizeof(buf), fp)) {
-	meta_job_output(NULL, buf, "");
-	printf("%s", buf);
-	fflush(stdout);
+    outfd = childPipe[0];
+    metafd = filemon_readfd(Mybm.filemon);
+
+    maxfd = -1;
+    if (outfd > maxfd)
+	    maxfd = outfd;
+    if (metafd > maxfd)
+	    maxfd = metafd;
+
+    while (outfd != -1 || metafd != -1) {
+	FD_ZERO(&readfds);
+	if (outfd != -1) {
+	    FD_SET(outfd, &readfds);
+	}
+	if (metafd != -1) {
+	    FD_SET(metafd, &readfds);
+	}
+	nfds = select(maxfd + 1, &readfds, NULL, NULL, NULL);
+	if (nfds == -1) {
+	    if (errno == EINTR)
+		continue;
+	    err(1, "select");
+	}
+
+	if (outfd != -1 && FD_ISSET(outfd, &readfds)) do {
+	    /* XXX this is not line-buffered */
+	    ssize_t nread = read(outfd, buf, sizeof buf);
+	    if (nread == -1)
+		err(1, "read");
+	    if (nread == 0) {
+		close(outfd);
+		outfd = -1;
+		break;
+	    }
+	    fwrite(buf, 1, (size_t)nread, stdout);
+	    fflush(stdout);
+	} while (0);
+	if (metafd != -1 && FD_ISSET(metafd, &readfds)) {
+	    if (meta_job_event(NULL) <= 0)
+		metafd = -1;
+	}
     }
-    fclose(fp);
 }
 
 #endif	/* USE_META */

Index: src/usr.bin/make/meta.h
diff -u src/usr.bin/make/meta.h:1.5 src/usr.bin/make/meta.h:1.6
--- src/usr.bin/make/meta.h:1.5	Thu May 12 20:28:34 2016
+++ src/usr.bin/make/meta.h	Sun Jan 19 19:42:32 2020
@@ -1,4 +1,4 @@
-/*      $NetBSD: meta.h,v 1.5 2016/05/12 20:28:34 sjg Exp $ */
+/*      $NetBSD: meta.h,v 1.6 2020/01/19 19:42:32 riastradh Exp $ */
 
 /*
  * Things needed for 'meta' mode.
@@ -33,7 +33,7 @@
 
 typedef struct BuildMon {
     char	meta_fname[MAXPATHLEN];
-    int		filemon_fd;
+    struct filemon *filemon;
     int		mon_fd;
     FILE	*mfp;
 } BuildMon;
@@ -46,6 +46,9 @@ void meta_finish(void);
 void meta_mode_init(const char *);
 void meta_job_start(struct Job *, GNode *);
 void meta_job_child(struct Job *);
+void meta_job_parent(struct Job *, pid_t);
+int  meta_job_fd(struct Job *);
+int  meta_job_event(struct Job *);
 void meta_job_error(struct Job *, GNode *, int, int);
 void meta_job_output(struct Job *, char *, const char *);
 int  meta_cmd_finish(void *);
@@ -53,4 +56,4 @@ int  meta_job_finish(struct Job *);
 Boolean meta_oodate(GNode *, Boolean);
 void meta_compat_start(void);
 void meta_compat_child(void);
-void meta_compat_parent(void);
+void meta_compat_parent(pid_t);

Added files:

Index: src/usr.bin/make/filemon.c
diff -u /dev/null src/usr.bin/make/filemon.c:1.1
--- /dev/null	Sun Jan 19 19:42:32 2020
+++ src/usr.bin/make/filemon.c	Sun Jan 19 19:42:32 2020
@@ -0,0 +1,870 @@
+/*	$NetBSD: filemon.c,v 1.1 2020/01/19 19:42:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2019 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R. Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef USE_FILEMON
+
+#include "filemon.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/rbtree.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <sys/ktrace.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "make.h"
+
+#ifndef AT_CWD
+#define AT_CWD -1
+#endif
+
+struct filemon;
+struct filemon_key;
+struct filemon_state;
+
+typedef struct filemon_state *filemon_syscall_t(struct filemon *,
+    const struct filemon_key *, const struct ktr_syscall *);
+
+static filemon_syscall_t filemon_sys_chdir;
+static filemon_syscall_t filemon_sys_execve;
+static filemon_syscall_t filemon_sys_exit;
+static filemon_syscall_t filemon_sys_fork;
+static filemon_syscall_t filemon_sys_link;
+static filemon_syscall_t filemon_sys_open;
+static filemon_syscall_t filemon_sys_openat;
+static filemon_syscall_t filemon_sys_symlink;
+static filemon_syscall_t filemon_sys_unlink;
+static filemon_syscall_t filemon_sys_rename;
+
+static filemon_syscall_t *const filemon_syscalls[] = {
+	[SYS_chdir] = &filemon_sys_chdir,
+	[SYS_execve] = &filemon_sys_execve,
+	[SYS_exit] = &filemon_sys_exit,
+	[SYS_fork] = &filemon_sys_fork,
+	[SYS_link] = &filemon_sys_link,
+	[SYS_open] = &filemon_sys_open,
+	[SYS_openat] = &filemon_sys_openat,
+	[SYS_symlink] = &filemon_sys_symlink,
+	[SYS_unlink] = &filemon_sys_unlink,
+	[SYS_rename] = &filemon_sys_rename,
+};
+
+struct filemon {
+	int			ktrfd; /* kernel writes ktrace events here */
+	FILE			*in;   /* we read ktrace events from here */
+	FILE			*out;  /* we write filemon events to here */
+	rb_tree_t		active;
+	pid_t			child;
+
+	/* I/O state machine.  */
+	enum {
+		FILEMON_START = 0,
+		FILEMON_HEADER,
+		FILEMON_PAYLOAD,
+		FILEMON_ERROR,
+	}			state;
+	unsigned char		*p;
+	size_t			resid;
+
+	/* I/O buffer.  */
+	struct ktr_header	hdr;
+	union {
+		struct ktr_syscall	syscall;
+		struct ktr_sysret	sysret;
+		char			namei[PATH_MAX];
+		unsigned char		buf[4096];
+	}			payload;
+};
+
+struct filemon_state {
+	struct filemon_key {
+		pid_t		pid;
+		lwpid_t		lid;
+	}		key;
+	struct rb_node	node;
+	int		syscode;
+	void		(*show)(struct filemon *, const struct filemon_state *,
+			    const struct ktr_sysret *);
+	unsigned	i;
+	unsigned	npath;
+	char		*path[/*npath*/];
+};
+
+static int
+compare_filemon_states(void *cookie MAKE_ATTR_UNUSED, const void *na,
+    const void *nb)
+{
+	const struct filemon_state *Sa = na;
+	const struct filemon_state *Sb = nb;
+
+	if (Sa->key.pid < Sb->key.pid)
+		return -1;
+	if (Sa->key.pid > Sb->key.pid)
+		return +1;
+	if (Sa->key.lid < Sb->key.lid)
+		return -1;
+	if (Sa->key.lid > Sb->key.lid)
+		return +1;
+	return 0;
+}
+
+static int
+compare_filemon_key(void *cookie MAKE_ATTR_UNUSED, const void *n,
+    const void *k)
+{
+	const struct filemon_state *S = n;
+	const struct filemon_key *key = k;
+
+	if (S->key.pid < key->pid)
+		return -1;
+	if (S->key.pid > key->pid)
+		return +1;
+	if (S->key.lid < key->lid)
+		return -1;
+	if (S->key.lid > key->lid)
+		return +1;
+	return 0;
+}
+
+static const rb_tree_ops_t filemon_rb_ops = {
+	.rbto_compare_nodes = &compare_filemon_states,
+	.rbto_compare_key = &compare_filemon_key,
+	.rbto_node_offset = offsetof(struct filemon_state, node),
+	.rbto_context = NULL,
+};
+
+/*
+ * filemon_open()
+ *
+ *	Allocate a filemon descriptor.  Returns NULL and sets errno on
+ *	failure.
+ */
+struct filemon *
+filemon_open(void)
+{
+	struct filemon *F;
+	int ktrpipe[2];
+	int error;
+
+	/* Allocate and zero a struct filemon object.  */
+	F = calloc(1, sizeof(*F));
+	if (F == NULL)
+		return NULL;
+
+	/* Create a pipe for ktrace events.  */
+	if (pipe2(ktrpipe, O_CLOEXEC|O_NONBLOCK) == -1) {
+		error = errno;
+		goto fail0;
+	}
+
+	/* Create a file stream for reading the ktrace events.  */
+	if ((F->in = fdopen(ktrpipe[0], "r")) == NULL) {
+		error = errno;
+		goto fail1;
+	}
+	ktrpipe[0] = -1;	/* claimed by fdopen */
+
+	/*
+	 * Set the fd for writing ktrace events and initialize the
+	 * rbtree.  The rest can be safely initialized to zero.
+	 */
+	F->ktrfd = ktrpipe[1];
+	rb_tree_init(&F->active, &filemon_rb_ops);
+
+	/* Success!  */
+	return F;
+
+fail2: __unused
+	(void)fclose(F->in);
+fail1:	(void)close(ktrpipe[0]);
+	(void)close(ktrpipe[1]);
+fail0:	free(F);
+	errno = error;
+	return NULL;
+}
+
+/*
+ * filemon_closefd(F)
+ *
+ *	Internal subroutine to try to flush and close the output file.
+ *	If F is not open for output, do nothing.  Never leaves F open
+ *	for output even on failure.  Returns 0 on success; sets errno
+ *	and return -1 on failure.
+ */
+static int
+filemon_closefd(struct filemon *F)
+{
+	int error = 0;
+
+	/* If we're not open, nothing to do.  */
+	if (F->out == NULL)
+		return 0;
+
+	/*
+	 * Flush it, close it, and null it unconditionally, but be
+	 * careful to return the earliest error in errno.
+	 */
+	if (fflush(F->out) == EOF && error == 0)
+		error = errno;
+	if (fclose(F->out) == EOF && error == 0)
+		error = errno;
+	F->out = NULL;
+
+	/* Set errno and return -1 if anything went wrong.  */
+	if (error) {
+		errno = error;
+		return -1;
+	}
+
+	/* Success!  */
+	return 0;
+}
+
+/*
+ * filemon_setfd(F, fd)
+ *
+ *	Cause filemon activity on F to be sent to fd.  Claims ownership
+ *	of fd; caller should not use fd afterward, and any duplicates
+ *	of fd may see their file positions changed.
+ */
+int
+filemon_setfd(struct filemon *F, int fd)
+{
+
+	/*
+	 * Close an existing output file if done.  Fail now if there's
+	 * an error closing.
+	 */
+	if ((filemon_closefd(F)) == -1)
+		return -1;
+	assert(F->out == NULL);
+
+	/* Open a file stream and claim ownership of the fd.  */
+	if ((F->out = fdopen(fd, "a")) == NULL)
+		return -1;
+
+	/*
+	 * Print the opening output.  Any failure will be deferred
+	 * until closing.  For hysterical raisins, we show the parent
+	 * pid, not the child pid.
+	 */
+	fprintf(F->out, "# filemon version 4\n");
+	fprintf(F->out, "# Target pid %jd\n", (intmax_t)getpid());
+	fprintf(F->out, "V 4\n");
+
+	/* Success!  */
+	return 0;
+}
+
+/*
+ * filemon_setpid_parent(F, pid)
+ *
+ *	Set the traced pid, from the parent.  Never fails.
+ */
+void
+filemon_setpid_parent(struct filemon *F, pid_t pid)
+{
+
+	F->child = pid;
+}
+
+/*
+ * filemon_setpid_child(F, pid)
+ *
+ *	Set the traced pid, from the child.  Returns 0 on success; sets
+ *	errno and returns -1 on failure.
+ */
+int
+filemon_setpid_child(const struct filemon *F, pid_t pid)
+{
+	int ops, trpoints;
+
+	ops = KTROP_SET|KTRFLAG_DESCEND;
+	trpoints = KTRFACv2;
+	trpoints |= KTRFAC_SYSCALL|KTRFAC_NAMEI|KTRFAC_SYSRET;
+	trpoints |= KTRFAC_INHERIT;
+	if (fktrace(F->ktrfd, ops, trpoints, pid) == -1)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * filemon_close(F)
+ *
+ *	Close F for output if necessary, and free a filemon descriptor.
+ *	Returns 0 on success; sets errno and returns -1 on failure, but
+ *	frees the filemon descriptor either way;
+ */
+int
+filemon_close(struct filemon *F)
+{
+	struct filemon_state *S;
+	int error = 0;
+
+	/* Close for output.  */
+	if (filemon_closefd(F) == -1 && error == 0)
+		error = errno;
+
+	/* Close the ktrace pipe.  */
+	if (fclose(F->in) == EOF && error == 0)
+		error = errno;
+	if (close(F->ktrfd) == -1 && error == 0)
+		error = errno;
+
+	/* Free any active records.  */
+	while ((S = RB_TREE_MIN(&F->active)) != NULL) {
+		rb_tree_remove_node(&F->active, S);
+		free(S);
+	}
+
+	/* Free the filemon descriptor.  */
+	free(F);
+
+	/* Set errno and return -1 if anything went wrong.  */
+	if (error) {
+		errno = error;
+		return -1;
+	}
+
+	/* Success!  */
+	return 0;
+}
+
+/*
+ * filemon_readfd(F)
+ *
+ *	Returns a file descriptor which will select/poll ready for read
+ *	when there are filemon events to be processed by
+ *	filemon_process, or -1 if anything has gone wrong.
+ */
+int
+filemon_readfd(const struct filemon *F)
+{
+
+	if (F->state == FILEMON_ERROR)
+		return -1;
+	return fileno(F->in);
+}
+
+/*
+ * filemon_dispatch(F)
+ *
+ *	Internal subroutine to dispatch a filemon ktrace event.
+ *	Silently ignore events that we don't recognize.
+ */
+static void
+filemon_dispatch(struct filemon *F)
+{
+	const struct filemon_key key = {
+		.pid = F->hdr.ktr_pid,
+		.lid = F->hdr.ktr_lid,
+	};
+	struct filemon_state *S;
+
+	switch (F->hdr.ktr_type) {
+	case KTR_SYSCALL: {
+		struct ktr_syscall *call = &F->payload.syscall;
+		struct filemon_state *S1;
+
+		/* Validate the syscall code.  */
+		if (call->ktr_code < 0 ||
+		    (size_t)call->ktr_code >= __arraycount(filemon_syscalls) ||
+		    filemon_syscalls[call->ktr_code] == NULL)
+			break;
+
+		/*
+		 * Invoke the syscall-specific logic to create a new
+		 * active state.
+		 */
+		S = (*filemon_syscalls[call->ktr_code])(F, &key, call);
+		if (S == NULL)
+			break;
+
+		/*
+		 * Insert the active state, or ignore it if there
+		 * already is one.
+		 *
+		 * Collisions shouldn't happen because the states are
+		 * keyed by <pid,lid>, in which syscalls should happen
+		 * sequentially in CALL/RET pairs, but let's be
+		 * defensive.
+		 */
+		S1 = rb_tree_insert_node(&F->active, S);
+		if (S1 != S) {
+			/* XXX Which one to drop?  */
+			free(S);
+			break;
+		}
+		break;
+	}
+	case KTR_NAMEI:
+		/* Find an active syscall state, or drop it.  */
+		S = rb_tree_find_node(&F->active, &key);
+		if (S == NULL)
+			break;
+		/* Find the position of the next path, or drop it.  */
+		if (S->i >= S->npath)
+			break;
+		/* Record the path.  */
+		S->path[S->i++] = strndup(F->payload.namei,
+		    sizeof F->payload.namei);
+		break;
+	case KTR_SYSRET: {
+		struct ktr_sysret *ret = &F->payload.sysret;
+		unsigned i;
+
+		/* Find and remove an active syscall state, or drop it.  */
+		S = rb_tree_find_node(&F->active, &key);
+		if (S == NULL)
+			break;
+		rb_tree_remove_node(&F->active, S);
+
+		/*
+		 * If the active syscall state matches this return,
+		 * invoke the syscall-specific logic to show a filemon
+		 * event.
+		 */
+		/* XXX What to do if syscall code doesn't match?  */
+		if (S->i == S->npath && S->syscode == ret->ktr_code)
+			(*S->show)(F, S, ret);
+
+		/* Free the state now that it is no longer active.  */
+		for (i = 0; i < S->i; i++)
+			free(S->path[i]);
+		free(S);
+		break;
+	}
+	default:
+		/* Ignore all other ktrace events.  */
+		break;
+	}
+}
+
+/*
+ * filemon_process(F)
+ *
+ *	Process all pending events after filemon_readfd(F) has
+ *	selected/polled ready for read.
+ *
+ *	Returns -1 on failure, 0 on end of events, and anything else if
+ *	there may be more events.
+ *
+ *	XXX What about fairness to other activities in the event loop?
+ *	If we stop while there's events buffered in F->in, then select
+ *	or poll may not return ready even though there's work queued up
+ *	in the buffer of F->in, but if we don't stop then ktrace events
+ *	may overwhelm all other activity in the event loop.
+ */
+int
+filemon_process(struct filemon *F)
+{
+	size_t nread;
+
+top:	/* If the child has exited, nothing to do.  */
+	/* XXX What if one thread calls exit while another is running?  */
+	if (F->child == 0)
+		return 0;
+
+	/* If we're waiting for input, read some.  */
+	if (F->resid) {
+		nread = fread(F->p, 1, F->resid, F->in);
+		if (nread == 0) {
+			if (feof(F->in))
+				return 0;
+			assert(ferror(F->in));
+			/*
+			 * If interrupted or would block, there may be
+			 * more events.  Otherwise fail.
+			 */
+			if (errno == EAGAIN || errno == EINTR)
+				return 1;
+			F->state = FILEMON_ERROR;
+			F->p = NULL;
+			F->resid = 0;
+			return -1;
+		}
+		assert(nread <= F->resid);
+		F->p += nread;
+		F->resid -= nread;
+		if (F->resid)	/* may be more events */
+			return 1;
+	}
+
+	/* Process a state transition now that we've read a buffer.  */
+	switch (F->state) {
+	case FILEMON_START:	/* just started filemon; read header next */
+		F->state = FILEMON_HEADER;
+		F->p = (void *)&F->hdr;
+		F->resid = sizeof F->hdr;
+		goto top;
+	case FILEMON_HEADER:	/* read header */
+		/* Sanity-check ktrace header; then read payload.  */
+		if (F->hdr.ktr_len < 0 ||
+		    (size_t)F->hdr.ktr_len > sizeof F->payload) {
+			F->state = FILEMON_ERROR;
+			F->p = NULL;
+			F->resid = 0;
+			errno = EIO;
+			return -1;
+		}
+		F->state = FILEMON_PAYLOAD;
+		F->p = (void *)&F->payload;
+		F->resid = (size_t)F->hdr.ktr_len;
+		goto top;
+	case FILEMON_PAYLOAD:	/* read header and payload */
+		/* Dispatch ktrace event; then read next header.  */
+		filemon_dispatch(F);
+		F->state = FILEMON_HEADER;
+		F->p = (void *)&F->hdr;
+		F->resid = sizeof F->hdr;
+		goto top;
+	default:		/* paranoia */
+		F->state = FILEMON_ERROR;
+		/*FALLTHROUGH*/
+	case FILEMON_ERROR:	/* persistent error indicator */
+		F->p = NULL;
+		F->resid = 0;
+		errno = EIO;
+		return -1;
+	}
+}
+
+static struct filemon_state *
+syscall_enter(struct filemon *F MAKE_ATTR_UNUSED,
+    const struct filemon_key *key, const struct ktr_syscall *call,
+    unsigned npath,
+    void (*show)(struct filemon *, const struct filemon_state *,
+	const struct ktr_sysret *))
+{
+	struct filemon_state *S;
+	unsigned i;
+
+	S = calloc(1, offsetof(struct filemon_state, path[npath]));
+	if (S == NULL)
+		return NULL;
+	S->key = *key;
+	S->show = show;
+	S->syscode = call->ktr_code;
+	S->i = 0;
+	S->npath = npath;
+	for (i = 0; i < npath; i++)
+		 S->path[i] = NULL; /* paranoia */
+
+	return S;
+}
+
+static void
+show_paths(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret, const char *prefix)
+{
+	unsigned i;
+
+	/* Caller must ensure all paths have been specified.  */
+	assert(S->i == S->npath);
+
+	/*
+	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
+	 * we're not producing output.
+	 */
+	if (ret->ktr_error && ret->ktr_error != -2)
+		return;
+	if (F->out == NULL)
+		return;
+
+	/*
+	 * Print the prefix, pid, and paths -- with the paths quoted if
+	 * there's more than one.
+	 */
+	fprintf(F->out, "%s %jd", prefix, (intmax_t)S->key.pid);
+	for (i = 0; i < S->npath; i++) {
+		const char *q = S->npath > 1 ? "'" : "";
+		fprintf(F->out, " %s%s%s", q, S->path[i], q);
+	}
+	fprintf(F->out, "\n");
+}
+
+static void
+show_retval(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret, const char *prefix)
+{
+
+	/*
+	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
+	 * we're not producing output.
+	 */
+	if (ret->ktr_error && ret->ktr_error != -2)
+		return;
+	if (F->out == NULL)
+		return;
+
+	fprintf(F->out, "%s %jd %jd\n", prefix, (intmax_t)S->key.pid,
+	    (intmax_t)ret->ktr_retval);
+}
+
+static void
+show_chdir(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "C");
+}
+
+static void
+show_execve(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	return show_paths(F, S, ret, "E");
+}
+
+static void
+show_fork(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_retval(F, S, ret, "F");
+}
+
+static void
+show_link(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "L"); /* XXX same as symlink */
+}
+
+static void
+show_open_read(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "R");
+}
+
+static void
+show_open_write(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "W");
+}
+
+static void
+show_open_readwrite(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "R");
+	show_paths(F, S, ret, "W");
+}
+
+static void
+show_openat_read(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	if (S->path[0][0] != '/')
+		show_paths(F, S, ret, "A");
+	show_paths(F, S, ret, "R");
+}
+
+static void
+show_openat_write(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	if (S->path[0][0] != '/')
+		show_paths(F, S, ret, "A");
+	show_paths(F, S, ret, "W");
+}
+
+static void
+show_openat_readwrite(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	if (S->path[0][0] != '/')
+		show_paths(F, S, ret, "A");
+	show_paths(F, S, ret, "R");
+	show_paths(F, S, ret, "W");
+}
+
+static void
+show_symlink(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "L"); /* XXX same as link */
+}
+
+static void
+show_unlink(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "D");
+}
+
+static void
+show_rename(struct filemon *F, const struct filemon_state *S,
+    const struct ktr_sysret *ret)
+{
+	show_paths(F, S, ret, "M");
+}
+
+static struct filemon_state *
+filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	return syscall_enter(F, key, call, 1, &show_chdir);
+}
+
+static struct filemon_state *
+filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	return syscall_enter(F, key, call, 1, &show_execve);
+}
+
+static struct filemon_state *
+filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	const register_t *args = (const void *)&call[1];
+	int status = args[0];
+
+	if (F->out) {
+		fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
+		if (key->pid == F->child) {
+			fprintf(F->out, "# Bye bye\n");
+			F->child = 0;
+		}
+	}
+	return NULL;
+}
+
+static struct filemon_state *
+filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	return syscall_enter(F, key, call, 0, &show_fork);
+}
+
+static struct filemon_state *
+filemon_sys_link(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	return syscall_enter(F, key, call, 2, &show_link);
+}
+
+static struct filemon_state *
+filemon_sys_open(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	const register_t *args = (const void *)&call[1];
+	int flags;
+
+	if (call->ktr_argsize < 2)
+		return NULL;
+	flags = args[1];
+
+	if ((flags & O_RDWR) == O_RDWR)
+		return syscall_enter(F, key, call, 1, &show_open_readwrite);
+	else if ((flags & O_WRONLY) == O_WRONLY)
+		return syscall_enter(F, key, call, 1, &show_open_write);
+	else if ((flags & O_RDONLY) == O_RDONLY)
+		return syscall_enter(F, key, call, 1, &show_open_read);
+	else
+		return NULL;	/* XXX Do we care if no read or write?  */
+}
+
+static struct filemon_state *
+filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	const register_t *args = (const void *)&call[1];
+	int flags, fd;
+
+	if (call->ktr_argsize < 3)
+		return NULL;
+	fd = args[0];
+	flags = args[2];
+
+	if (fd == AT_CWD) {
+		if ((flags & O_RDWR) == O_RDWR)
+			return syscall_enter(F, key, call, 1,
+			    &show_open_readwrite);
+		else if ((flags & O_WRONLY) == O_WRONLY)
+			return syscall_enter(F, key, call, 1,
+			    &show_open_write);
+		else if ((flags & O_RDONLY) == O_RDONLY)
+			return syscall_enter(F, key, call, 1, &show_open_read);
+		else
+			return NULL;
+	} else {
+		if ((flags & O_RDWR) == O_RDWR)
+			return syscall_enter(F, key, call, 1,
+			    &show_openat_readwrite);
+		else if ((flags & O_WRONLY) == O_WRONLY)
+			return syscall_enter(F, key, call, 1,
+			    &show_openat_write);
+		else if ((flags & O_RDONLY) == O_RDONLY)
+			return syscall_enter(F, key, call, 1,
+			    &show_openat_read);
+		else
+			return NULL;
+	}
+}
+
+static struct filemon_state *
+filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	return syscall_enter(F, key, call, 2, &show_symlink);
+}
+
+static struct filemon_state *
+filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	return syscall_enter(F, key, call, 1, &show_unlink);
+}
+
+static struct filemon_state *
+filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
+    const struct ktr_syscall *call)
+{
+	return syscall_enter(F, key, call, 2, &show_rename);
+}
+
+#endif	/* USE_META */
Index: src/usr.bin/make/filemon.h
diff -u /dev/null src/usr.bin/make/filemon.h:1.1
--- /dev/null	Sun Jan 19 19:42:32 2020
+++ src/usr.bin/make/filemon.h	Sun Jan 19 19:42:32 2020
@@ -0,0 +1,50 @@
+/*	$NetBSD: filemon.h,v 1.1 2020/01/19 19:42:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2019 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R. Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FILEMON_H
+#define	FILEMON_H
+
+#include <sys/types.h>
+
+struct filemon;
+
+struct filemon *
+	filemon_open(void);
+int	filemon_close(struct filemon *);
+
+int	filemon_setfd(struct filemon *, int);
+void	filemon_setpid_parent(struct filemon *, pid_t);
+int	filemon_setpid_child(const struct filemon *, pid_t);
+
+int	filemon_readfd(const struct filemon *);
+int	filemon_process(struct filemon *);
+
+#endif	/* FILEMON_H */

Reply via email to