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/* To get asprintf(3) */
18#define _GNU_SOURCE
19
20#include <assert.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <poll.h>
24#include <signal.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <sys/queue.h>
28#include <sys/socket.h>
29#include <sys/types.h>
30#include <string.h>
31#include <unistd.h>
32
33#include "sys/event.h"
34#include "private.h"
35
36static char *
37kevent_filter_dump(const struct kevent *kev)
38{
39    static char __thread buf[64];
40
41    snprintf(&buf[0], sizeof(buf), "%d (%s)",
42            kev->filter, filter_name(kev->filter));
43    return (&buf[0]);
44}
45
46static char *
47kevent_fflags_dump(const struct kevent *kev)
48{
49    static char __thread buf[1024];
50
51#define KEVFFL_DUMP(attrib) \
52    if (kev->fflags & attrib) \
53    strncat(buf, #attrib" ", 64);
54
55    snprintf(buf, sizeof(buf), "fflags=0x%04x (", kev->fflags);
56    if (kev->filter == EVFILT_VNODE) {
57        KEVFFL_DUMP(NOTE_DELETE);
58        KEVFFL_DUMP(NOTE_WRITE);
59        KEVFFL_DUMP(NOTE_EXTEND);
60        KEVFFL_DUMP(NOTE_ATTRIB);
61        KEVFFL_DUMP(NOTE_LINK);
62        KEVFFL_DUMP(NOTE_RENAME);
63    } else if (kev->filter == EVFILT_USER) {
64        KEVFFL_DUMP(NOTE_FFNOP);
65        KEVFFL_DUMP(NOTE_FFAND);
66        KEVFFL_DUMP(NOTE_FFOR);
67        KEVFFL_DUMP(NOTE_FFCOPY);
68        KEVFFL_DUMP(NOTE_TRIGGER);
69    }  else {
70        strncat(buf, " ", 1);
71    }
72    buf[strlen(buf) - 1] = ')';
73
74#undef KEVFFL_DUMP
75
76    return (buf);
77}
78
79static char *
80kevent_flags_dump(const struct kevent *kev)
81{
82    static char __thread buf[1024];
83
84#define KEVFL_DUMP(attrib) \
85    if (kev->flags & attrib) \
86	strncat(buf, #attrib" ", 64);
87
88    snprintf(buf, sizeof(buf), "flags=0x%04x (", kev->flags);
89    KEVFL_DUMP(EV_ADD);
90    KEVFL_DUMP(EV_ENABLE);
91    KEVFL_DUMP(EV_DISABLE);
92    KEVFL_DUMP(EV_DELETE);
93    KEVFL_DUMP(EV_ONESHOT);
94    KEVFL_DUMP(EV_CLEAR);
95    KEVFL_DUMP(EV_EOF);
96    KEVFL_DUMP(EV_ERROR);
97    KEVFL_DUMP(EV_DISPATCH);
98    KEVFL_DUMP(EV_RECEIPT);
99    buf[strlen(buf) - 1] = ')';
100
101#undef KEVFL_DUMP
102
103    return (buf);
104}
105
106const char *
107kevent_dump(const struct kevent *kev)
108{
109    static char __thread buf[1024];
110
111    snprintf(buf, sizeof(buf),
112            "{ ident=%d, filter=%s, %s, %s, data=%d, udata=%p }",
113            (u_int) kev->ident,
114            kevent_filter_dump(kev),
115            kevent_flags_dump(kev),
116            kevent_fflags_dump(kev),
117            (int) kev->data,
118            kev->udata);
119
120    return (buf);
121}
122
123static int
124kevent_copyin_one(struct kqueue *kq, const struct kevent *src)
125{
126    struct knote  *kn = NULL;
127    struct filter *filt;
128    int rv;
129
130    if (src->flags & EV_DISPATCH && src->flags & EV_ONESHOT) {
131        errno = EINVAL;
132        return (-1);
133    }
134
135    if (filter_lookup(&filt, kq, src->filter) < 0)
136        return (-1);
137
138    //dbg_printf("src=%s\n", kevent_dump(src));
139
140    kn = knote_lookup(filt, src->ident);
141    if (kn == NULL) {
142        if (src->flags & EV_ADD) {
143            if ((kn = knote_new()) == NULL) {
144                errno = ENOENT;
145                return (-1);
146            }
147            memcpy(&kn->kev, src, sizeof(kn->kev));
148            kn->kev.flags &= ~EV_ENABLE;
149            kn->kev.flags |= EV_ADD;//FIXME why?
150            assert(filt->kn_create);
151            if (filt->kn_create(filt, kn) < 0) {
152                knote_free(filt, kn);
153                errno = EFAULT;
154                return (-1);
155            }
156            knote_insert(filt, kn);
157            dbg_printf("created kevent %s\n", kevent_dump(src));
158            if (src->flags & EV_DISABLE) {
159                kn->kev.flags |= EV_DISABLE;
160                return (filt->kn_disable(filt, kn));
161            }
162            return (0);
163        } else {
164            errno = ENOENT;
165            return (-1);
166        }
167    }
168
169    if (src->flags & EV_DELETE) {
170        rv = filt->kn_delete(filt, kn);
171        knote_free(filt, kn);
172        return (rv);
173    } else if (src->flags & EV_DISABLE) {
174        kn->kev.flags |= EV_DISABLE;
175        return (filt->kn_disable(filt, kn));
176    } else if (src->flags & EV_ENABLE) {
177        kn->kev.flags &= ~EV_DISABLE;
178        return (filt->kn_enable(filt, kn));
179    }
180
181    /* Implicit EV_ADD */
182    kn->kev.udata = src->udata;
183    return (filt->kn_modify(filt, kn, src));
184
185#if DEADWOOD
186    /* Special case for EVFILT_USER:
187       Ignore user-generated events that are not of interest */
188    if (src->fflags & NOTE_TRIGGER) {
189        filter_unlock(filt);
190        continue;
191    }
192#endif
193}
194
195/** @return number of events added to the eventlist */
196static int
197kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
198        struct kevent *eventlist, int nevents)
199{
200    int status, nret;
201
202    dbg_printf("nchanges=%d nevents=%d", nchanges, nevents);
203
204    /* TODO: refactor, this has become convoluted to support EV_RECEIPT */
205    for (nret = 0; nchanges > 0; src++, nchanges--) {
206
207        if (kevent_copyin_one(kq, src) < 0) {
208            dbg_printf("errno=%s",strerror(errno));
209            status = errno;
210            goto err_path;
211        } else {
212            if (src->flags & EV_RECEIPT) {
213                status = 0;
214                goto err_path;
215            }
216        }
217
218        continue;
219
220err_path:
221        if (nevents > 0) {
222            memcpy(eventlist, src, sizeof(*src));
223            eventlist->data = status;
224            nevents--;
225            eventlist++;
226            nret++;
227        } else {
228            return (-1);
229        }
230    }
231
232    return (nret);
233}
234
235int __attribute__((visibility("default")))
236kevent(int kqfd, const struct kevent *changelist, int nchanges,
237        struct kevent *eventlist, int nevents,
238        const struct timespec *timeout)
239{
240    struct kqueue *kq;
241    int rv, n, nret;
242
243    nret = 0;
244
245    kq = kqueue_get(kqfd);
246    if (kq == NULL) {
247        errno = ENOENT;
248        return (-1);
249    }
250
251    rv = kqueue_validate(kq);
252    if (rv < 0) {
253        return (-1);
254    } else if (rv == 0) {
255        errno = EBADF;
256        return (-1);
257    }
258
259    /*
260     * Process each kevent on the changelist.
261     */
262    if (nchanges) {
263        kqueue_lock(kq);
264        rv = kevent_copyin(kq, changelist, nchanges, eventlist, nevents);
265        kqueue_unlock(kq);
266        dbg_printf("changelist: rv=%d", rv);
267        if (rv < 0)
268            goto errout;
269        if (rv > 0) {
270            eventlist += rv;
271            nevents -= rv;
272        }
273    }
274
275    /* Determine if we need to wait for events. */
276    if (nevents > MAX_KEVENT)
277        nevents = MAX_KEVENT;
278    if (nevents == 0)
279        goto out;
280
281    /* Handle spurious wakeups where no events are generated. */
282    for (nret = 0; nret == 0;)
283    {
284        /* Wait for one or more events. */
285        n = kevent_wait(kq, timeout);
286        if (n < 0) {
287            dbg_puts("kevent_wait failed");
288            goto errout;
289        }
290        if (n == 0)
291            goto out;      /* Timeout */
292
293        /* Copy the events to the caller */
294        kqueue_lock(kq);
295        nret = kevent_copyout(kq, n, eventlist, nevents);
296        kqueue_unlock(kq);
297    }
298
299    if (KQUEUE_DEBUG) {
300        dbg_printf("returning %d events", nret);
301        for (n = 0; n < nret; n++) {
302            dbg_printf("eventlist[%d] = %s", n, kevent_dump(&eventlist[n]));
303        }
304    }
305
306    goto out;
307
308errout:
309    nret = -1;
310
311out:
312    kqueue_put(kq);
313    return (nret);
314}
315