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
17#include <sys/stat.h>
18
19#include <err.h>
20
21#include "config.h"
22#include "common.h"
23
24static int sigusr1_caught = 0;
25
26
27static void
28sig_handler(__unused int signum)
29{
30    sigusr1_caught = 1;
31}
32
33static void
34add_and_delete(void)
35{
36    struct kevent kev;
37    pid_t pid;
38
39    /* Create a child that waits to be killed and then exits */
40    pid = fork();
41    if (pid == 0) {
42        struct stat s;
43        if (fstat(kqfd, &s) != -1)
44            errx(1, "kqueue inherited across fork! (%s() at %s:%d)",
45                __func__, __FILE__, __LINE__);
46
47        pause();
48        exit(2);
49    }
50    printf(" -- child created (pid %d)\n", (int) pid);
51
52    test_begin("kevent(EVFILT_PROC, EV_ADD)");
53
54    test_no_kevents();
55    kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
56    test_no_kevents();
57
58    success();
59
60    test_begin("kevent(EVFILT_PROC, EV_DELETE)");
61
62    sleep(1);
63    test_no_kevents();
64    kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
65    if (kill(pid, SIGKILL) < 0)
66        err(1, "kill");
67    sleep(1);
68    test_no_kevents();
69
70    success();
71
72}
73
74static void
75proc_track(int sleep_time)
76{
77    char test_id[64];
78    struct kevent kev;
79    pid_t pid;
80    int pipe_fd[2];
81    ssize_t result;
82
83    snprintf(test_id, sizeof(test_id),
84             "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time);
85    test_begin(test_id);
86    test_no_kevents();
87
88    if (pipe(pipe_fd)) {
89        err(1, "pipe (parent) failed! (%s() at %s:%d)",
90            __func__, __FILE__, __LINE__);
91    }
92
93    /* Create a child to track. */
94    pid = fork();
95    if (pid == 0) { /* Child */
96        pid_t grandchild = -1;
97
98        /*
99         * Give the parent a chance to start tracking us.
100         */
101        result = read(pipe_fd[1], test_id, 1);
102        if (result != 1) {
103            err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)",
104                result, __func__, __FILE__, __LINE__);
105        }
106
107        /*
108         * Spawn a grandchild that will immediately exit. If the kernel has bug
109         * 180385, the parent will see a kevent with both NOTE_CHILD and
110         * NOTE_EXIT. If that bug is fixed, it will see two separate kevents
111         * for those notes. Note that this triggers the conditions for
112         * detecting the bug quite reliably on a 1 CPU system (or if the test
113         * process is restricted to a single CPU), but may not trigger it on a
114         * multi-CPU system.
115         */
116        grandchild = fork();
117        if (grandchild == 0) { /* Grandchild */
118            if (sleep_time) sleep(sleep_time);
119            exit(1);
120        } else if (grandchild == -1) { /* Error */
121            err(1, "fork (grandchild) failed! (%s() at %s:%d)",
122                __func__, __FILE__, __LINE__);
123        } else { /* Child (Grandchild Parent) */
124            printf(" -- grandchild created (pid %d)\n", (int) grandchild);
125        }
126        if (sleep_time) sleep(sleep_time);
127        exit(0);
128    } else if (pid == -1) { /* Error */
129        err(1, "fork (child) failed! (%s() at %s:%d)",
130            __func__, __FILE__, __LINE__);
131    }
132
133    printf(" -- child created (pid %d)\n", (int) pid);
134
135    kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE,
136               NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK,
137               0, NULL);
138
139    printf(" -- tracking child (pid %d)\n", (int) pid);
140
141    /* Now that we're tracking the child, tell it to proceed. */
142    result = write(pipe_fd[0], test_id, 1);
143    if (result != 1) {
144        err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)",
145            result, __func__, __FILE__, __LINE__);
146    }
147
148    /*
149     * Several events should be received:
150     *  - NOTE_FORK (from child)
151     *  - NOTE_CHILD (from grandchild)
152     *  - NOTE_EXIT (from grandchild)
153     *  - NOTE_EXIT (from child)
154     *
155     * The NOTE_FORK and NOTE_EXIT from the child could be combined into a
156     * single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must
157     * not be combined.
158     *
159     * The loop continues until no events are received within a 5 second
160     * period, at which point it is assumed that no more will be coming. The
161     * loop is deliberately designed to attempt to get events even after all
162     * the expected ones are received in case some spurious events are
163     * generated as well as the expected ones.
164     */
165    {
166        int child_exit = 0;
167        int child_fork = 0;
168        int gchild_exit = 0;
169        int gchild_note = 0;
170        pid_t gchild_pid = -1;
171        int done = 0;
172        char *kev_str;
173
174        while (!done)
175        {
176            int handled = 0;
177            struct kevent *kevp;
178
179            kevp = kevent_get_timeout(kqfd, 5);
180            if (kevp == NULL) {
181                done = 1;
182            } else {
183                kev_str = kevent_to_str(kevp);
184                printf(" -- Received kevent: %s\n", kev_str);
185                free(kev_str);
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 == (uintptr_t)pid) && (!child_exit)) {
205                        ++child_exit;
206                        ++handled;
207                    } else if ((kevp->ident == (uintptr_t)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 == (uintptr_t)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
244static void
245event_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
274static void
275test_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
300void
301test_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
337void
338test_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
363void
364test_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
397void
398test_evfilt_proc(void)
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