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