2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 #include <sys/queue.h>
25 #include <sys/socket.h>
27 #include <sys/types.h>
32 #include <sys/inotify.h>
33 #include <sys/epoll.h>
35 #include "sys/event.h"
39 inotify_mask_dump(uint32_t mask)
41 static char __thread buf[1024];
43 #define INEVT_MASK_DUMP(attrib) \
45 strcat(buf, #attrib" ");
47 snprintf(buf, sizeof(buf), "mask = %d (", mask);
48 INEVT_MASK_DUMP(IN_ACCESS);
49 INEVT_MASK_DUMP(IN_MODIFY);
50 INEVT_MASK_DUMP(IN_ATTRIB);
51 INEVT_MASK_DUMP(IN_CLOSE_WRITE);
52 INEVT_MASK_DUMP(IN_CLOSE_NOWRITE);
53 INEVT_MASK_DUMP(IN_OPEN);
54 INEVT_MASK_DUMP(IN_MOVED_FROM);
55 INEVT_MASK_DUMP(IN_MOVED_TO);
56 INEVT_MASK_DUMP(IN_CREATE);
57 INEVT_MASK_DUMP(IN_DELETE);
58 INEVT_MASK_DUMP(IN_DELETE_SELF);
59 INEVT_MASK_DUMP(IN_MOVE_SELF);
60 buf[strlen(buf) - 1] = ')';
66 inotify_event_dump(struct inotify_event *evt)
68 static char __thread buf[1024];
70 snprintf(buf, sizeof(buf), "wd=%d mask=%s",
72 inotify_mask_dump(evt->mask));
78 fd_to_path(char *buf, size_t bufsz, int fd)
80 char path[1024]; //TODO: Maxpathlen, etc.
82 if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0)
85 memset(buf, 0, bufsz);
86 return (readlink(path, buf, bufsz));
90 /* TODO: USE this to get events with name field */
92 get_one_event(struct inotify_event *dst, int pfd)
96 dbg_puts("reading one inotify event");
98 n = read(pfd, dst, sizeof(*dst));
108 dbg_printf("read(2) from inotify wd: %zu bytes", n);
110 /* FIXME-TODO: if len > 0, read(len) */
119 add_watch(struct filter *filt, struct knote *kn)
124 /* Convert the fd to a pathname */
125 if (fd_to_path(&path[0], sizeof(path), kn->kev.ident) < 0)
128 /* Convert the fflags to the inotify mask */
130 if (kn->kev.fflags & NOTE_DELETE)
131 mask |= IN_ATTRIB | IN_DELETE_SELF;
132 if (kn->kev.fflags & NOTE_WRITE)
133 mask |= IN_MODIFY | IN_ATTRIB;
134 if (kn->kev.fflags & NOTE_EXTEND)
135 mask |= IN_MODIFY | IN_ATTRIB;
136 if ((kn->kev.fflags & NOTE_ATTRIB) ||
137 (kn->kev.fflags & NOTE_LINK))
139 if (kn->kev.fflags & NOTE_RENAME)
140 mask |= IN_MOVE_SELF;
141 if (kn->kev.flags & EV_ONESHOT)
144 dbg_printf("inotify_add_watch(2); inofd=%d, %s, path=%s",
145 filt->kf_pfd, inotify_mask_dump(mask), path);
146 kn->kev.data = inotify_add_watch(filt->kf_pfd, path, mask);
147 if (kn->kev.data < 0) {
148 dbg_printf("inotify_add_watch(2): %s", strerror(errno));
155 delete_watch(struct filter *filt, struct knote *kn)
157 if (kn->kev.data < 0)
159 if (inotify_rm_watch(filt->kf_pfd, kn->kev.data) < 0) {
160 dbg_printf("inotify_rm_watch(2): %s", strerror(errno));
163 dbg_printf("wd %d removed", (int) kn->kev.data);
170 evfilt_vnode_init(struct filter *filt)
172 filt->kf_pfd = inotify_init();
173 dbg_printf("inotify fd = %d", filt->kf_pfd);
174 if (filt->kf_pfd < 0)
181 evfilt_vnode_destroy(struct filter *filt)
187 evfilt_vnode_copyout(struct filter *filt,
191 struct inotify_event evt;
195 if (get_one_event(&evt, filt->kf_pfd) < 0)
198 dbg_printf("inotify event: %s", inotify_event_dump(&evt));
199 if (evt.mask & IN_IGNORED) {
200 /* TODO: possibly return error when fs is unmounted */
204 kn = knote_lookup_data(filt, evt.wd);
206 dbg_printf("no match for wd # %d", evt.wd);
210 memcpy(dst, &kn->kev, sizeof(*dst));
213 /* No error checking because fstat(2) should rarely fail */
215 if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY)
216 && fstat(kn->kev.ident, &sb) == 0) {
217 if (sb.st_nlink == 0 && kn->kev.fflags & NOTE_DELETE)
218 dst->fflags |= NOTE_DELETE;
219 if (sb.st_nlink != kn->data.vnode.nlink && kn->kev.fflags & NOTE_LINK)
220 dst->fflags |= NOTE_LINK;
221 #if HAVE_NOTE_TRUNCATE
222 if (sb.st_nsize == 0 && kn->kev.fflags & NOTE_TRUNCATE)
223 dst->fflags |= NOTE_TRUNCATE;
225 if (sb.st_size > kn->data.vnode.size && kn->kev.fflags & NOTE_WRITE)
226 dst->fflags |= NOTE_EXTEND;
227 kn->data.vnode.nlink = sb.st_nlink;
228 kn->data.vnode.size = sb.st_size;
231 if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE)
232 dst->fflags |= NOTE_WRITE;
233 if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB)
234 dst->fflags |= NOTE_ATTRIB;
235 if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME)
236 dst->fflags |= NOTE_RENAME;
237 if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE)
238 dst->fflags |= NOTE_DELETE;
240 if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE)
241 dst->fflags |= NOTE_WRITE;
242 if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB)
243 dst->fflags |= NOTE_ATTRIB;
244 if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME)
245 dst->fflags |= NOTE_RENAME;
246 if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE)
247 dst->fflags |= NOTE_DELETE;
249 if (kn->kev.flags & EV_DISPATCH) {
250 delete_watch(filt, kn); /* TODO: error checking */
252 } else if (kn->kev.flags & EV_ONESHOT) {
253 delete_watch(filt, kn); /* TODO: error checking */
254 knote_free(filt, kn);
261 evfilt_vnode_knote_create(struct filter *filt, struct knote *kn)
265 if (fstat(kn->kev.ident, &sb) < 0) {
266 dbg_puts("fstat failed");
269 kn->data.vnode.nlink = sb.st_nlink;
270 kn->data.vnode.size = sb.st_size;
273 return (add_watch(filt, kn));
277 evfilt_vnode_knote_modify(struct filter *filt, struct knote *kn,
278 const struct kevent *kev)
280 return (-1); /* FIXME - STUB */
284 evfilt_vnode_knote_delete(struct filter *filt, struct knote *kn)
286 return delete_watch(filt, kn);
290 evfilt_vnode_knote_enable(struct filter *filt, struct knote *kn)
292 return add_watch(filt, kn);
296 evfilt_vnode_knote_disable(struct filter *filt, struct knote *kn)
298 return delete_watch(filt, kn);
301 const struct filter evfilt_vnode = {
304 evfilt_vnode_destroy,
305 evfilt_vnode_copyout,
306 evfilt_vnode_knote_create,
307 evfilt_vnode_knote_modify,
308 evfilt_vnode_knote_delete,
309 evfilt_vnode_knote_enable,
310 evfilt_vnode_knote_disable,