1 /*
2 * Copyright (c) 2009 Mark Heily <[email protected]>
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
test_no_kevents(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
test_no_kevents_quietly(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 *
kevent_get(int kqfd)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 *
kevent_get_timeout(int kqfd,int seconds)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 *
kevent_fflags_dump(struct kevent * kev)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 *
kevent_flags_dump(struct kevent * kev)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 *
kevent_to_str(struct kevent * kev)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
kevent_add(int kqfd,struct kevent * kev,uintptr_t ident,short filter,u_short flags,u_int fflags,intptr_t data,void * udata)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
kevent_cmp(struct kevent * k1,struct kevent * k2)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
test_begin(const char * func)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
success(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
test_kqueue(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
test_kqueue_close(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
main(int argc,char ** argv)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