xref: /freebsd/sys/dev/filemon/filemon.c (revision 8183f2e31230be0231ab0fed0f73d8bba45854b1)
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.
4*8183f2e3SBryan 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>
43*8183f2e3SBryan 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/mutex.h>
47eb9aea5aSDavid E. O'Brien #include <sys/poll.h>
48eb9aea5aSDavid E. O'Brien #include <sys/proc.h>
49eb9aea5aSDavid E. O'Brien #include <sys/queue.h>
50*8183f2e3SBryan Drewery #include <sys/sx.h>
51eb9aea5aSDavid E. O'Brien #include <sys/syscall.h>
52eb9aea5aSDavid E. O'Brien #include <sys/sysent.h>
53eb9aea5aSDavid E. O'Brien #include <sys/sysproto.h>
54eb9aea5aSDavid E. O'Brien #include <sys/uio.h>
55eb9aea5aSDavid E. O'Brien 
56eb9aea5aSDavid E. O'Brien #if __FreeBSD_version >= 900041
574a144410SRobert Watson #include <sys/capsicum.h>
58eb9aea5aSDavid E. O'Brien #endif
59eb9aea5aSDavid E. O'Brien 
60eb9aea5aSDavid E. O'Brien #include "filemon.h"
61eb9aea5aSDavid E. O'Brien 
62eb9aea5aSDavid E. O'Brien #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
63eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_syscall.h>
64eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_proto.h>
65eb9aea5aSDavid E. O'Brien 
66eb9aea5aSDavid E. O'Brien extern struct sysentvec ia32_freebsd_sysvec;
67eb9aea5aSDavid E. O'Brien #endif
68eb9aea5aSDavid E. O'Brien 
69eb9aea5aSDavid E. O'Brien extern struct sysentvec elf32_freebsd_sysvec;
70eb9aea5aSDavid E. O'Brien extern struct sysentvec elf64_freebsd_sysvec;
71eb9aea5aSDavid E. O'Brien 
72eb9aea5aSDavid E. O'Brien static d_close_t	filemon_close;
73eb9aea5aSDavid E. O'Brien static d_ioctl_t	filemon_ioctl;
74eb9aea5aSDavid E. O'Brien static d_open_t		filemon_open;
75eb9aea5aSDavid E. O'Brien static int		filemon_unload(void);
76eb9aea5aSDavid E. O'Brien static void		filemon_load(void *);
77eb9aea5aSDavid E. O'Brien 
78eb9aea5aSDavid E. O'Brien static struct cdevsw filemon_cdevsw = {
79eb9aea5aSDavid E. O'Brien 	.d_version	= D_VERSION,
80eb9aea5aSDavid E. O'Brien 	.d_close	= filemon_close,
81eb9aea5aSDavid E. O'Brien 	.d_ioctl	= filemon_ioctl,
82eb9aea5aSDavid E. O'Brien 	.d_open		= filemon_open,
83eb9aea5aSDavid E. O'Brien 	.d_name		= "filemon",
84eb9aea5aSDavid E. O'Brien };
85eb9aea5aSDavid E. O'Brien 
86eb9aea5aSDavid E. O'Brien MALLOC_DECLARE(M_FILEMON);
87eb9aea5aSDavid E. O'Brien MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor");
88eb9aea5aSDavid E. O'Brien 
89eb9aea5aSDavid E. O'Brien struct filemon {
90eb9aea5aSDavid E. O'Brien 	TAILQ_ENTRY(filemon) link;	/* Link into the in-use list. */
91*8183f2e3SBryan Drewery 	struct sx	lock;		/* Lock mutex for this filemon. */
92eb9aea5aSDavid E. O'Brien 	struct file	*fp;		/* Output file pointer. */
93eb9aea5aSDavid E. O'Brien 	pid_t		pid;		/* The process ID being monitored. */
94eb9aea5aSDavid E. O'Brien 	char		fname1[MAXPATHLEN]; /* Temporary filename buffer. */
95eb9aea5aSDavid E. O'Brien 	char		fname2[MAXPATHLEN]; /* Temporary filename buffer. */
96eb9aea5aSDavid E. O'Brien 	char		msgbufr[1024];	/* Output message buffer. */
97eb9aea5aSDavid E. O'Brien };
98eb9aea5aSDavid E. O'Brien 
99eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse);
100eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free);
101*8183f2e3SBryan Drewery static struct sx access_lock;
102eb9aea5aSDavid E. O'Brien 
103eb9aea5aSDavid E. O'Brien static struct cdev *filemon_dev;
104eb9aea5aSDavid E. O'Brien 
105eb9aea5aSDavid E. O'Brien #include "filemon_lock.c"
106eb9aea5aSDavid E. O'Brien #include "filemon_wrapper.c"
107eb9aea5aSDavid E. O'Brien 
108eb9aea5aSDavid E. O'Brien static void
109eb9aea5aSDavid E. O'Brien filemon_dtr(void *data)
110eb9aea5aSDavid E. O'Brien {
111eb9aea5aSDavid E. O'Brien 	struct filemon *filemon = data;
112eb9aea5aSDavid E. O'Brien 
113eb9aea5aSDavid E. O'Brien 	if (filemon != NULL) {
114eb9aea5aSDavid E. O'Brien 		struct file *fp = filemon->fp;
115eb9aea5aSDavid E. O'Brien 
116eb9aea5aSDavid E. O'Brien 		/* Get exclusive write access. */
117eb9aea5aSDavid E. O'Brien 		filemon_lock_write();
118eb9aea5aSDavid E. O'Brien 
119eb9aea5aSDavid E. O'Brien 		/* Remove from the in-use list. */
120eb9aea5aSDavid E. O'Brien 		TAILQ_REMOVE(&filemons_inuse, filemon, link);
121eb9aea5aSDavid E. O'Brien 
122eb9aea5aSDavid E. O'Brien 		filemon->fp = NULL;
123eb9aea5aSDavid E. O'Brien 		filemon->pid = -1;
124eb9aea5aSDavid E. O'Brien 
125eb9aea5aSDavid E. O'Brien 		/* Add to the free list. */
126eb9aea5aSDavid E. O'Brien 		TAILQ_INSERT_TAIL(&filemons_free, filemon, link);
127eb9aea5aSDavid E. O'Brien 
128eb9aea5aSDavid E. O'Brien 		/* Give up write access. */
129eb9aea5aSDavid E. O'Brien 		filemon_unlock_write();
130eb9aea5aSDavid E. O'Brien 
131eb9aea5aSDavid E. O'Brien 		if (fp != NULL)
132eb9aea5aSDavid E. O'Brien 			fdrop(fp, curthread);
133eb9aea5aSDavid E. O'Brien 	}
134eb9aea5aSDavid E. O'Brien }
135eb9aea5aSDavid E. O'Brien 
136eb9aea5aSDavid E. O'Brien static int
137eb9aea5aSDavid E. O'Brien filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused,
138eb9aea5aSDavid E. O'Brien     struct thread *td)
139eb9aea5aSDavid E. O'Brien {
140eb9aea5aSDavid E. O'Brien 	int error = 0;
141eb9aea5aSDavid E. O'Brien 	struct filemon *filemon;
142872ce247SHiroki Sato 	struct proc *p;
1437008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041
1447008be5bSPawel Jakub Dawidek 	cap_rights_t rights;
1457008be5bSPawel Jakub Dawidek #endif
146eb9aea5aSDavid E. O'Brien 
147eb9aea5aSDavid E. O'Brien 	devfs_get_cdevpriv((void **) &filemon);
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:
1527008be5bSPawel Jakub Dawidek 		error = fget_write(td, *(int *)data,
1537008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041
1547008be5bSPawel Jakub Dawidek 		    cap_rights_init(&rights, CAP_PWRITE),
1557008be5bSPawel Jakub Dawidek #endif
1567008be5bSPawel Jakub Dawidek 		    &filemon->fp);
1577008be5bSPawel Jakub Dawidek 		if (error == 0)
158eb9aea5aSDavid E. O'Brien 			/* Write the file header. */
159eb9aea5aSDavid E. O'Brien 			filemon_comment(filemon);
160eb9aea5aSDavid E. O'Brien 		break;
161eb9aea5aSDavid E. O'Brien 
162eb9aea5aSDavid E. O'Brien 	/* Set the monitored process ID. */
163eb9aea5aSDavid E. O'Brien 	case FILEMON_SET_PID:
16489cac24eSHiroki Sato 		error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT,
16589cac24eSHiroki Sato 		    &p);
16689cac24eSHiroki Sato 		if (error == 0) {
167872ce247SHiroki Sato 			filemon->pid = p->p_pid;
168872ce247SHiroki Sato 			PROC_UNLOCK(p);
16989cac24eSHiroki Sato 		}
170eb9aea5aSDavid E. O'Brien 		break;
171eb9aea5aSDavid E. O'Brien 
172eb9aea5aSDavid E. O'Brien 	default:
173eb9aea5aSDavid E. O'Brien 		error = EINVAL;
174eb9aea5aSDavid E. O'Brien 		break;
175eb9aea5aSDavid E. O'Brien 	}
176eb9aea5aSDavid E. O'Brien 
177eb9aea5aSDavid E. O'Brien 	return (error);
178eb9aea5aSDavid E. O'Brien }
179eb9aea5aSDavid E. O'Brien 
180eb9aea5aSDavid E. O'Brien static int
181eb9aea5aSDavid E. O'Brien filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused,
182eb9aea5aSDavid E. O'Brien     struct thread *td __unused)
183eb9aea5aSDavid E. O'Brien {
184eb9aea5aSDavid E. O'Brien 	struct filemon *filemon;
185eb9aea5aSDavid E. O'Brien 
186eb9aea5aSDavid E. O'Brien 	/* Get exclusive write access. */
187eb9aea5aSDavid E. O'Brien 	filemon_lock_write();
188eb9aea5aSDavid E. O'Brien 
189eb9aea5aSDavid E. O'Brien 	if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL)
190eb9aea5aSDavid E. O'Brien 		TAILQ_REMOVE(&filemons_free, filemon, link);
191eb9aea5aSDavid E. O'Brien 
192eb9aea5aSDavid E. O'Brien 	/* Give up write access. */
193eb9aea5aSDavid E. O'Brien 	filemon_unlock_write();
194eb9aea5aSDavid E. O'Brien 
195eb9aea5aSDavid E. O'Brien 	if (filemon == NULL) {
196eb9aea5aSDavid E. O'Brien 		filemon = malloc(sizeof(struct filemon), M_FILEMON,
197eb9aea5aSDavid E. O'Brien 		    M_WAITOK | M_ZERO);
198eb9aea5aSDavid E. O'Brien 
199eb9aea5aSDavid E. O'Brien 		filemon->fp = NULL;
200eb9aea5aSDavid E. O'Brien 
201*8183f2e3SBryan 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 {
231*8183f2e3SBryan 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);
266*8183f2e3SBryan 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 
271*8183f2e3SBryan 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