1200483Srwatson/* 2200483Srwatson * Copyright (c) 2009 Mark Heily <mark@heily.com> 3200483Srwatson * 4200483Srwatson * Permission to use, copy, modify, and distribute this software for any 5200483Srwatson * purpose with or without fee is hereby granted, provided that the above 6200483Srwatson * copyright notice and this permission notice appear in all copies. 7200483Srwatson * 8200483Srwatson * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9200483Srwatson * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10200483Srwatson * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11200483Srwatson * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12200483Srwatson * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13200483Srwatson * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14200483Srwatson * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15200483Srwatson * 16200483Srwatson * $FreeBSD: stable/10/tests/sys/kqueue/libkqueue/proc.c 305467 2016-09-06 08:45:29Z ngie $ 17200483Srwatson */ 18200483Srwatson 19223845Sjonathan#include <sys/stat.h> 20223845Sjonathan 21223845Sjonathan#include <err.h> 22223845Sjonathan 23223845Sjonathan#include "config.h" 24200483Srwatson#include "common.h" 25200483Srwatson 26200483Srwatsonstatic int sigusr1_caught = 0; 27200483Srwatson 28200483Srwatsonint kqfd; 29200483Srwatson 30200483Srwatsonstatic void 31200483Srwatsonsig_handler(int signum) 32200483Srwatson{ 33200483Srwatson sigusr1_caught = 1; 34200483Srwatson} 35200483Srwatson 36200483Srwatsonstatic void 37200483Srwatsonadd_and_delete(void) 38200483Srwatson{ 39200483Srwatson struct kevent kev; 40200483Srwatson pid_t pid; 41200483Srwatson 42200483Srwatson /* Create a child that waits to be killed and then exits */ 43200483Srwatson pid = fork(); 44200483Srwatson if (pid == 0) { 45223845Sjonathan struct stat s; 46223865Sjonathan if (fstat(kqfd, &s) != -1) 47223865Sjonathan errx(1, "kqueue inherited across fork! (%s() at %s:%d)", 48223865Sjonathan __func__, __FILE__, __LINE__); 49223845Sjonathan 50200483Srwatson pause(); 51200483Srwatson exit(2); 52200483Srwatson } 53200483Srwatson printf(" -- child created (pid %d)\n", (int) pid); 54200483Srwatson 55200483Srwatson test_begin("kevent(EVFILT_PROC, EV_ADD)"); 56200483Srwatson 57200483Srwatson test_no_kevents(); 58200483Srwatson kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); 59200483Srwatson test_no_kevents(); 60200483Srwatson 61200483Srwatson success(); 62200483Srwatson 63200483Srwatson test_begin("kevent(EVFILT_PROC, EV_DELETE)"); 64200483Srwatson 65223845Sjonathan sleep(1); 66200483Srwatson test_no_kevents(); 67200483Srwatson kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL); 68200483Srwatson if (kill(pid, SIGKILL) < 0) 69200483Srwatson err(1, "kill"); 70200483Srwatson sleep(1); 71200483Srwatson test_no_kevents(); 72200483Srwatson 73200483Srwatson success(); 74200483Srwatson 75200483Srwatson} 76200483Srwatson 77295012Svangyzenstatic void 78295012Svangyzenproc_track(int sleep_time) 79295012Svangyzen{ 80295012Svangyzen char test_id[64]; 81295012Svangyzen struct kevent kev; 82295012Svangyzen pid_t pid; 83295012Svangyzen int pipe_fd[2]; 84295012Svangyzen ssize_t result; 85295012Svangyzen 86295012Svangyzen snprintf(test_id, sizeof(test_id), 87295012Svangyzen "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time); 88295012Svangyzen test_begin(test_id); 89295012Svangyzen test_no_kevents(); 90295012Svangyzen 91295012Svangyzen if (pipe(pipe_fd)) { 92295012Svangyzen err(1, "pipe (parent) failed! (%s() at %s:%d)", 93295012Svangyzen __func__, __FILE__, __LINE__); 94295012Svangyzen } 95295012Svangyzen 96295012Svangyzen /* Create a child to track. */ 97295012Svangyzen pid = fork(); 98295012Svangyzen if (pid == 0) { /* Child */ 99295012Svangyzen pid_t grandchild = -1; 100295012Svangyzen 101295012Svangyzen /* 102295012Svangyzen * Give the parent a chance to start tracking us. 103295012Svangyzen */ 104295012Svangyzen result = read(pipe_fd[1], test_id, 1); 105295012Svangyzen if (result != 1) { 106295012Svangyzen err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)", 107295012Svangyzen result, __func__, __FILE__, __LINE__); 108295012Svangyzen } 109295012Svangyzen 110295012Svangyzen /* 111295012Svangyzen * Spawn a grandchild that will immediately exit. If the kernel has bug 112295012Svangyzen * 180385, the parent will see a kevent with both NOTE_CHILD and 113295012Svangyzen * NOTE_EXIT. If that bug is fixed, it will see two separate kevents 114295012Svangyzen * for those notes. Note that this triggers the conditions for 115295012Svangyzen * detecting the bug quite reliably on a 1 CPU system (or if the test 116295012Svangyzen * process is restricted to a single CPU), but may not trigger it on a 117295012Svangyzen * multi-CPU system. 118295012Svangyzen */ 119295012Svangyzen grandchild = fork(); 120295012Svangyzen if (grandchild == 0) { /* Grandchild */ 121295012Svangyzen if (sleep_time) sleep(sleep_time); 122295012Svangyzen exit(1); 123295012Svangyzen } else if (grandchild == -1) { /* Error */ 124295012Svangyzen err(1, "fork (grandchild) failed! (%s() at %s:%d)", 125295012Svangyzen __func__, __FILE__, __LINE__); 126295012Svangyzen } else { /* Child (Grandchild Parent) */ 127295012Svangyzen printf(" -- grandchild created (pid %d)\n", (int) grandchild); 128295012Svangyzen } 129295012Svangyzen if (sleep_time) sleep(sleep_time); 130295012Svangyzen exit(0); 131295012Svangyzen } else if (pid == -1) { /* Error */ 132295012Svangyzen err(1, "fork (child) failed! (%s() at %s:%d)", 133295012Svangyzen __func__, __FILE__, __LINE__); 134295012Svangyzen } 135295012Svangyzen 136295012Svangyzen printf(" -- child created (pid %d)\n", (int) pid); 137295012Svangyzen 138295012Svangyzen kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE, 139295012Svangyzen NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK, 140295012Svangyzen 0, NULL); 141295012Svangyzen 142295012Svangyzen printf(" -- tracking child (pid %d)\n", (int) pid); 143295012Svangyzen 144295012Svangyzen /* Now that we're tracking the child, tell it to proceed. */ 145295012Svangyzen result = write(pipe_fd[0], test_id, 1); 146295012Svangyzen if (result != 1) { 147295012Svangyzen err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)", 148295012Svangyzen result, __func__, __FILE__, __LINE__); 149295012Svangyzen } 150295012Svangyzen 151295012Svangyzen /* 152295012Svangyzen * Several events should be received: 153295012Svangyzen * - NOTE_FORK (from child) 154295012Svangyzen * - NOTE_CHILD (from grandchild) 155295012Svangyzen * - NOTE_EXIT (from grandchild) 156295012Svangyzen * - NOTE_EXIT (from child) 157295012Svangyzen * 158295012Svangyzen * The NOTE_FORK and NOTE_EXIT from the child could be combined into a 159295012Svangyzen * single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must 160295012Svangyzen * not be combined. 161295012Svangyzen * 162295012Svangyzen * The loop continues until no events are received within a 5 second 163295012Svangyzen * period, at which point it is assumed that no more will be coming. The 164295012Svangyzen * loop is deliberately designed to attempt to get events even after all 165295012Svangyzen * the expected ones are received in case some spurious events are 166295012Svangyzen * generated as well as the expected ones. 167295012Svangyzen */ 168295012Svangyzen { 169295012Svangyzen int child_exit = 0; 170295012Svangyzen int child_fork = 0; 171295012Svangyzen int gchild_exit = 0; 172295012Svangyzen int gchild_note = 0; 173295012Svangyzen pid_t gchild_pid = -1; 174295012Svangyzen int done = 0; 175295012Svangyzen 176295012Svangyzen while (!done) 177295012Svangyzen { 178295012Svangyzen int handled = 0; 179295012Svangyzen struct kevent *kevp; 180295012Svangyzen 181295012Svangyzen kevp = kevent_get_timeout(kqfd, 5); 182295012Svangyzen if (kevp == NULL) { 183295012Svangyzen done = 1; 184295012Svangyzen } else { 185295012Svangyzen printf(" -- Received kevent: %s\n", kevent_to_str(kevp)); 186295012Svangyzen 187295012Svangyzen if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) { 188295012Svangyzen errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp)); 189295012Svangyzen } 190295012Svangyzen 191295012Svangyzen if (kevp->fflags & NOTE_CHILD) { 192295012Svangyzen if (kevp->data == pid) { 193295012Svangyzen if (!gchild_note) { 194295012Svangyzen ++gchild_note; 195295012Svangyzen gchild_pid = kevp->ident; 196295012Svangyzen ++handled; 197295012Svangyzen } else { 198295012Svangyzen errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp)); 199295012Svangyzen } 200295012Svangyzen } 201295012Svangyzen } 202295012Svangyzen 203295012Svangyzen if (kevp->fflags & NOTE_EXIT) { 204295012Svangyzen if ((kevp->ident == pid) && (!child_exit)) { 205295012Svangyzen ++child_exit; 206295012Svangyzen ++handled; 207295012Svangyzen } else if ((kevp->ident == gchild_pid) && (!gchild_exit)) { 208295012Svangyzen ++gchild_exit; 209295012Svangyzen ++handled; 210295012Svangyzen } else { 211295012Svangyzen errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp)); 212295012Svangyzen } 213295012Svangyzen } 214295012Svangyzen 215295012Svangyzen if (kevp->fflags & NOTE_FORK) { 216295012Svangyzen if ((kevp->ident == pid) && (!child_fork)) { 217295012Svangyzen ++child_fork; 218295012Svangyzen ++handled; 219295012Svangyzen } else { 220295012Svangyzen errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp)); 221295012Svangyzen } 222295012Svangyzen } 223295012Svangyzen 224295012Svangyzen if (!handled) { 225295012Svangyzen errx(1, "Spurious kevent: %s", kevent_to_str(kevp)); 226295012Svangyzen } 227295012Svangyzen 228295012Svangyzen free(kevp); 229295012Svangyzen } 230295012Svangyzen } 231295012Svangyzen 232295012Svangyzen /* Make sure all expected events were received. */ 233295012Svangyzen if (child_exit && child_fork && gchild_exit && gchild_note) { 234295012Svangyzen printf(" -- Received all expected events.\n"); 235295012Svangyzen } else { 236295012Svangyzen errx(1, "Did not receive all expected events."); 237295012Svangyzen } 238295012Svangyzen } 239295012Svangyzen 240295012Svangyzen success(); 241295012Svangyzen} 242295012Svangyzen 243223845Sjonathan#ifdef TODO 244200483Srwatsonstatic void 245200483Srwatsonevent_trigger(void) 246200483Srwatson{ 247200483Srwatson struct kevent kev; 248200483Srwatson pid_t pid; 249200483Srwatson 250200483Srwatson test_begin("kevent(EVFILT_PROC, wait)"); 251200483Srwatson 252200483Srwatson /* Create a child that waits to be killed and then exits */ 253200483Srwatson pid = fork(); 254200483Srwatson if (pid == 0) { 255200483Srwatson pause(); 256200483Srwatson printf(" -- child caught signal, exiting\n"); 257200483Srwatson exit(2); 258200483Srwatson } 259200483Srwatson printf(" -- child created (pid %d)\n", (int) pid); 260200483Srwatson 261200483Srwatson test_no_kevents(); 262200483Srwatson kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); 263200483Srwatson 264200483Srwatson /* Cause the child to exit, then retrieve the event */ 265200483Srwatson printf(" -- killing process %d\n", (int) pid); 266200483Srwatson if (kill(pid, SIGUSR1) < 0) 267200483Srwatson err(1, "kill"); 268200483Srwatson kevent_cmp(&kev, kevent_get(kqfd)); 269200483Srwatson test_no_kevents(); 270200483Srwatson 271200483Srwatson success(); 272200483Srwatson} 273200483Srwatson 274200483Srwatsonvoid 275200483Srwatsontest_kevent_signal_disable(void) 276200483Srwatson{ 277200483Srwatson const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)"; 278200483Srwatson struct kevent kev; 279200483Srwatson 280200483Srwatson test_begin(test_id); 281200483Srwatson 282200483Srwatson EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); 283200483Srwatson if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 284200483Srwatson err(1, "%s", test_id); 285200483Srwatson 286200483Srwatson /* Block SIGUSR1, then send it to ourselves */ 287200483Srwatson sigset_t mask; 288200483Srwatson sigemptyset(&mask); 289200483Srwatson sigaddset(&mask, SIGUSR1); 290200483Srwatson if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 291200483Srwatson err(1, "sigprocmask"); 292200483Srwatson if (kill(getpid(), SIGKILL) < 0) 293200483Srwatson err(1, "kill"); 294200483Srwatson 295200483Srwatson test_no_kevents(); 296200483Srwatson 297200483Srwatson success(); 298200483Srwatson} 299200483Srwatson 300200483Srwatsonvoid 301200483Srwatsontest_kevent_signal_enable(void) 302200483Srwatson{ 303200483Srwatson const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)"; 304200483Srwatson struct kevent kev; 305200483Srwatson 306200483Srwatson test_begin(test_id); 307200483Srwatson 308200483Srwatson EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); 309200483Srwatson if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 310200483Srwatson err(1, "%s", test_id); 311200483Srwatson 312200483Srwatson /* Block SIGUSR1, then send it to ourselves */ 313200483Srwatson sigset_t mask; 314200483Srwatson sigemptyset(&mask); 315200483Srwatson sigaddset(&mask, SIGUSR1); 316200483Srwatson if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 317200483Srwatson err(1, "sigprocmask"); 318200483Srwatson if (kill(getpid(), SIGUSR1) < 0) 319200483Srwatson err(1, "kill"); 320200483Srwatson 321200483Srwatson kev.flags = EV_ADD | EV_CLEAR; 322200483Srwatson#if LIBKQUEUE 323200483Srwatson kev.data = 1; /* WORKAROUND */ 324200483Srwatson#else 325200483Srwatson kev.data = 2; // one extra time from test_kevent_signal_disable() 326200483Srwatson#endif 327200483Srwatson kevent_cmp(&kev, kevent_get(kqfd)); 328200483Srwatson 329200483Srwatson /* Delete the watch */ 330200483Srwatson kev.flags = EV_DELETE; 331200483Srwatson if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 332200483Srwatson err(1, "%s", test_id); 333200483Srwatson 334200483Srwatson success(); 335200483Srwatson} 336200483Srwatson 337200483Srwatsonvoid 338200483Srwatsontest_kevent_signal_del(void) 339200483Srwatson{ 340200483Srwatson const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)"; 341200483Srwatson struct kevent kev; 342200483Srwatson 343200483Srwatson test_begin(test_id); 344200483Srwatson 345200483Srwatson /* Delete the kevent */ 346200483Srwatson EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); 347200483Srwatson if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 348200483Srwatson err(1, "%s", test_id); 349200483Srwatson 350200483Srwatson /* Block SIGUSR1, then send it to ourselves */ 351200483Srwatson sigset_t mask; 352200483Srwatson sigemptyset(&mask); 353200483Srwatson sigaddset(&mask, SIGUSR1); 354200483Srwatson if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 355200483Srwatson err(1, "sigprocmask"); 356200483Srwatson if (kill(getpid(), SIGUSR1) < 0) 357200483Srwatson err(1, "kill"); 358200483Srwatson 359200483Srwatson test_no_kevents(); 360200483Srwatson success(); 361200483Srwatson} 362200483Srwatson 363200483Srwatsonvoid 364200483Srwatsontest_kevent_signal_oneshot(void) 365200483Srwatson{ 366200483Srwatson const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)"; 367200483Srwatson struct kevent kev; 368200483Srwatson 369200483Srwatson test_begin(test_id); 370200483Srwatson 371200483Srwatson EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); 372200483Srwatson if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 373200483Srwatson err(1, "%s", test_id); 374200483Srwatson 375200483Srwatson /* Block SIGUSR1, then send it to ourselves */ 376200483Srwatson sigset_t mask; 377200483Srwatson sigemptyset(&mask); 378200483Srwatson sigaddset(&mask, SIGUSR1); 379200483Srwatson if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 380200483Srwatson err(1, "sigprocmask"); 381200483Srwatson if (kill(getpid(), SIGUSR1) < 0) 382200483Srwatson err(1, "kill"); 383200483Srwatson 384200483Srwatson kev.flags |= EV_CLEAR; 385200483Srwatson kev.data = 1; 386200483Srwatson kevent_cmp(&kev, kevent_get(kqfd)); 387200483Srwatson 388200483Srwatson /* Send another one and make sure we get no events */ 389200483Srwatson if (kill(getpid(), SIGUSR1) < 0) 390200483Srwatson err(1, "kill"); 391200483Srwatson test_no_kevents(); 392200483Srwatson 393200483Srwatson success(); 394200483Srwatson} 395200483Srwatson#endif 396200483Srwatson 397200483Srwatsonvoid 398200483Srwatsontest_evfilt_proc() 399200483Srwatson{ 400200483Srwatson kqfd = kqueue(); 401200483Srwatson 402200483Srwatson signal(SIGUSR1, sig_handler); 403200483Srwatson 404200483Srwatson add_and_delete(); 405295012Svangyzen proc_track(0); /* Run without sleeping before children exit. */ 406295012Svangyzen proc_track(1); /* Sleep a bit in the children before exiting. */ 407223845Sjonathan 408223845Sjonathan#if TODO 409200483Srwatson event_trigger(); 410223845Sjonathan#endif 411200483Srwatson 412200483Srwatson signal(SIGUSR1, SIG_DFL); 413200483Srwatson 414200483Srwatson#if TODO 415200483Srwatson test_kevent_signal_add(); 416200483Srwatson test_kevent_signal_del(); 417200483Srwatson test_kevent_signal_get(); 418200483Srwatson test_kevent_signal_disable(); 419200483Srwatson test_kevent_signal_enable(); 420200483Srwatson test_kevent_signal_oneshot(); 421200483Srwatson#endif 422200483Srwatson close(kqfd); 423200483Srwatson} 424