From 4a4107f0ebb91f373fbf887ea2bb302e51b2c4bd Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sun, 25 Nov 2018 18:41:56 +0100
Subject: [PATCH] WIP filemon

---
 etc/mtree/BSD.include.dist              |   2 +
 include/Makefile                        |   2 +-
 share/man/man4/Makefile                 |   1 +
 share/man/man4/filemon.4                | 175 ++++++
 sys/conf/files                          |   1 +
 sys/conf/options                        |   2 +
 sys/config/LINT64                       |   3 +
 sys/cpu/x86_64/misc/elf_machdep.c       |   2 +-
 sys/dev/misc/Makefile                   |   2 +-
 sys/dev/misc/filemon/Makefile           |   7 +
 sys/dev/misc/filemon/filemon.c          | 306 ++++++++++
 sys/dev/misc/filemon/filemon.h          |  34 ++
 sys/dev/misc/filemon/filemon_lock.c     | 121 ++++
 sys/dev/misc/filemon/filemon_wrapper.c  | 706 ++++++++++++++++++++++++
 tools/regression/filemon/Makefile       |  86 +++
 tools/regression/filemon/filemontest.c  |  83 +++
 tools/regression/filemon/test_script.sh |  44 ++
 tools/regression/filemon/timed-forkb.c  | 177 ++++++
 usr.bin/bmake/Makefile                  |   6 +
 usr.bin/script/script.1                 |  12 +-
 usr.bin/script/script.c                 |  46 +-
 21 files changed, 1809 insertions(+), 9 deletions(-)
 create mode 100644 share/man/man4/filemon.4
 create mode 100644 sys/dev/misc/filemon/Makefile
 create mode 100644 sys/dev/misc/filemon/filemon.c
 create mode 100644 sys/dev/misc/filemon/filemon.h
 create mode 100644 sys/dev/misc/filemon/filemon_lock.c
 create mode 100644 sys/dev/misc/filemon/filemon_wrapper.c
 create mode 100644 tools/regression/filemon/Makefile
 create mode 100644 tools/regression/filemon/filemontest.c
 create mode 100755 tools/regression/filemon/test_script.sh
 create mode 100644 tools/regression/filemon/timed-forkb.c

diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
index ca12541fbf..03869f0ad4 100644
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -265,6 +265,8 @@
         misc
             evdev
             ..
+            filemon
+            ..
             lpt
             ..
             ppi
diff --git a/include/Makefile b/include/Makefile
index 6a0dfb59a7..80471613a9 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -56,7 +56,7 @@ LSUBDIRS= \
 	bus/cam bus/pccard bus/pci bus/isa bus/ppbus bus/smbus bus/u4b \
 	dev/disk/dm \
 	dev/disk/mpt/mpilib \
-	dev/misc/lpt dev/misc/ppi dev/misc/putter \
+	dev/misc/filemon dev/misc/lpt dev/misc/ppi dev/misc/putter \
 	dev/netif/wi \
 	dev/raid/ciss dev/raid/mfi \
 	dev/smbus/smb \
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 4fffa4387c..11e5e642b5 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -95,6 +95,7 @@ MAN=	aac.4 \
 	exca.4 \
 	fd.4 \
 	fdc.4 \
+	filemon.4 \
 	firewire.4 \
 	fwe.4 \
 	fwohci.4 \
diff --git a/share/man/man4/filemon.4 b/share/man/man4/filemon.4
new file mode 100644
index 0000000000..a01b3b6b5d
--- /dev/null
+++ b/share/man/man4/filemon.4
@@ -0,0 +1,175 @@
+.\" Copyright (c) 2012
+.\"	David E. O'Brien <obrien@FreeBSD.org>.  All rights reserved.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgment:
+.\"	This product includes software developed by David E. O'Brien and
+.\"	contributors.
+.\" 4. Neither the name of the author nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANT ABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR 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.
+.\"
+.\" $FreeBSD: head/share/man/man4/filemon.4 271049 2014-09-03 19:06:08Z obrien $
+.\"
+.Dd May 29, 2016
+.Dt FILEMON 4
+.Os
+.Sh NAME
+.Nm filemon
+.Nd the filemon device
+.Sh SYNOPSIS
+.In dev/misc/filemon/filemon.h
+.Sh DESCRIPTION
+The
+.Nm
+device allows a process to collect file operations data of its children.
+The device
+.Pa /dev/filemon
+responds to two
+.Xr ioctl 2
+calls.
+.Pp
+System calls are denoted using the following single letters:
+.Pp
+.Bl -tag -width indent -compact
+.It Ql C
+.Xr chdir 2
+.It Ql D
+.Xr unlink 2
+.It Ql E
+.Xr exec 2
+.It Ql F
+.Xr fork 2 ,
+.Xr vfork 2
+.It Ql L
+.Xr link 2 ,
+.Xr linkat 2 ,
+.Xr symlink 2 ,
+.Xr symlinkat 2
+.It Ql M
+.Xr rename 2
+.It Ql R
+.Xr open 2
+for read
+.It Ql S
+.Xr stat 2
+.It Ql W
+.Xr open 2
+for write
+.It Ql X
+.Xr _exit 2
+.El
+.Pp
+Note that
+.Ql R
+following
+.Ql W
+records can represent a single
+.Xr open 2
+for R/W,
+or two separate
+.Xr open 2
+calls, one for
+.Ql R
+and one for
+.Ql W .
+Note that only successful system calls are captured.
+.Sh IOCTLS
+User mode programs communicate with the
+.Nm
+driver through a number of ioctls which are described below.
+Each takes a single argument.
+.Bl -tag -width ".Dv FILEMON_SET_PID"
+.It Dv FILEMON_SET_FD
+Write the internal tracing buffer to the supplied open file descriptor.
+.It Dv FILEMON_SET_PID
+Child process ID to trace.
+.El
+.Sh RETURN VALUES
+.\" .Rv -std ioctl
+The
+.Fn ioctl
+function returns the value 0 if successful;
+otherwise the value \-1 is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh FILES
+.Bl -tag -width ".Pa /dev/filemon"
+.It Pa /dev/filemon
+.El
+.Sh EXAMPLES
+.Bd -literal
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <dev/filemon/filemon.h>
+#include <fcntl.h>
+#include <err.h>
+#include <unistd.h>
+
+static void
+open_filemon(void)
+{
+	pid_t child;
+	int fm_fd, fm_log;
+
+	if ((fm_fd = open("/dev/filemon", O_RDWR | O_CLOEXEC)) == -1)
+		err(1, "open(\e"/dev/filemon\e", O_RDWR)");
+	if ((fm_log = open("filemon.out",
+	    O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, DEFFILEMODE)) == -1)
+		err(1, "open(filemon.out)");
+
+	if (ioctl(fm_fd, FILEMON_SET_FD, &fm_log) == -1)
+		err(1, "Cannot set filemon log file descriptor");
+
+	if ((child = fork()) == 0) {
+		child = getpid();
+		if (ioctl(fm_fd, FILEMON_SET_PID, &child) == -1)
+			err(1, "Cannot set filemon PID");
+		/* Do something here. */
+	} else {
+		wait(&child);
+		close(fm_fd);
+	}
+}
+.Ed
+.Pp
+Creates a file named
+.Pa filemon.out
+and configures the
+.Nm
+device to write the
+.Nm
+buffer contents to it.
+.Sh SEE ALSO
+.\".Xr dtrace 1 ,
+.Xr ktrace 1 ,
+.Xr script 1 ,
+.Xr truss 1 ,
+.Xr ioctl 2
+.Sh HISTORY
+A
+.Nm
+device appeared in
+.Fx 9.1 .
diff --git a/sys/conf/files b/sys/conf/files
index f4cc3fa5e6..9ddc8b684f 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -363,6 +363,7 @@ dev/netif/ig_hal/e1000_osdep.c	optional ig_hal
 dev/netif/ig_hal/e1000_phy.c	optional ig_hal
 dev/netif/ig_hal/e1000_vf.c	optional ig_hal
 dev/netif/et/if_et.c		optional et
+dev/misc/filemon/filemon.c	optional filemon
 bus/firewire/firewire.c		optional firewire
 bus/firewire/fwcrom.c		optional firewire
 bus/firewire/fwdev.c		optional firewire
diff --git a/sys/conf/options b/sys/conf/options
index 8a0b116d14..f73608b5c9 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -673,6 +673,8 @@ PANIC_REBOOT_WAIT_TIME	opt_panic.h
 EVDEV_SUPPORT		opt_evdev.h
 EVDEV_DEBUG		opt_evdev.h
 
+FILEMON			opt_dontuse.h
+
 # Obsolete options (will be removed in the future)
 #
 FFS_ROOT		opt_dontuse.h
diff --git a/sys/config/LINT64 b/sys/config/LINT64
index 71c0eb4070..638c6b304d 100644
--- a/sys/config/LINT64
+++ b/sys/config/LINT64
@@ -2156,6 +2156,9 @@ device		evdev		# input event device support
 options		EVDEV_SUPPORT	# evdev support in legacy drivers
 options		EVDEV_DEBUG	# enable event debug messages
 
+# filemon device
+options		FILEMON
+
 # More undocumented options for linting.
 # Note that documenting these are not considered an affront.
 
diff --git a/sys/cpu/x86_64/misc/elf_machdep.c b/sys/cpu/x86_64/misc/elf_machdep.c
index eb659b18cc..c3877d3b6b 100644
--- a/sys/cpu/x86_64/misc/elf_machdep.c
+++ b/sys/cpu/x86_64/misc/elf_machdep.c
@@ -37,7 +37,7 @@
 #include <machine/elf.h>
 #include <machine/md_var.h>
 
-static struct sysentvec elf64_dragonfly_sysvec = {
+struct sysentvec elf64_dragonfly_sysvec = {
         .sv_size	= SYS_MAXSYSCALL,
         .sv_table	= sysent,
         .sv_sigsize	= 0,
diff --git a/sys/dev/misc/Makefile b/sys/dev/misc/Makefile
index 968c4d97cd..c3fcdc0eae 100644
--- a/sys/dev/misc/Makefile
+++ b/sys/dev/misc/Makefile
@@ -1,4 +1,4 @@
-SUBDIR= amdsbwd cmx cpuctl dcons ecc ichwd ipmi joy kbdmux led lpbb \
+SUBDIR= amdsbwd cmx cpuctl dcons ecc filemon ichwd ipmi joy kbdmux led lpbb \
 	nmdm pcfclock putter snp syscons tbridge coremctl dimm aperf
 
 .if ${MACHINE_ARCH} == "x86_64"
diff --git a/sys/dev/misc/filemon/Makefile b/sys/dev/misc/filemon/Makefile
new file mode 100644
index 0000000000..8d9c17f6dd
--- /dev/null
+++ b/sys/dev/misc/filemon/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD: head/sys/modules/filemon/Makefile 251368 2013-06-04 06:38:01Z obrien $
+
+KMOD=	filemon
+SRCS=	${KMOD}.c
+SRCS+=	opt_compat.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/misc/filemon/filemon.c b/sys/dev/misc/filemon/filemon.c
new file mode 100644
index 0000000000..c384f32fb4
--- /dev/null
+++ b/sys/dev/misc/filemon/filemon.c
@@ -0,0 +1,306 @@
+/*-
+ * Copyright (c) 2011, David E. O'Brien.
+ * Copyright (c) 2009-2011, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * 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 JUNIPER NETWORKS 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 JUNIPER NETWORKS 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.
+ *
+ * $FreeBSD: head/sys/dev/filemon/filemon.c 263233 2014-03-16 10:55:57Z rwatson $
+ */
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/file2.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/condvar.h>
+#include <sys/conf.h>
+#include <sys/devfs.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/uio.h>
+
+#include "filemon.h"
+
+#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
+#include <compat/freebsd32/freebsd32_syscall.h>
+#include <compat/freebsd32/freebsd32_proto.h>
+
+extern struct sysentvec ia32_dragonfly_sysvec;
+#endif
+
+extern struct sysentvec elf32_dragonfly_sysvec;
+extern struct sysentvec elf64_dragonfly_sysvec;
+
+static d_close_t	filemon_close;
+static d_ioctl_t	filemon_ioctl;
+static d_open_t		filemon_open;
+static int		filemon_unload(void);
+static void		filemon_load(void *);
+
+static struct dev_ops filemon_ops = {
+	{ "filemon", 0, D_MPSAFE },
+	.d_close	= filemon_close,
+	.d_ioctl	= filemon_ioctl,
+	.d_open		= filemon_open,
+};
+
+MALLOC_DECLARE(M_FILEMON);
+MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor");
+
+struct filemon {
+	TAILQ_ENTRY(filemon) link;	/* Link into the in-use list. */
+	struct lock	lock;		/* Lock mutex for this filemon. */
+	struct cv	cv;		/* Lock condition variable for this
+					   filemon. */
+	struct file	*fp;		/* Output file pointer. */
+	struct thread	*locker;	/* Ptr to the thread locking this
+					   filemon. */
+	pid_t		pid;		/* The process ID being monitored. */
+	char		fname1[MAXPATHLEN]; /* Temporary filename buffer. */
+	char		fname2[MAXPATHLEN]; /* Temporary filename buffer. */
+	char		msgbufr[1024];	/* Output message buffer. */
+};
+
+static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse);
+static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free);
+static int n_readers = 0;
+static struct lock access_lock;
+static struct cv access_cv;
+static struct thread *access_owner = NULL;
+static struct thread *access_requester = NULL;
+
+static struct cdev *filemon_dev;
+
+#include "filemon_lock.c"
+#include "filemon_wrapper.c"
+
+static void
+filemon_dtr(void *data)
+{
+	struct filemon *filemon = data;
+
+	if (filemon != NULL) {
+		struct file *fp = filemon->fp;
+
+		/* Get exclusive write access. */
+		filemon_lock_write();
+
+		/* Remove from the in-use list. */
+		TAILQ_REMOVE(&filemons_inuse, filemon, link);
+
+		filemon->fp = NULL;
+		filemon->pid = -1;
+
+		/* Add to the free list. */
+		TAILQ_INSERT_TAIL(&filemons_free, filemon, link);
+
+		/* Give up write access. */
+		filemon_unlock_write();
+
+		if (fp != NULL)
+			fdrop(fp);
+	}
+}
+
+static int
+filemon_ioctl(struct dev_ioctl_args *ap)
+{
+	u_long cmd = ap->a_cmd;
+	caddr_t data = ap->a_data;
+	struct file *fp = ap->a_fp;
+	struct thread *td = curthread;
+	int error = 0;
+	struct filemon *filemon;
+	struct proc *p;
+
+	devfs_get_cdevpriv(fp, (void **) &filemon);
+
+	switch (cmd) {
+	/* Set the output file descriptor. */
+	case FILEMON_SET_FD:
+		filemon->fp = holdfp(td, *(int *)data, FWRITE);
+		if (filemon->fp) {
+			/* Write the file header. */
+			filemon_comment(filemon);
+		} else {
+			error = EBADF;
+		}
+		break;
+
+	/* Set the monitored process ID. */
+	case FILEMON_SET_PID:
+		p = pfind(*((pid_t *)data));
+		if (p) {
+			filemon->pid = p->p_pid;
+			PRELE(p);
+		} else {
+			error = ESRCH;
+		}
+		break;
+
+	default:
+		error = EINVAL;
+		break;
+	}
+
+	return (error);
+}
+
+static int
+filemon_open(struct dev_open_args *ap)
+{
+	struct file *fp = ap->a_fp;
+	struct filemon *filemon;
+
+	/* Get exclusive write access. */
+	filemon_lock_write();
+
+	if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL)
+		TAILQ_REMOVE(&filemons_free, filemon, link);
+
+	/* Give up write access. */
+	filemon_unlock_write();
+
+	if (filemon == NULL) {
+		filemon = kmalloc(sizeof(struct filemon), M_FILEMON,
+		    M_WAITOK | M_ZERO);
+
+		filemon->fp = NULL;
+
+		lockinit(&filemon->lock, "filemon", 0, LK_CANRECURSE);
+		cv_init(&filemon->cv, "filemon");
+	}
+
+	filemon->pid = curproc->p_pid;
+
+	devfs_set_cdevpriv(fp, filemon, filemon_dtr);
+
+	/* Get exclusive write access. */
+	filemon_lock_write();
+
+	/* Add to the in-use list. */
+	TAILQ_INSERT_TAIL(&filemons_inuse, filemon, link);
+
+	/* Give up write access. */
+	filemon_unlock_write();
+
+	return (0);
+}
+
+static int
+filemon_close(struct dev_close_args *ap)
+{
+
+	return (0);
+}
+
+static void
+filemon_load(void *dummy __unused)
+{
+	lockinit(&access_lock, "filemon", 0, LK_CANRECURSE);
+	cv_init(&access_cv, "filemon");
+
+	/* Install the syscall wrappers. */
+	filemon_wrapper_install();
+
+	filemon_dev = make_dev(&filemon_ops, 0, UID_ROOT, GID_WHEEL, 0666,
+	    "filemon");
+}
+
+static int
+filemon_unload(void)
+{
+ 	struct filemon *filemon;
+	int error = 0;
+
+	/* Get exclusive write access. */
+	filemon_lock_write();
+
+	if (TAILQ_FIRST(&filemons_inuse) != NULL)
+		error = EBUSY;
+	else {
+		destroy_dev(filemon_dev);
+
+		/* Deinstall the syscall wrappers. */
+		filemon_wrapper_deinstall();
+	}
+
+	/* Give up write access. */
+	filemon_unlock_write();
+
+	if (error == 0) {
+		/* kfree() filemon structs free list. */
+		filemon_lock_write();
+		while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) {
+			TAILQ_REMOVE(&filemons_free, filemon, link);
+			lockuninit(&filemon->lock);
+			cv_destroy(&filemon->cv);
+			kfree(filemon, M_FILEMON);
+		}
+		filemon_unlock_write();
+
+		lockuninit(&access_lock);
+		cv_destroy(&access_cv);
+	}
+
+	return (error);
+}
+
+static int
+filemon_modevent(module_t mod __unused, int type, void *data)
+{
+	int error = 0;
+
+	switch (type) {
+	case MOD_LOAD:
+		filemon_load(data);
+		break;
+
+	case MOD_UNLOAD:
+		error = filemon_unload();
+		break;
+
+	case MOD_SHUTDOWN:
+		break;
+
+	default:
+		error = EOPNOTSUPP;
+		break;
+
+	}
+
+	return (error);
+}
+
+DEV_MODULE(filemon, filemon_modevent, NULL);
+MODULE_VERSION(filemon, 1);
diff --git a/sys/dev/misc/filemon/filemon.h b/sys/dev/misc/filemon/filemon.h
new file mode 100644
index 0000000000..8ed8a10415
--- /dev/null
+++ b/sys/dev/misc/filemon/filemon.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2011, David E. O'Brien.
+ * Copyright (c) 2009-2011, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * 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 JUNIPER NETWORKS 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 JUNIPER NETWORKS 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.
+ *
+ * $FreeBSD: head/sys/dev/filemon/filemon.h 236592 2012-06-04 22:54:19Z obrien $
+ */
+
+#define FILEMON_SET_FD		_IOWR('S', 1, int)
+#define FILEMON_SET_PID		_IOWR('S', 2, pid_t)
+
+#define FILEMON_VERSION		4	/* output format
+					   (bump when adding record types) */
diff --git a/sys/dev/misc/filemon/filemon_lock.c b/sys/dev/misc/filemon/filemon_lock.c
new file mode 100644
index 0000000000..0a44ac3bd6
--- /dev/null
+++ b/sys/dev/misc/filemon/filemon_lock.c
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 2009-2011, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * 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 JUNIPER NETWORKS 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 JUNIPER NETWORKS 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.
+ *
+ * $FreeBSD: head/sys/dev/filemon/filemon_lock.c 236592 2012-06-04 22:54:19Z obrien $
+ */
+
+static void
+filemon_filemon_lock(struct filemon *filemon)
+{
+	lockmgr(&filemon->lock, LK_EXCLUSIVE);
+
+	while (filemon->locker != NULL && filemon->locker != curthread)
+		cv_wait(&filemon->cv, &filemon->lock);
+
+	filemon->locker = curthread;
+
+	lockmgr(&filemon->lock, LK_RELEASE);
+}
+
+static void
+filemon_filemon_unlock(struct filemon *filemon)
+{
+	lockmgr(&filemon->lock, LK_EXCLUSIVE);
+
+	if (filemon->locker == curthread)
+		filemon->locker = NULL;
+
+	/* Wake up threads waiting. */
+	cv_broadcast(&filemon->cv);
+
+	lockmgr(&filemon->lock, LK_RELEASE);
+}
+
+static void
+filemon_lock_read(void)
+{
+	lockmgr(&access_lock, LK_EXCLUSIVE);
+
+	while (access_owner != NULL || access_requester != NULL)
+		cv_wait(&access_cv, &access_lock);
+
+	n_readers++;
+
+	/* Wake up threads waiting. */
+	cv_broadcast(&access_cv);
+
+	lockmgr(&access_lock, LK_RELEASE);
+}
+
+static void
+filemon_unlock_read(void)
+{
+	lockmgr(&access_lock, LK_EXCLUSIVE);
+
+	if (n_readers > 0)
+		n_readers--;
+
+	/* Wake up a thread waiting. */
+	cv_broadcast(&access_cv);
+
+	lockmgr(&access_lock, LK_RELEASE);
+}
+
+static void
+filemon_lock_write(void)
+{
+	lockmgr(&access_lock, LK_EXCLUSIVE);
+
+	while (access_owner != curthread) {
+		if (access_owner == NULL &&
+		    (access_requester == NULL ||
+		    access_requester == curthread)) {
+			access_owner = curthread;
+			access_requester = NULL;
+		} else {
+			if (access_requester == NULL)
+				access_requester = curthread;
+
+			cv_wait(&access_cv, &access_lock);
+		}
+	}
+
+	lockmgr(&access_lock, LK_RELEASE);
+}
+
+static void
+filemon_unlock_write(void)
+{
+	lockmgr(&access_lock, LK_EXCLUSIVE);
+
+	/* Sanity check that the current thread actually has the write lock. */
+	if (access_owner == curthread)
+		access_owner = NULL;
+
+	/* Wake up a thread waiting. */
+	cv_broadcast(&access_cv);
+
+	lockmgr(&access_lock, LK_RELEASE);
+}
diff --git a/sys/dev/misc/filemon/filemon_wrapper.c b/sys/dev/misc/filemon/filemon_wrapper.c
new file mode 100644
index 0000000000..2ea524e5e6
--- /dev/null
+++ b/sys/dev/misc/filemon/filemon_wrapper.c
@@ -0,0 +1,706 @@
+/*-
+ * Copyright (c) 2011, David E. O'Brien.
+ * Copyright (c) 2009-2011, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * 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 JUNIPER NETWORKS 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 JUNIPER NETWORKS 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.
+ *
+ * $FreeBSD: head/sys/dev/filemon/filemon_wrapper.c 253977 2013-08-06 02:14:30Z hrs $
+ */
+
+#include "opt_compat.h"
+
+static void
+filemon_output(struct filemon *filemon, char *msg, size_t len)
+{
+	struct uio auio;
+	struct iovec aiov;
+
+	if (filemon->fp == NULL)
+		return;
+
+	aiov.iov_base = msg;
+	aiov.iov_len = len;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_resid = len;
+	auio.uio_segflg = UIO_SYSSPACE;
+	auio.uio_rw = UIO_WRITE;
+	auio.uio_td = curthread;
+	auio.uio_offset = (off_t) -1;
+
+	bwillwrite(len);
+
+	fo_write(filemon->fp, &auio, curthread->td_ucred, 0);
+}
+
+static struct filemon *
+filemon_pid_check(struct proc *p)
+{
+	struct filemon *filemon;
+
+	while (p->p_pptr) {
+		TAILQ_FOREACH(filemon, &filemons_inuse, link) {
+			if (p->p_pid == filemon->pid)
+				return (filemon);
+		}
+		p = p->p_pptr;
+	}
+	return (NULL);
+}
+
+static void
+filemon_comment(struct filemon *filemon)
+{
+	int len;
+	struct timeval now;
+
+	/* Load timestamp before locking.  Less accurate but less contention. */
+	getmicrotime(&now);
+
+	/* Grab a read lock on the filemon inuse list. */
+	filemon_lock_read();
+
+	/* Lock the found filemon structure. */
+	filemon_filemon_lock(filemon);
+
+	len = ksnprintf(filemon->msgbufr, sizeof(filemon->msgbufr),
+	    "# filemon version %d\n# Target pid %d\n# Start %ju.%06ju\nV %d\n",
+	    FILEMON_VERSION, curproc->p_pid, (uintmax_t)now.tv_sec,
+	    (uintmax_t)now.tv_usec, FILEMON_VERSION);
+
+	filemon_output(filemon, filemon->msgbufr, len);
+
+	/* Unlock the found filemon structure. */
+	filemon_filemon_unlock(filemon);
+
+	/* Release the read lock. */
+	filemon_unlock_read();
+}
+
+static int
+filemon_wrapper_chdir(struct chdir_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_chdir(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "C %d %s\n",
+			    curproc->p_pid, filemon->fname1);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_execve(struct execve_args *uap)
+{
+	char fname[MAXPATHLEN];
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	copyinstr(uap->fname, fname, sizeof(fname), &done);
+
+	if ((ret = sys_execve(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "E %d %s\n",
+			    curproc->p_pid, fname);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
+static int
+filemon_wrapper_freebsd32_execve(struct freebsd32_execve_args *uap)
+{
+	char fname[MAXPATHLEN];
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	copyinstr(uap->fname, fname, sizeof(fname), &done);
+
+	if ((ret = freebsd32_execve(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "E %d %s\n",
+			    curproc->p_pid, fname);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+#endif
+
+static int
+filemon_wrapper_fork(struct fork_args *uap)
+{
+	int ret;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_fork(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "F %d %ld\n",
+			    curproc->p_pid, uap->sysmsg_fds[0]);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_open(struct open_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_open(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+
+			if (uap->flags & O_RDWR) {
+				/*
+				 * We'll get the W record below, but need
+				 * to also output an R to distingish from
+				 * O_WRONLY.
+				 */
+				len = ksnprintf(filemon->msgbufr,
+				    sizeof(filemon->msgbufr), "R %d %s\n",
+				    curproc->p_pid, filemon->fname1);
+				filemon_output(filemon, filemon->msgbufr, len);
+			}
+
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "%c %d %s\n",
+			    (uap->flags & O_ACCMODE) ? 'W':'R',
+			    curproc->p_pid, filemon->fname1);
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_rename(struct rename_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_rename(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->from, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+			copyinstr(uap->to, filemon->fname2,
+			    sizeof(filemon->fname2), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "M %d '%s' '%s'\n",
+			    curproc->p_pid, filemon->fname1, filemon->fname2);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_link(struct link_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_link(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+			copyinstr(uap->link, filemon->fname2,
+			    sizeof(filemon->fname2), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "L %d '%s' '%s'\n",
+			    curproc->p_pid, filemon->fname1, filemon->fname2);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_symlink(struct symlink_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_symlink(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+			copyinstr(uap->link, filemon->fname2,
+			    sizeof(filemon->fname2), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "L %d '%s' '%s'\n",
+			    curproc->p_pid, filemon->fname1, filemon->fname2);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_linkat(struct linkat_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_linkat(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path1, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+			copyinstr(uap->path2, filemon->fname2,
+			    sizeof(filemon->fname2), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "L %d '%s' '%s'\n",
+			    curproc->p_pid, filemon->fname1, filemon->fname2);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_stat(struct stat_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_stat(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "S %d %s\n",
+			    curproc->p_pid, filemon->fname1);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
+static int
+filemon_wrapper_freebsd32_stat(struct freebsd32_stat_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = freebsd32_stat(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "S %d %s\n",
+			    curproc->p_pid, filemon->fname1);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+#endif
+
+static void
+filemon_wrapper_exit(struct exit_args *uap)
+{
+	size_t len;
+	struct filemon *filemon;
+	struct timeval now;
+
+	/* Get timestamp before locking. */
+	getmicrotime(&now);
+
+	/* Grab a read lock on the filemon inuse list. */
+	filemon_lock_read();
+
+	if ((filemon = filemon_pid_check(curproc)) != NULL) {
+		/* Lock the found filemon structure. */
+		filemon_filemon_lock(filemon);
+
+		len = ksnprintf(filemon->msgbufr, sizeof(filemon->msgbufr),
+		    "X %d %d\n", curproc->p_pid, uap->rval);
+
+		filemon_output(filemon, filemon->msgbufr, len);
+
+		/* Check if the monitored process is about to exit. */
+		if (filemon->pid == curproc->p_pid) {
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr),
+			    "# Stop %ju.%06ju\n# Bye bye\n",
+			    (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+			filemon->pid = -1;
+		}
+
+		/* Unlock the found filemon structure. */
+		filemon_filemon_unlock(filemon);
+	}
+
+	/* Release the read lock. */
+	filemon_unlock_read();
+
+	sys_exit(uap);
+}
+
+static int
+filemon_wrapper_unlink(struct unlink_args *uap)
+{
+	int ret;
+	size_t done;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_unlink(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			copyinstr(uap->path, filemon->fname1,
+			    sizeof(filemon->fname1), &done);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "D %d %s\n",
+			    curproc->p_pid, filemon->fname1);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static int
+filemon_wrapper_vfork(struct vfork_args *uap)
+{
+	int ret;
+	size_t len;
+	struct filemon *filemon;
+
+	if ((ret = sys_vfork(uap)) == 0) {
+		/* Grab a read lock on the filemon inuse list. */
+		filemon_lock_read();
+
+		if ((filemon = filemon_pid_check(curproc)) != NULL) {
+			/* Lock the found filemon structure. */
+			filemon_filemon_lock(filemon);
+
+			len = ksnprintf(filemon->msgbufr,
+			    sizeof(filemon->msgbufr), "F %d %ld\n",
+			    curproc->p_pid, uap->sysmsg_fds[0]);
+
+			filemon_output(filemon, filemon->msgbufr, len);
+
+			/* Unlock the found filemon structure. */
+			filemon_filemon_unlock(filemon);
+		}
+
+		/* Release the read lock. */
+		filemon_unlock_read();
+	}
+
+	return (ret);
+}
+
+static void
+filemon_wrapper_install(void)
+{
+#if defined(__LP64__)
+	struct sysent *sv_table = elf64_dragonfly_sysvec.sv_table;
+#else
+	struct sysent *sv_table = elf32_dragonfly_sysvec.sv_table;
+#endif
+
+	sv_table[SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir;
+	sv_table[SYS_exit].sy_call = (sy_call_t *) filemon_wrapper_exit;
+	sv_table[SYS_execve].sy_call = (sy_call_t *) filemon_wrapper_execve;
+	sv_table[SYS_fork].sy_call = (sy_call_t *) filemon_wrapper_fork;
+	sv_table[SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open;
+	sv_table[SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename;
+	sv_table[SYS_stat].sy_call = (sy_call_t *) filemon_wrapper_stat;
+	sv_table[SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink;
+	sv_table[SYS_vfork].sy_call = (sy_call_t *) filemon_wrapper_vfork;
+	sv_table[SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link;
+	sv_table[SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink;
+	sv_table[SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
+
+#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
+	sv_table = ia32_dragonfly_sysvec.sv_table;
+
+	sv_table[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir;
+	sv_table[FREEBSD32_SYS_exit].sy_call = (sy_call_t *) filemon_wrapper_exit;
+	sv_table[FREEBSD32_SYS_freebsd32_execve].sy_call = (sy_call_t *) filemon_wrapper_freebsd32_execve;
+	sv_table[FREEBSD32_SYS_fork].sy_call = (sy_call_t *) filemon_wrapper_fork;
+	sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open;
+	sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename;
+	sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *) filemon_wrapper_freebsd32_stat;
+	sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink;
+	sv_table[FREEBSD32_SYS_vfork].sy_call = (sy_call_t *) filemon_wrapper_vfork;
+	sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link;
+	sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink;
+	sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
+#endif	/* COMPAT_ARCH32 */
+}
+
+static void
+filemon_wrapper_deinstall(void)
+{
+#if defined(__LP64__)
+	struct sysent *sv_table = elf64_dragonfly_sysvec.sv_table;
+#else
+	struct sysent *sv_table = elf32_dragonfly_sysvec.sv_table;
+#endif
+
+	sv_table[SYS_chdir].sy_call = (sy_call_t *)sys_chdir;
+	sv_table[SYS_exit].sy_call = (sy_call_t *)sys_exit;
+	sv_table[SYS_execve].sy_call = (sy_call_t *)sys_execve;
+	sv_table[SYS_fork].sy_call = (sy_call_t *)sys_fork;
+	sv_table[SYS_open].sy_call = (sy_call_t *)sys_open;
+	sv_table[SYS_rename].sy_call = (sy_call_t *)sys_rename;
+	sv_table[SYS_stat].sy_call = (sy_call_t *)sys_stat;
+	sv_table[SYS_unlink].sy_call = (sy_call_t *)sys_unlink;
+	sv_table[SYS_vfork].sy_call = (sy_call_t *)sys_vfork;
+	sv_table[SYS_link].sy_call = (sy_call_t *)sys_link;
+	sv_table[SYS_symlink].sy_call = (sy_call_t *)sys_symlink;
+	sv_table[SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
+
+#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
+	sv_table = ia32_dragonfly_sysvec.sv_table;
+
+	sv_table[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *)sys_chdir;
+	sv_table[FREEBSD32_SYS_exit].sy_call = (sy_call_t *)sys_exit;
+	sv_table[FREEBSD32_SYS_freebsd32_execve].sy_call = (sy_call_t *)freebsd32_execve;
+	sv_table[FREEBSD32_SYS_fork].sy_call = (sy_call_t *)sys_fork;
+	sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *)sys_open;
+	sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *)sys_rename;
+	sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *)freebsd32_stat;
+	sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *)sys_unlink;
+	sv_table[FREEBSD32_SYS_vfork].sy_call = (sy_call_t *)sys_vfork;
+	sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *)sys_link;
+	sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *)sys_symlink;
+	sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
+#endif	/* COMPAT_ARCH32 */
+}
diff --git a/tools/regression/filemon/Makefile b/tools/regression/filemon/Makefile
new file mode 100644
index 0000000000..ccdbfb0ef1
--- /dev/null
+++ b/tools/regression/filemon/Makefile
@@ -0,0 +1,86 @@
+# $FreeBSD: head/tools/regression/filemon/Makefile 268351 2014-07-07 00:27:09Z marcel $
+
+#.if ${MACHINE_ARCH} == "x86_64"
+#BI_BITS=
+#.endif
+
+_BINS=	\
+	filemontest \
+	timed-forkb
+#	sizeof_long
+
+BINS=	${_BINS}
+.if defined(BI_BITS)
+BINS+=	${_BINS:C/$/32/g}
+.endif
+
+bins: ${BINS}
+all: bins
+
+MAN=
+
+WARNS?=	6
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+# We don't want to genreate CTF files. Force that here.
+MK_CTF=no
+
+CLEANFILES+=	${BINS}
+
+
+.for f32 in ${BINS}
+${f32}32: ${f32}.c
+	${CC} -m32 -DBIT=\"32\" -o ${.TARGET} ${CFLAGS} ${.ALLSRC}
+.endfor
+
+tests:
+	kldstat | grep filemon
+	@echo ""
+	${MAKE} test01
+	${MAKE} test02
+.if defined(BI_BITS)
+	${MAKE} test32
+.endif
+	@echo "filemon(4) tests passed."
+
+# Cannot use .OBJDIR -- 'filemontest' expects 'test_script.sh' in .
+#test01: ${BINS:Mfilemontest*} ${BINS:Msizeof_long*} clean-test
+test01: ${BINS:Mfilemontest*} clean-test
+.for BIN in ${BINS:Mfilemontest}
+	cd ${.CURDIR} ; \
+		for A in 1 2 3 4 5 6 7 8 9 0; do \
+		for B in 1 2 3 4 5 6 7 8 9 0; do \
+		for C in 1 2 3 4 5 6 7 8 9 0; do \
+			test -x ${BIN} && ${.OBJDIR}/${BIN} ;\
+		done ;\
+		done ;\
+		done
+	@cd ${.CURDIR} ; set +e ; egrep '(Start|Stop) .*\.' filemon_log.* | \
+	    grep -q -v '\.[0-9][0-9][0-9][0-9][0-9][0-9]$$' || printf "Time stamp format OK\n\n"
+.endfor
+	@cd ${.CURDIR} ; set +e ; for F in filemon_log.* ; do \
+	    tail -1 $$F | grep -q '# Bye bye' || echo "$$F missing filemon bye-bye" ; \
+	    NL=`wc -l $$F | awk '{print $$1}'` ; \
+	    if [ "$${NL}" != 100 ]; then echo "$$F BAD, contains $${NL} lines" ; exit 1 ; fi ; done
+
+test02: ${BINS:Mtimed-forkb*}
+	@echo "Without filemon(4) active:"
+	./timed-forkb
+	@echo "With filemon(4) active:"
+	script -f typescript-timed-forkb ./timed-forkb
+	ls -l typescript-timed-forkb.filemon
+
+test32: ${BINS:M*32*}
+	script -f typescript.${.TARGET} ./sizeof_long32 >/dev/null
+	@tail -1 typescript.test32.filemon | grep -q '# Bye bye' || (echo '32-bit comapt filemon Missing "bye-bye"' ; exit 1)
+	@egrep -q '^X [0-9]+ 0$$' typescript.test32.filemon || (echo "32-bit binary exit ERROR" ; exit 1)
+	@printf "filemon(4) 32bit FreeBSD support passed.\n\n"
+
+CLEANFILES+=	typescript-timed-forkb typescript-timed-forkb.filemon
+
+clean-test:
+	cd ${.CURDIR} ; rm -f filemon_log*.*
+
+clean-tests: clean-test
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/filemon/filemontest.c b/tools/regression/filemon/filemontest.c
new file mode 100644
index 0000000000..3e4afa503d
--- /dev/null
+++ b/tools/regression/filemon/filemontest.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2009-2011, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * 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 JUNIPER NETWORKS 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 JUNIPER NETWORKS 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.
+ *
+ * $FreeBSD: head/tools/regression/filemon/filemontest.c 251368 2013-06-04 06:38:01Z obrien $
+ */
+
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+
+#include <dev/misc/filemon/filemon.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <err.h>
+
+/*
+ * This simple test of filemon expects a test script called
+ * "test_script.sh" in the cwd.
+ */
+
+#ifndef BIT
+#define BIT ""
+#endif
+
+int
+main(void) {
+	char log_name[] = "filemon_log" BIT ".XXXXXX";
+	pid_t child;
+	int fm_fd, fm_log;
+
+	if ((fm_fd = open("/dev/filemon", O_RDWR)) == -1)
+		err(1, "open(\"/dev/filemon\", O_RDWR)");
+	if ((fm_log = mkstemp(log_name)) == -1)
+		err(1, "mkstemp(%s)", log_name);
+
+	if (ioctl(fm_fd, FILEMON_SET_FD, &fm_log) == -1)
+		err(1, "Cannot set filemon log file descriptor");
+
+	/* Set up these two fd's to close on exec. */
+	(void)fcntl(fm_fd, F_SETFD, FD_CLOEXEC);
+	(void)fcntl(fm_log, F_SETFD, FD_CLOEXEC);
+
+	switch (child = fork()) {
+	case 0:
+		child = getpid();
+		if (ioctl(fm_fd, FILEMON_SET_PID, &child) == -1)
+			err(1, "Cannot set filemon PID to %d", child);
+		system("env BIT=" BIT "	./test_script.sh");
+		break;
+	case -1:
+		err(1, "Cannot fork");
+	default:
+		wait(&child);
+		close(fm_fd);
+//		printf("Results in %s\n", log_name);
+		break;
+	}
+	return 0;
+}
diff --git a/tools/regression/filemon/test_script.sh b/tools/regression/filemon/test_script.sh
new file mode 100755
index 0000000000..8b097e7cc7
--- /dev/null
+++ b/tools/regression/filemon/test_script.sh
@@ -0,0 +1,44 @@
+#! /bin/sh
+#
+# Copyright (c) 2011, Juniper Networks, Inc.
+# All rights reserved.
+#
+# 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 JUNIPER NETWORKS 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 JUNIPER NETWORKS 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.
+#
+# $FreeBSD: head/tools/regression/filemon/test_script.sh 251368 2013-06-04 06:38:01Z obrien $
+
+trap 'rm -f $f1 $f2; exit 1' 1 2 3 13 15
+echo shazbot > /dev/null
+f1=`mktemp /tmp/filemon_test.XXXXXX`
+f2=`mktemp /tmp/ed-script.XXXXXX`
+> $f1
+echo "One line to rule them all" >> $f1
+wc -l $f1 > /dev/null
+#	ed(1)'s /tmp/ed.* buffer file will be opened RW
+echo ',s/$/./g'	> $f2
+echo 'wq'	>>$f2
+ed -s $f1 < $f2
+#echo ",s/$/./\
+#w" | ed -s $f1
+#rm $f1 $f2
+uptime > /dev/null
+#sizeof_long${BIT} > /dev/null
diff --git a/tools/regression/filemon/timed-forkb.c b/tools/regression/filemon/timed-forkb.c
new file mode 100644
index 0000000000..8835738cb9
--- /dev/null
+++ b/tools/regression/filemon/timed-forkb.c
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 2012 David O'Brien
+ * All rights reserved.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD: head/tools/regression/filemon/timed-forkb.c 251174 2013-05-31 04:27:41Z obrien $
+ */
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <err.h>
+#include <assert.h>
+
+#ifndef SLEEP
+#define	SLEEP	20	/* seconds */
+#endif
+
+static int verbose;
+
+static void
+usage(void)
+{
+	fprintf(stderr, "usage: %s\n", getprogname());
+	fprintf(stderr, "\t\t-n : length of fork(2) chain\n");
+	fprintf(stderr, "\t\t-t : limit run-time seconds\n");
+	exit(1);
+	/* NOTREACHED */
+}
+
+void term(int);
+void
+term(int signum)
+{
+
+	if (getpid() == getpgrp() || verbose) {
+		fprintf(stderr,
+	    "pid %d pgroup %d (ppid %d): Received SIGTERM(%d), exiting...\n",
+		    getpid(), getpgrp(), getppid(), signum);
+	 }
+	exit(1);
+}
+
+void angel_of_mercy(int);
+void
+angel_of_mercy(int sig __unused)
+{
+
+	signal(SIGALRM, SIG_IGN);	/* ignore this signal */
+	printf("Master process: alarmed waking up\n");
+	killpg(0, SIGTERM);
+	return;
+}
+
+int bombing_run(unsigned, int);
+int
+bombing_run(unsigned chainlen, int stime)
+{
+	struct rusage ru;
+	pid_t pid, cpid __unused;
+	int status;
+
+	if (chainlen) {
+		switch (pid = fork()) {
+		case -1:
+			errx(1, "%s: can't fork", __func__);
+
+		case 0:
+			/* This is the code the child runs. */
+			bombing_run(--chainlen, stime);
+			break;
+
+		default:
+			/* This is the code the parent runs. */
+			if (getpid() == getpgrp()) {
+				signal(SIGALRM, angel_of_mercy);
+				alarm(stime);	// time for bombing run...
+				cpid = wait4(pid, &status, 0, &ru);
+				alarm(0);
+				printf(
+		"Cleanly shutting down - pid %d pgroup %d (ppid %d)\n",
+				    getpid(), getpgrp(), getppid());
+			} else {
+				cpid = wait4(pid, &status, 0, &ru);
+			}
+		}
+	}
+
+	return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+	time_t start /*,tvec*/;
+	char *endptr, *ctm;
+	size_t len;
+	int nflag, tflag;
+	int ch, k, maxprocperuid;
+
+	(void)signal(SIGTERM, term);
+
+	nflag = 0;
+	tflag = SLEEP;
+
+	start = time(NULL);
+	ctm = ctime(&start);
+	ctm[24] = '\0';		// see: man 3 ctime
+	fprintf(stderr, "*** fork() generation started on \"%s\" ***\n", ctm);
+
+	while ((ch = getopt(argc, argv, "n:t:v")) != -1)
+		switch (ch) {
+		case 'n':
+			nflag = strtol(optarg, &endptr, 10);
+			if (nflag <= 0 || *endptr != '\0')
+				errx(1, "illegal number, -n argument -- %s",
+				    optarg);
+			break;
+		case 't':
+			tflag = strtol(optarg, &endptr, 10);
+			if (tflag <= 0 || *endptr != '\0')
+				errx(1, "illegal number, -t argument -- %s",
+				    optarg);
+			break;
+		case 'v':
+			++verbose;
+			break;
+		default:
+			usage();
+		}
+	argv += optind;
+
+	if (!nflag) {
+		len = sizeof(maxprocperuid);
+		k = sysctlbyname("kern.maxprocperuid", &maxprocperuid, &len,
+		    NULL, 0);
+		assert(k != ENOMEM);
+		/* Try to allow a shell to still be started. */
+		nflag = maxprocperuid - 10;
+	}
+
+	// Ensure a unique process group to make killing all children easier.
+	setpgrp(0,0);
+	printf("    pid %d pgroup %d (ppid %d), %d fork chain over %d sec\n",
+	    getpid(), getpgrp(), getppid(), nflag - 1, tflag);
+
+	return bombing_run(nflag, tflag);
+}
diff --git a/usr.bin/bmake/Makefile b/usr.bin/bmake/Makefile
index 33dd8d5244..9e443e7ff5 100644
--- a/usr.bin/bmake/Makefile
+++ b/usr.bin/bmake/Makefile
@@ -61,6 +61,12 @@ COPTS.var.c=	-Wno-format-nonliteral
 WARNS=	1
 .endif
 
+# meta mode can be useful even without filemon 
+FILEMON_H ?= /usr/include/dev/misc/filemon/filemon.h
+.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h"
+COPTS.meta.c += -DHAVE_FILEMON_H -I${FILEMON_H:H}
+.endif
+
 DPADD=		${LIBUTIL}
 LDADD=		-lutil
 
diff --git a/usr.bin/script/script.1 b/usr.bin/script/script.1
index 45d969eea6..3f3a6afc37 100644
--- a/usr.bin/script/script.1
+++ b/usr.bin/script/script.1
@@ -36,7 +36,7 @@
 .Nd make typescript of terminal session
 .Sh SYNOPSIS
 .Nm
-.Op Fl adkpqr
+.Op Fl adfkpqr
 .Op Fl F Ar pipe
 .Op Fl t Ar time
 .Op Ar file Op Ar command ...
@@ -83,6 +83,13 @@ This will allow a user to create a named pipe using
 .Xr mkfifo 1
 and another user may watch the live session using a utility like
 .Xr cat 1 .
+.It Fl f
+Create
+.Ar file.filemon
+or
+.Pa typescript.filemon
+using
+.Xr filemon 4 .
 .It Fl k
 Log keys sent to the program as well as output.
 .It Fl p
@@ -162,7 +169,8 @@ is assumed.
 for the
 .Em history
 mechanism
-.Pc
+.Pc ,
+.Xr filemon 4
 .Sh HISTORY
 The
 .Nm
diff --git a/usr.bin/script/script.c b/usr.bin/script/script.c
index 8fa672cfbb..93e8b65119 100644
--- a/usr.bin/script/script.c
+++ b/usr.bin/script/script.c
@@ -40,6 +40,8 @@
 #include <sys/uio.h>
 #include <sys/endian.h>
 
+#include <dev/misc/filemon/filemon.h>
+
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -66,7 +68,7 @@ static int master, slave;
 static int child;
 static const char *fname;
 static char *fmfname;
-static int qflg, ttyflg;
+static int fflg, qflg, ttyflg;
 static int usesleep, rawout, showexit;
 
 static struct termios tt;
@@ -92,11 +94,14 @@ main(int argc, char *argv[])
 	fd_set rfd;
 	int aflg, Fflg, kflg, pflg, ch, k, n;
 	int flushtime, readstdin;
+	int fm_fd, fm_log;
 
 	aflg = Fflg = kflg = pflg = 0;
 	usesleep = 1;
 	rawout = 0;
 	flushtime = 30;
+	fm_fd = -1;	/* Shut up stupid "may be used uninitialized" GCC
+			   warning. (not needed w/clang) */
 	showexit = 0;
 
 	while ((ch = getopt(argc, argv, "adFfkpqrt:")) != -1)
@@ -110,6 +115,9 @@ main(int argc, char *argv[])
 		case 'F':
 			Fflg = 1;
 			break;
+		case 'f':
+			fflg = 1;
+			break;
 		case 'k':
 			kflg = 1;
 			break;
@@ -144,6 +152,20 @@ main(int argc, char *argv[])
 	if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL)
 		err(1, "%s", fname);
 
+	if (fflg) {
+		asprintf(&fmfname, "%s.filemon", fname);
+		if (!fmfname)
+			err(1, "%s.filemon", fname);
+		if ((fm_fd = open("/dev/filemon", O_RDWR | O_CLOEXEC)) == -1)
+			err(1, "open(\"/dev/filemon\", O_RDWR)");
+		if ((fm_log = open(fmfname,
+		    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
+			err(1, "open(%s)", fmfname);
+		if (ioctl(fm_fd, FILEMON_SET_FD, &fm_log) < 0)
+			err(1, "Cannot set filemon log file descriptor");
+	}
+
 	if (pflg)
 		playback(fscript);
 
@@ -178,6 +200,10 @@ main(int argc, char *argv[])
 			}
 		}
 		fflush(fscript);
+		if (fflg) {
+			printf("Filemon started, output file is %s\n",
+			    fmfname);
+		}
 	}
 	if (ttyflg) {
 		rtt = tt;
@@ -191,9 +217,17 @@ main(int argc, char *argv[])
 		warn("fork");
 		done(1);
 	}
-	if (child == 0)
-		doshell(argv);
+	if (child == 0) {
+		if (fflg) {
+			int pid;
 
+			pid = getpid();
+			if (ioctl(fm_fd, FILEMON_SET_PID, &pid) < 0)
+				err(1, "Cannot set filemon PID");
+		}
+ 
+		doshell(argv);
+	}
 	close(slave);
 
 	start = tvec = time(0);
@@ -265,7 +299,7 @@ static void
 usage(void)
 {
 	fprintf(stderr,
-	    "usage: script [-adkpqr] [-t time] [file [command ...]]\n");
+	    "usage: script [-adfkpqr] [-t time] [file [command ...]]\n");
 	exit(1);
 }
 
@@ -328,6 +362,10 @@ done(int eno)
 			    ctime(&tvec));
 		}
 		printf("\nScript done, output file is %s\n", fname);
+		if (fflg) {
+			printf("Filemon done, output file is %s\n",
+			    fmfname);
+		}
 	}
 	fclose(fscript);
 	close(master);
-- 
2.19.1

