Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/kqueue/libkqueue/proc.c
39566 views
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
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
425