1eb9aea5aSDavid E. O'Brien /*- 2eb9aea5aSDavid E. O'Brien * Copyright (c) 2011, David E. O'Brien. 3eb9aea5aSDavid E. O'Brien * Copyright (c) 2009-2011, Juniper Networks, Inc. 4e0d84b9eSBryan Drewery * Copyright (c) 2015-2016, EMC Corp. 5eb9aea5aSDavid E. O'Brien * All rights reserved. 6eb9aea5aSDavid E. O'Brien * 7eb9aea5aSDavid E. O'Brien * Redistribution and use in source and binary forms, with or without 8eb9aea5aSDavid E. O'Brien * modification, are permitted provided that the following conditions 9eb9aea5aSDavid E. O'Brien * are met: 10eb9aea5aSDavid E. O'Brien * 1. Redistributions of source code must retain the above copyright 11eb9aea5aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer. 12eb9aea5aSDavid E. O'Brien * 2. Redistributions in binary form must reproduce the above copyright 13eb9aea5aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer in the 14eb9aea5aSDavid E. O'Brien * documentation and/or other materials provided with the distribution. 15eb9aea5aSDavid E. O'Brien * 16eb9aea5aSDavid E. O'Brien * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND 17eb9aea5aSDavid E. O'Brien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18eb9aea5aSDavid E. O'Brien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19eb9aea5aSDavid E. O'Brien * ARE DISCLAIMED. IN NO EVENT SHALL JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE 20eb9aea5aSDavid E. O'Brien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21eb9aea5aSDavid E. O'Brien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22eb9aea5aSDavid E. O'Brien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23eb9aea5aSDavid E. O'Brien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24eb9aea5aSDavid E. O'Brien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25eb9aea5aSDavid E. O'Brien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26eb9aea5aSDavid E. O'Brien * SUCH DAMAGE. 27eb9aea5aSDavid E. O'Brien */ 28eb9aea5aSDavid E. O'Brien 29af13de0fSJohn Baldwin #include <sys/cdefs.h> 30eb9aea5aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 31eb9aea5aSDavid E. O'Brien 32f9d4b392SDavid E. O'Brien #include "opt_compat.h" 33f9d4b392SDavid E. O'Brien 34af13de0fSJohn Baldwin #include <sys/param.h> 35eb9aea5aSDavid E. O'Brien #include <sys/file.h> 36eb9aea5aSDavid E. O'Brien #include <sys/systm.h> 37eb9aea5aSDavid E. O'Brien #include <sys/buf.h> 385b83ad4bSBryan Drewery #include <sys/capsicum.h> 39eb9aea5aSDavid E. O'Brien #include <sys/condvar.h> 40eb9aea5aSDavid E. O'Brien #include <sys/conf.h> 41eb9aea5aSDavid E. O'Brien #include <sys/fcntl.h> 42eb9aea5aSDavid E. O'Brien #include <sys/ioccom.h> 43eb9aea5aSDavid E. O'Brien #include <sys/kernel.h> 448183f2e3SBryan Drewery #include <sys/lock.h> 45eb9aea5aSDavid E. O'Brien #include <sys/malloc.h> 46eb9aea5aSDavid E. O'Brien #include <sys/module.h> 47eb9aea5aSDavid E. O'Brien #include <sys/poll.h> 48eb9aea5aSDavid E. O'Brien #include <sys/proc.h> 498183f2e3SBryan Drewery #include <sys/sx.h> 50eb9aea5aSDavid E. O'Brien #include <sys/syscall.h> 51eb9aea5aSDavid E. O'Brien #include <sys/sysent.h> 52eb9aea5aSDavid E. O'Brien #include <sys/sysproto.h> 53eb9aea5aSDavid E. O'Brien #include <sys/uio.h> 54eb9aea5aSDavid E. O'Brien 55eb9aea5aSDavid E. O'Brien #include "filemon.h" 56eb9aea5aSDavid E. O'Brien 57eb9aea5aSDavid E. O'Brien #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 58eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_syscall.h> 59eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_proto.h> 60eb9aea5aSDavid E. O'Brien 61eb9aea5aSDavid E. O'Brien extern struct sysentvec ia32_freebsd_sysvec; 62eb9aea5aSDavid E. O'Brien #endif 63eb9aea5aSDavid E. O'Brien 64eb9aea5aSDavid E. O'Brien extern struct sysentvec elf32_freebsd_sysvec; 65eb9aea5aSDavid E. O'Brien extern struct sysentvec elf64_freebsd_sysvec; 66eb9aea5aSDavid E. O'Brien 67eb9aea5aSDavid E. O'Brien static d_close_t filemon_close; 68eb9aea5aSDavid E. O'Brien static d_ioctl_t filemon_ioctl; 69eb9aea5aSDavid E. O'Brien static d_open_t filemon_open; 70eb9aea5aSDavid E. O'Brien 71eb9aea5aSDavid E. O'Brien static struct cdevsw filemon_cdevsw = { 72eb9aea5aSDavid E. O'Brien .d_version = D_VERSION, 73eb9aea5aSDavid E. O'Brien .d_close = filemon_close, 74eb9aea5aSDavid E. O'Brien .d_ioctl = filemon_ioctl, 75eb9aea5aSDavid E. O'Brien .d_open = filemon_open, 76eb9aea5aSDavid E. O'Brien .d_name = "filemon", 77eb9aea5aSDavid E. O'Brien }; 78eb9aea5aSDavid E. O'Brien 79eb9aea5aSDavid E. O'Brien MALLOC_DECLARE(M_FILEMON); 80eb9aea5aSDavid E. O'Brien MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); 81eb9aea5aSDavid E. O'Brien 82e0d84b9eSBryan Drewery /* 83e0d84b9eSBryan Drewery * The filemon->lock protects several things currently: 84e0d84b9eSBryan Drewery * - fname1/fname2/msgbufr are pre-allocated and used per syscall 85e0d84b9eSBryan Drewery * for logging and copyins rather than stack variables. 86e0d84b9eSBryan Drewery * - Serializing the filemon's log output. 87e0d84b9eSBryan Drewery * - Preventing inheritance or removal of the filemon into proc.p_filemon. 88e0d84b9eSBryan Drewery */ 89eb9aea5aSDavid E. O'Brien struct filemon { 90e0d84b9eSBryan Drewery struct sx lock; /* Lock for this filemon. */ 91eb9aea5aSDavid E. O'Brien struct file *fp; /* Output file pointer. */ 92eb9aea5aSDavid E. O'Brien char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ 93eb9aea5aSDavid E. O'Brien char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ 94eb9aea5aSDavid E. O'Brien char msgbufr[1024]; /* Output message buffer. */ 95*4177d9f7SBryan Drewery int error; /* Log write error, returned on close(2). */ 96e0d84b9eSBryan Drewery u_int refcnt; /* Pointer reference count. */ 97e0d84b9eSBryan Drewery u_int proccnt; /* Process count. */ 98eb9aea5aSDavid E. O'Brien }; 99eb9aea5aSDavid E. O'Brien 100eb9aea5aSDavid E. O'Brien static struct cdev *filemon_dev; 101e0d84b9eSBryan Drewery static void filemon_output(struct filemon *filemon, char *msg, size_t len); 102eb9aea5aSDavid E. O'Brien 103e0d84b9eSBryan Drewery static __inline struct filemon * 104e0d84b9eSBryan Drewery filemon_acquire(struct filemon *filemon) 105e0d84b9eSBryan Drewery { 106e0d84b9eSBryan Drewery 107e0d84b9eSBryan Drewery if (filemon != NULL) 108e0d84b9eSBryan Drewery refcount_acquire(&filemon->refcnt); 109e0d84b9eSBryan Drewery return (filemon); 110e0d84b9eSBryan Drewery } 111e0d84b9eSBryan Drewery 112e0d84b9eSBryan Drewery /* 1134d9fbc55SBryan Drewery * Release a reference and free on the last one. 114e0d84b9eSBryan Drewery */ 115e0d84b9eSBryan Drewery static void 116e0d84b9eSBryan Drewery filemon_release(struct filemon *filemon) 117e0d84b9eSBryan Drewery { 118e0d84b9eSBryan Drewery 119e0d84b9eSBryan Drewery if (refcount_release(&filemon->refcnt) == 0) 120e0d84b9eSBryan Drewery return; 121e0d84b9eSBryan Drewery /* 122e0d84b9eSBryan Drewery * There are valid cases of releasing while locked, such as in 123e0d84b9eSBryan Drewery * filemon_untrack_processes, but none which are done where there 124e0d84b9eSBryan Drewery * is not at least 1 reference remaining. 125e0d84b9eSBryan Drewery */ 126e0d84b9eSBryan Drewery sx_assert(&filemon->lock, SA_UNLOCKED); 127e0d84b9eSBryan Drewery 128e0d84b9eSBryan Drewery sx_destroy(&filemon->lock); 129e0d84b9eSBryan Drewery free(filemon, M_FILEMON); 130e0d84b9eSBryan Drewery } 131e0d84b9eSBryan Drewery 132e0d84b9eSBryan Drewery /* 133e0d84b9eSBryan Drewery * Acquire the proc's p_filemon reference and lock the filemon. 134e0d84b9eSBryan Drewery * The proc's p_filemon may not match this filemon on return. 135e0d84b9eSBryan Drewery */ 136e0d84b9eSBryan Drewery static struct filemon * 137e0d84b9eSBryan Drewery filemon_proc_get(struct proc *p) 138e0d84b9eSBryan Drewery { 139e0d84b9eSBryan Drewery struct filemon *filemon; 140e0d84b9eSBryan Drewery 141e0d84b9eSBryan Drewery PROC_LOCK(p); 142e0d84b9eSBryan Drewery filemon = filemon_acquire(p->p_filemon); 143e0d84b9eSBryan Drewery PROC_UNLOCK(p); 144e0d84b9eSBryan Drewery 145e0d84b9eSBryan Drewery if (filemon == NULL) 146e0d84b9eSBryan Drewery return (NULL); 147e0d84b9eSBryan Drewery /* 148e0d84b9eSBryan Drewery * The p->p_filemon may have changed by now. That case is handled 149e0d84b9eSBryan Drewery * by the exit and fork hooks and filemon_attach_proc specially. 150e0d84b9eSBryan Drewery */ 151e0d84b9eSBryan Drewery sx_xlock(&filemon->lock); 152e0d84b9eSBryan Drewery return (filemon); 153e0d84b9eSBryan Drewery } 154e0d84b9eSBryan Drewery 155e0d84b9eSBryan Drewery /* Remove and release the filemon on the given process. */ 156e0d84b9eSBryan Drewery static void 157e0d84b9eSBryan Drewery filemon_proc_drop(struct proc *p) 158e0d84b9eSBryan Drewery { 159e0d84b9eSBryan Drewery struct filemon *filemon; 160e0d84b9eSBryan Drewery 161e0d84b9eSBryan Drewery KASSERT(p->p_filemon != NULL, ("%s: proc %p NULL p_filemon", 162e0d84b9eSBryan Drewery __func__, p)); 163e0d84b9eSBryan Drewery sx_assert(&p->p_filemon->lock, SA_XLOCKED); 164e0d84b9eSBryan Drewery PROC_LOCK(p); 165e0d84b9eSBryan Drewery filemon = p->p_filemon; 166e0d84b9eSBryan Drewery p->p_filemon = NULL; 167e0d84b9eSBryan Drewery --filemon->proccnt; 168e0d84b9eSBryan Drewery PROC_UNLOCK(p); 169e0d84b9eSBryan Drewery /* 170e0d84b9eSBryan Drewery * This should not be the last reference yet. filemon_release() 171e0d84b9eSBryan Drewery * cannot be called with filemon locked, which the caller expects 172e0d84b9eSBryan Drewery * will stay locked. 173e0d84b9eSBryan Drewery */ 174e0d84b9eSBryan Drewery KASSERT(filemon->refcnt > 1, ("%s: proc %p dropping filemon %p " 175e0d84b9eSBryan Drewery "with last reference", __func__, p, filemon)); 176e0d84b9eSBryan Drewery filemon_release(filemon); 177e0d84b9eSBryan Drewery } 178e0d84b9eSBryan Drewery 179e0d84b9eSBryan Drewery /* Unlock and release the filemon. */ 180e0d84b9eSBryan Drewery static __inline void 181e0d84b9eSBryan Drewery filemon_drop(struct filemon *filemon) 182e0d84b9eSBryan Drewery { 183e0d84b9eSBryan Drewery 184e0d84b9eSBryan Drewery sx_xunlock(&filemon->lock); 185e0d84b9eSBryan Drewery filemon_release(filemon); 186e0d84b9eSBryan Drewery } 187e0d84b9eSBryan Drewery 188eb9aea5aSDavid E. O'Brien #include "filemon_wrapper.c" 189eb9aea5aSDavid E. O'Brien 190eb9aea5aSDavid E. O'Brien static void 1912b198fe9SBryan Drewery filemon_comment(struct filemon *filemon) 1922b198fe9SBryan Drewery { 1932b198fe9SBryan Drewery int len; 1942b198fe9SBryan Drewery struct timeval now; 1952b198fe9SBryan Drewery 1962b198fe9SBryan Drewery getmicrotime(&now); 1972b198fe9SBryan Drewery 1982b198fe9SBryan Drewery len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), 1992b198fe9SBryan Drewery "# filemon version %d\n# Target pid %d\n# Start %ju.%06ju\nV %d\n", 2002b198fe9SBryan Drewery FILEMON_VERSION, curproc->p_pid, (uintmax_t)now.tv_sec, 2012b198fe9SBryan Drewery (uintmax_t)now.tv_usec, FILEMON_VERSION); 2022b198fe9SBryan Drewery 2032b198fe9SBryan Drewery filemon_output(filemon, filemon->msgbufr, len); 2042b198fe9SBryan Drewery } 2052b198fe9SBryan Drewery 206e0d84b9eSBryan Drewery /* 207e0d84b9eSBryan Drewery * Invalidate the passed filemon in all processes. 208e0d84b9eSBryan Drewery */ 209e0d84b9eSBryan Drewery static void 210e0d84b9eSBryan Drewery filemon_untrack_processes(struct filemon *filemon) 211e0d84b9eSBryan Drewery { 212e0d84b9eSBryan Drewery struct proc *p; 213e0d84b9eSBryan Drewery 214e0d84b9eSBryan Drewery sx_assert(&filemon->lock, SA_XLOCKED); 215e0d84b9eSBryan Drewery 216e0d84b9eSBryan Drewery /* Avoid allproc loop if there is no need. */ 217e0d84b9eSBryan Drewery if (filemon->proccnt == 0) 218e0d84b9eSBryan Drewery return; 219e0d84b9eSBryan Drewery 220e0d84b9eSBryan Drewery /* 221e0d84b9eSBryan Drewery * Processes in this list won't go away while here since 222e0d84b9eSBryan Drewery * filemon_event_process_exit() will lock on filemon->lock 223e0d84b9eSBryan Drewery * which we hold. 224e0d84b9eSBryan Drewery */ 225e0d84b9eSBryan Drewery sx_slock(&allproc_lock); 226e0d84b9eSBryan Drewery FOREACH_PROC_IN_SYSTEM(p) { 227e0d84b9eSBryan Drewery /* 228e0d84b9eSBryan Drewery * No PROC_LOCK is needed to compare here since it is 229e0d84b9eSBryan Drewery * guaranteed to not change since we have its filemon 230e0d84b9eSBryan Drewery * locked. Everything that changes this p_filemon will 231e0d84b9eSBryan Drewery * be locked on it. 232e0d84b9eSBryan Drewery */ 233e0d84b9eSBryan Drewery if (p->p_filemon == filemon) 234e0d84b9eSBryan Drewery filemon_proc_drop(p); 235e0d84b9eSBryan Drewery } 236e0d84b9eSBryan Drewery sx_sunlock(&allproc_lock); 237e0d84b9eSBryan Drewery 238e0d84b9eSBryan Drewery /* 239e0d84b9eSBryan Drewery * It's possible some references were acquired but will be 240e0d84b9eSBryan Drewery * dropped shortly as they are restricted from being 241e0d84b9eSBryan Drewery * inherited. There is at least the reference in cdevpriv remaining. 242e0d84b9eSBryan Drewery */ 243e0d84b9eSBryan Drewery KASSERT(filemon->refcnt > 0, ("%s: filemon %p should have " 244e0d84b9eSBryan Drewery "references still.", __func__, filemon)); 245e0d84b9eSBryan Drewery KASSERT(filemon->proccnt == 0, ("%s: filemon %p should not have " 246e0d84b9eSBryan Drewery "attached procs still.", __func__, filemon)); 247e0d84b9eSBryan Drewery } 248e0d84b9eSBryan Drewery 2494d9fbc55SBryan Drewery /* 2504d9fbc55SBryan Drewery * Close out the log. 2514d9fbc55SBryan Drewery */ 2524d9fbc55SBryan Drewery static void 2534d9fbc55SBryan Drewery filemon_close_log(struct filemon *filemon) 2544d9fbc55SBryan Drewery { 2554d9fbc55SBryan Drewery struct file *fp; 2564d9fbc55SBryan Drewery struct timeval now; 2574d9fbc55SBryan Drewery size_t len; 2584d9fbc55SBryan Drewery 2594d9fbc55SBryan Drewery sx_assert(&filemon->lock, SA_XLOCKED); 2604d9fbc55SBryan Drewery if (filemon->fp == NULL) 2614d9fbc55SBryan Drewery return; 2624d9fbc55SBryan Drewery 2634d9fbc55SBryan Drewery getmicrotime(&now); 2644d9fbc55SBryan Drewery 2654d9fbc55SBryan Drewery len = snprintf(filemon->msgbufr, 2664d9fbc55SBryan Drewery sizeof(filemon->msgbufr), 2674d9fbc55SBryan Drewery "# Stop %ju.%06ju\n# Bye bye\n", 2684d9fbc55SBryan Drewery (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); 2694d9fbc55SBryan Drewery 2704d9fbc55SBryan Drewery filemon_output(filemon, filemon->msgbufr, len); 2714d9fbc55SBryan Drewery fp = filemon->fp; 2724d9fbc55SBryan Drewery filemon->fp = NULL; 2734d9fbc55SBryan Drewery 2744d9fbc55SBryan Drewery sx_xunlock(&filemon->lock); 2754d9fbc55SBryan Drewery fdrop(fp, curthread); 2764d9fbc55SBryan Drewery sx_xlock(&filemon->lock); 2774d9fbc55SBryan Drewery 2784d9fbc55SBryan Drewery return; 2794d9fbc55SBryan Drewery } 280e0d84b9eSBryan Drewery 281*4177d9f7SBryan Drewery /* 282*4177d9f7SBryan Drewery * The devfs file is being closed. Untrace all processes. It is possible 283*4177d9f7SBryan Drewery * filemon_close/close(2) was not called. 284*4177d9f7SBryan Drewery */ 2852b198fe9SBryan Drewery static void 286eb9aea5aSDavid E. O'Brien filemon_dtr(void *data) 287eb9aea5aSDavid E. O'Brien { 288eb9aea5aSDavid E. O'Brien struct filemon *filemon = data; 289eb9aea5aSDavid E. O'Brien 290e0d84b9eSBryan Drewery if (filemon == NULL) 291e0d84b9eSBryan Drewery return; 292eb9aea5aSDavid E. O'Brien 293e0dae8f1SBryan Drewery sx_xlock(&filemon->lock); 294e0d84b9eSBryan Drewery /* 2954d9fbc55SBryan Drewery * Detach the filemon. It cannot be inherited after this. 296e0d84b9eSBryan Drewery */ 297e0d84b9eSBryan Drewery filemon_untrack_processes(filemon); 2984d9fbc55SBryan Drewery filemon_close_log(filemon); 299e0d84b9eSBryan Drewery filemon_drop(filemon); 300eb9aea5aSDavid E. O'Brien } 301e0d84b9eSBryan Drewery 302e0d84b9eSBryan Drewery /* Attach the filemon to the process. */ 303e0d84b9eSBryan Drewery static int 304e0d84b9eSBryan Drewery filemon_attach_proc(struct filemon *filemon, struct proc *p) 305e0d84b9eSBryan Drewery { 306e0d84b9eSBryan Drewery struct filemon *filemon2; 307e0d84b9eSBryan Drewery 308e0d84b9eSBryan Drewery sx_assert(&filemon->lock, SA_XLOCKED); 309e0d84b9eSBryan Drewery PROC_LOCK_ASSERT(p, MA_OWNED); 310e0d84b9eSBryan Drewery KASSERT((p->p_flag & P_WEXIT) == 0, 311e0d84b9eSBryan Drewery ("%s: filemon %p attaching to exiting process %p", 312e0d84b9eSBryan Drewery __func__, filemon, p)); 313e0d84b9eSBryan Drewery 314e0d84b9eSBryan Drewery if (p->p_filemon == filemon) 315e0d84b9eSBryan Drewery return (0); 316e0d84b9eSBryan Drewery /* 317e0d84b9eSBryan Drewery * Don't allow truncating other process traces. It is 318e0d84b9eSBryan Drewery * not really intended to trace procs other than curproc 319e0d84b9eSBryan Drewery * anyhow. 320e0d84b9eSBryan Drewery */ 321e0d84b9eSBryan Drewery if (p->p_filemon != NULL && p != curproc) 322e0d84b9eSBryan Drewery return (EBUSY); 323e0d84b9eSBryan Drewery /* 324e0d84b9eSBryan Drewery * Historic behavior of filemon has been to let a child initiate 325e0d84b9eSBryan Drewery * tracing on itself and cease existing tracing. Bmake 326e0d84b9eSBryan Drewery * .META + .MAKE relies on this. It is only relevant for attaching to 327e0d84b9eSBryan Drewery * curproc. 328e0d84b9eSBryan Drewery */ 329e0d84b9eSBryan Drewery while (p->p_filemon != NULL) { 330e0d84b9eSBryan Drewery PROC_UNLOCK(p); 331e0d84b9eSBryan Drewery sx_xunlock(&filemon->lock); 332e0d84b9eSBryan Drewery while ((filemon2 = filemon_proc_get(p)) != NULL) { 333e0d84b9eSBryan Drewery /* It may have changed. */ 334e0d84b9eSBryan Drewery if (p->p_filemon == filemon2) 335e0d84b9eSBryan Drewery filemon_proc_drop(p); 336e0d84b9eSBryan Drewery filemon_drop(filemon2); 337e0d84b9eSBryan Drewery } 338e0d84b9eSBryan Drewery sx_xlock(&filemon->lock); 339e0d84b9eSBryan Drewery PROC_LOCK(p); 340e0d84b9eSBryan Drewery /* 341e0d84b9eSBryan Drewery * It may have been attached to, though unlikely. 342e0d84b9eSBryan Drewery * Try again if needed. 343e0d84b9eSBryan Drewery */ 344e0d84b9eSBryan Drewery } 345e0d84b9eSBryan Drewery 346e0d84b9eSBryan Drewery KASSERT(p->p_filemon == NULL, 347e0d84b9eSBryan Drewery ("%s: proc %p didn't detach filemon %p", __func__, p, 348e0d84b9eSBryan Drewery p->p_filemon)); 349e0d84b9eSBryan Drewery p->p_filemon = filemon_acquire(filemon); 350e0d84b9eSBryan Drewery ++filemon->proccnt; 351e0d84b9eSBryan Drewery 352e0d84b9eSBryan Drewery return (0); 353eb9aea5aSDavid E. O'Brien } 354eb9aea5aSDavid E. O'Brien 355eb9aea5aSDavid E. O'Brien static int 356eb9aea5aSDavid E. O'Brien filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, 357eb9aea5aSDavid E. O'Brien struct thread *td) 358eb9aea5aSDavid E. O'Brien { 359eb9aea5aSDavid E. O'Brien int error = 0; 360eb9aea5aSDavid E. O'Brien struct filemon *filemon; 361872ce247SHiroki Sato struct proc *p; 3627008be5bSPawel Jakub Dawidek cap_rights_t rights; 363eb9aea5aSDavid E. O'Brien 364e8c87a09SBryan Drewery if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 365e8c87a09SBryan Drewery return (error); 366eb9aea5aSDavid E. O'Brien 367e0dae8f1SBryan Drewery sx_xlock(&filemon->lock); 36864c368a4SBryan Drewery 369eb9aea5aSDavid E. O'Brien switch (cmd) { 370eb9aea5aSDavid E. O'Brien /* Set the output file descriptor. */ 371eb9aea5aSDavid E. O'Brien case FILEMON_SET_FD: 372044fd543SBryan Drewery if (filemon->fp != NULL) { 373044fd543SBryan Drewery error = EEXIST; 374044fd543SBryan Drewery break; 375044fd543SBryan Drewery } 376fac4a7acSBryan Drewery 3777008be5bSPawel Jakub Dawidek error = fget_write(td, *(int *)data, 3787008be5bSPawel Jakub Dawidek cap_rights_init(&rights, CAP_PWRITE), 3797008be5bSPawel Jakub Dawidek &filemon->fp); 3807008be5bSPawel Jakub Dawidek if (error == 0) 381eb9aea5aSDavid E. O'Brien /* Write the file header. */ 382eb9aea5aSDavid E. O'Brien filemon_comment(filemon); 383eb9aea5aSDavid E. O'Brien break; 384eb9aea5aSDavid E. O'Brien 385eb9aea5aSDavid E. O'Brien /* Set the monitored process ID. */ 386eb9aea5aSDavid E. O'Brien case FILEMON_SET_PID: 387e0d84b9eSBryan Drewery /* Invalidate any existing processes already set. */ 388e0d84b9eSBryan Drewery filemon_untrack_processes(filemon); 389e0d84b9eSBryan Drewery 39089cac24eSHiroki Sato error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, 39189cac24eSHiroki Sato &p); 39289cac24eSHiroki Sato if (error == 0) { 393e0d84b9eSBryan Drewery KASSERT(p->p_filemon != filemon, 394e0d84b9eSBryan Drewery ("%s: proc %p didn't untrack filemon %p", 395e0d84b9eSBryan Drewery __func__, p, filemon)); 396e0d84b9eSBryan Drewery error = filemon_attach_proc(filemon, p); 397872ce247SHiroki Sato PROC_UNLOCK(p); 39889cac24eSHiroki Sato } 399eb9aea5aSDavid E. O'Brien break; 400eb9aea5aSDavid E. O'Brien 401eb9aea5aSDavid E. O'Brien default: 402eb9aea5aSDavid E. O'Brien error = EINVAL; 403eb9aea5aSDavid E. O'Brien break; 404eb9aea5aSDavid E. O'Brien } 405eb9aea5aSDavid E. O'Brien 406e0dae8f1SBryan Drewery sx_xunlock(&filemon->lock); 407eb9aea5aSDavid E. O'Brien return (error); 408eb9aea5aSDavid E. O'Brien } 409eb9aea5aSDavid E. O'Brien 410eb9aea5aSDavid E. O'Brien static int 411eb9aea5aSDavid E. O'Brien filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, 412eb9aea5aSDavid E. O'Brien struct thread *td __unused) 413eb9aea5aSDavid E. O'Brien { 414e0d84b9eSBryan Drewery int error; 415eb9aea5aSDavid E. O'Brien struct filemon *filemon; 416eb9aea5aSDavid E. O'Brien 417e0d84b9eSBryan Drewery filemon = malloc(sizeof(*filemon), M_FILEMON, 418eb9aea5aSDavid E. O'Brien M_WAITOK | M_ZERO); 4198183f2e3SBryan Drewery sx_init(&filemon->lock, "filemon"); 420e0d84b9eSBryan Drewery refcount_init(&filemon->refcnt, 1); 421eb9aea5aSDavid E. O'Brien 422e0d84b9eSBryan Drewery error = devfs_set_cdevpriv(filemon, filemon_dtr); 423e0d84b9eSBryan Drewery if (error != 0) 424e0d84b9eSBryan Drewery filemon_release(filemon); 425eb9aea5aSDavid E. O'Brien 426e0d84b9eSBryan Drewery return (error); 427eb9aea5aSDavid E. O'Brien } 428eb9aea5aSDavid E. O'Brien 429*4177d9f7SBryan Drewery /* Called on close of last devfs file handle, before filemon_dtr(). */ 430eb9aea5aSDavid E. O'Brien static int 431eb9aea5aSDavid E. O'Brien filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, 432eb9aea5aSDavid E. O'Brien struct thread *td __unused) 433eb9aea5aSDavid E. O'Brien { 434*4177d9f7SBryan Drewery struct filemon *filemon; 435*4177d9f7SBryan Drewery int error; 436eb9aea5aSDavid E. O'Brien 437*4177d9f7SBryan Drewery if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 438*4177d9f7SBryan Drewery return (error); 439*4177d9f7SBryan Drewery 440*4177d9f7SBryan Drewery sx_xlock(&filemon->lock); 441*4177d9f7SBryan Drewery filemon_close_log(filemon); 442*4177d9f7SBryan Drewery error = filemon->error; 443*4177d9f7SBryan Drewery sx_xunlock(&filemon->lock); 444*4177d9f7SBryan Drewery /* 445*4177d9f7SBryan Drewery * Processes are still being traced but won't log anything 446*4177d9f7SBryan Drewery * now. After this call returns filemon_dtr() is called which 447*4177d9f7SBryan Drewery * will detach processes. 448*4177d9f7SBryan Drewery */ 449*4177d9f7SBryan Drewery 450*4177d9f7SBryan Drewery return (error); 451eb9aea5aSDavid E. O'Brien } 452eb9aea5aSDavid E. O'Brien 453eb9aea5aSDavid E. O'Brien static void 454eb9aea5aSDavid E. O'Brien filemon_load(void *dummy __unused) 455eb9aea5aSDavid E. O'Brien { 456eb9aea5aSDavid E. O'Brien 457eb9aea5aSDavid E. O'Brien /* Install the syscall wrappers. */ 458eb9aea5aSDavid E. O'Brien filemon_wrapper_install(); 459eb9aea5aSDavid E. O'Brien 460eb9aea5aSDavid E. O'Brien filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, 461eb9aea5aSDavid E. O'Brien "filemon"); 462eb9aea5aSDavid E. O'Brien } 463eb9aea5aSDavid E. O'Brien 464eb9aea5aSDavid E. O'Brien static int 465eb9aea5aSDavid E. O'Brien filemon_unload(void) 466eb9aea5aSDavid E. O'Brien { 467eb9aea5aSDavid E. O'Brien 468eb9aea5aSDavid E. O'Brien destroy_dev(filemon_dev); 469eb9aea5aSDavid E. O'Brien filemon_wrapper_deinstall(); 470eb9aea5aSDavid E. O'Brien 471e0d84b9eSBryan Drewery return (0); 472eb9aea5aSDavid E. O'Brien } 473eb9aea5aSDavid E. O'Brien 474eb9aea5aSDavid E. O'Brien static int 475eb9aea5aSDavid E. O'Brien filemon_modevent(module_t mod __unused, int type, void *data) 476eb9aea5aSDavid E. O'Brien { 477eb9aea5aSDavid E. O'Brien int error = 0; 478eb9aea5aSDavid E. O'Brien 479eb9aea5aSDavid E. O'Brien switch (type) { 480eb9aea5aSDavid E. O'Brien case MOD_LOAD: 481eb9aea5aSDavid E. O'Brien filemon_load(data); 482eb9aea5aSDavid E. O'Brien break; 483eb9aea5aSDavid E. O'Brien 484eb9aea5aSDavid E. O'Brien case MOD_UNLOAD: 485eb9aea5aSDavid E. O'Brien error = filemon_unload(); 486eb9aea5aSDavid E. O'Brien break; 487eb9aea5aSDavid E. O'Brien 4884039c531SBryan Drewery case MOD_QUIESCE: 4894039c531SBryan Drewery /* 4904039c531SBryan Drewery * The wrapper implementation is unsafe for reliable unload. 4914039c531SBryan Drewery * Require forcing an unload. 4924039c531SBryan Drewery */ 4934039c531SBryan Drewery error = EBUSY; 4941e35cdf6SBryan Drewery break; 4954039c531SBryan Drewery 496eb9aea5aSDavid E. O'Brien case MOD_SHUTDOWN: 497eb9aea5aSDavid E. O'Brien break; 498eb9aea5aSDavid E. O'Brien 499eb9aea5aSDavid E. O'Brien default: 500eb9aea5aSDavid E. O'Brien error = EOPNOTSUPP; 501eb9aea5aSDavid E. O'Brien break; 502eb9aea5aSDavid E. O'Brien 503eb9aea5aSDavid E. O'Brien } 504eb9aea5aSDavid E. O'Brien 505eb9aea5aSDavid E. O'Brien return (error); 506eb9aea5aSDavid E. O'Brien } 507eb9aea5aSDavid E. O'Brien 508eb9aea5aSDavid E. O'Brien DEV_MODULE(filemon, filemon_modevent, NULL); 509eb9aea5aSDavid E. O'Brien MODULE_VERSION(filemon, 1); 510