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 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 30 _test_no_kevents(const char *file, int line) 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 printf("\n[%s:%d]: Unexpected event:", file, line); 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 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 * 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 * 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 * 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 * 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 * 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 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 242 _kevent_cmp(struct kevent *k1, struct kevent *k2, const char *file, int line) 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("[%s:%d]: kevent_cmp: mismatch:\n %s !=\n %s\n", 262 file, line, kev1_str, kev2_str); 263 free(kev1_str); 264 free(kev2_str); 265 abort(); 266 } 267 } 268 269 void 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 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 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 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 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