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