xref: /freebsd/tests/sys/kqueue/libkqueue/proc.c (revision e1c4c8dd8d2d10b6104f06856a77bd5b4813a801)
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 
24 static int sigusr1_caught = 0;
25 
26 
27 static void
28 sig_handler(__unused int signum)
29 {
30     sigusr1_caught = 1;
31 }
32 
33 static void
34 add_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 
74 static void
75 proc_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
244 static void
245 event_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 
274 static void
275 test_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 
300 void
301 test_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 
337 void
338 test_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 
363 void
364 test_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 
397 void
398 test_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