]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/filemon/filemon.c
o Fix filemon and bmake meta-mode stability issues. [EN-16:01]
[FreeBSD/releng/10.2.git] / sys / dev / filemon / filemon.c
1 /*-
2  * Copyright (c) 2011, David E. O'Brien.
3  * Copyright (c) 2009-2011, Juniper Networks, Inc.
4  * Copyright (c) 2015, EMC Corp.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "opt_compat.h"
33
34 #include <sys/param.h>
35 #include <sys/file.h>
36 #include <sys/systm.h>
37 #include <sys/buf.h>
38 #include <sys/condvar.h>
39 #include <sys/conf.h>
40 #include <sys/fcntl.h>
41 #include <sys/ioccom.h>
42 #include <sys/kernel.h>
43 #include <sys/lock.h>
44 #include <sys/malloc.h>
45 #include <sys/module.h>
46 #include <sys/mutex.h>
47 #include <sys/poll.h>
48 #include <sys/proc.h>
49 #include <sys/queue.h>
50 #include <sys/sx.h>
51 #include <sys/syscall.h>
52 #include <sys/sysent.h>
53 #include <sys/sysproto.h>
54 #include <sys/uio.h>
55
56 #if __FreeBSD_version >= 900041
57 #include <sys/capsicum.h>
58 #endif
59
60 #include "filemon.h"
61
62 #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
63 #include <compat/freebsd32/freebsd32_syscall.h>
64 #include <compat/freebsd32/freebsd32_proto.h>
65
66 extern struct sysentvec ia32_freebsd_sysvec;
67 #endif
68
69 extern struct sysentvec elf32_freebsd_sysvec;
70 extern struct sysentvec elf64_freebsd_sysvec;
71
72 static d_close_t        filemon_close;
73 static d_ioctl_t        filemon_ioctl;
74 static d_open_t         filemon_open;
75 static int              filemon_unload(void);
76 static void             filemon_load(void *);
77
78 static struct cdevsw filemon_cdevsw = {
79         .d_version      = D_VERSION,
80         .d_close        = filemon_close,
81         .d_ioctl        = filemon_ioctl,
82         .d_open         = filemon_open,
83         .d_name         = "filemon",
84 };
85
86 MALLOC_DECLARE(M_FILEMON);
87 MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor");
88
89 struct filemon {
90         TAILQ_ENTRY(filemon) link;      /* Link into the in-use list. */
91         struct sx       lock;           /* Lock mutex for this filemon. */
92         struct file     *fp;            /* Output file pointer. */
93         pid_t           pid;            /* The process ID being monitored. */
94         char            fname1[MAXPATHLEN]; /* Temporary filename buffer. */
95         char            fname2[MAXPATHLEN]; /* Temporary filename buffer. */
96         char            msgbufr[1024];  /* Output message buffer. */
97 };
98
99 static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse);
100 static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free);
101 static struct sx access_lock;
102
103 static struct cdev *filemon_dev;
104
105 #include "filemon_lock.c"
106 #include "filemon_wrapper.c"
107
108 static void
109 filemon_dtr(void *data)
110 {
111         struct filemon *filemon = data;
112
113         if (filemon != NULL) {
114                 struct file *fp = filemon->fp;
115
116                 /* Get exclusive write access. */
117                 filemon_lock_write();
118
119                 /* Remove from the in-use list. */
120                 TAILQ_REMOVE(&filemons_inuse, filemon, link);
121
122                 filemon->fp = NULL;
123                 filemon->pid = -1;
124
125                 /* Add to the free list. */
126                 TAILQ_INSERT_TAIL(&filemons_free, filemon, link);
127
128                 /* Give up write access. */
129                 filemon_unlock_write();
130
131                 if (fp != NULL)
132                         fdrop(fp, curthread);
133         }
134 }
135
136 static int
137 filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused,
138     struct thread *td)
139 {
140         int error = 0;
141         struct filemon *filemon;
142         struct proc *p;
143 #if __FreeBSD_version >= 900041
144         cap_rights_t rights;
145 #endif
146
147         devfs_get_cdevpriv((void **) &filemon);
148
149         switch (cmd) {
150         /* Set the output file descriptor. */
151         case FILEMON_SET_FD:
152                 error = fget_write(td, *(int *)data,
153 #if __FreeBSD_version >= 900041
154                     cap_rights_init(&rights, CAP_PWRITE),
155 #endif
156                     &filemon->fp);
157                 if (error == 0)
158                         /* Write the file header. */
159                         filemon_comment(filemon);
160                 break;
161
162         /* Set the monitored process ID. */
163         case FILEMON_SET_PID:
164                 error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT,
165                     &p);
166                 if (error == 0) {
167                         filemon->pid = p->p_pid;
168                         PROC_UNLOCK(p);
169                 }
170                 break;
171
172         default:
173                 error = EINVAL;
174                 break;
175         }
176
177         return (error);
178 }
179
180 static int
181 filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused,
182     struct thread *td __unused)
183 {
184         struct filemon *filemon;
185
186         /* Get exclusive write access. */
187         filemon_lock_write();
188
189         if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL)
190                 TAILQ_REMOVE(&filemons_free, filemon, link);
191
192         /* Give up write access. */
193         filemon_unlock_write();
194
195         if (filemon == NULL) {
196                 filemon = malloc(sizeof(struct filemon), M_FILEMON,
197                     M_WAITOK | M_ZERO);
198
199                 filemon->fp = NULL;
200
201                 sx_init(&filemon->lock, "filemon");
202         }
203
204         filemon->pid = curproc->p_pid;
205
206         devfs_set_cdevpriv(filemon, filemon_dtr);
207
208         /* Get exclusive write access. */
209         filemon_lock_write();
210
211         /* Add to the in-use list. */
212         TAILQ_INSERT_TAIL(&filemons_inuse, filemon, link);
213
214         /* Give up write access. */
215         filemon_unlock_write();
216
217         return (0);
218 }
219
220 static int
221 filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused,
222     struct thread *td __unused)
223 {
224
225         return (0);
226 }
227
228 static void
229 filemon_load(void *dummy __unused)
230 {
231         sx_init(&access_lock, "filemons_inuse");
232
233         /* Install the syscall wrappers. */
234         filemon_wrapper_install();
235
236         filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666,
237             "filemon");
238 }
239
240 static int
241 filemon_unload(void)
242 {
243         struct filemon *filemon;
244         int error = 0;
245
246         /* Get exclusive write access. */
247         filemon_lock_write();
248
249         if (TAILQ_FIRST(&filemons_inuse) != NULL)
250                 error = EBUSY;
251         else {
252                 destroy_dev(filemon_dev);
253
254                 /* Deinstall the syscall wrappers. */
255                 filemon_wrapper_deinstall();
256         }
257
258         /* Give up write access. */
259         filemon_unlock_write();
260
261         if (error == 0) {
262                 /* free() filemon structs free list. */
263                 filemon_lock_write();
264                 while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) {
265                         TAILQ_REMOVE(&filemons_free, filemon, link);
266                         sx_destroy(&filemon->lock);
267                         free(filemon, M_FILEMON);
268                 }
269                 filemon_unlock_write();
270
271                 sx_destroy(&access_lock);
272         }
273
274         return (error);
275 }
276
277 static int
278 filemon_modevent(module_t mod __unused, int type, void *data)
279 {
280         int error = 0;
281
282         switch (type) {
283         case MOD_LOAD:
284                 filemon_load(data);
285                 break;
286
287         case MOD_UNLOAD:
288                 error = filemon_unload();
289                 break;
290
291         case MOD_SHUTDOWN:
292                 break;
293
294         default:
295                 error = EOPNOTSUPP;
296                 break;
297
298         }
299
300         return (error);
301 }
302
303 DEV_MODULE(filemon, filemon_modevent, NULL);
304 MODULE_VERSION(filemon, 1);