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. 48183f2e3SBryan Drewery * Copyright (c) 2015, 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> 38eb9aea5aSDavid E. O'Brien #include <sys/condvar.h> 39eb9aea5aSDavid E. O'Brien #include <sys/conf.h> 40eb9aea5aSDavid E. O'Brien #include <sys/fcntl.h> 41eb9aea5aSDavid E. O'Brien #include <sys/ioccom.h> 42eb9aea5aSDavid E. O'Brien #include <sys/kernel.h> 438183f2e3SBryan Drewery #include <sys/lock.h> 44eb9aea5aSDavid E. O'Brien #include <sys/malloc.h> 45eb9aea5aSDavid E. O'Brien #include <sys/module.h> 46eb9aea5aSDavid E. O'Brien #include <sys/poll.h> 47eb9aea5aSDavid E. O'Brien #include <sys/proc.h> 48eb9aea5aSDavid E. O'Brien #include <sys/queue.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 #if __FreeBSD_version >= 900041 564a144410SRobert Watson #include <sys/capsicum.h> 57eb9aea5aSDavid E. O'Brien #endif 58eb9aea5aSDavid E. O'Brien 59eb9aea5aSDavid E. O'Brien #include "filemon.h" 60eb9aea5aSDavid E. O'Brien 61eb9aea5aSDavid E. O'Brien #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 62eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_syscall.h> 63eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_proto.h> 64eb9aea5aSDavid E. O'Brien 65eb9aea5aSDavid E. O'Brien extern struct sysentvec ia32_freebsd_sysvec; 66eb9aea5aSDavid E. O'Brien #endif 67eb9aea5aSDavid E. O'Brien 68eb9aea5aSDavid E. O'Brien extern struct sysentvec elf32_freebsd_sysvec; 69eb9aea5aSDavid E. O'Brien extern struct sysentvec elf64_freebsd_sysvec; 70eb9aea5aSDavid E. O'Brien 71eb9aea5aSDavid E. O'Brien static d_close_t filemon_close; 72eb9aea5aSDavid E. O'Brien static d_ioctl_t filemon_ioctl; 73eb9aea5aSDavid E. O'Brien static d_open_t filemon_open; 74eb9aea5aSDavid E. O'Brien static int filemon_unload(void); 75eb9aea5aSDavid E. O'Brien static void filemon_load(void *); 76eb9aea5aSDavid E. O'Brien 77eb9aea5aSDavid E. O'Brien static struct cdevsw filemon_cdevsw = { 78eb9aea5aSDavid E. O'Brien .d_version = D_VERSION, 79eb9aea5aSDavid E. O'Brien .d_close = filemon_close, 80eb9aea5aSDavid E. O'Brien .d_ioctl = filemon_ioctl, 81eb9aea5aSDavid E. O'Brien .d_open = filemon_open, 82eb9aea5aSDavid E. O'Brien .d_name = "filemon", 83eb9aea5aSDavid E. O'Brien }; 84eb9aea5aSDavid E. O'Brien 85eb9aea5aSDavid E. O'Brien MALLOC_DECLARE(M_FILEMON); 86eb9aea5aSDavid E. O'Brien MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); 87eb9aea5aSDavid E. O'Brien 88eb9aea5aSDavid E. O'Brien struct filemon { 89eb9aea5aSDavid E. O'Brien TAILQ_ENTRY(filemon) link; /* Link into the in-use list. */ 908183f2e3SBryan Drewery struct sx lock; /* Lock mutex for this filemon. */ 91eb9aea5aSDavid E. O'Brien struct file *fp; /* Output file pointer. */ 92eb9aea5aSDavid E. O'Brien pid_t pid; /* The process ID being monitored. */ 93eb9aea5aSDavid E. O'Brien char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ 94eb9aea5aSDavid E. O'Brien char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ 95eb9aea5aSDavid E. O'Brien char msgbufr[1024]; /* Output message buffer. */ 96eb9aea5aSDavid E. O'Brien }; 97eb9aea5aSDavid E. O'Brien 98eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse); 99eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free); 1008183f2e3SBryan Drewery static struct sx access_lock; 101eb9aea5aSDavid E. O'Brien 102eb9aea5aSDavid E. O'Brien static struct cdev *filemon_dev; 103eb9aea5aSDavid E. O'Brien 104eb9aea5aSDavid E. O'Brien #include "filemon_lock.c" 105eb9aea5aSDavid E. O'Brien #include "filemon_wrapper.c" 106eb9aea5aSDavid E. O'Brien 107eb9aea5aSDavid E. O'Brien static void 108*2b198fe9SBryan Drewery filemon_comment(struct filemon *filemon) 109*2b198fe9SBryan Drewery { 110*2b198fe9SBryan Drewery int len; 111*2b198fe9SBryan Drewery struct timeval now; 112*2b198fe9SBryan Drewery 113*2b198fe9SBryan Drewery getmicrotime(&now); 114*2b198fe9SBryan Drewery 115*2b198fe9SBryan Drewery len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), 116*2b198fe9SBryan Drewery "# filemon version %d\n# Target pid %d\n# Start %ju.%06ju\nV %d\n", 117*2b198fe9SBryan Drewery FILEMON_VERSION, curproc->p_pid, (uintmax_t)now.tv_sec, 118*2b198fe9SBryan Drewery (uintmax_t)now.tv_usec, FILEMON_VERSION); 119*2b198fe9SBryan Drewery 120*2b198fe9SBryan Drewery filemon_output(filemon, filemon->msgbufr, len); 121*2b198fe9SBryan Drewery } 122*2b198fe9SBryan Drewery 123*2b198fe9SBryan Drewery static void 124eb9aea5aSDavid E. O'Brien filemon_dtr(void *data) 125eb9aea5aSDavid E. O'Brien { 126eb9aea5aSDavid E. O'Brien struct filemon *filemon = data; 127eb9aea5aSDavid E. O'Brien 128eb9aea5aSDavid E. O'Brien if (filemon != NULL) { 129eb9aea5aSDavid E. O'Brien struct file *fp = filemon->fp; 130eb9aea5aSDavid E. O'Brien 131eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 132eb9aea5aSDavid E. O'Brien filemon_lock_write(); 133eb9aea5aSDavid E. O'Brien 134eb9aea5aSDavid E. O'Brien /* Remove from the in-use list. */ 135eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_inuse, filemon, link); 136eb9aea5aSDavid E. O'Brien 137eb9aea5aSDavid E. O'Brien filemon->fp = NULL; 138eb9aea5aSDavid E. O'Brien filemon->pid = -1; 139eb9aea5aSDavid E. O'Brien 140eb9aea5aSDavid E. O'Brien /* Add to the free list. */ 141eb9aea5aSDavid E. O'Brien TAILQ_INSERT_TAIL(&filemons_free, filemon, link); 142eb9aea5aSDavid E. O'Brien 143eb9aea5aSDavid E. O'Brien /* Give up write access. */ 144eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 145eb9aea5aSDavid E. O'Brien 146eb9aea5aSDavid E. O'Brien if (fp != NULL) 147eb9aea5aSDavid E. O'Brien fdrop(fp, curthread); 148eb9aea5aSDavid E. O'Brien } 149eb9aea5aSDavid E. O'Brien } 150eb9aea5aSDavid E. O'Brien 151eb9aea5aSDavid E. O'Brien static int 152eb9aea5aSDavid E. O'Brien filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, 153eb9aea5aSDavid E. O'Brien struct thread *td) 154eb9aea5aSDavid E. O'Brien { 155eb9aea5aSDavid E. O'Brien int error = 0; 156eb9aea5aSDavid E. O'Brien struct filemon *filemon; 157872ce247SHiroki Sato struct proc *p; 1587008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041 1597008be5bSPawel Jakub Dawidek cap_rights_t rights; 1607008be5bSPawel Jakub Dawidek #endif 161eb9aea5aSDavid E. O'Brien 162e8c87a09SBryan Drewery if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 163e8c87a09SBryan Drewery return (error); 164eb9aea5aSDavid E. O'Brien 16564c368a4SBryan Drewery filemon_filemon_lock(filemon); 16664c368a4SBryan Drewery 167eb9aea5aSDavid E. O'Brien switch (cmd) { 168eb9aea5aSDavid E. O'Brien /* Set the output file descriptor. */ 169eb9aea5aSDavid E. O'Brien case FILEMON_SET_FD: 170fac4a7acSBryan Drewery if (filemon->fp != NULL) 171fac4a7acSBryan Drewery fdrop(filemon->fp, td); 172fac4a7acSBryan Drewery 1737008be5bSPawel Jakub Dawidek error = fget_write(td, *(int *)data, 1747008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041 1757008be5bSPawel Jakub Dawidek cap_rights_init(&rights, CAP_PWRITE), 1767008be5bSPawel Jakub Dawidek #endif 1777008be5bSPawel Jakub Dawidek &filemon->fp); 1787008be5bSPawel Jakub Dawidek if (error == 0) 179eb9aea5aSDavid E. O'Brien /* Write the file header. */ 180eb9aea5aSDavid E. O'Brien filemon_comment(filemon); 181eb9aea5aSDavid E. O'Brien break; 182eb9aea5aSDavid E. O'Brien 183eb9aea5aSDavid E. O'Brien /* Set the monitored process ID. */ 184eb9aea5aSDavid E. O'Brien case FILEMON_SET_PID: 18589cac24eSHiroki Sato error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, 18689cac24eSHiroki Sato &p); 18789cac24eSHiroki Sato if (error == 0) { 188872ce247SHiroki Sato filemon->pid = p->p_pid; 189872ce247SHiroki Sato PROC_UNLOCK(p); 19089cac24eSHiroki Sato } 191eb9aea5aSDavid E. O'Brien break; 192eb9aea5aSDavid E. O'Brien 193eb9aea5aSDavid E. O'Brien default: 194eb9aea5aSDavid E. O'Brien error = EINVAL; 195eb9aea5aSDavid E. O'Brien break; 196eb9aea5aSDavid E. O'Brien } 197eb9aea5aSDavid E. O'Brien 19864c368a4SBryan Drewery filemon_filemon_unlock(filemon); 199eb9aea5aSDavid E. O'Brien return (error); 200eb9aea5aSDavid E. O'Brien } 201eb9aea5aSDavid E. O'Brien 202eb9aea5aSDavid E. O'Brien static int 203eb9aea5aSDavid E. O'Brien filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, 204eb9aea5aSDavid E. O'Brien struct thread *td __unused) 205eb9aea5aSDavid E. O'Brien { 206eb9aea5aSDavid E. O'Brien struct filemon *filemon; 207eb9aea5aSDavid E. O'Brien 208eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 209eb9aea5aSDavid E. O'Brien filemon_lock_write(); 210eb9aea5aSDavid E. O'Brien 211eb9aea5aSDavid E. O'Brien if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) 212eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_free, filemon, link); 213eb9aea5aSDavid E. O'Brien 214eb9aea5aSDavid E. O'Brien /* Give up write access. */ 215eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 216eb9aea5aSDavid E. O'Brien 217eb9aea5aSDavid E. O'Brien if (filemon == NULL) { 218eb9aea5aSDavid E. O'Brien filemon = malloc(sizeof(struct filemon), M_FILEMON, 219eb9aea5aSDavid E. O'Brien M_WAITOK | M_ZERO); 2208183f2e3SBryan Drewery sx_init(&filemon->lock, "filemon"); 221eb9aea5aSDavid E. O'Brien } 222eb9aea5aSDavid E. O'Brien 223eb9aea5aSDavid E. O'Brien filemon->pid = curproc->p_pid; 224eb9aea5aSDavid E. O'Brien 225eb9aea5aSDavid E. O'Brien devfs_set_cdevpriv(filemon, filemon_dtr); 226eb9aea5aSDavid E. O'Brien 227eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 228eb9aea5aSDavid E. O'Brien filemon_lock_write(); 229eb9aea5aSDavid E. O'Brien 230eb9aea5aSDavid E. O'Brien /* Add to the in-use list. */ 231eb9aea5aSDavid E. O'Brien TAILQ_INSERT_TAIL(&filemons_inuse, filemon, link); 232eb9aea5aSDavid E. O'Brien 233eb9aea5aSDavid E. O'Brien /* Give up write access. */ 234eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 235eb9aea5aSDavid E. O'Brien 236eb9aea5aSDavid E. O'Brien return (0); 237eb9aea5aSDavid E. O'Brien } 238eb9aea5aSDavid E. O'Brien 239eb9aea5aSDavid E. O'Brien static int 240eb9aea5aSDavid E. O'Brien filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, 241eb9aea5aSDavid E. O'Brien struct thread *td __unused) 242eb9aea5aSDavid E. O'Brien { 243eb9aea5aSDavid E. O'Brien 244eb9aea5aSDavid E. O'Brien return (0); 245eb9aea5aSDavid E. O'Brien } 246eb9aea5aSDavid E. O'Brien 247eb9aea5aSDavid E. O'Brien static void 248eb9aea5aSDavid E. O'Brien filemon_load(void *dummy __unused) 249eb9aea5aSDavid E. O'Brien { 2508183f2e3SBryan Drewery sx_init(&access_lock, "filemons_inuse"); 251eb9aea5aSDavid E. O'Brien 252eb9aea5aSDavid E. O'Brien /* Install the syscall wrappers. */ 253eb9aea5aSDavid E. O'Brien filemon_wrapper_install(); 254eb9aea5aSDavid E. O'Brien 255eb9aea5aSDavid E. O'Brien filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, 256eb9aea5aSDavid E. O'Brien "filemon"); 257eb9aea5aSDavid E. O'Brien } 258eb9aea5aSDavid E. O'Brien 259eb9aea5aSDavid E. O'Brien static int 260eb9aea5aSDavid E. O'Brien filemon_unload(void) 261eb9aea5aSDavid E. O'Brien { 262eb9aea5aSDavid E. O'Brien struct filemon *filemon; 263eb9aea5aSDavid E. O'Brien int error = 0; 264eb9aea5aSDavid E. O'Brien 265eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 266eb9aea5aSDavid E. O'Brien filemon_lock_write(); 267eb9aea5aSDavid E. O'Brien 268eb9aea5aSDavid E. O'Brien if (TAILQ_FIRST(&filemons_inuse) != NULL) 269eb9aea5aSDavid E. O'Brien error = EBUSY; 270eb9aea5aSDavid E. O'Brien else { 271eb9aea5aSDavid E. O'Brien destroy_dev(filemon_dev); 272eb9aea5aSDavid E. O'Brien 273eb9aea5aSDavid E. O'Brien /* Deinstall the syscall wrappers. */ 274eb9aea5aSDavid E. O'Brien filemon_wrapper_deinstall(); 275eb9aea5aSDavid E. O'Brien } 276eb9aea5aSDavid E. O'Brien 277eb9aea5aSDavid E. O'Brien /* Give up write access. */ 278eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 279eb9aea5aSDavid E. O'Brien 280eb9aea5aSDavid E. O'Brien if (error == 0) { 281eb9aea5aSDavid E. O'Brien /* free() filemon structs free list. */ 282eb9aea5aSDavid E. O'Brien filemon_lock_write(); 283eb9aea5aSDavid E. O'Brien while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) { 284eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_free, filemon, link); 2858183f2e3SBryan Drewery sx_destroy(&filemon->lock); 286eb9aea5aSDavid E. O'Brien free(filemon, M_FILEMON); 287eb9aea5aSDavid E. O'Brien } 288eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 289eb9aea5aSDavid E. O'Brien 2908183f2e3SBryan Drewery sx_destroy(&access_lock); 291eb9aea5aSDavid E. O'Brien } 292eb9aea5aSDavid E. O'Brien 293eb9aea5aSDavid E. O'Brien return (error); 294eb9aea5aSDavid E. O'Brien } 295eb9aea5aSDavid E. O'Brien 296eb9aea5aSDavid E. O'Brien static int 297eb9aea5aSDavid E. O'Brien filemon_modevent(module_t mod __unused, int type, void *data) 298eb9aea5aSDavid E. O'Brien { 299eb9aea5aSDavid E. O'Brien int error = 0; 300eb9aea5aSDavid E. O'Brien 301eb9aea5aSDavid E. O'Brien switch (type) { 302eb9aea5aSDavid E. O'Brien case MOD_LOAD: 303eb9aea5aSDavid E. O'Brien filemon_load(data); 304eb9aea5aSDavid E. O'Brien break; 305eb9aea5aSDavid E. O'Brien 306eb9aea5aSDavid E. O'Brien case MOD_UNLOAD: 307eb9aea5aSDavid E. O'Brien error = filemon_unload(); 308eb9aea5aSDavid E. O'Brien break; 309eb9aea5aSDavid E. O'Brien 310eb9aea5aSDavid E. O'Brien case MOD_SHUTDOWN: 311eb9aea5aSDavid E. O'Brien break; 312eb9aea5aSDavid E. O'Brien 313eb9aea5aSDavid E. O'Brien default: 314eb9aea5aSDavid E. O'Brien error = EOPNOTSUPP; 315eb9aea5aSDavid E. O'Brien break; 316eb9aea5aSDavid E. O'Brien 317eb9aea5aSDavid E. O'Brien } 318eb9aea5aSDavid E. O'Brien 319eb9aea5aSDavid E. O'Brien return (error); 320eb9aea5aSDavid E. O'Brien } 321eb9aea5aSDavid E. O'Brien 322eb9aea5aSDavid E. O'Brien DEV_MODULE(filemon, filemon_modevent, NULL); 323eb9aea5aSDavid E. O'Brien MODULE_VERSION(filemon, 1); 324