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