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
_test_no_kevents(const char * file,int line)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
test_no_kevents_quietly(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 *
kevent_get(int fd)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 *
kevent_get_timeout(int fd,int seconds)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 *
kevent_fflags_dump(struct kevent * kev)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 *
kevent_flags_dump(struct kevent * kev)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 *
kevent_to_str(struct kevent * kev)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
kevent_add(int fd,struct kevent * kev,uintptr_t ident,short filter,u_short flags,u_int fflags,intptr_t data,void * udata)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
_kevent_cmp(struct kevent * k1,struct kevent * k2,const char * file,int line)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
test_begin(const char * func)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
success(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
test_kqueue(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
test_kqueue_close(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
main(int argc,char ** argv)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