]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/unit_tests/ptrace_test_12507045_src/ptrace_test.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / tools / tests / unit_tests / ptrace_test_12507045_src / ptrace_test.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <signal.h>
5 #include <string.h>
6 #include <err.h>
7 #include <sys/types.h>
8 #include <sys/time.h>
9 #include <sys/event.h>
10 #include <sys/ptrace.h>
11 #include <errno.h>
12 #include <sys/proc.h>
13 #include <libproc.h>
14 #include <stdarg.h>
15
16 /*
17 * We create a process hierarchy of:
18 *
19 * grandparent -> parent -> child
20 * \
21 * \--> debugger
22 *
23 * When the debugger calls ptrace(2) on child, it
24 * is temporarily reparented.
25 *
26 * We may also create a hierarchy of:
27 *
28 * grandparent -> parent/debugger -> child
29 *
30 */
31
32 typedef enum {
33 eParentExitAfterWaitpid = 0,
34 eParentExitAfterWaitpidAndSIGCHLD,
35 eParentExitBeforeWaitpid,
36 eParentExitAfterDebuggerAttach,
37 eParentExitBeforeDebuggerAttach,
38 eParentIsDebugger
39 } parent_exit_t;
40
41 typedef enum {
42 eDebuggerExitAfterKillAndWaitpid = 0,
43 eDebuggerExitAfterKillWithoutWaitpid,
44 eDebuggerExitAfterDetach,
45 eDebuggerExitWithoutDetach
46 } debugger_exit_t;
47
48 void do_grandparent(pid_t parent, pid_t child, pid_t debugger, debugger_exit_t debugger_exit_time) __attribute__((noreturn));
49 void do_parent(pid_t child, pid_t debugger, parent_exit_t parent_exit_time, debugger_exit_t debugger_exit_time) __attribute__((noreturn));
50 void do_child(void) __attribute__((noreturn));
51 void do_debugger(pid_t child, debugger_exit_t debugger_exit_time) __attribute__((noreturn));
52
53 bool iszombie(pid_t p);
54
55 char *str_kev_filter(int filter);
56 char *str_kev_flags(int filter, uint16_t flags);
57 char *str_kev_fflags(int filter, uint32_t fflags);
58 char *str_kev_data(int filter, uint32_t fflags, int64_t data, uint64_t udata);
59 char *print_exit(pid_t p, int stat_loc);
60
61 void logline(const char *format, ...);
62
63 void usage(void);
64 int test_all_permutations(void);
65 void test(parent_exit_t parent_exit_time, debugger_exit_t debugger_exit_time) __attribute__((noreturn));
66
67 int main(int argc, char *argv[]) {
68 int ch;
69
70 int parent_exit_time = -1;
71 int debugger_exit_time = -1;
72
73 while ((ch = getopt(argc, argv, "p:w:")) != -1) {
74 switch (ch) {
75 case 'p':
76 parent_exit_time = atoi(optarg);
77 break;
78 case 'w':
79 debugger_exit_time = atoi(optarg);
80 break;
81 case '?':
82 default:
83 usage();
84 }
85 }
86
87 /* no explicit options, loop through them all */
88 if (parent_exit_time == -1 &&
89 debugger_exit_time == -1) {
90 return test_all_permutations();
91 }
92
93 if (parent_exit_time == -1 ||
94 debugger_exit_time == -1) {
95 usage();
96 }
97
98 test((parent_exit_t)parent_exit_time,
99 (debugger_exit_t)debugger_exit_time);
100
101 return 0; /* never reached */
102 }
103
104 void test(parent_exit_t parent_exit_time, debugger_exit_t debugger_exit_time)
105 {
106 pid_t parent, child, debugger;
107 int ret;
108 int fds[2];
109
110 /* pipe for parent to send child pid to grandparent */
111 ret = pipe(fds);
112 if (-1 == ret) {
113 err(1, "failed to create pipe");
114 }
115
116 parent = fork();
117 if (parent == 0) {
118 /* parent sub-branch */
119
120 ret = close(fds[0]);
121 if (ret == -1) {
122 err(1, "close read end of pipe");
123 }
124
125 child = fork();
126 if (child == 0) {
127 /* child */
128 ret = close(fds[1]);
129 if (ret == -1) {
130 err(1, "close write end of pipe");
131 }
132
133 do_child();
134 } else if (child == -1) {
135 err(1, "parent failed to fork child");
136 } else {
137 /* parent */
138 if (-1 == write(fds[1], &child, sizeof(child))) {
139 err(1, "writing child pid to grandparent");
140 }
141
142 if (parent_exit_time == eParentIsDebugger) {
143 debugger = -1;
144
145 if (-1 == write(fds[1], &debugger, sizeof(debugger))) {
146 err(1, "writing debugger pid to grandparent");
147 }
148 ret = close(fds[1]);
149 if (ret == -1) {
150 err(1, "close write end of pipe");
151 }
152
153 do_debugger(child, debugger_exit_time);
154 } else {
155 debugger = fork();
156 if (debugger == 0) {
157 /* debugger */
158 ret = close(fds[1]);
159 if (ret == -1) {
160 err(1, "close write end of pipe");
161 }
162
163 do_debugger(child, debugger_exit_time);
164 } else if (debugger == -1) {
165 err(1, "parent failed to fork debugger");
166 } else {
167 /* still parent */
168 if (-1 == write(fds[1], &debugger, sizeof(debugger))) {
169 err(1, "writing debugger pid to grandparent");
170 }
171 ret = close(fds[1]);
172 if (ret == -1) {
173 err(1, "close write end of pipe");
174 }
175
176 do_parent(child, debugger, parent_exit_time, debugger_exit_time);
177 }
178 }
179 }
180 } else if (parent == -1) {
181 err(1, "grandparent failed to fork parent");
182 } else {
183 ret = close(fds[1]);
184 if (ret == -1) {
185 err(1, "close write end of pipe");
186 }
187
188 if (-1 == read(fds[0], &child, sizeof(child))) {
189 err(1, "could not read child pid");
190 }
191
192 if (-1 == read(fds[0], &debugger, sizeof(debugger))) {
193 err(1, "could not read debugger pid");
194 }
195
196 ret = close(fds[0]);
197 if (ret == -1) {
198 err(1, "close read end of pipe");
199 }
200
201 do_grandparent(parent, child, debugger, debugger_exit_time);
202 }
203 }
204
205 void usage(void)
206 {
207 errx(1, "Usage: %s [-p <parent_exit_time> -w <debugger_exit_time>]", getprogname());
208 }
209
210 int test_all_permutations(void)
211 {
212 int p, w;
213 bool has_failure = false;
214
215 for (p = 0; p <= 5; p++) {
216 for (w = 0; w <= 3; w++) {
217 int testpid;
218 int ret;
219
220 testpid = fork();
221 if (testpid == 0) {
222 logline("-------------------------------------------------------");
223 logline("*** Executing self-test: %s -p %d -w %d",
224 getprogname(), p, w);
225 test((parent_exit_t)p,
226 (debugger_exit_t)w);
227 _exit(1); /* never reached */
228 } else if (testpid == -1) {
229 err(1, "failed to fork test pid");
230 } else {
231 int stat_loc;
232
233 ret = waitpid(testpid, &stat_loc, 0);
234 if (ret == -1)
235 err(1, "waitpid(%d) by test harness failed", testpid);
236
237 logline("test process: %s", print_exit(testpid, stat_loc));
238 if (!WIFEXITED(stat_loc) || (0 != WEXITSTATUS(stat_loc))) {
239 logline("FAILED TEST");
240 has_failure = true;
241 }
242 }
243 }
244 }
245
246 if (has_failure) {
247 logline("test failures found");
248 return 1;
249 }
250
251 return 0;
252 }
253
254 void do_grandparent(pid_t parent, pid_t child, pid_t debugger, debugger_exit_t debugger_exit_time)
255 {
256 pid_t result;
257 int stat_loc;
258 int exit_code = 0;
259 int kq;
260 int ret;
261 struct kevent64_s kev;
262 int neededdeathcount = (debugger != -1) ? 3 : 2;
263
264 setprogname("GRANDPARENT");
265
266 logline("grandparent pid %d has parent pid %d and child pid %d. waiting for parent process exit...", getpid(), parent, child);
267
268 /* make sure we can at least observe real child's exit */
269 kq = kqueue();
270 if (kq < 0)
271 err(1, "kqueue");
272
273 EV_SET64(&kev, child, EVFILT_PROC, EV_ADD|EV_ENABLE,
274 NOTE_EXIT, 0, child, 0, 0);
275 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
276 if (ret == -1)
277 err(1, "kevent64 EVFILT_PROC");
278
279 EV_SET64(&kev, parent, EVFILT_PROC, EV_ADD|EV_ENABLE,
280 NOTE_EXIT, 0, parent, 0, 0);
281 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
282 if (ret == -1)
283 err(1, "kevent64 EVFILT_PROC");
284
285 if (debugger != -1) {
286 EV_SET64(&kev, debugger, EVFILT_PROC, EV_ADD|EV_ENABLE,
287 NOTE_EXIT, 0, debugger, 0, 0);
288 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
289 if (ret == -1)
290 err(1, "kevent64 EVFILT_PROC");
291 }
292
293 EV_SET64(&kev, 5, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT,
294 NOTE_SECONDS, 5, 0, 0, 0);
295 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
296 if (ret == -1)
297 err(1, "kevent64 EVFILT_TIMER");
298
299 while(1) {
300
301 ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL);
302 if (ret == -1) {
303 if (errno == EINTR)
304 continue;
305 err(1, "kevent64");
306 } else if (ret == 0) {
307 break;
308 }
309
310 logline("kevent64 returned ident %llu filter %s fflags %s data %s",
311 kev.ident, str_kev_filter(kev.filter),
312 str_kev_fflags(kev.filter, kev.fflags),
313 str_kev_data(kev.filter, kev.fflags, kev.data, kev.udata));
314 if (kev.filter == EVFILT_PROC) {
315 if (child == kev.udata) {
316 neededdeathcount--;
317 } else if (parent == kev.udata) {
318 neededdeathcount--;
319 } else if ((debugger != -1) && (debugger == kev.udata)) {
320 neededdeathcount--;
321 }
322 } else if (kev.filter == EVFILT_TIMER) {
323 logline("timed out waiting for NOTE_EXIT");
324 exit_code = 1;
325 break;
326 }
327
328 if (neededdeathcount == 0) {
329 break;
330 }
331 }
332
333 result = waitpid(parent, &stat_loc, 0);
334 if (result == -1)
335 err(1, "waitpid(%d) by grandparent failed", parent);
336
337
338 logline("parent process: %s", print_exit(parent, stat_loc));
339 if (!WIFEXITED(stat_loc) || (0 != WEXITSTATUS(stat_loc))) {
340 exit_code = 1;
341 }
342
343 if (iszombie(parent)) {
344 logline("parent %d is now a zombie", parent);
345 exit_code = 1;
346 }
347
348 if (iszombie(child)) {
349 logline("child %d is now a zombie", child);
350 exit_code = 1;
351 }
352
353 if ((debugger != -1) && iszombie(debugger)) {
354 logline("debugger %d is now a zombie", debugger);
355 exit_code = 1;
356 }
357
358 exit(exit_code);
359 }
360
361 /*
362 * debugger will register kevents, wait for quorum on events, then exit
363 */
364 void do_parent(pid_t child, pid_t debugger, parent_exit_t parent_exit_time, debugger_exit_t debugger_exit_time)
365 {
366 int kq;
367 int ret;
368 struct kevent64_s kev;
369 int deathcount = 0;
370 int childsignalcount = 0;
371 int stat_loc;
372
373 setprogname("PARENT");
374
375 logline("parent pid %d has child pid %d and debugger pid %d. waiting for processes to exit...", getpid(), child, debugger);
376
377 kq = kqueue();
378 if (kq < 0)
379 err(1, "kqueue");
380
381 EV_SET64(&kev, child, EVFILT_PROC, EV_ADD|EV_ENABLE,
382 NOTE_EXIT|NOTE_EXITSTATUS|NOTE_EXIT_DETAIL|NOTE_FORK|NOTE_EXEC|NOTE_SIGNAL,
383 0, child, 0, 0);
384 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
385 if (ret == -1)
386 err(1, "kevent64 EVFILT_PROC");
387
388 EV_SET64(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD|EV_ENABLE,
389 0, 0, child, 0, 0);
390 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
391 if (ret == -1)
392 err(1, "kevent64 EVFILT_SIGNAL");
393
394 EV_SET64(&kev, 7, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT,
395 NOTE_SECONDS, 7, 0, 0, 0);
396 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
397 if (ret == -1)
398 err(1, "kevent64 EVFILT_TIMER");
399
400 while(1) {
401 ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL);
402 if (ret == -1) {
403 if (errno == EINTR)
404 continue;
405 err(1, "kevent64");
406 } else if (ret == 0) {
407 break;
408 }
409
410 logline("kevent64 returned ident %llu filter %s fflags %s data %s",
411 kev.ident, str_kev_filter(kev.filter),
412 str_kev_fflags(kev.filter, kev.fflags),
413 str_kev_data(kev.filter, kev.fflags, kev.data, kev.udata));
414 if (kev.filter == EVFILT_SIGNAL) {
415 /* must be SIGCHLD */
416 deathcount++;
417 } else if (kev.filter == EVFILT_PROC) {
418 if (child == kev.udata) {
419 if ((kev.fflags & (NOTE_EXIT|NOTE_EXITSTATUS)) == (NOTE_EXIT|NOTE_EXITSTATUS)) {
420 deathcount++;
421 } else if (kev.fflags & NOTE_SIGNAL) {
422 childsignalcount++;
423 if ((parent_exit_time == eParentExitAfterDebuggerAttach) && (childsignalcount >= 2)) {
424 /* second signal is attach */
425 logline("exiting because of eParentExitAfterDebuggerAttach");
426 exit(0);
427 }
428 } else if (kev.fflags & NOTE_FORK) {
429 if (parent_exit_time == eParentExitBeforeDebuggerAttach) {
430 logline("exiting because of eParentExitBeforeDebuggerAttach");
431 exit(0);
432 }
433 }
434 }
435 } else if (kev.filter == EVFILT_TIMER) {
436 errx(1, "timed out waiting for NOTE_EXIT");
437 }
438
439 if (deathcount >= (parent_exit_time == eParentExitAfterWaitpidAndSIGCHLD ? 2 : 1)) {
440 break;
441 }
442 }
443
444 if (parent_exit_time == eParentExitBeforeWaitpid) {
445 logline("exiting because of eParentExitBeforeWaitpid");
446 exit(0);
447 }
448
449 ret = waitpid(child, &stat_loc, 0);
450 if (ret == -1)
451 err(1, "waitpid(%d) by parent failed", child);
452
453 logline("child process: %s", print_exit(child, stat_loc));
454 if (!WIFSIGNALED(stat_loc) || (SIGKILL != WTERMSIG(stat_loc)))
455 errx(1, "child did not exit as expected");
456
457 ret = waitpid(debugger, &stat_loc, 0);
458 if (ret == -1)
459 err(1, "waitpid(%d) by parent failed", debugger);
460
461 logline("debugger process: %s", print_exit(debugger, stat_loc));
462 if (!WIFEXITED(stat_loc) || (0 != WEXITSTATUS(stat_loc)))
463 errx(1, "debugger did not exit as expected");
464
465 /* Received both SIGCHLD and NOTE_EXIT, as needed */
466 logline("exiting beacuse of eParentExitAfterWaitpid/eParentExitAfterWaitpidAndSIGCHLD");
467 exit(0);
468 }
469
470 /* child will spin waiting to be killed by debugger or parent or someone */
471 void do_child(void)
472 {
473 pid_t doublechild;
474 int ret;
475 setprogname("CHILD");
476
477 logline("child pid %d. waiting for external termination...", getpid());
478
479 usleep(500000);
480
481 doublechild = fork();
482 if (doublechild == 0) {
483 exit(0);
484 } else if (doublechild == -1) {
485 err(1, "doublechild");
486 } else {
487 ret = waitpid(doublechild, NULL, 0);
488 if (ret == -1)
489 err(1, "waitpid(%d) by parent failed", doublechild);
490 }
491
492 while (1) {
493 sleep(60);
494 }
495 }
496
497 /*
498 * debugger will register kevents, attach+kill child, wait for quorum on events,
499 * then exit.
500 */
501 void do_debugger(pid_t child, debugger_exit_t debugger_exit_time)
502 {
503 int kq;
504 int ret;
505 struct kevent64_s kev;
506 int deathcount = 0;
507 int stat_loc;
508
509 setprogname("DEBUGGER");
510
511 logline("debugger pid %d has child pid %d. waiting for process exit...", getpid(), child);
512
513 sleep(1);
514 fprintf(stderr, "\n");
515 ret = ptrace(PT_ATTACH, child, 0, 0);
516 if (ret == -1)
517 err(1, "ptrace(PT_ATTACH)");
518
519 ret = waitpid(child, &stat_loc, WUNTRACED);
520 if (ret == -1)
521 err(1, "waitpid(child, WUNTRACED)");
522
523 logline("child process stopped: %s", print_exit(child, stat_loc));
524
525 if (debugger_exit_time == eDebuggerExitWithoutDetach) {
526 logline("exiting because of eDebuggerExitWithoutDetach");
527 exit(0);
528 } else if (debugger_exit_time == eDebuggerExitAfterDetach) {
529 ret = ptrace(PT_DETACH, child, 0, 0);
530 if (ret == -1)
531 err(1, "ptrace(PT_DETACH)");
532
533 ret = kill(child, SIGKILL);
534 if (ret == -1)
535 err(1, "kill(SIGKILL)");
536
537 logline("exiting because of eDebuggerExitAfterDetach");
538 exit(0);
539 }
540
541 kq = kqueue();
542 if (kq < 0)
543 err(1, "kqueue");
544
545 EV_SET64(&kev, child, EVFILT_PROC, EV_ADD|EV_ENABLE,
546 NOTE_EXIT|NOTE_EXITSTATUS|NOTE_EXIT_DETAIL|NOTE_FORK|NOTE_EXEC|NOTE_SIGNAL,
547 0, child, 0, 0);
548 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
549 if (ret == -1)
550 err(1, "kevent64 EVFILT_PROC");
551
552 EV_SET64(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD|EV_ENABLE,
553 0, 0, child, 0, 0);
554 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL);
555 if (ret == -1)
556 err(1, "kevent64 EVFILT_SIGNAL");
557
558 sleep(1);
559 fprintf(stderr, "\n");
560 ret = ptrace(PT_KILL, child, 0, 0);
561 if (ret == -1)
562 err(1, "ptrace(PT_KILL)");
563
564 while(1) {
565 ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL);
566 if (ret == -1) {
567 if (errno == EINTR)
568 continue;
569 err(1, "kevent64");
570 } else if (ret == 0) {
571 continue;
572 }
573
574 logline("kevent64 returned ident %llu filter %s fflags %s data %s",
575 kev.ident, str_kev_filter(kev.filter),
576 str_kev_fflags(kev.filter, kev.fflags),
577 str_kev_data(kev.filter, kev.fflags, kev.data, kev.udata));
578 if (kev.filter == EVFILT_SIGNAL) {
579 /* must be SIGCHLD */
580 deathcount++;
581 } else if (kev.filter == EVFILT_PROC) {
582 if ((kev.fflags & (NOTE_EXIT|NOTE_EXITSTATUS)) == (NOTE_EXIT|NOTE_EXITSTATUS)) {
583 deathcount++;
584 }
585 }
586
587 if (deathcount >= 2) {
588 break;
589 }
590 }
591
592 if (debugger_exit_time == eDebuggerExitAfterKillWithoutWaitpid) {
593 logline("exiting because of eDebuggerExitAfterKillWithoutWaitpid");
594 exit(0);
595 }
596
597 sleep(1);
598 fprintf(stderr, "\n");
599 ret = waitpid(child, &stat_loc, 0);
600 if (ret == -1)
601 err(1, "waitpid(%d) by debugger failed", child);
602
603 logline("child process: %s", print_exit(child, stat_loc));
604
605 /* Received both SIGCHLD and NOTE_EXIT */
606 exit(0);
607 }
608
609 void logline(const char *format, ...)
610 {
611 char *line = NULL;
612 char newformat[1024];
613
614 snprintf(newformat, sizeof(newformat), "%s: %s\n", getprogname(), format);
615
616 va_list va;
617
618 va_start(va, format);
619 vasprintf(&line, newformat, va);
620 va_end(va);
621
622 if (line) {
623 write(STDOUT_FILENO, line, strlen(line));
624 free(line);
625 } else {
626 write(STDOUT_FILENO, "error\n", 6);
627 }
628 }
629
630
631 char *str_kev_filter(int filter)
632 {
633 static char filter_string[32];
634 if (filter == EVFILT_PROC)
635 strlcpy(filter_string, "EVFILT_PROC", sizeof(filter_string));
636 else if (filter == EVFILT_SIGNAL)
637 strlcpy(filter_string, "EVFILT_SIGNAL", sizeof(filter_string));
638 else if (filter == EVFILT_TIMER)
639 strlcpy(filter_string, "EVFILT_TIMER", sizeof(filter_string));
640 else
641 strlcpy(filter_string, "EVFILT_UNKNOWN", sizeof(filter_string));
642
643 return filter_string;
644 }
645
646 char *str_kev_flags(int filter, uint16_t flags)
647 {
648 static char flags_string[128];
649
650 flags_string[0] = '\0';
651 if (filter & EV_ADD) strlcat(flags_string, "|EV_ADD", sizeof(flags_string));
652 if (filter & EV_DELETE) strlcat(flags_string, "|EV_DELETE", sizeof(flags_string));
653 if (filter & EV_ENABLE) strlcat(flags_string, "|EV_ENABLE", sizeof(flags_string));
654 if (filter & EV_DISABLE) strlcat(flags_string, "|EV_DISABLE", sizeof(flags_string));
655 if (filter & EV_RECEIPT) strlcat(flags_string, "|EV_RECEIPT", sizeof(flags_string));
656 if (filter & EV_ONESHOT) strlcat(flags_string, "|EV_ONESHOT", sizeof(flags_string));
657 if (filter & EV_CLEAR) strlcat(flags_string, "|EV_CLEAR", sizeof(flags_string));
658 if (filter & EV_DISPATCH) strlcat(flags_string, "|EV_DISPATCH", sizeof(flags_string));
659 if (filter & EV_EOF) strlcat(flags_string, "|EV_EOF", sizeof(flags_string));
660 if (filter & EV_ERROR) strlcat(flags_string, "|EV_ERROR", sizeof(flags_string));
661
662 if (flags_string[0] == '|')
663 return &flags_string[1];
664 else
665 return flags_string;
666 }
667
668 char *str_kev_fflags(int filter, uint32_t fflags)
669 {
670 static char fflags_string[128];
671
672 fflags_string[0] = '\0';
673
674 if (filter == EVFILT_SIGNAL) {
675 if (fflags & NOTE_SIGNAL) strlcat(fflags_string, "|NOTE_SIGNAL", sizeof(fflags_string));
676 } else if (filter == EVFILT_PROC) {
677 if (fflags & NOTE_EXIT) strlcat(fflags_string, "|NOTE_EXIT", sizeof(fflags_string));
678 if (fflags & NOTE_FORK) strlcat(fflags_string, "|NOTE_FORK", sizeof(fflags_string));
679 if (fflags & NOTE_EXEC) strlcat(fflags_string, "|NOTE_EXEC", sizeof(fflags_string));
680 if (fflags & NOTE_SIGNAL) strlcat(fflags_string, "|NOTE_SIGNAL", sizeof(fflags_string));
681 if (fflags & NOTE_EXITSTATUS) strlcat(fflags_string, "|NOTE_EXITSTATUS", sizeof(fflags_string));
682 if (fflags & NOTE_EXIT_DETAIL) strlcat(fflags_string, "|NOTE_EXIT_DETAIL", sizeof(fflags_string));
683 if (fflags & NOTE_EXIT_DECRYPTFAIL) strlcat(fflags_string, "|NOTE_EXIT_DECRYPTFAIL", sizeof(fflags_string));
684 if (fflags & NOTE_EXIT_MEMORY) strlcat(fflags_string, "|NOTE_EXIT_MEMORY", sizeof(fflags_string));
685 #ifdef NOTE_EXIT_CSERROR
686 if (fflags & NOTE_EXIT_CSERROR) strlcat(fflags_string, "|NOTE_EXIT_CSERROR", sizeof(fflags_string));
687 #endif
688 } else if (filter == EVFILT_TIMER) {
689 if (fflags & NOTE_SECONDS) strlcat(fflags_string, "|NOTE_SECONDS", sizeof(fflags_string));
690 } else {
691 strlcat(fflags_string, "UNKNOWN", sizeof(fflags_string));
692 }
693
694 if (fflags_string[0] == '|')
695 return &fflags_string[1];
696 else
697 return fflags_string;
698 }
699
700 char *str_kev_data(int filter, uint32_t fflags, int64_t data, uint64_t udata)
701 {
702 static char data_string[128];
703
704 if (filter == EVFILT_PROC) {
705 if ((fflags & (NOTE_EXIT|NOTE_EXITSTATUS)) == (NOTE_EXIT|NOTE_EXITSTATUS)) {
706 if (WIFEXITED(data)) {
707 snprintf(data_string, sizeof(data_string), "pid %llu exited with status %d", udata, WEXITSTATUS(data));
708 } else if (WIFSIGNALED(data)) {
709 snprintf(data_string, sizeof(data_string), "pid %llu received signal %d%s", udata, WTERMSIG(data), WCOREDUMP(data) ? " (core dumped)" : "");
710 } else if (WIFSTOPPED(data)) {
711 snprintf(data_string, sizeof(data_string), "pid %llu stopped with signal %d", udata, WSTOPSIG(data));
712 } else {
713 snprintf(data_string, sizeof(data_string), "pid %llu unknown exit status 0x%08llx", udata, data);
714 }
715 } else if (fflags & NOTE_EXIT) {
716 snprintf(data_string, sizeof(data_string), "pid %llu exited", udata);
717 } else {
718 data_string[0] = '\0';
719 }
720 } else if (filter == EVFILT_TIMER) {
721 snprintf(data_string, sizeof(data_string), "timer fired %lld time(s)", data);
722 } else {
723 data_string[0] = '\0';
724 }
725
726 return data_string;
727 }
728
729 char *print_exit(pid_t p, int stat_loc)
730 {
731 return str_kev_data(EVFILT_PROC, NOTE_EXIT|NOTE_EXITSTATUS, stat_loc, p);
732 }
733
734 bool iszombie(pid_t p)
735 {
736 int ret;
737 struct proc_bsdshortinfo bsdinfo;
738
739 ret = proc_pidinfo(p, PROC_PIDT_SHORTBSDINFO, 1, &bsdinfo, sizeof(bsdinfo));
740 if (ret != sizeof(bsdinfo)) {
741 return false;
742 }
743
744 if (bsdinfo.pbsi_status == SZOMB) {
745 return true;
746 } else {
747 return false;
748 }
749 }