Path: blob/main/tests/sys/kqueue/libkqueue/proc.c
101843 views
/*1* Copyright (c) 2009 Mark Heily <[email protected]>2*3* Permission to use, copy, modify, and distribute this software for any4* purpose with or without fee is hereby granted, provided that the above5* copyright notice and this permission notice appear in all copies.6*7* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES8* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF9* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR10* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES11* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN12* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF13* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.14*/1516#include <sys/stat.h>1718#include <err.h>1920#include "config.h"21#include "common.h"2223static int sigusr1_caught = 0;242526static void27sig_handler(__unused int signum)28{29sigusr1_caught = 1;30}3132static void33add_and_delete(void)34{35struct kevent kev;36pid_t pid;3738/* Create a child that waits to be killed and then exits */39pid = fork();40if (pid == 0) {41struct stat s;42if (fstat(kqfd, &s) != -1)43errx(1, "kqueue inherited across fork! (%s() at %s:%d)",44__func__, __FILE__, __LINE__);4546pause();47exit(2);48}49printf(" -- child created (pid %d)\n", (int) pid);5051test_begin("kevent(EVFILT_PROC, EV_ADD)");5253test_no_kevents();54kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);55test_no_kevents();5657success();5859test_begin("kevent(EVFILT_PROC, EV_DELETE)");6061sleep(1);62test_no_kevents();63kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);64if (kill(pid, SIGKILL) < 0)65err(1, "kill");66sleep(1);67test_no_kevents();6869success();7071}7273static void74proc_track(int sleep_time)75{76char test_id[64];77struct kevent kev;78pid_t pid;79int pipe_fd[2];80ssize_t result;8182snprintf(test_id, sizeof(test_id),83"kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time);84test_begin(test_id);85test_no_kevents();8687if (pipe(pipe_fd)) {88err(1, "pipe (parent) failed! (%s() at %s:%d)",89__func__, __FILE__, __LINE__);90}9192/* Create a child to track. */93pid = fork();94if (pid == 0) { /* Child */95pid_t grandchild = -1;9697/*98* Give the parent a chance to start tracking us.99*/100result = read(pipe_fd[1], test_id, 1);101if (result != 1) {102err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)",103result, __func__, __FILE__, __LINE__);104}105106/*107* Spawn a grandchild that will immediately exit. If the kernel has bug108* 180385, the parent will see a kevent with both NOTE_CHILD and109* NOTE_EXIT. If that bug is fixed, it will see two separate kevents110* for those notes. Note that this triggers the conditions for111* detecting the bug quite reliably on a 1 CPU system (or if the test112* process is restricted to a single CPU), but may not trigger it on a113* multi-CPU system.114*/115grandchild = fork();116if (grandchild == 0) { /* Grandchild */117if (sleep_time) sleep(sleep_time);118exit(1);119} else if (grandchild == -1) { /* Error */120err(1, "fork (grandchild) failed! (%s() at %s:%d)",121__func__, __FILE__, __LINE__);122} else { /* Child (Grandchild Parent) */123printf(" -- grandchild created (pid %d)\n", (int) grandchild);124}125if (sleep_time) sleep(sleep_time);126exit(0);127} else if (pid == -1) { /* Error */128err(1, "fork (child) failed! (%s() at %s:%d)",129__func__, __FILE__, __LINE__);130}131132printf(" -- child created (pid %d)\n", (int) pid);133134kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE,135NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK,1360, NULL);137138printf(" -- tracking child (pid %d)\n", (int) pid);139140/* Now that we're tracking the child, tell it to proceed. */141result = write(pipe_fd[0], test_id, 1);142if (result != 1) {143err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)",144result, __func__, __FILE__, __LINE__);145}146147/*148* Several events should be received:149* - NOTE_FORK (from child)150* - NOTE_CHILD (from grandchild)151* - NOTE_EXIT (from grandchild)152* - NOTE_EXIT (from child)153*154* The NOTE_FORK and NOTE_EXIT from the child could be combined into a155* single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must156* not be combined.157*158* The loop continues until no events are received within a 5 second159* period, at which point it is assumed that no more will be coming. The160* loop is deliberately designed to attempt to get events even after all161* the expected ones are received in case some spurious events are162* generated as well as the expected ones.163*/164{165int child_exit = 0;166int child_fork = 0;167int gchild_exit = 0;168int gchild_note = 0;169pid_t gchild_pid = -1;170int done = 0;171char *kev_str;172173while (!done)174{175int handled = 0;176struct kevent *kevp;177178kevp = kevent_get_timeout(kqfd, 5);179if (kevp == NULL) {180done = 1;181} else {182kev_str = kevent_to_str(kevp);183printf(" -- Received kevent: %s\n", kev_str);184free(kev_str);185186if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) {187errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp));188}189190if (kevp->fflags & NOTE_CHILD) {191if (kevp->data == pid) {192if (!gchild_note) {193++gchild_note;194gchild_pid = kevp->ident;195++handled;196} else {197errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp));198}199}200}201202if (kevp->fflags & NOTE_EXIT) {203if ((kevp->ident == (uintptr_t)pid) && (!child_exit)) {204++child_exit;205++handled;206} else if ((kevp->ident == (uintptr_t)gchild_pid) && (!gchild_exit)) {207++gchild_exit;208++handled;209} else {210errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp));211}212}213214if (kevp->fflags & NOTE_FORK) {215if ((kevp->ident == (uintptr_t)pid) && (!child_fork)) {216++child_fork;217++handled;218} else {219errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp));220}221}222223if (!handled) {224errx(1, "Spurious kevent: %s", kevent_to_str(kevp));225}226227free(kevp);228}229}230231/* Make sure all expected events were received. */232if (child_exit && child_fork && gchild_exit && gchild_note) {233printf(" -- Received all expected events.\n");234} else {235errx(1, "Did not receive all expected events.");236}237}238239success();240}241242#ifdef TODO243static void244event_trigger(void)245{246struct kevent kev;247pid_t pid;248249test_begin("kevent(EVFILT_PROC, wait)");250251/* Create a child that waits to be killed and then exits */252pid = fork();253if (pid == 0) {254pause();255printf(" -- child caught signal, exiting\n");256exit(2);257}258printf(" -- child created (pid %d)\n", (int) pid);259260test_no_kevents();261kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);262263/* Cause the child to exit, then retrieve the event */264printf(" -- killing process %d\n", (int) pid);265if (kill(pid, SIGUSR1) < 0)266err(1, "kill");267kevent_cmp(&kev, kevent_get(kqfd));268test_no_kevents();269270success();271}272273static void274test_kevent_signal_disable(void)275{276const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";277struct kevent kev;278279test_begin(test_id);280281EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);282if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)283err(1, "%s", test_id);284285/* Block SIGUSR1, then send it to ourselves */286sigset_t mask;287sigemptyset(&mask);288sigaddset(&mask, SIGUSR1);289if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)290err(1, "sigprocmask");291if (kill(getpid(), SIGKILL) < 0)292err(1, "kill");293294test_no_kevents();295296success();297}298299void300test_kevent_signal_enable(void)301{302const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";303struct kevent kev;304305test_begin(test_id);306307EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);308if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)309err(1, "%s", test_id);310311/* Block SIGUSR1, then send it to ourselves */312sigset_t mask;313sigemptyset(&mask);314sigaddset(&mask, SIGUSR1);315if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)316err(1, "sigprocmask");317if (kill(getpid(), SIGUSR1) < 0)318err(1, "kill");319320kev.flags = EV_ADD | EV_CLEAR;321#if LIBKQUEUE322kev.data = 1; /* WORKAROUND */323#else324kev.data = 2; // one extra time from test_kevent_signal_disable()325#endif326kevent_cmp(&kev, kevent_get(kqfd));327328/* Delete the watch */329kev.flags = EV_DELETE;330if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)331err(1, "%s", test_id);332333success();334}335336void337test_kevent_signal_del(void)338{339const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";340struct kevent kev;341342test_begin(test_id);343344/* Delete the kevent */345EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);346if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)347err(1, "%s", test_id);348349/* Block SIGUSR1, then send it to ourselves */350sigset_t mask;351sigemptyset(&mask);352sigaddset(&mask, SIGUSR1);353if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)354err(1, "sigprocmask");355if (kill(getpid(), SIGUSR1) < 0)356err(1, "kill");357358test_no_kevents();359success();360}361362void363test_kevent_signal_oneshot(void)364{365const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";366struct kevent kev;367368test_begin(test_id);369370EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);371if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)372err(1, "%s", test_id);373374/* Block SIGUSR1, then send it to ourselves */375sigset_t mask;376sigemptyset(&mask);377sigaddset(&mask, SIGUSR1);378if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)379err(1, "sigprocmask");380if (kill(getpid(), SIGUSR1) < 0)381err(1, "kill");382383kev.flags |= EV_CLEAR;384kev.data = 1;385kevent_cmp(&kev, kevent_get(kqfd));386387/* Send another one and make sure we get no events */388if (kill(getpid(), SIGUSR1) < 0)389err(1, "kill");390test_no_kevents();391392success();393}394#endif395396void397test_evfilt_proc(void)398{399kqfd = kqueue();400401signal(SIGUSR1, sig_handler);402403add_and_delete();404proc_track(0); /* Run without sleeping before children exit. */405proc_track(1); /* Sleep a bit in the children before exiting. */406407#if TODO408event_trigger();409#endif410411signal(SIGUSR1, SIG_DFL);412413#if TODO414test_kevent_signal_add();415test_kevent_signal_del();416test_kevent_signal_get();417test_kevent_signal_disable();418test_kevent_signal_enable();419test_kevent_signal_oneshot();420#endif421close(kqfd);422}423424425