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/stat.h> 18 19 #include <err.h> 20 21 #include "config.h" 22 #include "common.h" 23 24 static int sigusr1_caught = 0; 25 26 27 static void 28 sig_handler(__unused int signum) 29 { 30 sigusr1_caught = 1; 31 } 32 33 static void 34 add_and_delete(void) 35 { 36 struct kevent kev; 37 pid_t pid; 38 39 /* Create a child that waits to be killed and then exits */ 40 pid = fork(); 41 if (pid == 0) { 42 struct stat s; 43 if (fstat(kqfd, &s) != -1) 44 errx(1, "kqueue inherited across fork! (%s() at %s:%d)", 45 __func__, __FILE__, __LINE__); 46 47 pause(); 48 exit(2); 49 } 50 printf(" -- child created (pid %d)\n", (int) pid); 51 52 test_begin("kevent(EVFILT_PROC, EV_ADD)"); 53 54 test_no_kevents(); 55 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); 56 test_no_kevents(); 57 58 success(); 59 60 test_begin("kevent(EVFILT_PROC, EV_DELETE)"); 61 62 sleep(1); 63 test_no_kevents(); 64 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL); 65 if (kill(pid, SIGKILL) < 0) 66 err(1, "kill"); 67 sleep(1); 68 test_no_kevents(); 69 70 success(); 71 72 } 73 74 static void 75 proc_track(int sleep_time) 76 { 77 char test_id[64]; 78 struct kevent kev; 79 pid_t pid; 80 int pipe_fd[2]; 81 ssize_t result; 82 83 snprintf(test_id, sizeof(test_id), 84 "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time); 85 test_begin(test_id); 86 test_no_kevents(); 87 88 if (pipe(pipe_fd)) { 89 err(1, "pipe (parent) failed! (%s() at %s:%d)", 90 __func__, __FILE__, __LINE__); 91 } 92 93 /* Create a child to track. */ 94 pid = fork(); 95 if (pid == 0) { /* Child */ 96 pid_t grandchild = -1; 97 98 /* 99 * Give the parent a chance to start tracking us. 100 */ 101 result = read(pipe_fd[1], test_id, 1); 102 if (result != 1) { 103 err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)", 104 result, __func__, __FILE__, __LINE__); 105 } 106 107 /* 108 * Spawn a grandchild that will immediately exit. If the kernel has bug 109 * 180385, the parent will see a kevent with both NOTE_CHILD and 110 * NOTE_EXIT. If that bug is fixed, it will see two separate kevents 111 * for those notes. Note that this triggers the conditions for 112 * detecting the bug quite reliably on a 1 CPU system (or if the test 113 * process is restricted to a single CPU), but may not trigger it on a 114 * multi-CPU system. 115 */ 116 grandchild = fork(); 117 if (grandchild == 0) { /* Grandchild */ 118 if (sleep_time) sleep(sleep_time); 119 exit(1); 120 } else if (grandchild == -1) { /* Error */ 121 err(1, "fork (grandchild) failed! (%s() at %s:%d)", 122 __func__, __FILE__, __LINE__); 123 } else { /* Child (Grandchild Parent) */ 124 printf(" -- grandchild created (pid %d)\n", (int) grandchild); 125 } 126 if (sleep_time) sleep(sleep_time); 127 exit(0); 128 } else if (pid == -1) { /* Error */ 129 err(1, "fork (child) failed! (%s() at %s:%d)", 130 __func__, __FILE__, __LINE__); 131 } 132 133 printf(" -- child created (pid %d)\n", (int) pid); 134 135 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE, 136 NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK, 137 0, NULL); 138 139 printf(" -- tracking child (pid %d)\n", (int) pid); 140 141 /* Now that we're tracking the child, tell it to proceed. */ 142 result = write(pipe_fd[0], test_id, 1); 143 if (result != 1) { 144 err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)", 145 result, __func__, __FILE__, __LINE__); 146 } 147 148 /* 149 * Several events should be received: 150 * - NOTE_FORK (from child) 151 * - NOTE_CHILD (from grandchild) 152 * - NOTE_EXIT (from grandchild) 153 * - NOTE_EXIT (from child) 154 * 155 * The NOTE_FORK and NOTE_EXIT from the child could be combined into a 156 * single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must 157 * not be combined. 158 * 159 * The loop continues until no events are received within a 5 second 160 * period, at which point it is assumed that no more will be coming. The 161 * loop is deliberately designed to attempt to get events even after all 162 * the expected ones are received in case some spurious events are 163 * generated as well as the expected ones. 164 */ 165 { 166 int child_exit = 0; 167 int child_fork = 0; 168 int gchild_exit = 0; 169 int gchild_note = 0; 170 pid_t gchild_pid = -1; 171 int done = 0; 172 char *kev_str; 173 174 while (!done) 175 { 176 int handled = 0; 177 struct kevent *kevp; 178 179 kevp = kevent_get_timeout(kqfd, 5); 180 if (kevp == NULL) { 181 done = 1; 182 } else { 183 kev_str = kevent_to_str(kevp); 184 printf(" -- Received kevent: %s\n", kev_str); 185 free(kev_str); 186 187 if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) { 188 errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp)); 189 } 190 191 if (kevp->fflags & NOTE_CHILD) { 192 if (kevp->data == pid) { 193 if (!gchild_note) { 194 ++gchild_note; 195 gchild_pid = kevp->ident; 196 ++handled; 197 } else { 198 errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp)); 199 } 200 } 201 } 202 203 if (kevp->fflags & NOTE_EXIT) { 204 if ((kevp->ident == (uintptr_t)pid) && (!child_exit)) { 205 ++child_exit; 206 ++handled; 207 } else if ((kevp->ident == (uintptr_t)gchild_pid) && (!gchild_exit)) { 208 ++gchild_exit; 209 ++handled; 210 } else { 211 errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp)); 212 } 213 } 214 215 if (kevp->fflags & NOTE_FORK) { 216 if ((kevp->ident == (uintptr_t)pid) && (!child_fork)) { 217 ++child_fork; 218 ++handled; 219 } else { 220 errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp)); 221 } 222 } 223 224 if (!handled) { 225 errx(1, "Spurious kevent: %s", kevent_to_str(kevp)); 226 } 227 228 free(kevp); 229 } 230 } 231 232 /* Make sure all expected events were received. */ 233 if (child_exit && child_fork && gchild_exit && gchild_note) { 234 printf(" -- Received all expected events.\n"); 235 } else { 236 errx(1, "Did not receive all expected events."); 237 } 238 } 239 240 success(); 241 } 242 243 #ifdef TODO 244 static void 245 event_trigger(void) 246 { 247 struct kevent kev; 248 pid_t pid; 249 250 test_begin("kevent(EVFILT_PROC, wait)"); 251 252 /* Create a child that waits to be killed and then exits */ 253 pid = fork(); 254 if (pid == 0) { 255 pause(); 256 printf(" -- child caught signal, exiting\n"); 257 exit(2); 258 } 259 printf(" -- child created (pid %d)\n", (int) pid); 260 261 test_no_kevents(); 262 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); 263 264 /* Cause the child to exit, then retrieve the event */ 265 printf(" -- killing process %d\n", (int) pid); 266 if (kill(pid, SIGUSR1) < 0) 267 err(1, "kill"); 268 kevent_cmp(&kev, kevent_get(kqfd)); 269 test_no_kevents(); 270 271 success(); 272 } 273 274 static void 275 test_kevent_signal_disable(void) 276 { 277 const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)"; 278 struct kevent kev; 279 280 test_begin(test_id); 281 282 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); 283 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 284 err(1, "%s", test_id); 285 286 /* Block SIGUSR1, then send it to ourselves */ 287 sigset_t mask; 288 sigemptyset(&mask); 289 sigaddset(&mask, SIGUSR1); 290 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 291 err(1, "sigprocmask"); 292 if (kill(getpid(), SIGKILL) < 0) 293 err(1, "kill"); 294 295 test_no_kevents(); 296 297 success(); 298 } 299 300 void 301 test_kevent_signal_enable(void) 302 { 303 const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)"; 304 struct kevent kev; 305 306 test_begin(test_id); 307 308 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); 309 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 310 err(1, "%s", test_id); 311 312 /* Block SIGUSR1, then send it to ourselves */ 313 sigset_t mask; 314 sigemptyset(&mask); 315 sigaddset(&mask, SIGUSR1); 316 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 317 err(1, "sigprocmask"); 318 if (kill(getpid(), SIGUSR1) < 0) 319 err(1, "kill"); 320 321 kev.flags = EV_ADD | EV_CLEAR; 322 #if LIBKQUEUE 323 kev.data = 1; /* WORKAROUND */ 324 #else 325 kev.data = 2; // one extra time from test_kevent_signal_disable() 326 #endif 327 kevent_cmp(&kev, kevent_get(kqfd)); 328 329 /* Delete the watch */ 330 kev.flags = EV_DELETE; 331 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 332 err(1, "%s", test_id); 333 334 success(); 335 } 336 337 void 338 test_kevent_signal_del(void) 339 { 340 const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)"; 341 struct kevent kev; 342 343 test_begin(test_id); 344 345 /* Delete the kevent */ 346 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); 347 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 348 err(1, "%s", test_id); 349 350 /* Block SIGUSR1, then send it to ourselves */ 351 sigset_t mask; 352 sigemptyset(&mask); 353 sigaddset(&mask, SIGUSR1); 354 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 355 err(1, "sigprocmask"); 356 if (kill(getpid(), SIGUSR1) < 0) 357 err(1, "kill"); 358 359 test_no_kevents(); 360 success(); 361 } 362 363 void 364 test_kevent_signal_oneshot(void) 365 { 366 const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)"; 367 struct kevent kev; 368 369 test_begin(test_id); 370 371 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); 372 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 373 err(1, "%s", test_id); 374 375 /* Block SIGUSR1, then send it to ourselves */ 376 sigset_t mask; 377 sigemptyset(&mask); 378 sigaddset(&mask, SIGUSR1); 379 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 380 err(1, "sigprocmask"); 381 if (kill(getpid(), SIGUSR1) < 0) 382 err(1, "kill"); 383 384 kev.flags |= EV_CLEAR; 385 kev.data = 1; 386 kevent_cmp(&kev, kevent_get(kqfd)); 387 388 /* Send another one and make sure we get no events */ 389 if (kill(getpid(), SIGUSR1) < 0) 390 err(1, "kill"); 391 test_no_kevents(); 392 393 success(); 394 } 395 #endif 396 397 void 398 test_evfilt_proc(void) 399 { 400 kqfd = kqueue(); 401 402 signal(SIGUSR1, sig_handler); 403 404 add_and_delete(); 405 proc_track(0); /* Run without sleeping before children exit. */ 406 proc_track(1); /* Sleep a bit in the children before exiting. */ 407 408 #if TODO 409 event_trigger(); 410 #endif 411 412 signal(SIGUSR1, SIG_DFL); 413 414 #if TODO 415 test_kevent_signal_add(); 416 test_kevent_signal_del(); 417 test_kevent_signal_get(); 418 test_kevent_signal_disable(); 419 test_kevent_signal_enable(); 420 test_kevent_signal_oneshot(); 421 #endif 422 close(kqfd); 423 } 424