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: releng/10.3/tests/sys/kqueue/main.c 223845 2011-07-07 18:07:03Z jonathan $
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
45    puts("confirming that there are no events pending");
46    memset(&timeo, 0, sizeof(timeo));
47    nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
48    if (nfds != 0) {
49        puts("\nUnexpected event:");
50        puts(kevent_to_str(&kev));
51        errx(1, "%d event(s) pending, but none expected:", nfds);
52    }
53}
54
55/* Retrieve a single kevent */
56struct kevent *
57kevent_get(int kqfd)
58{
59    int nfds;
60    struct kevent *kev;
61
62    if ((kev = calloc(1, sizeof(*kev))) == NULL)
63	err(1, "out of memory");
64
65    nfds = kevent(kqfd, NULL, 0, kev, 1, NULL);
66    if (nfds < 1)
67        err(1, "kevent(2)");
68
69    return (kev);
70}
71
72char *
73kevent_fflags_dump(struct kevent *kev)
74{
75    char *buf;
76
77#define KEVFFL_DUMP(attrib) \
78    if (kev->fflags & attrib) \
79	strncat(buf, #attrib" ", 64);
80
81    if ((buf = calloc(1, 1024)) == NULL)
82	abort();
83
84    /* Not every filter has meaningful fflags */
85    if (kev->filter != EVFILT_VNODE) {
86    	snprintf(buf, 1024, "fflags = %d", kev->fflags);
87	return (buf);
88    }
89
90    snprintf(buf, 1024, "fflags = %d (", kev->fflags);
91    KEVFFL_DUMP(NOTE_DELETE);
92    KEVFFL_DUMP(NOTE_WRITE);
93    KEVFFL_DUMP(NOTE_EXTEND);
94#if HAVE_NOTE_TRUNCATE
95    KEVFFL_DUMP(NOTE_TRUNCATE);
96#endif
97    KEVFFL_DUMP(NOTE_ATTRIB);
98    KEVFFL_DUMP(NOTE_LINK);
99    KEVFFL_DUMP(NOTE_RENAME);
100#if HAVE_NOTE_REVOKE
101    KEVFFL_DUMP(NOTE_REVOKE);
102#endif
103    buf[strlen(buf) - 1] = ')';
104
105    return (buf);
106}
107
108char *
109kevent_flags_dump(struct kevent *kev)
110{
111    char *buf;
112
113#define KEVFL_DUMP(attrib) \
114    if (kev->flags & attrib) \
115	strncat(buf, #attrib" ", 64);
116
117    if ((buf = calloc(1, 1024)) == NULL)
118	abort();
119
120    snprintf(buf, 1024, "flags = %d (", kev->flags);
121    KEVFL_DUMP(EV_ADD);
122    KEVFL_DUMP(EV_ENABLE);
123    KEVFL_DUMP(EV_DISABLE);
124    KEVFL_DUMP(EV_DELETE);
125    KEVFL_DUMP(EV_ONESHOT);
126    KEVFL_DUMP(EV_CLEAR);
127    KEVFL_DUMP(EV_EOF);
128    KEVFL_DUMP(EV_ERROR);
129#if HAVE_EV_DISPATCH
130    KEVFL_DUMP(EV_DISPATCH);
131#endif
132#if HAVE_EV_RECEIPT
133    KEVFL_DUMP(EV_RECEIPT);
134#endif
135    buf[strlen(buf) - 1] = ')';
136
137    return (buf);
138}
139
140/* Copied from ../kevent.c kevent_dump() and improved */
141const char *
142kevent_to_str(struct kevent *kev)
143{
144    char buf[512];
145
146    snprintf(&buf[0], sizeof(buf),
147            "[ident=%d, filter=%d, %s, %s, data=%d, udata=%p]",
148            (u_int) kev->ident,
149            kev->filter,
150            kevent_flags_dump(kev),
151            kevent_fflags_dump(kev),
152            (int) kev->data,
153            kev->udata);
154
155    return (strdup(buf));
156}
157
158void
159kevent_add(int kqfd, struct kevent *kev,
160        uintptr_t ident,
161        short     filter,
162        u_short   flags,
163        u_int     fflags,
164        intptr_t  data,
165        void      *udata)
166{
167    EV_SET(kev, ident, filter, flags, fflags, data, NULL);
168    if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
169        printf("Unable to add the following kevent:\n%s\n",
170                kevent_to_str(kev));
171        err(1, "kevent(): %s", strerror(errno));
172    }
173}
174
175void
176kevent_cmp(struct kevent *k1, struct kevent *k2)
177{
178/* XXX-
179   Workaround for inconsistent implementation of kevent(2)
180 */
181#ifdef __FreeBSD__
182    if (k1->flags & EV_ADD)
183        k2->flags |= EV_ADD;
184#endif
185    if (memcmp(k1, k2, sizeof(*k1)) != 0) {
186        printf("kevent_cmp: mismatch:\n  %s !=\n  %s\n",
187              kevent_to_str(k1), kevent_to_str(k2));
188        abort();
189    }
190}
191
192void
193test_begin(const char *func)
194{
195    if (cur_test_id)
196        free(cur_test_id);
197    cur_test_id = strdup(func);
198    if (!cur_test_id)
199        err(1, "strdup failed");
200
201    printf("\n\nTest %d: %s\n", testnum++, func);
202}
203
204void
205success(void)
206{
207    printf("%-70s %s\n", cur_test_id, "passed");
208    free(cur_test_id);
209    cur_test_id = NULL;
210}
211
212void
213test_kqueue(void)
214{
215    test_begin("kqueue()");
216    if ((kqfd = kqueue()) < 0)
217        err(1, "kqueue()");
218    test_no_kevents();
219    success();
220}
221
222void
223test_kqueue_close(void)
224{
225    test_begin("close(kq)");
226    if (close(kqfd) < 0)
227        err(1, "close()");
228    success();
229}
230
231int
232main(int argc, char **argv)
233{
234    int test_proc = 1;
235    int test_socket = 1;
236    int test_signal = 1;
237    int test_vnode = 1;
238    int test_timer = 1;
239#ifdef __FreeBSD__
240    int test_user = 1;
241#else
242    /* XXX-FIXME temporary */
243    int test_user = 0;
244#endif
245
246    while (argc) {
247        if (strcmp(argv[0], "--no-proc") == 0)
248            test_proc = 0;
249        if (strcmp(argv[0], "--no-socket") == 0)
250            test_socket = 0;
251        if (strcmp(argv[0], "--no-timer") == 0)
252            test_timer = 0;
253        if (strcmp(argv[0], "--no-signal") == 0)
254            test_signal = 0;
255        if (strcmp(argv[0], "--no-vnode") == 0)
256            test_vnode = 0;
257        if (strcmp(argv[0], "--no-user") == 0)
258            test_user = 0;
259        argv++;
260        argc--;
261    }
262
263    test_kqueue();
264    test_kqueue_close();
265
266    if (test_socket)
267        test_evfilt_read();
268    if (test_signal)
269        test_evfilt_signal();
270    if (test_vnode)
271        test_evfilt_vnode();
272#if HAVE_EVFILT_USER
273    if (test_user)
274        test_evfilt_user();
275#endif
276    if (test_timer)
277        test_evfilt_timer();
278    if (test_proc)
279        test_evfilt_proc();
280
281    printf("\n---\n"
282            "+OK All %d tests completed.\n", testnum - 1);
283    return (0);
284}
285