1 /*
2 * Copyright (c) 2009 Mark Heily <[email protected]>
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
sig_handler(__unused int signum)30 sig_handler(__unused int signum)
31 {
32 sigusr1_caught = 1;
33 }
34
35 static void
add_and_delete(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
proc_track(int sleep_time)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
event_trigger(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
test_kevent_signal_disable(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
test_kevent_signal_enable(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
test_kevent_signal_del(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
test_kevent_signal_oneshot(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
test_evfilt_proc(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