1/*
2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
3 *
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.
7 *
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.
15 */
16
17#include <errno.h>
18#include <err.h>
19#include <fcntl.h>
20#include <pthread.h>
21#include <signal.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <sys/queue.h>
25#include <sys/socket.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <string.h>
29#include <unistd.h>
30
31#include <limits.h>
32#include <sys/inotify.h>
33#include <sys/epoll.h>
34
35#include "sys/event.h"
36#include "private.h"
37
38static char *
39inotify_mask_dump(uint32_t mask)
40{
41    static char __thread buf[1024];
42
43#define INEVT_MASK_DUMP(attrib) \
44    if (mask & attrib) \
45       strcat(buf, #attrib" ");
46
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] = ')';
61
62    return (buf);
63}
64
65static char *
66inotify_event_dump(struct inotify_event *evt)
67{
68    static char __thread buf[1024];
69
70    snprintf(buf, sizeof(buf), "wd=%d mask=%s",
71            evt->wd,
72            inotify_mask_dump(evt->mask));
73
74    return (buf);
75}
76
77static int
78fd_to_path(char *buf, size_t bufsz, int fd)
79{
80    char path[1024];    //TODO: Maxpathlen, etc.
81
82    if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0)
83        return (-1);
84
85    memset(buf, 0, bufsz);
86    return (readlink(path, buf, bufsz));
87}
88
89
90/* TODO: USE this to get events with name field */
91int
92get_one_event(struct inotify_event *dst, int pfd)
93{
94    ssize_t n;
95
96    dbg_puts("reading one inotify event");
97    for (;;) {
98        n = read(pfd, dst, sizeof(*dst));
99        if (n < 0) {
100            if (errno == EINTR)
101                continue;
102            dbg_perror("read");
103            return (-1);
104        } else {
105            break;
106        }
107    }
108    dbg_printf("read(2) from inotify wd: %zu bytes", n);
109
110    /* FIXME-TODO: if len > 0, read(len) */
111    if (dst->len != 0)
112        abort();
113
114
115    return (0);
116}
117
118static int
119add_watch(struct filter *filt, struct knote *kn)
120{
121    char path[PATH_MAX];
122    uint32_t mask;
123
124    /* Convert the fd to a pathname */
125    if (fd_to_path(&path[0], sizeof(path), kn->kev.ident) < 0)
126        return (-1);
127
128    /* Convert the fflags to the inotify mask */
129    mask = 0;
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))
138        mask |= IN_ATTRIB;
139    if (kn->kev.fflags & NOTE_RENAME)
140        mask |= IN_MOVE_SELF;
141    if (kn->kev.flags & EV_ONESHOT)
142        mask |= IN_ONESHOT;
143
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));
149        return (-1);
150    }
151    return (0);
152}
153
154static int
155delete_watch(struct filter *filt, struct knote *kn)
156{
157    if (kn->kev.data < 0)
158        return (0);
159    if (inotify_rm_watch(filt->kf_pfd, kn->kev.data) < 0) {
160        dbg_printf("inotify_rm_watch(2): %s", strerror(errno));
161        return (-1);
162    }
163    dbg_printf("wd %d removed", (int) kn->kev.data);
164    kn->kev.data = -1;
165
166    return (0);
167}
168
169int
170evfilt_vnode_init(struct filter *filt)
171{
172    filt->kf_pfd = inotify_init();
173    dbg_printf("inotify fd = %d", filt->kf_pfd);
174    if (filt->kf_pfd < 0)
175        return (-1);
176
177    return (0);
178}
179
180void
181evfilt_vnode_destroy(struct filter *filt)
182{
183    close(filt->kf_pfd);
184}
185
186int
187evfilt_vnode_copyout(struct filter *filt,
188            struct kevent *dst,
189            int nevents)
190{
191    struct inotify_event evt;
192    struct stat sb;
193    struct knote *kn;
194
195    if (get_one_event(&evt, filt->kf_pfd) < 0)
196        return (-1);
197
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 */
201        return (0);
202    }
203
204    kn = knote_lookup_data(filt, evt.wd);
205    if (kn == NULL) {
206        dbg_printf("no match for wd # %d", evt.wd);
207        return (-1);
208    }
209
210    memcpy(dst, &kn->kev, sizeof(*dst));
211    dst->data = 0;
212
213    /* No error checking because fstat(2) should rarely fail */
214    //FIXME: EINTR
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;
224#endif
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;
229    }
230
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;
239
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;
248
249    if (kn->kev.flags & EV_DISPATCH) {
250        delete_watch(filt, kn); /* TODO: error checking */
251        KNOTE_DISABLE(kn);
252    } else if (kn->kev.flags & EV_ONESHOT) {
253        delete_watch(filt, kn); /* TODO: error checking */
254        knote_free(filt, kn);
255    }
256
257    return (1);
258}
259
260int
261evfilt_vnode_knote_create(struct filter *filt, struct knote *kn)
262{
263    struct stat sb;
264
265    if (fstat(kn->kev.ident, &sb) < 0) {
266        dbg_puts("fstat failed");
267        return (-1);
268    }
269    kn->data.vnode.nlink = sb.st_nlink;
270    kn->data.vnode.size = sb.st_size;
271    kn->kev.data = -1;
272
273    return (add_watch(filt, kn));
274}
275
276int
277evfilt_vnode_knote_modify(struct filter *filt, struct knote *kn,
278        const struct kevent *kev)
279{
280    return (-1); /* FIXME - STUB */
281}
282
283int
284evfilt_vnode_knote_delete(struct filter *filt, struct knote *kn)
285{
286    return delete_watch(filt, kn);
287}
288
289int
290evfilt_vnode_knote_enable(struct filter *filt, struct knote *kn)
291{
292    return add_watch(filt, kn);
293}
294
295int
296evfilt_vnode_knote_disable(struct filter *filt, struct knote *kn)
297{
298    return delete_watch(filt, kn);
299}
300
301const struct filter evfilt_vnode = {
302    EVFILT_VNODE,
303    evfilt_vnode_init,
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,
311};
312