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