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