xref: /freebsd/tests/sys/kqueue/libkqueue/proc.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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$
17  */
18 
19 #include <sys/stat.h>
20 
21 #include <err.h>
22 
23 #include "config.h"
24 #include "common.h"
25 
26 static int sigusr1_caught = 0;
27 
28 
29 static void
30 sig_handler(__unused int signum)
31 {
32     sigusr1_caught = 1;
33 }
34 
35 static void
36 add_and_delete(void)
37 {
38     struct kevent kev;
39     pid_t pid;
40 
41     /* Create a child that waits to be killed and then exits */
42     pid = fork();
43     if (pid == 0) {
44         struct stat s;
45         if (fstat(kqfd, &s) != -1)
46             errx(1, "kqueue inherited across fork! (%s() at %s:%d)",
47                 __func__, __FILE__, __LINE__);
48 
49         pause();
50         exit(2);
51     }
52     printf(" -- child created (pid %d)\n", (int) pid);
53 
54     test_begin("kevent(EVFILT_PROC, EV_ADD)");
55 
56     test_no_kevents();
57     kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
58     test_no_kevents();
59 
60     success();
61 
62     test_begin("kevent(EVFILT_PROC, EV_DELETE)");
63 
64     sleep(1);
65     test_no_kevents();
66     kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
67     if (kill(pid, SIGKILL) < 0)
68         err(1, "kill");
69     sleep(1);
70     test_no_kevents();
71 
72     success();
73 
74 }
75 
76 static void
77 proc_track(int sleep_time)
78 {
79     char test_id[64];
80     struct kevent kev;
81     pid_t pid;
82     int pipe_fd[2];
83     ssize_t result;
84 
85     snprintf(test_id, sizeof(test_id),
86              "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time);
87     test_begin(test_id);
88     test_no_kevents();
89 
90     if (pipe(pipe_fd)) {
91         err(1, "pipe (parent) failed! (%s() at %s:%d)",
92             __func__, __FILE__, __LINE__);
93     }
94 
95     /* Create a child to track. */
96     pid = fork();
97     if (pid == 0) { /* Child */
98         pid_t grandchild = -1;
99 
100         /*
101          * Give the parent a chance to start tracking us.
102          */
103         result = read(pipe_fd[1], test_id, 1);
104         if (result != 1) {
105             err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)",
106                 result, __func__, __FILE__, __LINE__);
107         }
108 
109         /*
110          * Spawn a grandchild that will immediately exit. If the kernel has bug
111          * 180385, the parent will see a kevent with both NOTE_CHILD and
112          * NOTE_EXIT. If that bug is fixed, it will see two separate kevents
113          * for those notes. Note that this triggers the conditions for
114          * detecting the bug quite reliably on a 1 CPU system (or if the test
115          * process is restricted to a single CPU), but may not trigger it on a
116          * multi-CPU system.
117          */
118         grandchild = fork();
119         if (grandchild == 0) { /* Grandchild */
120             if (sleep_time) sleep(sleep_time);
121             exit(1);
122         } else if (grandchild == -1) { /* Error */
123             err(1, "fork (grandchild) failed! (%s() at %s:%d)",
124                 __func__, __FILE__, __LINE__);
125         } else { /* Child (Grandchild Parent) */
126             printf(" -- grandchild created (pid %d)\n", (int) grandchild);
127         }
128         if (sleep_time) sleep(sleep_time);
129         exit(0);
130     } else if (pid == -1) { /* Error */
131         err(1, "fork (child) failed! (%s() at %s:%d)",
132             __func__, __FILE__, __LINE__);
133     }
134 
135     printf(" -- child created (pid %d)\n", (int) pid);
136 
137     kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE,
138                NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK,
139                0, NULL);
140 
141     printf(" -- tracking child (pid %d)\n", (int) pid);
142 
143     /* Now that we're tracking the child, tell it to proceed. */
144     result = write(pipe_fd[0], test_id, 1);
145     if (result != 1) {
146         err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)",
147             result, __func__, __FILE__, __LINE__);
148     }
149 
150     /*
151      * Several events should be received:
152      *  - NOTE_FORK (from child)
153      *  - NOTE_CHILD (from grandchild)
154      *  - NOTE_EXIT (from grandchild)
155      *  - NOTE_EXIT (from child)
156      *
157      * The NOTE_FORK and NOTE_EXIT from the child could be combined into a
158      * single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must
159      * not be combined.
160      *
161      * The loop continues until no events are received within a 5 second
162      * period, at which point it is assumed that no more will be coming. The
163      * loop is deliberately designed to attempt to get events even after all
164      * the expected ones are received in case some spurious events are
165      * generated as well as the expected ones.
166      */
167     {
168         int child_exit = 0;
169         int child_fork = 0;
170         int gchild_exit = 0;
171         int gchild_note = 0;
172         pid_t gchild_pid = -1;
173         int done = 0;
174         char *kev_str;
175 
176         while (!done)
177         {
178             int handled = 0;
179             struct kevent *kevp;
180 
181             kevp = kevent_get_timeout(kqfd, 5);
182             if (kevp == NULL) {
183                 done = 1;
184             } else {
185                 kev_str = kevent_to_str(kevp);
186                 printf(" -- Received kevent: %s\n", kev_str);
187                 free(kev_str);
188 
189                 if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) {
190                     errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp));
191                 }
192 
193                 if (kevp->fflags & NOTE_CHILD) {
194                     if (kevp->data == pid) {
195                         if (!gchild_note) {
196                             ++gchild_note;
197                             gchild_pid = kevp->ident;
198                             ++handled;
199                         } else {
200                             errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp));
201                         }
202                     }
203                 }
204 
205                 if (kevp->fflags & NOTE_EXIT) {
206                     if ((kevp->ident == (uintptr_t)pid) && (!child_exit)) {
207                         ++child_exit;
208                         ++handled;
209                     } else if ((kevp->ident == (uintptr_t)gchild_pid) && (!gchild_exit)) {
210                         ++gchild_exit;
211                         ++handled;
212                     } else {
213                         errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp));
214                     }
215                 }
216 
217                 if (kevp->fflags & NOTE_FORK) {
218                     if ((kevp->ident == (uintptr_t)pid) && (!child_fork)) {
219                         ++child_fork;
220                         ++handled;
221                     } else {
222                         errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp));
223                     }
224                 }
225 
226                 if (!handled) {
227                     errx(1, "Spurious kevent: %s", kevent_to_str(kevp));
228                 }
229 
230                 free(kevp);
231             }
232         }
233 
234         /* Make sure all expected events were received. */
235         if (child_exit && child_fork && gchild_exit && gchild_note) {
236             printf(" -- Received all expected events.\n");
237         } else {
238             errx(1, "Did not receive all expected events.");
239         }
240     }
241 
242     success();
243 }
244 
245 #ifdef TODO
246 static void
247 event_trigger(void)
248 {
249     struct kevent kev;
250     pid_t pid;
251 
252     test_begin("kevent(EVFILT_PROC, wait)");
253 
254     /* Create a child that waits to be killed and then exits */
255     pid = fork();
256     if (pid == 0) {
257         pause();
258         printf(" -- child caught signal, exiting\n");
259         exit(2);
260     }
261     printf(" -- child created (pid %d)\n", (int) pid);
262 
263     test_no_kevents();
264     kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
265 
266     /* Cause the child to exit, then retrieve the event */
267     printf(" -- killing process %d\n", (int) pid);
268     if (kill(pid, SIGUSR1) < 0)
269         err(1, "kill");
270     kevent_cmp(&kev, kevent_get(kqfd));
271     test_no_kevents();
272 
273     success();
274 }
275 
276 static void
277 test_kevent_signal_disable(void)
278 {
279     const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
280     struct kevent kev;
281 
282     test_begin(test_id);
283 
284     EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
285     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
286         err(1, "%s", test_id);
287 
288     /* Block SIGUSR1, then send it to ourselves */
289     sigset_t mask;
290     sigemptyset(&mask);
291     sigaddset(&mask, SIGUSR1);
292     if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
293         err(1, "sigprocmask");
294     if (kill(getpid(), SIGKILL) < 0)
295         err(1, "kill");
296 
297     test_no_kevents();
298 
299     success();
300 }
301 
302 void
303 test_kevent_signal_enable(void)
304 {
305     const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
306     struct kevent kev;
307 
308     test_begin(test_id);
309 
310     EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
311     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
312         err(1, "%s", test_id);
313 
314     /* Block SIGUSR1, then send it to ourselves */
315     sigset_t mask;
316     sigemptyset(&mask);
317     sigaddset(&mask, SIGUSR1);
318     if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
319         err(1, "sigprocmask");
320     if (kill(getpid(), SIGUSR1) < 0)
321         err(1, "kill");
322 
323     kev.flags = EV_ADD | EV_CLEAR;
324 #if LIBKQUEUE
325     kev.data = 1; /* WORKAROUND */
326 #else
327     kev.data = 2; // one extra time from test_kevent_signal_disable()
328 #endif
329     kevent_cmp(&kev, kevent_get(kqfd));
330 
331     /* Delete the watch */
332     kev.flags = EV_DELETE;
333     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
334         err(1, "%s", test_id);
335 
336     success();
337 }
338 
339 void
340 test_kevent_signal_del(void)
341 {
342     const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
343     struct kevent kev;
344 
345     test_begin(test_id);
346 
347     /* Delete the kevent */
348     EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
349     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
350         err(1, "%s", test_id);
351 
352     /* Block SIGUSR1, then send it to ourselves */
353     sigset_t mask;
354     sigemptyset(&mask);
355     sigaddset(&mask, SIGUSR1);
356     if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
357         err(1, "sigprocmask");
358     if (kill(getpid(), SIGUSR1) < 0)
359         err(1, "kill");
360 
361     test_no_kevents();
362     success();
363 }
364 
365 void
366 test_kevent_signal_oneshot(void)
367 {
368     const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";
369     struct kevent kev;
370 
371     test_begin(test_id);
372 
373     EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
374     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
375         err(1, "%s", test_id);
376 
377     /* Block SIGUSR1, then send it to ourselves */
378     sigset_t mask;
379     sigemptyset(&mask);
380     sigaddset(&mask, SIGUSR1);
381     if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
382         err(1, "sigprocmask");
383     if (kill(getpid(), SIGUSR1) < 0)
384         err(1, "kill");
385 
386     kev.flags |= EV_CLEAR;
387     kev.data = 1;
388     kevent_cmp(&kev, kevent_get(kqfd));
389 
390     /* Send another one and make sure we get no events */
391     if (kill(getpid(), SIGUSR1) < 0)
392         err(1, "kill");
393     test_no_kevents();
394 
395     success();
396 }
397 #endif
398 
399 void
400 test_evfilt_proc(void)
401 {
402     kqfd = kqueue();
403 
404     signal(SIGUSR1, sig_handler);
405 
406     add_and_delete();
407     proc_track(0); /* Run without sleeping before children exit. */
408     proc_track(1); /* Sleep a bit in the children before exiting. */
409 
410 #if TODO
411     event_trigger();
412 #endif
413 
414     signal(SIGUSR1, SIG_DFL);
415 
416 #if TODO
417     test_kevent_signal_add();
418     test_kevent_signal_del();
419     test_kevent_signal_get();
420     test_kevent_signal_disable();
421     test_kevent_signal_enable();
422     test_kevent_signal_oneshot();
423 #endif
424     close(kqfd);
425 }
426