1 /* $OpenBSD: kqueue-process.c,v 1.13 2018/08/03 15:19:44 visa Exp $ */
2 /*
3 * Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain
4 */
5
6 #include <sys/types.h>
7 #include <sys/event.h>
8 #include <sys/time.h>
9 #include <sys/wait.h>
10
11 #include <err.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #include "main.h"
18
19 static int process_child(void);
20
21 static int pfd1[2];
22 static int pfd2[2];
23
24 int
do_process(void)25 do_process(void)
26 {
27 struct kevent ke;
28 struct timespec ts;
29 int kq, status;
30 pid_t pid, pid2;
31 int didfork, didchild;
32 int i;
33 char ch = 0;
34
35 /*
36 * Timeout in case something doesn't work.
37 */
38 ts.tv_sec = 10;
39 ts.tv_nsec = 0;
40
41 ASS((kq = kqueue()) >= 0,
42 warn("kqueue"));
43
44 /* Open pipes for synchronizing the children with the parent. */
45 if (pipe(pfd1) == -1)
46 err(1, "pipe 1");
47 if (pipe(pfd2) == -1)
48 err(1, "pipe 2");
49
50 switch ((pid = fork())) {
51 case -1:
52 err(1, "fork");
53 case 0:
54 _exit(process_child());
55 }
56
57 EV_SET(&ke, pid, EVFILT_PROC, EV_ADD|EV_ENABLE|EV_CLEAR,
58 NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_TRACK, 0, NULL);
59 ASS(kevent(kq, &ke, 1, NULL, 0, NULL) == 0,
60 warn("can't register events on kqueue"));
61
62 /* negative case */
63 EV_SET(&ke, pid + (1ULL << 30), EVFILT_PROC, EV_ADD|EV_ENABLE|EV_CLEAR,
64 NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_TRACK, 0, NULL);
65 ASS(kevent(kq, &ke, 1, NULL, 0, NULL) != 0,
66 warnx("can register bogus pid on kqueue"));
67 ASS(errno == ESRCH,
68 warn("register bogus pid on kqueue returned wrong error"));
69
70 ASS(write(pfd1[1], &ch, 1) == 1,
71 warn("write sync 1"));
72
73 didfork = didchild = 0;
74
75 pid2 = -1;
76 for (i = 0; i < 2; i++) {
77 ASS(kevent(kq, NULL, 0, &ke, 1, &ts) == 1,
78 warnx("didn't receive event"));
79 ASSX(ke.filter == EVFILT_PROC);
80 switch (ke.fflags) {
81 case NOTE_CHILD:
82 didchild = 1;
83 ASSX((pid_t)ke.data == pid);
84 pid2 = ke.ident;
85 fprintf(stderr, "child %d (from %d)\n", pid2, pid);
86 break;
87 case NOTE_FORK:
88 didfork = 1;
89 ASSX(ke.ident == pid);
90 fprintf(stderr, "fork\n");
91 break;
92 case NOTE_TRACKERR:
93 errx(1, "child tracking failed due to resource shortage");
94 default:
95 errx(1, "kevent returned weird event 0x%x pid %d",
96 ke.fflags, (pid_t)ke.ident);
97 }
98 }
99
100 ASSX(pid2 != -1);
101
102 /* Both children now sleeping. */
103
104 ASSX(didchild == 1);
105 ASSX(didfork == 1);
106
107 ASS(write(pfd2[1], &ch, 1) == 1,
108 warn("write sync 2.1"));
109 ASS(write(pfd1[1], &ch, 1) == 1,
110 warn("write sync 2"));
111
112 /*
113 * Wait for child's exit. It also implies child-child has exited.
114 * This should ensure that NOTE_EXIT has been posted for both children.
115 * Child-child's events should get aggregated.
116 */
117 if (wait(&status) < 0)
118 err(1, "wait");
119
120 for (i = 0; i < 2; i++) {
121 ASS(kevent(kq, NULL, 0, &ke, 1, &ts) == 1,
122 warnx("didn't receive event"));
123 ASSX(ke.filter == EVFILT_PROC);
124 switch (ke.fflags) {
125 case NOTE_EXIT:
126 ASSX((pid_t)ke.ident == pid);
127 fprintf(stderr, "child exit %d\n", pid);
128 break;
129 case NOTE_EXEC | NOTE_EXIT:
130 ASSX((pid_t)ke.ident == pid2);
131 fprintf(stderr, "child-child exec/exit %d\n", pid2);
132 break;
133 default:
134 errx(1, "kevent returned weird event 0x%x pid %d",
135 ke.fflags, (pid_t)ke.ident);
136 }
137 }
138
139 if (!WIFEXITED(status))
140 errx(1, "child didn't exit?");
141
142 close(kq);
143 return (WEXITSTATUS(status) != 0);
144 }
145
146 static int
process_child(void)147 process_child(void)
148 {
149 int status;
150 char ch;
151
152 ASS(read(pfd1[0], &ch, 1) == 1,
153 warn("read sync 1"));
154
155 /* fork and see if tracking works. */
156 switch (fork()) {
157 case -1:
158 err(1, "fork");
159 case 0:
160 ASS(read(pfd2[0], &ch, 1) == 1,
161 warn("read sync 2.1"));
162 execl("/usr/bin/true", "true", (char *)NULL);
163 err(1, "execl(true)");
164 }
165
166 ASS(read(pfd1[0], &ch, 1) == 1,
167 warn("read sync 2"));
168
169 if (wait(&status) < 0)
170 err(1, "wait 2");
171 if (!WIFEXITED(status))
172 errx(1, "child-child didn't exit?");
173
174 return 0;
175 }
176