summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>2012-06-04 22:54:19 +0000
committerobrien <obrien@FreeBSD.org>2012-06-04 22:54:19 +0000
commit316161913dee073c7d5792d4a337b778cd8c7a64 (patch)
treedefb2c0e3341ce03aba00cb12daa09048f23c70a
parent0dd8d2fe744efc9f42823fb6f0dd9b6294723b18 (diff)
downloadFreeBSD-src-316161913dee073c7d5792d4a337b778cd8c7a64.zip
FreeBSD-src-316161913dee073c7d5792d4a337b778cd8c7a64.tar.gz
Add the 'filemon' device. 'filemon' is a kernel module that provides a device
interface for processes to record system calls of its children. Submitted by: Juniper Networks.
-rw-r--r--sys/dev/filemon/filemon.c377
-rw-r--r--sys/dev/filemon/filemon.h34
-rw-r--r--sys/dev/filemon/filemon_lock.c122
-rw-r--r--sys/dev/filemon/filemon_wrapper.c746
-rw-r--r--sys/modules/Makefile2
-rw-r--r--sys/modules/filemon/Makefile11
6 files changed, 1292 insertions, 0 deletions
diff --git a/sys/dev/filemon/filemon.c b/sys/dev/filemon/filemon.c
new file mode 100644
index 0000000..936a091
--- /dev/null
+++ b/sys/dev/filemon/filemon.c
@@ -0,0 +1,377 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/condvar.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.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>
+
+#if __FreeBSD_version >= 900041
+#include <sys/capability.h>
+#endif
+
+#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_freebsd_sysvec;
+#endif
+
+extern struct sysentvec elf32_freebsd_sysvec;
+extern struct sysentvec elf64_freebsd_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 cdevsw filemon_cdevsw = {
+ .d_version = D_VERSION,
+ .d_close = filemon_close,
+ .d_ioctl = filemon_ioctl,
+ .d_open = filemon_open,
+ .d_name = "filemon",
+};
+
+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 mtx mtx; /* 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 mtx access_mtx;
+static struct cv access_cv;
+static struct thread *access_owner = NULL;
+static struct thread *access_requester = NULL;
+
+#if __FreeBSD_version < 701000
+static struct clonedevs *filemon_clones;
+static eventhandler_tag eh_tag;
+#else
+static struct cdev *filemon_dev;
+#endif
+
+#include "filemon_lock.c"
+#include "filemon_wrapper.c"
+
+#if __FreeBSD_version < 701000
+static void
+filemon_clone(void *arg, struct ucred *cred, char *name, int namelen,
+ struct cdev **dev)
+{
+ int u = -1;
+ size_t len;
+
+ if (*dev != NULL)
+ return;
+
+ len = strlen(name);
+
+ if (len != 7)
+ return;
+
+ if (bcmp(name,"filemon", 7) != 0)
+ return;
+
+ /* Clone the device to the new minor number. */
+ if (clone_create(&filemon_clones, &filemon_cdevsw, &u, dev, 0) != 0)
+ /* Create the /dev/filemonNN entry. */
+ *dev = make_dev_cred(&filemon_cdevsw, u, cred, UID_ROOT,
+ GID_WHEEL, 0666, "filemon%d", u);
+ if (*dev != NULL) {
+ dev_ref(*dev);
+ (*dev)->si_flags |= SI_CHEAPCLONE;
+ }
+}
+#endif
+
+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, curthread);
+ }
+}
+
+static int
+filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused,
+ struct thread *td)
+{
+ int error = 0;
+ struct filemon *filemon;
+
+#if __FreeBSD_version < 701000
+ filemon = dev->si_drv1;
+#else
+ devfs_get_cdevpriv((void **) &filemon);
+#endif
+
+ switch (cmd) {
+ /* Set the output file descriptor. */
+ case FILEMON_SET_FD:
+#if __FreeBSD_version < 900041
+#define FGET_WRITE(a1, a2, a3) fget_write((a1), (a2), (a3))
+#else
+#define FGET_WRITE(a1, a2, a3) fget_write((a1), (a2), CAP_WRITE | CAP_SEEK, (a3))
+#endif
+ if ((error = FGET_WRITE(td, *(int *)data, &filemon->fp)) == 0)
+ /* Write the file header. */
+ filemon_comment(filemon);
+ break;
+
+ /* Set the monitored process ID. */
+ case FILEMON_SET_PID:
+ filemon->pid = *((pid_t *) data);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused,
+ struct thread *td __unused)
+{
+ 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 = malloc(sizeof(struct filemon), M_FILEMON,
+ M_WAITOK | M_ZERO);
+
+ filemon->fp = NULL;
+
+ mtx_init(&filemon->mtx, "filemon", "filemon", MTX_DEF);
+ cv_init(&filemon->cv, "filemon");
+ }
+
+ filemon->pid = curproc->p_pid;
+
+#if __FreeBSD_version < 701000
+ dev->si_drv1 = filemon;
+#else
+ devfs_set_cdevpriv(filemon, filemon_dtr);
+#endif
+
+ /* 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 cdev *dev __unused, int flag __unused, int fmt __unused,
+ struct thread *td __unused)
+{
+#if __FreeBSD_version < 701000
+ filemon_dtr(dev->si_drv1);
+
+ dev->si_drv1 = NULL;
+
+ /* Schedule this cloned device to be destroyed. */
+ destroy_dev_sched(dev);
+#endif
+
+ return (0);
+}
+
+static void
+filemon_load(void *dummy __unused)
+{
+ mtx_init(&access_mtx, "filemon", "filemon", MTX_DEF);
+ cv_init(&access_cv, "filemon");
+
+ /* Install the syscall wrappers. */
+ filemon_wrapper_install();
+
+#if __FreeBSD_version < 701000
+ /* Enable device cloning. */
+ clone_setup(&filemon_clones);
+
+ /* Setup device cloning events. */
+ eh_tag = EVENTHANDLER_REGISTER(dev_clone, filemon_clone, 0, 1000);
+#else
+ filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666,
+ "filemon");
+#endif
+}
+
+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 {
+#if __FreeBSD_version >= 701000
+ destroy_dev(filemon_dev);
+#endif
+
+ /* Deinstall the syscall wrappers. */
+ filemon_wrapper_deinstall();
+ }
+
+ /* Give up write access. */
+ filemon_unlock_write();
+
+ if (error == 0) {
+#if __FreeBSD_version < 701000
+ /*
+ * Check if there is still an event handler callback registered.
+ */
+ if (eh_tag != 0) {
+ /* De-register the device cloning event handler. */
+ EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
+ eh_tag = 0;
+
+ /* Stop device cloning. */
+ clone_cleanup(&filemon_clones);
+ }
+#endif
+ /* free() filemon structs free list. */
+ filemon_lock_write();
+ while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) {
+ TAILQ_REMOVE(&filemons_free, filemon, link);
+ mtx_destroy(&filemon->mtx);
+ cv_destroy(&filemon->cv);
+ free(filemon, M_FILEMON);
+ }
+ filemon_unlock_write();
+
+ mtx_destroy(&access_mtx);
+ 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/filemon/filemon.h b/sys/dev/filemon/filemon.h
new file mode 100644
index 0000000..95d2ef3
--- /dev/null
+++ b/sys/dev/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$
+ */
+
+#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/filemon/filemon_lock.c b/sys/dev/filemon/filemon_lock.c
new file mode 100644
index 0000000..6e836d1
--- /dev/null
+++ b/sys/dev/filemon/filemon_lock.c
@@ -0,0 +1,122 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+static void
+filemon_filemon_lock(struct filemon *filemon)
+{
+ mtx_lock(&filemon->mtx);
+
+ while (filemon->locker != NULL && filemon->locker != curthread)
+ cv_wait(&filemon->cv, &filemon->mtx);
+
+ filemon->locker = curthread;
+
+ mtx_unlock(&filemon->mtx);
+}
+
+static void
+filemon_filemon_unlock(struct filemon *filemon)
+{
+ mtx_lock(&filemon->mtx);
+
+ if (filemon->locker == curthread)
+ filemon->locker = NULL;
+
+ /* Wake up threads waiting. */
+ cv_broadcast(&filemon->cv);
+
+ mtx_unlock(&filemon->mtx);
+}
+
+static void
+filemon_lock_read(void)
+{
+ mtx_lock(&access_mtx);
+
+ while (access_owner != NULL || access_requester != NULL)
+ cv_wait(&access_cv, &access_mtx);
+
+ n_readers++;
+
+ /* Wake up threads waiting. */
+ cv_broadcast(&access_cv);
+
+ mtx_unlock(&access_mtx);
+}
+
+static void
+filemon_unlock_read(void)
+{
+ mtx_lock(&access_mtx);
+
+ if (n_readers > 0)
+ n_readers--;
+
+ /* Wake up a thread waiting. */
+ cv_broadcast(&access_cv);
+
+ mtx_unlock(&access_mtx);
+}
+
+static void
+filemon_lock_write(void)
+{
+ mtx_lock(&access_mtx);
+
+ 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_mtx);
+ }
+ }
+
+ mtx_unlock(&access_mtx);
+}
+
+static void
+filemon_unlock_write(void)
+{
+ mtx_lock(&access_mtx);
+
+ /* 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);
+
+ mtx_unlock(&access_mtx);
+}
diff --git a/sys/dev/filemon/filemon_wrapper.c b/sys/dev/filemon/filemon_wrapper.c
new file mode 100644
index 0000000..e59dca3
--- /dev/null
+++ b/sys/dev/filemon/filemon_wrapper.c
@@ -0,0 +1,746 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if __FreeBSD_version > 800032
+#define FILEMON_HAS_LINKAT
+#endif
+
+#if __FreeBSD_version < 900044 /* r225617 (2011-09-16) failed to bump
+ __FreeBSD_version. This really should
+ be based on "900045". "900044" is r225469
+ (2011-09-10) so this code is broken for
+ 9-CURRENT September 10th-16th. */
+#define sys_chdir chdir
+#define sys_execve execve
+#define sys_fork fork
+#define sys_link link
+#define sys_open open
+#define sys_rename rename
+#define sys_stat stat
+#define sys_symlink symlink
+#define sys_unlink unlink
+#define sys_vfork vfork
+#define sys_sys_exit sys_exit
+#ifdef FILEMON_HAS_LINKAT
+#define sys_linkat linkat
+#endif
+#endif /* __FreeBSD_version */
+
+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();
+
+ fo_write(filemon->fp, &auio, curthread->td_ucred, 0, curthread);
+}
+
+static struct filemon *
+filemon_pid_check(struct proc *p)
+{
+ struct filemon *filemon;
+
+ TAILQ_FOREACH(filemon, &filemons_inuse, link) {
+ if (p->p_pid == filemon->pid)
+ return (filemon);
+ }
+
+ if (p->p_pptr == NULL)
+ return (NULL);
+
+ return (filemon_pid_check(p->p_pptr));
+}
+
+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 = snprintf(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 thread *td, struct chdir_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_chdir(td, 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 = snprintf(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 thread *td, 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(td, 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 = snprintf(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 thread *td,
+ 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(td, 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 = snprintf(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 thread *td, struct fork_args *uap)
+{
+ int ret;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_fork(td, 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 = snprintf(filemon->msgbufr,
+ sizeof(filemon->msgbufr), "F %d %ld\n",
+ curproc->p_pid, (long)curthread->td_retval[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 thread *td, struct open_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_open(td, 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 = snprintf(filemon->msgbufr,
+ sizeof(filemon->msgbufr), "R %d %s\n",
+ curproc->p_pid, filemon->fname1);
+ filemon_output(filemon, filemon->msgbufr, len);
+ }
+
+
+ len = snprintf(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 thread *td, struct rename_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_rename(td, 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 = snprintf(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 thread *td, struct link_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_link(td, 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 = snprintf(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 thread *td, struct symlink_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_symlink(td, 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 = snprintf(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);
+}
+
+#ifdef FILEMON_HAS_LINKAT
+static int
+filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_linkat(td, 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 = snprintf(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);
+}
+#endif
+
+static int
+filemon_wrapper_stat(struct thread *td, struct stat_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_stat(td, 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 = snprintf(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 thread *td,
+ struct freebsd32_stat_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = freebsd32_stat(td, 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 = snprintf(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_sys_exit(struct thread *td, struct sys_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 = snprintf(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 = snprintf(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);
+ }
+
+ /* Unlock the found filemon structure. */
+ filemon_filemon_unlock(filemon);
+ }
+
+ /* Release the read lock. */
+ filemon_unlock_read();
+
+ sys_sys_exit(td, uap);
+}
+
+static int
+filemon_wrapper_unlink(struct thread *td, struct unlink_args *uap)
+{
+ int ret;
+ size_t done;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_unlink(td, 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 = snprintf(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 thread *td, struct vfork_args *uap)
+{
+ int ret;
+ size_t len;
+ struct filemon *filemon;
+
+ if ((ret = sys_vfork(td, 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 = snprintf(filemon->msgbufr,
+ sizeof(filemon->msgbufr), "F %d %ld\n",
+ curproc->p_pid, (long)curthread->td_retval[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(__i386__)
+ struct sysent *sv_table = elf32_freebsd_sysvec.sv_table;
+#elif defined(__amd64__)
+ struct sysent *sv_table = elf64_freebsd_sysvec.sv_table;
+#else
+#error Machine type not supported
+#endif
+
+ sv_table[SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir;
+ sv_table[SYS_exit].sy_call = (sy_call_t *) filemon_wrapper_sys_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;
+#ifdef FILEMON_HAS_LINKAT
+ sv_table[SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
+#endif
+
+#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
+ sv_table = ia32_freebsd_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_sys_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;
+#ifdef FILEMON_HAS_LINKAT
+ sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
+#endif
+#endif /* COMPAT_ARCH32 */
+}
+
+static void
+filemon_wrapper_deinstall(void)
+{
+#if defined(__i386__)
+ struct sysent *sv_table = elf32_freebsd_sysvec.sv_table;
+#elif defined(__amd64__)
+ struct sysent *sv_table = elf64_freebsd_sysvec.sv_table;
+#else
+#error Machine type not supported
+#endif
+
+ sv_table[SYS_chdir].sy_call = (sy_call_t *)sys_chdir;
+ sv_table[SYS_exit].sy_call = (sy_call_t *)sys_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;
+#ifdef FILEMON_HAS_LINKAT
+ sv_table[SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
+#endif
+
+#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
+ sv_table = ia32_freebsd_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_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;
+#ifdef FILEMON_HAS_LINKAT
+ sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
+#endif
+#endif /* COMPAT_ARCH32 */
+}
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index dff6c89..6f9a9e2 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -105,6 +105,7 @@ SUBDIR= ${_3dfx} \
fdc \
fdescfs \
${_fe} \
+ ${_filemon} \
firewire \
firmware \
${_fxp} \
@@ -359,6 +360,7 @@ SUBDIR= ${_3dfx} \
.if ${MACHINE_CPUARCH} != "powerpc" && ${MACHINE_CPUARCH} != "arm" && \
${MACHINE_CPUARCH} != "mips"
+_filemon= filemon
_syscons= syscons
_vpo= vpo
.endif
diff --git a/sys/modules/filemon/Makefile b/sys/modules/filemon/Makefile
new file mode 100644
index 0000000..3bb5351
--- /dev/null
+++ b/sys/modules/filemon/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+MAINTAINER= obrien@FreeBSD.org
+
+.PATH: ${.CURDIR}/../../dev/filemon
+
+KMOD= filemon
+SRCS= ${KMOD}.c
+SRCS+= vnode_if.h opt_compat.h opt_capsicum.h
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud