1eb9aea5aSDavid E. O'Brien /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4eb9aea5aSDavid E. O'Brien * Copyright (c) 2011, David E. O'Brien. 5eb9aea5aSDavid E. O'Brien * Copyright (c) 2009-2011, Juniper Networks, Inc. 6e0d84b9eSBryan Drewery * Copyright (c) 2015-2016, EMC Corp. 7eb9aea5aSDavid E. O'Brien * All rights reserved. 8eb9aea5aSDavid E. O'Brien * 9eb9aea5aSDavid E. O'Brien * Redistribution and use in source and binary forms, with or without 10eb9aea5aSDavid E. O'Brien * modification, are permitted provided that the following conditions 11eb9aea5aSDavid E. O'Brien * are met: 12eb9aea5aSDavid E. O'Brien * 1. Redistributions of source code must retain the above copyright 13eb9aea5aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer. 14eb9aea5aSDavid E. O'Brien * 2. Redistributions in binary form must reproduce the above copyright 15eb9aea5aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer in the 16eb9aea5aSDavid E. O'Brien * documentation and/or other materials provided with the distribution. 17eb9aea5aSDavid E. O'Brien * 18eb9aea5aSDavid E. O'Brien * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND 19eb9aea5aSDavid E. O'Brien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20eb9aea5aSDavid E. O'Brien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21eb9aea5aSDavid E. O'Brien * ARE DISCLAIMED. IN NO EVENT SHALL JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE 22eb9aea5aSDavid E. O'Brien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23eb9aea5aSDavid E. O'Brien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24eb9aea5aSDavid E. O'Brien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25eb9aea5aSDavid E. O'Brien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26eb9aea5aSDavid E. O'Brien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27eb9aea5aSDavid E. O'Brien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28eb9aea5aSDavid E. O'Brien * SUCH DAMAGE. 29eb9aea5aSDavid E. O'Brien */ 30eb9aea5aSDavid E. O'Brien 31af13de0fSJohn Baldwin #include <sys/cdefs.h> 32eb9aea5aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 33eb9aea5aSDavid 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 576c37a3d4SBryan Drewery #if defined(COMPAT_FREEBSD32) 58eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_syscall.h> 59eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_proto.h> 606c37a3d4SBryan Drewery #include <compat/freebsd32/freebsd32_util.h> 61eb9aea5aSDavid E. O'Brien #endif 62eb9aea5aSDavid E. O'Brien 63eb9aea5aSDavid E. O'Brien static d_close_t filemon_close; 64eb9aea5aSDavid E. O'Brien static d_ioctl_t filemon_ioctl; 65eb9aea5aSDavid E. O'Brien static d_open_t filemon_open; 66eb9aea5aSDavid E. O'Brien 67eb9aea5aSDavid E. O'Brien static struct cdevsw filemon_cdevsw = { 68eb9aea5aSDavid E. O'Brien .d_version = D_VERSION, 69eb9aea5aSDavid E. O'Brien .d_close = filemon_close, 70eb9aea5aSDavid E. O'Brien .d_ioctl = filemon_ioctl, 71eb9aea5aSDavid E. O'Brien .d_open = filemon_open, 72eb9aea5aSDavid E. O'Brien .d_name = "filemon", 73eb9aea5aSDavid E. O'Brien }; 74eb9aea5aSDavid E. O'Brien 75eb9aea5aSDavid E. O'Brien MALLOC_DECLARE(M_FILEMON); 76eb9aea5aSDavid E. O'Brien MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); 77eb9aea5aSDavid E. O'Brien 78e0d84b9eSBryan Drewery /* 79e0d84b9eSBryan Drewery * The filemon->lock protects several things currently: 80e0d84b9eSBryan Drewery * - fname1/fname2/msgbufr are pre-allocated and used per syscall 81e0d84b9eSBryan Drewery * for logging and copyins rather than stack variables. 82e0d84b9eSBryan Drewery * - Serializing the filemon's log output. 83e0d84b9eSBryan Drewery * - Preventing inheritance or removal of the filemon into proc.p_filemon. 84e0d84b9eSBryan Drewery */ 85eb9aea5aSDavid E. O'Brien struct filemon { 86e0d84b9eSBryan Drewery struct sx lock; /* Lock for this filemon. */ 87eb9aea5aSDavid E. O'Brien struct file *fp; /* Output file pointer. */ 88f14fbe72SBryan Drewery struct ucred *cred; /* Credential of tracer. */ 89eb9aea5aSDavid E. O'Brien char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ 90eb9aea5aSDavid E. O'Brien char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ 91*bc0d7285SBryan Drewery char msgbufr[2*MAXPATHLEN + 100]; /* Output message buffer. */ 924177d9f7SBryan Drewery int error; /* Log write error, returned on close(2). */ 93e0d84b9eSBryan Drewery u_int refcnt; /* Pointer reference count. */ 94e0d84b9eSBryan Drewery u_int proccnt; /* Process count. */ 95eb9aea5aSDavid E. O'Brien }; 96eb9aea5aSDavid E. O'Brien 97eb9aea5aSDavid E. O'Brien static struct cdev *filemon_dev; 98e0d84b9eSBryan Drewery static void filemon_output(struct filemon *filemon, char *msg, size_t len); 99eb9aea5aSDavid E. O'Brien 100e0d84b9eSBryan Drewery static __inline struct filemon * 101e0d84b9eSBryan Drewery filemon_acquire(struct filemon *filemon) 102e0d84b9eSBryan Drewery { 103e0d84b9eSBryan Drewery 104e0d84b9eSBryan Drewery if (filemon != NULL) 105e0d84b9eSBryan Drewery refcount_acquire(&filemon->refcnt); 106e0d84b9eSBryan Drewery return (filemon); 107e0d84b9eSBryan Drewery } 108e0d84b9eSBryan Drewery 109e0d84b9eSBryan Drewery /* 1104d9fbc55SBryan Drewery * Release a reference and free on the last one. 111e0d84b9eSBryan Drewery */ 112e0d84b9eSBryan Drewery static void 113e0d84b9eSBryan Drewery filemon_release(struct filemon *filemon) 114e0d84b9eSBryan Drewery { 115e0d84b9eSBryan Drewery 116e0d84b9eSBryan Drewery if (refcount_release(&filemon->refcnt) == 0) 117e0d84b9eSBryan Drewery return; 118e0d84b9eSBryan Drewery /* 119e0d84b9eSBryan Drewery * There are valid cases of releasing while locked, such as in 120e0d84b9eSBryan Drewery * filemon_untrack_processes, but none which are done where there 121e0d84b9eSBryan Drewery * is not at least 1 reference remaining. 122e0d84b9eSBryan Drewery */ 123e0d84b9eSBryan Drewery sx_assert(&filemon->lock, SA_UNLOCKED); 124e0d84b9eSBryan Drewery 125f14fbe72SBryan Drewery if (filemon->cred != NULL) 126f14fbe72SBryan Drewery crfree(filemon->cred); 127e0d84b9eSBryan Drewery sx_destroy(&filemon->lock); 128e0d84b9eSBryan Drewery free(filemon, M_FILEMON); 129e0d84b9eSBryan Drewery } 130e0d84b9eSBryan Drewery 131e0d84b9eSBryan Drewery /* 132e0d84b9eSBryan Drewery * Acquire the proc's p_filemon reference and lock the filemon. 133e0d84b9eSBryan Drewery * The proc's p_filemon may not match this filemon on return. 134e0d84b9eSBryan Drewery */ 135e0d84b9eSBryan Drewery static struct filemon * 136e0d84b9eSBryan Drewery filemon_proc_get(struct proc *p) 137e0d84b9eSBryan Drewery { 138e0d84b9eSBryan Drewery struct filemon *filemon; 139e0d84b9eSBryan Drewery 140f566b25eSBryan Drewery if (p->p_filemon == NULL) 141f566b25eSBryan Drewery return (NULL); 142e0d84b9eSBryan Drewery PROC_LOCK(p); 143e0d84b9eSBryan Drewery filemon = filemon_acquire(p->p_filemon); 144e0d84b9eSBryan Drewery PROC_UNLOCK(p); 145e0d84b9eSBryan Drewery 146e0d84b9eSBryan Drewery if (filemon == NULL) 147e0d84b9eSBryan Drewery return (NULL); 148e0d84b9eSBryan Drewery /* 149e0d84b9eSBryan Drewery * The p->p_filemon may have changed by now. That case is handled 150e0d84b9eSBryan Drewery * by the exit and fork hooks and filemon_attach_proc specially. 151e0d84b9eSBryan Drewery */ 152e0d84b9eSBryan Drewery sx_xlock(&filemon->lock); 153e0d84b9eSBryan Drewery return (filemon); 154e0d84b9eSBryan Drewery } 155e0d84b9eSBryan Drewery 156e0d84b9eSBryan Drewery /* Remove and release the filemon on the given process. */ 157e0d84b9eSBryan Drewery static void 158e0d84b9eSBryan Drewery filemon_proc_drop(struct proc *p) 159e0d84b9eSBryan Drewery { 160e0d84b9eSBryan Drewery struct filemon *filemon; 161e0d84b9eSBryan Drewery 162e0d84b9eSBryan Drewery KASSERT(p->p_filemon != NULL, ("%s: proc %p NULL p_filemon", 163e0d84b9eSBryan Drewery __func__, p)); 164e0d84b9eSBryan Drewery sx_assert(&p->p_filemon->lock, SA_XLOCKED); 165e0d84b9eSBryan Drewery PROC_LOCK(p); 166e0d84b9eSBryan Drewery filemon = p->p_filemon; 167e0d84b9eSBryan Drewery p->p_filemon = NULL; 168e0d84b9eSBryan Drewery --filemon->proccnt; 169e0d84b9eSBryan Drewery PROC_UNLOCK(p); 170e0d84b9eSBryan Drewery /* 171e0d84b9eSBryan Drewery * This should not be the last reference yet. filemon_release() 172e0d84b9eSBryan Drewery * cannot be called with filemon locked, which the caller expects 173e0d84b9eSBryan Drewery * will stay locked. 174e0d84b9eSBryan Drewery */ 175e0d84b9eSBryan Drewery KASSERT(filemon->refcnt > 1, ("%s: proc %p dropping filemon %p " 176e0d84b9eSBryan Drewery "with last reference", __func__, p, filemon)); 177e0d84b9eSBryan Drewery filemon_release(filemon); 178e0d84b9eSBryan Drewery } 179e0d84b9eSBryan Drewery 180e0d84b9eSBryan Drewery /* Unlock and release the filemon. */ 181e0d84b9eSBryan Drewery static __inline void 182e0d84b9eSBryan Drewery filemon_drop(struct filemon *filemon) 183e0d84b9eSBryan Drewery { 184e0d84b9eSBryan Drewery 185e0d84b9eSBryan Drewery sx_xunlock(&filemon->lock); 186e0d84b9eSBryan Drewery filemon_release(filemon); 187e0d84b9eSBryan Drewery } 188e0d84b9eSBryan Drewery 189eb9aea5aSDavid E. O'Brien #include "filemon_wrapper.c" 190eb9aea5aSDavid E. O'Brien 191eb9aea5aSDavid E. O'Brien static void 19233094a87SBryan Drewery filemon_write_header(struct filemon *filemon) 1932b198fe9SBryan Drewery { 1942b198fe9SBryan Drewery int len; 1952b198fe9SBryan Drewery struct timeval now; 1962b198fe9SBryan Drewery 1972b198fe9SBryan Drewery getmicrotime(&now); 1982b198fe9SBryan Drewery 1992b198fe9SBryan Drewery len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), 2002b198fe9SBryan Drewery "# filemon version %d\n# Target pid %d\n# Start %ju.%06ju\nV %d\n", 2012b198fe9SBryan Drewery FILEMON_VERSION, curproc->p_pid, (uintmax_t)now.tv_sec, 2022b198fe9SBryan Drewery (uintmax_t)now.tv_usec, FILEMON_VERSION); 203*bc0d7285SBryan Drewery if (len < sizeof(filemon->msgbufr)) 2042b198fe9SBryan Drewery filemon_output(filemon, filemon->msgbufr, len); 2052b198fe9SBryan Drewery } 2062b198fe9SBryan Drewery 207e0d84b9eSBryan Drewery /* 208e0d84b9eSBryan Drewery * Invalidate the passed filemon in all processes. 209e0d84b9eSBryan Drewery */ 210e0d84b9eSBryan Drewery static void 211e0d84b9eSBryan Drewery filemon_untrack_processes(struct filemon *filemon) 212e0d84b9eSBryan Drewery { 213e0d84b9eSBryan Drewery struct proc *p; 214e0d84b9eSBryan Drewery 215e0d84b9eSBryan Drewery sx_assert(&filemon->lock, SA_XLOCKED); 216e0d84b9eSBryan Drewery 217e0d84b9eSBryan Drewery /* Avoid allproc loop if there is no need. */ 218e0d84b9eSBryan Drewery if (filemon->proccnt == 0) 219e0d84b9eSBryan Drewery return; 220e0d84b9eSBryan Drewery 221e0d84b9eSBryan Drewery /* 222e0d84b9eSBryan Drewery * Processes in this list won't go away while here since 223e0d84b9eSBryan Drewery * filemon_event_process_exit() will lock on filemon->lock 224e0d84b9eSBryan Drewery * which we hold. 225e0d84b9eSBryan Drewery */ 226e0d84b9eSBryan Drewery sx_slock(&allproc_lock); 227e0d84b9eSBryan Drewery FOREACH_PROC_IN_SYSTEM(p) { 228e0d84b9eSBryan Drewery /* 229e0d84b9eSBryan Drewery * No PROC_LOCK is needed to compare here since it is 230e0d84b9eSBryan Drewery * guaranteed to not change since we have its filemon 231e0d84b9eSBryan Drewery * locked. Everything that changes this p_filemon will 232e0d84b9eSBryan Drewery * be locked on it. 233e0d84b9eSBryan Drewery */ 234e0d84b9eSBryan Drewery if (p->p_filemon == filemon) 235e0d84b9eSBryan Drewery filemon_proc_drop(p); 236e0d84b9eSBryan Drewery } 237e0d84b9eSBryan Drewery sx_sunlock(&allproc_lock); 238e0d84b9eSBryan Drewery 239e0d84b9eSBryan Drewery /* 240e0d84b9eSBryan Drewery * It's possible some references were acquired but will be 241e0d84b9eSBryan Drewery * dropped shortly as they are restricted from being 242e0d84b9eSBryan Drewery * inherited. There is at least the reference in cdevpriv remaining. 243e0d84b9eSBryan Drewery */ 244e0d84b9eSBryan Drewery KASSERT(filemon->refcnt > 0, ("%s: filemon %p should have " 245e0d84b9eSBryan Drewery "references still.", __func__, filemon)); 246e0d84b9eSBryan Drewery KASSERT(filemon->proccnt == 0, ("%s: filemon %p should not have " 247e0d84b9eSBryan Drewery "attached procs still.", __func__, filemon)); 248e0d84b9eSBryan Drewery } 249e0d84b9eSBryan Drewery 2504d9fbc55SBryan Drewery /* 2514d9fbc55SBryan Drewery * Close out the log. 2524d9fbc55SBryan Drewery */ 2534d9fbc55SBryan Drewery static void 2544d9fbc55SBryan Drewery filemon_close_log(struct filemon *filemon) 2554d9fbc55SBryan Drewery { 2564d9fbc55SBryan Drewery struct file *fp; 2574d9fbc55SBryan Drewery struct timeval now; 2584d9fbc55SBryan Drewery size_t len; 2594d9fbc55SBryan Drewery 2604d9fbc55SBryan Drewery sx_assert(&filemon->lock, SA_XLOCKED); 2614d9fbc55SBryan Drewery if (filemon->fp == NULL) 2624d9fbc55SBryan Drewery return; 2634d9fbc55SBryan Drewery 2644d9fbc55SBryan Drewery getmicrotime(&now); 2654d9fbc55SBryan Drewery 2664d9fbc55SBryan Drewery len = snprintf(filemon->msgbufr, 2674d9fbc55SBryan Drewery sizeof(filemon->msgbufr), 2684d9fbc55SBryan Drewery "# Stop %ju.%06ju\n# Bye bye\n", 2694d9fbc55SBryan Drewery (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); 2704d9fbc55SBryan Drewery 271*bc0d7285SBryan Drewery if (len < sizeof(filemon->msgbufr)) 2724d9fbc55SBryan Drewery filemon_output(filemon, filemon->msgbufr, len); 2734d9fbc55SBryan Drewery fp = filemon->fp; 2744d9fbc55SBryan Drewery filemon->fp = NULL; 2754d9fbc55SBryan Drewery 2764d9fbc55SBryan Drewery sx_xunlock(&filemon->lock); 2774d9fbc55SBryan Drewery fdrop(fp, curthread); 2784d9fbc55SBryan Drewery sx_xlock(&filemon->lock); 2794d9fbc55SBryan Drewery } 280e0d84b9eSBryan Drewery 2814177d9f7SBryan Drewery /* 2824177d9f7SBryan Drewery * The devfs file is being closed. Untrace all processes. It is possible 2834177d9f7SBryan Drewery * filemon_close/close(2) was not called. 2844177d9f7SBryan 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)); 313f14fbe72SBryan Drewery KASSERT((p->p_flag & P_INEXEC) == 0, 314f14fbe72SBryan Drewery ("%s: filemon %p attaching to execing process %p", 315f14fbe72SBryan Drewery __func__, filemon, p)); 316e0d84b9eSBryan Drewery 317e0d84b9eSBryan Drewery if (p->p_filemon == filemon) 318e0d84b9eSBryan Drewery return (0); 319e0d84b9eSBryan Drewery /* 320e0d84b9eSBryan Drewery * Don't allow truncating other process traces. It is 321e0d84b9eSBryan Drewery * not really intended to trace procs other than curproc 322e0d84b9eSBryan Drewery * anyhow. 323e0d84b9eSBryan Drewery */ 324e0d84b9eSBryan Drewery if (p->p_filemon != NULL && p != curproc) 325e0d84b9eSBryan Drewery return (EBUSY); 326e0d84b9eSBryan Drewery /* 327e0d84b9eSBryan Drewery * Historic behavior of filemon has been to let a child initiate 328e0d84b9eSBryan Drewery * tracing on itself and cease existing tracing. Bmake 329e0d84b9eSBryan Drewery * .META + .MAKE relies on this. It is only relevant for attaching to 330e0d84b9eSBryan Drewery * curproc. 331e0d84b9eSBryan Drewery */ 332e0d84b9eSBryan Drewery while (p->p_filemon != NULL) { 333e0d84b9eSBryan Drewery PROC_UNLOCK(p); 334e0d84b9eSBryan Drewery sx_xunlock(&filemon->lock); 335e0d84b9eSBryan Drewery while ((filemon2 = filemon_proc_get(p)) != NULL) { 336e0d84b9eSBryan Drewery /* It may have changed. */ 337e0d84b9eSBryan Drewery if (p->p_filemon == filemon2) 338e0d84b9eSBryan Drewery filemon_proc_drop(p); 339e0d84b9eSBryan Drewery filemon_drop(filemon2); 340e0d84b9eSBryan Drewery } 341e0d84b9eSBryan Drewery sx_xlock(&filemon->lock); 342e0d84b9eSBryan Drewery PROC_LOCK(p); 343e0d84b9eSBryan Drewery /* 344e0d84b9eSBryan Drewery * It may have been attached to, though unlikely. 345e0d84b9eSBryan Drewery * Try again if needed. 346e0d84b9eSBryan Drewery */ 347e0d84b9eSBryan Drewery } 348e0d84b9eSBryan Drewery 349e0d84b9eSBryan Drewery KASSERT(p->p_filemon == NULL, 350e0d84b9eSBryan Drewery ("%s: proc %p didn't detach filemon %p", __func__, p, 351e0d84b9eSBryan Drewery p->p_filemon)); 352e0d84b9eSBryan Drewery p->p_filemon = filemon_acquire(filemon); 353e0d84b9eSBryan Drewery ++filemon->proccnt; 354e0d84b9eSBryan Drewery 355e0d84b9eSBryan Drewery return (0); 356eb9aea5aSDavid E. O'Brien } 357eb9aea5aSDavid E. O'Brien 358eb9aea5aSDavid E. O'Brien static int 359eb9aea5aSDavid E. O'Brien filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, 360eb9aea5aSDavid E. O'Brien struct thread *td) 361eb9aea5aSDavid E. O'Brien { 362eb9aea5aSDavid E. O'Brien int error = 0; 363eb9aea5aSDavid E. O'Brien struct filemon *filemon; 364872ce247SHiroki Sato struct proc *p; 365eb9aea5aSDavid E. O'Brien 366e8c87a09SBryan Drewery if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 367e8c87a09SBryan Drewery return (error); 368eb9aea5aSDavid E. O'Brien 369e0dae8f1SBryan Drewery sx_xlock(&filemon->lock); 37064c368a4SBryan Drewery 371eb9aea5aSDavid E. O'Brien switch (cmd) { 372eb9aea5aSDavid E. O'Brien /* Set the output file descriptor. */ 373eb9aea5aSDavid E. O'Brien case FILEMON_SET_FD: 374044fd543SBryan Drewery if (filemon->fp != NULL) { 375044fd543SBryan Drewery error = EEXIST; 376044fd543SBryan Drewery break; 377044fd543SBryan Drewery } 378fac4a7acSBryan Drewery 3797008be5bSPawel Jakub Dawidek error = fget_write(td, *(int *)data, 380cbd92ce6SMatt Macy &cap_pwrite_rights, 3817008be5bSPawel Jakub Dawidek &filemon->fp); 3827008be5bSPawel Jakub Dawidek if (error == 0) 383eb9aea5aSDavid E. O'Brien /* Write the file header. */ 38433094a87SBryan Drewery filemon_write_header(filemon); 385eb9aea5aSDavid E. O'Brien break; 386eb9aea5aSDavid E. O'Brien 387eb9aea5aSDavid E. O'Brien /* Set the monitored process ID. */ 388eb9aea5aSDavid E. O'Brien case FILEMON_SET_PID: 389e0d84b9eSBryan Drewery /* Invalidate any existing processes already set. */ 390e0d84b9eSBryan Drewery filemon_untrack_processes(filemon); 391e0d84b9eSBryan Drewery 392f14fbe72SBryan Drewery error = pget(*((pid_t *)data), 393f14fbe72SBryan Drewery PGET_CANDEBUG | PGET_NOTWEXIT | PGET_NOTINEXEC, &p); 39489cac24eSHiroki Sato if (error == 0) { 395e0d84b9eSBryan Drewery KASSERT(p->p_filemon != filemon, 396e0d84b9eSBryan Drewery ("%s: proc %p didn't untrack filemon %p", 397e0d84b9eSBryan Drewery __func__, p, filemon)); 398e0d84b9eSBryan Drewery error = filemon_attach_proc(filemon, p); 399872ce247SHiroki Sato PROC_UNLOCK(p); 40089cac24eSHiroki Sato } 401eb9aea5aSDavid E. O'Brien break; 402eb9aea5aSDavid E. O'Brien 403eb9aea5aSDavid E. O'Brien default: 404eb9aea5aSDavid E. O'Brien error = EINVAL; 405eb9aea5aSDavid E. O'Brien break; 406eb9aea5aSDavid E. O'Brien } 407eb9aea5aSDavid E. O'Brien 408e0dae8f1SBryan Drewery sx_xunlock(&filemon->lock); 409eb9aea5aSDavid E. O'Brien return (error); 410eb9aea5aSDavid E. O'Brien } 411eb9aea5aSDavid E. O'Brien 412eb9aea5aSDavid E. O'Brien static int 413eb9aea5aSDavid E. O'Brien filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, 414f14fbe72SBryan Drewery struct thread *td) 415eb9aea5aSDavid E. O'Brien { 416e0d84b9eSBryan Drewery int error; 417eb9aea5aSDavid E. O'Brien struct filemon *filemon; 418eb9aea5aSDavid E. O'Brien 419e0d84b9eSBryan Drewery filemon = malloc(sizeof(*filemon), M_FILEMON, 420eb9aea5aSDavid E. O'Brien M_WAITOK | M_ZERO); 4218183f2e3SBryan Drewery sx_init(&filemon->lock, "filemon"); 422e0d84b9eSBryan Drewery refcount_init(&filemon->refcnt, 1); 423f14fbe72SBryan Drewery filemon->cred = crhold(td->td_ucred); 424eb9aea5aSDavid E. O'Brien 425e0d84b9eSBryan Drewery error = devfs_set_cdevpriv(filemon, filemon_dtr); 426e0d84b9eSBryan Drewery if (error != 0) 427e0d84b9eSBryan Drewery filemon_release(filemon); 428eb9aea5aSDavid E. O'Brien 429e0d84b9eSBryan Drewery return (error); 430eb9aea5aSDavid E. O'Brien } 431eb9aea5aSDavid E. O'Brien 4324177d9f7SBryan Drewery /* Called on close of last devfs file handle, before filemon_dtr(). */ 433eb9aea5aSDavid E. O'Brien static int 434eb9aea5aSDavid E. O'Brien filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, 435eb9aea5aSDavid E. O'Brien struct thread *td __unused) 436eb9aea5aSDavid E. O'Brien { 4374177d9f7SBryan Drewery struct filemon *filemon; 4384177d9f7SBryan Drewery int error; 439eb9aea5aSDavid E. O'Brien 4404177d9f7SBryan Drewery if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 4414177d9f7SBryan Drewery return (error); 4424177d9f7SBryan Drewery 4434177d9f7SBryan Drewery sx_xlock(&filemon->lock); 4444177d9f7SBryan Drewery filemon_close_log(filemon); 4454177d9f7SBryan Drewery error = filemon->error; 4464177d9f7SBryan Drewery sx_xunlock(&filemon->lock); 4474177d9f7SBryan Drewery /* 4484177d9f7SBryan Drewery * Processes are still being traced but won't log anything 4494177d9f7SBryan Drewery * now. After this call returns filemon_dtr() is called which 4504177d9f7SBryan Drewery * will detach processes. 4514177d9f7SBryan Drewery */ 4524177d9f7SBryan Drewery 4534177d9f7SBryan Drewery return (error); 454eb9aea5aSDavid E. O'Brien } 455eb9aea5aSDavid E. O'Brien 456eb9aea5aSDavid E. O'Brien static void 457eb9aea5aSDavid E. O'Brien filemon_load(void *dummy __unused) 458eb9aea5aSDavid E. O'Brien { 459eb9aea5aSDavid E. O'Brien 460eb9aea5aSDavid E. O'Brien /* Install the syscall wrappers. */ 461eb9aea5aSDavid E. O'Brien filemon_wrapper_install(); 462eb9aea5aSDavid E. O'Brien 463eb9aea5aSDavid E. O'Brien filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, 464eb9aea5aSDavid E. O'Brien "filemon"); 465eb9aea5aSDavid E. O'Brien } 466eb9aea5aSDavid E. O'Brien 467eb9aea5aSDavid E. O'Brien static int 468eb9aea5aSDavid E. O'Brien filemon_unload(void) 469eb9aea5aSDavid E. O'Brien { 470eb9aea5aSDavid E. O'Brien 471eb9aea5aSDavid E. O'Brien destroy_dev(filemon_dev); 472eb9aea5aSDavid E. O'Brien filemon_wrapper_deinstall(); 473eb9aea5aSDavid E. O'Brien 474e0d84b9eSBryan Drewery return (0); 475eb9aea5aSDavid E. O'Brien } 476eb9aea5aSDavid E. O'Brien 477eb9aea5aSDavid E. O'Brien static int 478eb9aea5aSDavid E. O'Brien filemon_modevent(module_t mod __unused, int type, void *data) 479eb9aea5aSDavid E. O'Brien { 480eb9aea5aSDavid E. O'Brien int error = 0; 481eb9aea5aSDavid E. O'Brien 482eb9aea5aSDavid E. O'Brien switch (type) { 483eb9aea5aSDavid E. O'Brien case MOD_LOAD: 484eb9aea5aSDavid E. O'Brien filemon_load(data); 485eb9aea5aSDavid E. O'Brien break; 486eb9aea5aSDavid E. O'Brien 487eb9aea5aSDavid E. O'Brien case MOD_UNLOAD: 488eb9aea5aSDavid E. O'Brien error = filemon_unload(); 489eb9aea5aSDavid E. O'Brien break; 490eb9aea5aSDavid E. O'Brien 4914039c531SBryan Drewery case MOD_QUIESCE: 4924039c531SBryan Drewery /* 4934039c531SBryan Drewery * The wrapper implementation is unsafe for reliable unload. 4944039c531SBryan Drewery * Require forcing an unload. 4954039c531SBryan Drewery */ 4964039c531SBryan Drewery error = EBUSY; 4971e35cdf6SBryan Drewery break; 4984039c531SBryan Drewery 499eb9aea5aSDavid E. O'Brien case MOD_SHUTDOWN: 500eb9aea5aSDavid E. O'Brien break; 501eb9aea5aSDavid E. O'Brien 502eb9aea5aSDavid E. O'Brien default: 503eb9aea5aSDavid E. O'Brien error = EOPNOTSUPP; 504eb9aea5aSDavid E. O'Brien break; 505eb9aea5aSDavid E. O'Brien 506eb9aea5aSDavid E. O'Brien } 507eb9aea5aSDavid E. O'Brien 508eb9aea5aSDavid E. O'Brien return (error); 509eb9aea5aSDavid E. O'Brien } 510eb9aea5aSDavid E. O'Brien 511eb9aea5aSDavid E. O'Brien DEV_MODULE(filemon, filemon_modevent, NULL); 512eb9aea5aSDavid E. O'Brien MODULE_VERSION(filemon, 1); 513