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
sig_handler(__unused int signum)28 sig_handler(__unused int signum)
29 {
30 sigusr1_caught = 1;
31 }
32
33 static void
add_and_delete(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
proc_track(int sleep_time)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
event_trigger(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
test_kevent_signal_disable(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
test_kevent_signal_enable(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
test_kevent_signal_del(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
test_kevent_signal_oneshot(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
test_evfilt_proc(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