xref: /freebsd/tests/sys/kqueue/libkqueue/proc.c (revision 5ab1c5846ff41be24b1f6beb0317bf8258cd4409)
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 int kqfd;
29 
30 static void
31 sig_handler(int signum)
32 {
33     sigusr1_caught = 1;
34 }
35 
36 static void
37 add_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 
77 static void
78 proc_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
247 static void
248 event_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 
277 void
278 test_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 
303 void
304 test_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 
340 void
341 test_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 
366 void
367 test_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 
400 void
401 test_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