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