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