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