xref: /freebsd/sys/dev/filemon/filemon.c (revision e8c87a09dc53c500c3f68aa1f473eae2102e9c5f)
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
108eb9aea5aSDavid E. O'Brien filemon_dtr(void *data)
109eb9aea5aSDavid E. O'Brien {
110eb9aea5aSDavid E. O'Brien 	struct filemon *filemon = data;
111eb9aea5aSDavid E. O'Brien 
112eb9aea5aSDavid E. O'Brien 	if (filemon != NULL) {
113eb9aea5aSDavid E. O'Brien 		struct file *fp = filemon->fp;
114eb9aea5aSDavid E. O'Brien 
115eb9aea5aSDavid E. O'Brien 		/* Get exclusive write access. */
116eb9aea5aSDavid E. O'Brien 		filemon_lock_write();
117eb9aea5aSDavid E. O'Brien 
118eb9aea5aSDavid E. O'Brien 		/* Remove from the in-use list. */
119eb9aea5aSDavid E. O'Brien 		TAILQ_REMOVE(&filemons_inuse, filemon, link);
120eb9aea5aSDavid E. O'Brien 
121eb9aea5aSDavid E. O'Brien 		filemon->fp = NULL;
122eb9aea5aSDavid E. O'Brien 		filemon->pid = -1;
123eb9aea5aSDavid E. O'Brien 
124eb9aea5aSDavid E. O'Brien 		/* Add to the free list. */
125eb9aea5aSDavid E. O'Brien 		TAILQ_INSERT_TAIL(&filemons_free, filemon, link);
126eb9aea5aSDavid E. O'Brien 
127eb9aea5aSDavid E. O'Brien 		/* Give up write access. */
128eb9aea5aSDavid E. O'Brien 		filemon_unlock_write();
129eb9aea5aSDavid E. O'Brien 
130eb9aea5aSDavid E. O'Brien 		if (fp != NULL)
131eb9aea5aSDavid E. O'Brien 			fdrop(fp, curthread);
132eb9aea5aSDavid E. O'Brien 	}
133eb9aea5aSDavid E. O'Brien }
134eb9aea5aSDavid E. O'Brien 
135eb9aea5aSDavid E. O'Brien static int
136eb9aea5aSDavid E. O'Brien filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused,
137eb9aea5aSDavid E. O'Brien     struct thread *td)
138eb9aea5aSDavid E. O'Brien {
139eb9aea5aSDavid E. O'Brien 	int error = 0;
140eb9aea5aSDavid E. O'Brien 	struct filemon *filemon;
141872ce247SHiroki Sato 	struct proc *p;
1427008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041
1437008be5bSPawel Jakub Dawidek 	cap_rights_t rights;
1447008be5bSPawel Jakub Dawidek #endif
145eb9aea5aSDavid E. O'Brien 
146*e8c87a09SBryan Drewery 	if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0)
147*e8c87a09SBryan Drewery 		return (error);
148eb9aea5aSDavid E. O'Brien 
149eb9aea5aSDavid E. O'Brien 	switch (cmd) {
150eb9aea5aSDavid E. O'Brien 	/* Set the output file descriptor. */
151eb9aea5aSDavid E. O'Brien 	case FILEMON_SET_FD:
152fac4a7acSBryan Drewery 		if (filemon->fp != NULL)
153fac4a7acSBryan Drewery 			fdrop(filemon->fp, td);
154fac4a7acSBryan Drewery 
1557008be5bSPawel Jakub Dawidek 		error = fget_write(td, *(int *)data,
1567008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041
1577008be5bSPawel Jakub Dawidek 		    cap_rights_init(&rights, CAP_PWRITE),
1587008be5bSPawel Jakub Dawidek #endif
1597008be5bSPawel Jakub Dawidek 		    &filemon->fp);
1607008be5bSPawel Jakub Dawidek 		if (error == 0)
161eb9aea5aSDavid E. O'Brien 			/* Write the file header. */
162eb9aea5aSDavid E. O'Brien 			filemon_comment(filemon);
163eb9aea5aSDavid E. O'Brien 		break;
164eb9aea5aSDavid E. O'Brien 
165eb9aea5aSDavid E. O'Brien 	/* Set the monitored process ID. */
166eb9aea5aSDavid E. O'Brien 	case FILEMON_SET_PID:
16789cac24eSHiroki Sato 		error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT,
16889cac24eSHiroki Sato 		    &p);
16989cac24eSHiroki Sato 		if (error == 0) {
170872ce247SHiroki Sato 			filemon->pid = p->p_pid;
171872ce247SHiroki Sato 			PROC_UNLOCK(p);
17289cac24eSHiroki Sato 		}
173eb9aea5aSDavid E. O'Brien 		break;
174eb9aea5aSDavid E. O'Brien 
175eb9aea5aSDavid E. O'Brien 	default:
176eb9aea5aSDavid E. O'Brien 		error = EINVAL;
177eb9aea5aSDavid E. O'Brien 		break;
178eb9aea5aSDavid E. O'Brien 	}
179eb9aea5aSDavid E. O'Brien 
180eb9aea5aSDavid E. O'Brien 	return (error);
181eb9aea5aSDavid E. O'Brien }
182eb9aea5aSDavid E. O'Brien 
183eb9aea5aSDavid E. O'Brien static int
184eb9aea5aSDavid E. O'Brien filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused,
185eb9aea5aSDavid E. O'Brien     struct thread *td __unused)
186eb9aea5aSDavid E. O'Brien {
187eb9aea5aSDavid E. O'Brien 	struct filemon *filemon;
188eb9aea5aSDavid E. O'Brien 
189eb9aea5aSDavid E. O'Brien 	/* Get exclusive write access. */
190eb9aea5aSDavid E. O'Brien 	filemon_lock_write();
191eb9aea5aSDavid E. O'Brien 
192eb9aea5aSDavid E. O'Brien 	if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL)
193eb9aea5aSDavid E. O'Brien 		TAILQ_REMOVE(&filemons_free, filemon, link);
194eb9aea5aSDavid E. O'Brien 
195eb9aea5aSDavid E. O'Brien 	/* Give up write access. */
196eb9aea5aSDavid E. O'Brien 	filemon_unlock_write();
197eb9aea5aSDavid E. O'Brien 
198eb9aea5aSDavid E. O'Brien 	if (filemon == NULL) {
199eb9aea5aSDavid E. O'Brien 		filemon = malloc(sizeof(struct filemon), M_FILEMON,
200eb9aea5aSDavid E. O'Brien 		    M_WAITOK | M_ZERO);
2018183f2e3SBryan Drewery 		sx_init(&filemon->lock, "filemon");
202eb9aea5aSDavid E. O'Brien 	}
203eb9aea5aSDavid E. O'Brien 
204eb9aea5aSDavid E. O'Brien 	filemon->pid = curproc->p_pid;
205eb9aea5aSDavid E. O'Brien 
206eb9aea5aSDavid E. O'Brien 	devfs_set_cdevpriv(filemon, filemon_dtr);
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 	/* Add to the in-use list. */
212eb9aea5aSDavid E. O'Brien 	TAILQ_INSERT_TAIL(&filemons_inuse, 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 	return (0);
218eb9aea5aSDavid E. O'Brien }
219eb9aea5aSDavid E. O'Brien 
220eb9aea5aSDavid E. O'Brien static int
221eb9aea5aSDavid E. O'Brien filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused,
222eb9aea5aSDavid E. O'Brien     struct thread *td __unused)
223eb9aea5aSDavid E. O'Brien {
224eb9aea5aSDavid E. O'Brien 
225eb9aea5aSDavid E. O'Brien 	return (0);
226eb9aea5aSDavid E. O'Brien }
227eb9aea5aSDavid E. O'Brien 
228eb9aea5aSDavid E. O'Brien static void
229eb9aea5aSDavid E. O'Brien filemon_load(void *dummy __unused)
230eb9aea5aSDavid E. O'Brien {
2318183f2e3SBryan Drewery 	sx_init(&access_lock, "filemons_inuse");
232eb9aea5aSDavid E. O'Brien 
233eb9aea5aSDavid E. O'Brien 	/* Install the syscall wrappers. */
234eb9aea5aSDavid E. O'Brien 	filemon_wrapper_install();
235eb9aea5aSDavid E. O'Brien 
236eb9aea5aSDavid E. O'Brien 	filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666,
237eb9aea5aSDavid E. O'Brien 	    "filemon");
238eb9aea5aSDavid E. O'Brien }
239eb9aea5aSDavid E. O'Brien 
240eb9aea5aSDavid E. O'Brien static int
241eb9aea5aSDavid E. O'Brien filemon_unload(void)
242eb9aea5aSDavid E. O'Brien {
243eb9aea5aSDavid E. O'Brien  	struct filemon *filemon;
244eb9aea5aSDavid E. O'Brien 	int error = 0;
245eb9aea5aSDavid E. O'Brien 
246eb9aea5aSDavid E. O'Brien 	/* Get exclusive write access. */
247eb9aea5aSDavid E. O'Brien 	filemon_lock_write();
248eb9aea5aSDavid E. O'Brien 
249eb9aea5aSDavid E. O'Brien 	if (TAILQ_FIRST(&filemons_inuse) != NULL)
250eb9aea5aSDavid E. O'Brien 		error = EBUSY;
251eb9aea5aSDavid E. O'Brien 	else {
252eb9aea5aSDavid E. O'Brien 		destroy_dev(filemon_dev);
253eb9aea5aSDavid E. O'Brien 
254eb9aea5aSDavid E. O'Brien 		/* Deinstall the syscall wrappers. */
255eb9aea5aSDavid E. O'Brien 		filemon_wrapper_deinstall();
256eb9aea5aSDavid E. O'Brien 	}
257eb9aea5aSDavid E. O'Brien 
258eb9aea5aSDavid E. O'Brien 	/* Give up write access. */
259eb9aea5aSDavid E. O'Brien 	filemon_unlock_write();
260eb9aea5aSDavid E. O'Brien 
261eb9aea5aSDavid E. O'Brien 	if (error == 0) {
262eb9aea5aSDavid E. O'Brien 		/* free() filemon structs free list. */
263eb9aea5aSDavid E. O'Brien 		filemon_lock_write();
264eb9aea5aSDavid E. O'Brien 		while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) {
265eb9aea5aSDavid E. O'Brien 			TAILQ_REMOVE(&filemons_free, filemon, link);
2668183f2e3SBryan Drewery 			sx_destroy(&filemon->lock);
267eb9aea5aSDavid E. O'Brien 			free(filemon, M_FILEMON);
268eb9aea5aSDavid E. O'Brien 		}
269eb9aea5aSDavid E. O'Brien 		filemon_unlock_write();
270eb9aea5aSDavid E. O'Brien 
2718183f2e3SBryan Drewery 		sx_destroy(&access_lock);
272eb9aea5aSDavid E. O'Brien 	}
273eb9aea5aSDavid E. O'Brien 
274eb9aea5aSDavid E. O'Brien 	return (error);
275eb9aea5aSDavid E. O'Brien }
276eb9aea5aSDavid E. O'Brien 
277eb9aea5aSDavid E. O'Brien static int
278eb9aea5aSDavid E. O'Brien filemon_modevent(module_t mod __unused, int type, void *data)
279eb9aea5aSDavid E. O'Brien {
280eb9aea5aSDavid E. O'Brien 	int error = 0;
281eb9aea5aSDavid E. O'Brien 
282eb9aea5aSDavid E. O'Brien 	switch (type) {
283eb9aea5aSDavid E. O'Brien 	case MOD_LOAD:
284eb9aea5aSDavid E. O'Brien 		filemon_load(data);
285eb9aea5aSDavid E. O'Brien 		break;
286eb9aea5aSDavid E. O'Brien 
287eb9aea5aSDavid E. O'Brien 	case MOD_UNLOAD:
288eb9aea5aSDavid E. O'Brien 		error = filemon_unload();
289eb9aea5aSDavid E. O'Brien 		break;
290eb9aea5aSDavid E. O'Brien 
291eb9aea5aSDavid E. O'Brien 	case MOD_SHUTDOWN:
292eb9aea5aSDavid E. O'Brien 		break;
293eb9aea5aSDavid E. O'Brien 
294eb9aea5aSDavid E. O'Brien 	default:
295eb9aea5aSDavid E. O'Brien 		error = EOPNOTSUPP;
296eb9aea5aSDavid E. O'Brien 		break;
297eb9aea5aSDavid E. O'Brien 
298eb9aea5aSDavid E. O'Brien 	}
299eb9aea5aSDavid E. O'Brien 
300eb9aea5aSDavid E. O'Brien 	return (error);
301eb9aea5aSDavid E. O'Brien }
302eb9aea5aSDavid E. O'Brien 
303eb9aea5aSDavid E. O'Brien DEV_MODULE(filemon, filemon_modevent, NULL);
304eb9aea5aSDavid E. O'Brien MODULE_VERSION(filemon, 1);
305