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