proc.c revision 341275
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: stable/11/tests/sys/kqueue/libkqueue/proc.c 341275 2018-11-30 02:06:30Z dab $
17 */
18
19#include <sys/stat.h>
20
21#include <err.h>
22
23#include "config.h"
24#include "common.h"
25
26static int sigusr1_caught = 0;
27
28int kqfd;
29
30static void
31sig_handler(int signum)
32{
33    sigusr1_caught = 1;
34}
35
36static void
37add_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
77static void
78proc_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        char *kev_str;
176
177        while (!done)
178        {
179            int handled = 0;
180            struct kevent *kevp;
181
182            kevp = kevent_get_timeout(kqfd, 5);
183            if (kevp == NULL) {
184                done = 1;
185            } else {
186                kev_str = kevent_to_str(kevp);
187                printf(" -- Received kevent: %s\n", kev_str);
188                free(kev_str);
189
190                if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) {
191                    errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp));
192                }
193
194                if (kevp->fflags & NOTE_CHILD) {
195                    if (kevp->data == pid) {
196                        if (!gchild_note) {
197                            ++gchild_note;
198                            gchild_pid = kevp->ident;
199                            ++handled;
200                        } else {
201                            errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp));
202                        }
203                    }
204                }
205
206                if (kevp->fflags & NOTE_EXIT) {
207                    if ((kevp->ident == pid) && (!child_exit)) {
208                        ++child_exit;
209                        ++handled;
210                    } else if ((kevp->ident == gchild_pid) && (!gchild_exit)) {
211                        ++gchild_exit;
212                        ++handled;
213                    } else {
214                        errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp));
215                    }
216                }
217
218                if (kevp->fflags & NOTE_FORK) {
219                    if ((kevp->ident == pid) && (!child_fork)) {
220                        ++child_fork;
221                        ++handled;
222                    } else {
223                        errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp));
224                    }
225                }
226
227                if (!handled) {
228                    errx(1, "Spurious kevent: %s", kevent_to_str(kevp));
229                }
230
231                free(kevp);
232            }
233        }
234
235        /* Make sure all expected events were received. */
236        if (child_exit && child_fork && gchild_exit && gchild_note) {
237            printf(" -- Received all expected events.\n");
238        } else {
239            errx(1, "Did not receive all expected events.");
240        }
241    }
242
243    success();
244}
245
246#ifdef TODO
247static void
248event_trigger(void)
249{
250    struct kevent kev;
251    pid_t pid;
252
253    test_begin("kevent(EVFILT_PROC, wait)");
254
255    /* Create a child that waits to be killed and then exits */
256    pid = fork();
257    if (pid == 0) {
258        pause();
259        printf(" -- child caught signal, exiting\n");
260        exit(2);
261    }
262    printf(" -- child created (pid %d)\n", (int) pid);
263
264    test_no_kevents();
265    kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
266
267    /* Cause the child to exit, then retrieve the event */
268    printf(" -- killing process %d\n", (int) pid);
269    if (kill(pid, SIGUSR1) < 0)
270        err(1, "kill");
271    kevent_cmp(&kev, kevent_get(kqfd));
272    test_no_kevents();
273
274    success();
275}
276
277void
278test_kevent_signal_disable(void)
279{
280    const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
281    struct kevent kev;
282
283    test_begin(test_id);
284
285    EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
286    if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
287        err(1, "%s", test_id);
288
289    /* Block SIGUSR1, then send it to ourselves */
290    sigset_t mask;
291    sigemptyset(&mask);
292    sigaddset(&mask, SIGUSR1);
293    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
294        err(1, "sigprocmask");
295    if (kill(getpid(), SIGKILL) < 0)
296        err(1, "kill");
297
298    test_no_kevents();
299
300    success();
301}
302
303void
304test_kevent_signal_enable(void)
305{
306    const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
307    struct kevent kev;
308
309    test_begin(test_id);
310
311    EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
312    if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
313        err(1, "%s", test_id);
314
315    /* Block SIGUSR1, then send it to ourselves */
316    sigset_t mask;
317    sigemptyset(&mask);
318    sigaddset(&mask, SIGUSR1);
319    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
320        err(1, "sigprocmask");
321    if (kill(getpid(), SIGUSR1) < 0)
322        err(1, "kill");
323
324    kev.flags = EV_ADD | EV_CLEAR;
325#if LIBKQUEUE
326    kev.data = 1; /* WORKAROUND */
327#else
328    kev.data = 2; // one extra time from test_kevent_signal_disable()
329#endif
330    kevent_cmp(&kev, kevent_get(kqfd));
331
332    /* Delete the watch */
333    kev.flags = EV_DELETE;
334    if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
335        err(1, "%s", test_id);
336
337    success();
338}
339
340void
341test_kevent_signal_del(void)
342{
343    const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
344    struct kevent kev;
345
346    test_begin(test_id);
347
348    /* Delete the kevent */
349    EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
350    if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
351        err(1, "%s", test_id);
352
353    /* Block SIGUSR1, then send it to ourselves */
354    sigset_t mask;
355    sigemptyset(&mask);
356    sigaddset(&mask, SIGUSR1);
357    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
358        err(1, "sigprocmask");
359    if (kill(getpid(), SIGUSR1) < 0)
360        err(1, "kill");
361
362    test_no_kevents();
363    success();
364}
365
366void
367test_kevent_signal_oneshot(void)
368{
369    const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";
370    struct kevent kev;
371
372    test_begin(test_id);
373
374    EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
375    if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
376        err(1, "%s", test_id);
377
378    /* Block SIGUSR1, then send it to ourselves */
379    sigset_t mask;
380    sigemptyset(&mask);
381    sigaddset(&mask, SIGUSR1);
382    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
383        err(1, "sigprocmask");
384    if (kill(getpid(), SIGUSR1) < 0)
385        err(1, "kill");
386
387    kev.flags |= EV_CLEAR;
388    kev.data = 1;
389    kevent_cmp(&kev, kevent_get(kqfd));
390
391    /* Send another one and make sure we get no events */
392    if (kill(getpid(), SIGUSR1) < 0)
393        err(1, "kill");
394    test_no_kevents();
395
396    success();
397}
398#endif
399
400void
401test_evfilt_proc()
402{
403    kqfd = kqueue();
404
405    signal(SIGUSR1, sig_handler);
406
407    add_and_delete();
408    proc_track(0); /* Run without sleeping before children exit. */
409    proc_track(1); /* Sleep a bit in the children before exiting. */
410
411#if TODO
412    event_trigger();
413#endif
414
415    signal(SIGUSR1, SIG_DFL);
416
417#if TODO
418    test_kevent_signal_add();
419    test_kevent_signal_del();
420    test_kevent_signal_get();
421    test_kevent_signal_disable();
422    test_kevent_signal_enable();
423    test_kevent_signal_oneshot();
424#endif
425    close(kqfd);
426}
427