xref: /freebsd/tests/sys/kqueue/libkqueue/main.c (revision 2f513db72b034fd5ef7f080b11be5c711c15186a)
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     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 */
61 void
62 test_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 */
81 struct kevent *
82 kevent_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. */
98 struct kevent *
99 kevent_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 
119 char *
120 kevent_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 
169 char *
170 kevent_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 */
202 char *
203 kevent_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 
229 void
230 kevent_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 
250 void
251 kevent_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 
278 void
279 test_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 
290 void
291 success(void)
292 {
293     printf("%-70s %s\n", cur_test_id, "passed");
294     free(cur_test_id);
295     cur_test_id = NULL;
296 }
297 
298 void
299 test_kqueue(void)
300 {
301     test_begin("kqueue()");
302     if ((kqfd = kqueue()) < 0)
303         err(1, "kqueue()");
304     test_no_kevents();
305     success();
306 }
307 
308 void
309 test_kqueue_close(void)
310 {
311     test_begin("close(kq)");
312     if (close(kqfd) < 0)
313         err(1, "close()");
314     success();
315 }
316 
317 int
318 main(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