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 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 EV_SET(kev, ident, filter, flags, fflags, data, NULL); 239 if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) { 240 printf("Unable to add the following kevent:\n%s\n", 241 kevent_to_str(kev)); 242 err(1, "kevent(): %s", strerror(errno)); 243 } 244 } 245 246 void 247 kevent_cmp(struct kevent *k1, struct kevent *k2) 248 { 249 /* XXX- 250 Workaround for inconsistent implementation of kevent(2) 251 */ 252 #ifdef __FreeBSD__ 253 if (k1->flags & EV_ADD) 254 k2->flags |= EV_ADD; 255 #endif 256 if (k1->ident != k2->ident || k1->filter != k2->filter || 257 k1->flags != k2->flags || k1->fflags != k2->fflags || 258 k1->data != k2->data || k1->udata != k2->udata || 259 k1->ext[0] != k2->ext[0] || k1->ext[1] != k2->ext[1] || 260 k1->ext[0] != k2->ext[2] || k1->ext[0] != k2->ext[3]) { 261 printf("kevent_cmp: mismatch:\n %s !=\n %s\n", 262 kevent_to_str(k1), kevent_to_str(k2)); 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 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 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