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 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 printf(" -- Received kevent: %s\n", kevent_to_str(kevp)); 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 == pid) && (!child_exit)) { 205 ++child_exit; 206 ++handled; 207 } else if ((kevp->ident == 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 == 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 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() 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