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