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