Index: share/man/man4/filemon.4 =================================================================== --- share/man/man4/filemon.4 +++ share/man/man4/filemon.4 @@ -31,7 +31,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 28, 2016 +.Dd February 29, 2016 .Dt FILEMON 4 .Os .Sh NAME @@ -189,8 +189,3 @@ Loading .Nm may reduce system performance for the noted syscalls. -.Pp -Only children of the set process are logged. -Processes can escape being traced by double forking. -This is not seen as a problem as the intended use is build monitoring, which -does not make sense to have daemons for. Index: sys/dev/filemon/filemon.c =================================================================== --- sys/dev/filemon/filemon.c +++ sys/dev/filemon/filemon.c @@ -82,23 +82,26 @@ MALLOC_DECLARE(M_FILEMON); MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); +struct filemon_proc { + TAILQ_ENTRY(filemon_proc) proc; /* List of procs for this filemon. */ + struct proc *p; +}; + struct filemon { - TAILQ_ENTRY(filemon) link; /* Link into the in-use list. */ + TAILQ_HEAD(, filemon_proc) procs; /* Pointer to list of procs. */ struct sx lock; /* Lock mutex for this filemon. */ struct file *fp; /* Output file pointer. */ - struct proc *p; /* The process 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 struct sx access_lock; - static struct cdev *filemon_dev; -#include "filemon_lock.c" +static void filemon_free(struct filemon *); +static void filemon_track_process(struct filemon *, struct proc *); +static void filemon_untrack_process(struct filemon *, struct proc *); + #include "filemon_wrapper.c" static void @@ -118,34 +121,135 @@ } static void -filemon_dtr(void *data) +filemon_free(struct filemon *filemon) { - struct filemon *filemon = data; + size_t len; + struct timeval now; + + if (filemon->fp != NULL) { + getmicrotime(&now); + + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), + "# Stop %ju.%06ju\n# Bye bye\n", + (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); - if (filemon != NULL) { - struct file *fp; + filemon_output(filemon, filemon->msgbufr, len); + fdrop(filemon->fp, curthread); + } + + sx_xunlock(&filemon->lock); + sx_destroy(&filemon->lock); + free(filemon, M_FILEMON); +} + +static void +filemon_track_process(struct filemon *filemon, struct proc *p) +{ + struct filemon_proc *filemon_proc; - /* Follow same locking order as filemon_pid_check. */ - filemon_lock_write(); - sx_xlock(&filemon->lock); + sx_assert(&filemon->lock, SA_XLOCKED); - /* Remove from the in-use list. */ - TAILQ_REMOVE(&filemons_inuse, filemon, link); + filemon_proc = malloc(sizeof(struct filemon_proc), M_FILEMON, + M_WAITOK | M_ZERO); + filemon_proc->p = p; + TAILQ_INSERT_TAIL(&filemon->procs, filemon_proc, proc); +} - fp = filemon->fp; - filemon->fp = NULL; - filemon->p = NULL; +static void +filemon_proc_free(struct filemon *filemon, + struct filemon_proc *filemon_proc) +{ - /* Add to the free list. */ - TAILQ_INSERT_TAIL(&filemons_free, filemon, link); + TAILQ_REMOVE(&filemon->procs, filemon_proc, proc); + free(filemon_proc, M_FILEMON); +} + +static void +filemon_untrack_process(struct filemon *filemon, struct proc *p) +{ + struct filemon_proc *filemon_proc; + + sx_assert(&filemon->lock, SA_XLOCKED); + + TAILQ_FOREACH(filemon_proc, &filemon->procs, proc) { + if (filemon_proc->p == p) { + filemon_proc_free(filemon, filemon_proc); + break; + } + } +} + +static int +filemon_untrack_all_processes(void) +{ + int error; + struct filemon *filemon; + struct proc *p; + + error = 0; + sx_slock(&allproc_lock); + FOREACH_PROC_IN_SYSTEM(p) { + PROC_LOCK(p); + if ((filemon = p->p_filemon) != NULL) { + if (sx_try_xlock(&filemon->lock)) { + p->p_filemon = NULL; + PROC_UNLOCK(p); + filemon_untrack_process(filemon, p); + sx_xunlock(&filemon->lock); + } else { + PROC_UNLOCK(p); + error = EBUSY; + } + } else + PROC_UNLOCK(p); + } + sx_sunlock(&allproc_lock); + + return (error); +} - /* Give up write access. */ - sx_xunlock(&filemon->lock); - filemon_unlock_write(); +/* + * Invalidate the passed filemon in all processes. Uses the filemon.procs + * list for efficiency. + */ +static void +filemon_untrack_processes(struct filemon *filemon) +{ + struct filemon_proc *filemon_proc, *filemon_proc_tmp; + struct proc *p; - if (fp != NULL) - fdrop(fp, curthread); + sx_assert(&filemon->lock, SA_XLOCKED); + + /* + * Processes in this list won't go away while here since + * filemon_event_process_exit() will lock on filemon->lock + * which we hold. + */ + TAILQ_FOREACH_SAFE(filemon_proc, &filemon->procs, proc, + filemon_proc_tmp) { + p = filemon_proc->p; + PROC_LOCK(p); + p->p_filemon = NULL; + PROC_UNLOCK(p); + filemon_proc_free(filemon, filemon_proc); } + + return; +} + +/* The devfs file is being closed. Untrace all processes and cleanup. */ +static void +filemon_dtr(void *data) +{ + struct filemon *filemon = data; + + if (filemon == NULL) + return; + + sx_xlock(&filemon->lock); + filemon_untrack_processes(filemon); + filemon_free(filemon); } static int @@ -178,11 +282,19 @@ /* Set the monitored process ID. */ case FILEMON_SET_PID: + /* Invalidate any existing processes already set. */ + filemon_untrack_processes(filemon); + error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, &p); if (error == 0) { - filemon->p = p; + if (p->p_filemon != NULL) + error = EBUSY; + else + p->p_filemon = filemon; PROC_UNLOCK(p); + if (error == 0) + filemon_track_process(filemon, p); } break; @@ -199,35 +311,19 @@ filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, struct thread *td __unused) { + int error; 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); - sx_init(&filemon->lock, "filemon"); - } + filemon = malloc(sizeof(struct filemon), M_FILEMON, + M_WAITOK | M_ZERO); + sx_init(&filemon->lock, "filemon"); + TAILQ_INIT(&filemon->procs); - devfs_set_cdevpriv(filemon, filemon_dtr); + error = devfs_set_cdevpriv(filemon, filemon_dtr); + if (error != 0) + filemon_free(filemon); - /* 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); + return (error); } static int @@ -241,7 +337,6 @@ static void filemon_load(void *dummy __unused) { - sx_init(&access_lock, "filemons_inuse"); /* Install the syscall wrappers. */ filemon_wrapper_install(); @@ -253,38 +348,15 @@ 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(); - } + int error; - /* Give up write access. */ - filemon_unlock_write(); - - if (error == 0) { - /* free() filemon structs free list. */ - filemon_lock_write(); - while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) { - TAILQ_REMOVE(&filemons_free, filemon, link); - sx_destroy(&filemon->lock); - free(filemon, M_FILEMON); - } - filemon_unlock_write(); - - sx_destroy(&access_lock); - } + destroy_dev(filemon_dev); + error = filemon_untrack_all_processes(); + if (error != 0) + return (error); + filemon_wrapper_deinstall(); - return (error); + return (0); } static int @@ -301,6 +373,10 @@ error = filemon_unload(); break; + case MOD_QUIESCE: + error = filemon_untrack_all_processes(); + break; + case MOD_SHUTDOWN: break; Index: sys/dev/filemon/filemon_lock.c =================================================================== --- sys/dev/filemon/filemon_lock.c +++ /dev/null @@ -1,57 +0,0 @@ -/*- - * Copyright (c) 2009-2011, Juniper Networks, Inc. - * Copyright (c) 2015, EMC Corp. - * 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 -__FBSDID("$FreeBSD$"); - -static __inline void -filemon_lock_read(void) -{ - - sx_slock(&access_lock); -} - -static __inline void -filemon_unlock_read(void) -{ - - sx_sunlock(&access_lock); -} - -static __inline void -filemon_lock_write(void) -{ - - sx_xlock(&access_lock); -} - -static __inline void -filemon_unlock_write(void) -{ - - sx_xunlock(&access_lock); -} Index: sys/dev/filemon/filemon_wrapper.c =================================================================== --- sys/dev/filemon/filemon_wrapper.c +++ sys/dev/filemon/filemon_wrapper.c @@ -69,26 +69,14 @@ { struct filemon *filemon; - filemon_lock_read(); - if (TAILQ_EMPTY(&filemons_inuse)) { - filemon_unlock_read(); + PROC_LOCK(p); + filemon = p->p_filemon; + PROC_UNLOCK(p); + + if (filemon == NULL) return (NULL); - } - sx_slock(&proctree_lock); - while (p->p_pid != 0) { - TAILQ_FOREACH(filemon, &filemons_inuse, link) { - if (p == filemon->p) { - sx_sunlock(&proctree_lock); - sx_xlock(&filemon->lock); - filemon_unlock_read(); - return (filemon); - } - } - p = proc_realparent(p); - } - sx_sunlock(&proctree_lock); - filemon_unlock_read(); - return (NULL); + sx_xlock(&filemon->lock); + return (filemon); } static int @@ -407,10 +395,6 @@ { size_t len; struct filemon *filemon; - struct timeval now; - - /* Get timestamp before locking. */ - getmicrotime(&now); if ((filemon = filemon_pid_check(p)) != NULL) { len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), @@ -418,16 +402,11 @@ filemon_output(filemon, filemon->msgbufr, len); - /* Check if the monitored process is about to exit. */ - if (filemon->p == p) { - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), - "# Stop %ju.%06ju\n# Bye bye\n", - (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); + PROC_LOCK(p); + p->p_filemon = NULL; + PROC_UNLOCK(p); - filemon_output(filemon, filemon->msgbufr, len); - filemon->p = NULL; - } + filemon_untrack_process(filemon, p); sx_xunlock(&filemon->lock); } @@ -473,6 +452,12 @@ filemon_output(filemon, filemon->msgbufr, len); + PROC_LOCK(p2); + p2->p_filemon = filemon; + PROC_UNLOCK(p2); + + filemon_track_process(filemon, p2); + sx_xunlock(&filemon->lock); } } Index: sys/kern/kern_proc.c =================================================================== --- sys/kern/kern_proc.c +++ sys/kern/kern_proc.c @@ -249,6 +249,7 @@ EVENTHANDLER_INVOKE(process_init, p); p->p_stats = pstats_alloc(); p->p_pgrp = NULL; + p->p_filemon = NULL; SDT_PROBE3(proc, , init, return, p, size, flags); return (0); } Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -166,6 +166,7 @@ struct kaudit_record; struct kdtrace_proc; struct kdtrace_thread; +struct filemon; struct mqueue_notifier; struct nlminfo; struct p_sched; @@ -630,6 +631,7 @@ */ LIST_ENTRY(proc) p_orphan; /* (e) List of orphan processes. */ LIST_HEAD(, proc) p_orphans; /* (e) Pointer to list of orphans. */ + struct filemon *p_filemon; /* (c) filemon-specific data. */ }; #define p_session p_pgrp->pg_session