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