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 * $FreeBSD$
17 */
18
19#include <sys/types.h>
20
21#include "config.h"
22#include "common.h"
23
24int kqfd;
25static char *cur_test_id = NULL;
26static int testnum = 1;
27
28/* Checks if any events are pending, which is an error. */
29void
30test_no_kevents(void)
31{
32    int nfds;
33    struct timespec timeo;
34    struct kevent kev;
35    char *kev_str;
36
37    puts("confirming that there are no events pending");
38    memset(&timeo, 0, sizeof(timeo));
39    nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
40    if (nfds != 0) {
41        puts("\nUnexpected event:");
42        kev_str = kevent_to_str(&kev);
43        puts(kev_str);
44        free(kev_str);
45        errx(1, "%d event(s) pending, but none expected:", nfds);
46    }
47}
48
49/* Checks if any events are pending, which is an error. Do not print
50 * out anything unless events are found.
51*/
52void
53test_no_kevents_quietly(void)
54{
55    int nfds;
56    struct timespec timeo;
57    struct kevent kev;
58    char *kev_str;
59
60    memset(&timeo, 0, sizeof(timeo));
61    nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
62    if (nfds != 0) {
63        puts("\nUnexpected event:");
64        kev_str = kevent_to_str(&kev);
65        puts(kev_str);
66        free(kev_str);
67        errx(1, "%d event(s) pending, but none expected:", nfds);
68    }
69}
70
71/* Retrieve a single kevent */
72struct kevent *
73kevent_get(int fd)
74{
75    int nfds;
76    struct kevent *kev;
77
78    if ((kev = calloc(1, sizeof(*kev))) == NULL)
79        err(1, "out of memory");
80
81    nfds = kevent(fd, NULL, 0, kev, 1, NULL);
82    if (nfds < 1)
83        err(1, "kevent(2)");
84
85    return (kev);
86}
87
88/* Retrieve a single kevent, specifying a maximum time to wait for it. */
89struct kevent *
90kevent_get_timeout(int fd, int seconds)
91{
92    int nfds;
93    struct kevent *kev;
94    struct timespec timeout = {seconds, 0};
95
96    if ((kev = calloc(1, sizeof(*kev))) == NULL)
97        err(1, "out of memory");
98
99    nfds = kevent(fd, NULL, 0, kev, 1, &timeout);
100    if (nfds < 0) {
101        err(1, "kevent(2)");
102    } else if (nfds == 0) {
103        free(kev);
104        kev = NULL;
105    }
106
107    return (kev);
108}
109
110static char *
111kevent_fflags_dump(struct kevent *kev)
112{
113    char *buf;
114
115#define KEVFFL_DUMP(attrib) \
116    if (kev->fflags & attrib) \
117        strncat(buf, #attrib" ", 64);
118
119    if ((buf = calloc(1, 1024)) == NULL)
120        abort();
121
122    /* Not every filter has meaningful fflags */
123    if (kev->filter == EVFILT_PROC) {
124        snprintf(buf, 1024, "fflags = %x (", kev->fflags);
125        KEVFFL_DUMP(NOTE_EXIT);
126        KEVFFL_DUMP(NOTE_FORK);
127        KEVFFL_DUMP(NOTE_EXEC);
128        KEVFFL_DUMP(NOTE_CHILD);
129        KEVFFL_DUMP(NOTE_TRACKERR);
130        KEVFFL_DUMP(NOTE_TRACK);
131        buf[strlen(buf) - 1] = ')';
132    } else if (kev->filter == EVFILT_PROCDESC) {
133        snprintf(buf, 1024, "fflags = %x (", kev->fflags);
134        KEVFFL_DUMP(NOTE_EXIT);
135        KEVFFL_DUMP(NOTE_FORK);
136        KEVFFL_DUMP(NOTE_EXEC);
137        buf[strlen(buf) - 1] = ')';
138    } else if (kev->filter == EVFILT_VNODE) {
139        snprintf(buf, 1024, "fflags = %x (", kev->fflags);
140        KEVFFL_DUMP(NOTE_DELETE);
141        KEVFFL_DUMP(NOTE_WRITE);
142        KEVFFL_DUMP(NOTE_EXTEND);
143#if HAVE_NOTE_TRUNCATE
144        KEVFFL_DUMP(NOTE_TRUNCATE);
145#endif
146        KEVFFL_DUMP(NOTE_ATTRIB);
147        KEVFFL_DUMP(NOTE_LINK);
148        KEVFFL_DUMP(NOTE_RENAME);
149#if HAVE_NOTE_REVOKE
150        KEVFFL_DUMP(NOTE_REVOKE);
151#endif
152        buf[strlen(buf) - 1] = ')';
153    } else {
154        snprintf(buf, 1024, "fflags = %x", kev->fflags);
155    }
156
157    return (buf);
158}
159
160static char *
161kevent_flags_dump(struct kevent *kev)
162{
163    char *buf;
164
165#define KEVFL_DUMP(attrib) \
166    if (kev->flags & attrib) \
167        strncat(buf, #attrib" ", 64);
168
169    if ((buf = calloc(1, 1024)) == NULL)
170        abort();
171
172    snprintf(buf, 1024, "flags = %d (", kev->flags);
173    KEVFL_DUMP(EV_ADD);
174    KEVFL_DUMP(EV_ENABLE);
175    KEVFL_DUMP(EV_DISABLE);
176    KEVFL_DUMP(EV_DELETE);
177    KEVFL_DUMP(EV_ONESHOT);
178    KEVFL_DUMP(EV_CLEAR);
179    KEVFL_DUMP(EV_EOF);
180    KEVFL_DUMP(EV_ERROR);
181#if HAVE_EV_DISPATCH
182    KEVFL_DUMP(EV_DISPATCH);
183#endif
184#if HAVE_EV_RECEIPT
185    KEVFL_DUMP(EV_RECEIPT);
186#endif
187    buf[strlen(buf) - 1] = ')';
188
189    return (buf);
190}
191
192/* Copied from ../kevent.c kevent_dump() and improved */
193char *
194kevent_to_str(struct kevent *kev)
195{
196    char buf[512];
197    char *flags_str = kevent_flags_dump(kev);
198    char *fflags_str = kevent_fflags_dump(kev);
199
200    snprintf(&buf[0], sizeof(buf),
201            "[ident=%ju, filter=%d, %s, %s, data=%jd, udata=%p, "
202            "ext=[%jx %jx %jx %jx]",
203            (uintmax_t) kev->ident,
204            kev->filter,
205            flags_str,
206            fflags_str,
207            (uintmax_t)kev->data,
208            kev->udata,
209            (uintmax_t)kev->ext[0],
210            (uintmax_t)kev->ext[1],
211            (uintmax_t)kev->ext[2],
212            (uintmax_t)kev->ext[3]);
213
214    free(flags_str);
215    free(fflags_str);
216
217    return (strdup(buf));
218}
219
220void
221kevent_add(int fd, struct kevent *kev,
222        uintptr_t ident,
223        short     filter,
224        u_short   flags,
225        u_int     fflags,
226        intptr_t  data,
227        void      *udata)
228{
229    char *kev_str;
230
231    EV_SET(kev, ident, filter, flags, fflags, data, udata);
232    if (kevent(fd, kev, 1, NULL, 0, NULL) < 0) {
233        kev_str = kevent_to_str(kev);
234        printf("Unable to add the following kevent:\n%s\n",
235                kev_str);
236        free(kev_str);
237        err(1, "kevent(): %s", strerror(errno));
238    }
239}
240
241void
242kevent_cmp(struct kevent *k1, struct kevent *k2)
243{
244    char *kev1_str;
245    char *kev2_str;
246
247/* XXX-
248   Workaround for inconsistent implementation of kevent(2)
249 */
250#ifdef __FreeBSD__
251    if (k1->flags & EV_ADD)
252        k2->flags |= EV_ADD;
253#endif
254    if (k1->ident != k2->ident || k1->filter != k2->filter ||
255      k1->flags != k2->flags || k1->fflags != k2->fflags ||
256      k1->data != k2->data || k1->udata != k2->udata ||
257      k1->ext[0] != k2->ext[0] || k1->ext[1] != k2->ext[1] ||
258      k1->ext[0] != k2->ext[2] || k1->ext[0] != k2->ext[3]) {
259        kev1_str = kevent_to_str(k1);
260        kev2_str = kevent_to_str(k2);
261        printf("kevent_cmp: mismatch:\n  %s !=\n  %s\n",
262               kev1_str, kev2_str);
263        free(kev1_str);
264        free(kev2_str);
265        abort();
266    }
267}
268
269void
270test_begin(const char *func)
271{
272    if (cur_test_id)
273        free(cur_test_id);
274    cur_test_id = strdup(func);
275    if (!cur_test_id)
276        err(1, "strdup failed");
277
278    printf("\n\nTest %d: %s\n", testnum++, func);
279}
280
281void
282success(void)
283{
284    printf("%-70s %s\n", cur_test_id, "passed");
285    free(cur_test_id);
286    cur_test_id = NULL;
287}
288
289static void
290test_kqueue(void)
291{
292    test_begin("kqueue()");
293    if ((kqfd = kqueue()) < 0)
294        err(1, "kqueue()");
295    test_no_kevents();
296    success();
297}
298
299static void
300test_kqueue_close(void)
301{
302    test_begin("close(kq)");
303    if (close(kqfd) < 0)
304        err(1, "close()");
305    success();
306}
307
308int
309main(int argc, char **argv)
310{
311    int test_proc = 1;
312    int test_socket = 1;
313    int test_signal = 1;
314    int test_vnode = 1;
315    int test_timer = 1;
316#ifdef __FreeBSD__
317    int test_user = 1;
318#else
319    /* XXX-FIXME temporary */
320    int test_user = 0;
321#endif
322
323    while (argc) {
324        if (strcmp(argv[0], "--no-proc") == 0)
325            test_proc = 0;
326        if (strcmp(argv[0], "--no-socket") == 0)
327            test_socket = 0;
328        if (strcmp(argv[0], "--no-timer") == 0)
329            test_timer = 0;
330        if (strcmp(argv[0], "--no-signal") == 0)
331            test_signal = 0;
332        if (strcmp(argv[0], "--no-vnode") == 0)
333            test_vnode = 0;
334        if (strcmp(argv[0], "--no-user") == 0)
335            test_user = 0;
336        argv++;
337        argc--;
338    }
339
340    /*
341     * Some tests fork.  If output is fully buffered,
342     * the children inherit some buffered data and flush
343     * it when they exit, causing some data to be printed twice.
344     * Use line buffering to avoid this problem.
345     */
346    setlinebuf(stdout);
347    setlinebuf(stderr);
348
349    test_kqueue();
350    test_kqueue_close();
351
352    if (test_socket)
353        test_evfilt_read();
354    if (test_signal)
355        test_evfilt_signal();
356    if (test_vnode)
357        test_evfilt_vnode();
358#if HAVE_EVFILT_USER
359    if (test_user)
360        test_evfilt_user();
361#endif
362    if (test_timer)
363        test_evfilt_timer();
364    if (test_proc)
365        test_evfilt_proc();
366
367    printf("\n---\n"
368            "+OK All %d tests completed.\n", testnum - 1);
369    return (0);
370}
371