]>
Commit | Line | Data |
---|---|---|
743345f9 A |
1 | #include <darwintest.h> |
2 | ||
3 | #include <stdio.h> | |
4 | #include <unistd.h> | |
5 | #include <stdlib.h> | |
6 | #include <errno.h> | |
7 | #include <string.h> | |
8 | #include <assert.h> | |
9 | #include <signal.h> | |
10 | #include <spawn.h> | |
11 | #include <stdint.h> | |
12 | #include <sys/sysctl.h> | |
13 | #include <stdbool.h> | |
14 | #include <sysexits.h> | |
15 | #include <err.h> | |
16 | ||
cb323159 A |
17 | T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); |
18 | ||
743345f9 A |
19 | /* |
20 | * Test to validate that suspended-spawn DTRTs when a SIGKILL is recieved | |
21 | * while the process is waiting for SIGCONT. | |
22 | * | |
23 | * Also test that suspended-spawn correctly looks like a SIGSTOP while it's suspended. | |
24 | * | |
25 | * <rdar://problem/26184412> posix_spawn non-exec with POSIX_SPAWN_START_SUSPENDED, then killing instead of SIGCONT-ing causes unkillable hung processes | |
26 | */ | |
27 | ||
28 | static void | |
29 | spawn_and_signal(int signal) | |
30 | { | |
31 | /* do not buffer output to stdout */ | |
32 | setvbuf(stdout, NULL, _IONBF, 0); | |
33 | ||
34 | int ret; | |
35 | posix_spawnattr_t attr; | |
36 | ||
37 | ret = posix_spawnattr_init(&attr); | |
38 | T_QUIET; | |
39 | T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_init"); | |
40 | ||
41 | ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); | |
42 | T_QUIET; | |
43 | T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_setflags"); | |
44 | ||
45 | char * const prog = "/usr/bin/true"; | |
46 | char * const argv_child[] = { prog, NULL }; | |
47 | pid_t child_pid; | |
48 | extern char **environ; | |
49 | ||
50 | ret = posix_spawn(&child_pid, prog, NULL, &attr, argv_child, environ); | |
51 | T_ASSERT_POSIX_SUCCESS(ret, "posix_spawn"); | |
52 | ||
53 | printf("parent: spawned child with pid %d\n", child_pid); | |
54 | ||
55 | ret = posix_spawnattr_destroy(&attr); | |
56 | T_QUIET; | |
57 | T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_destroy"); | |
58 | ||
59 | int status = 0; | |
0a7de745 | 60 | int waitpid_result = waitpid(child_pid, &status, WUNTRACED | WNOHANG); |
743345f9 A |
61 | T_ASSERT_POSIX_SUCCESS(waitpid_result, "waitpid"); |
62 | ||
63 | T_ASSERT_EQ(waitpid_result, child_pid, "waitpid should return child we spawned"); | |
64 | ||
65 | T_ASSERT_EQ(WIFEXITED(status), 0, "before SIGCONT: must not have exited"); | |
66 | T_ASSERT_EQ(WIFSTOPPED(status), 1, "before SIGCONT: must be stopped"); | |
67 | ||
68 | printf("parent: continuing child process\n"); | |
69 | ||
70 | ret = kill(child_pid, signal); | |
71 | T_ASSERT_POSIX_SUCCESS(ret, "kill(signal)"); | |
72 | ||
73 | printf("parent: waiting for child process\n"); | |
74 | ||
75 | status = 0; | |
76 | waitpid_result = waitpid(child_pid, &status, 0); | |
77 | T_ASSERT_POSIX_SUCCESS(waitpid_result, "waitpid"); | |
78 | ||
79 | T_ASSERT_EQ(waitpid_result, child_pid, "waitpid should return child we spawned"); | |
80 | ||
81 | if (signal == SIGKILL) { | |
82 | T_ASSERT_EQ(WIFSIGNALED(status), 1, "child should have exited due to signal"); | |
83 | T_ASSERT_EQ(WTERMSIG(status), SIGKILL, "child should have exited due to SIGKILL"); | |
84 | } else { | |
85 | T_ASSERT_EQ(WIFEXITED(status), 1, "child should have exited normally"); | |
86 | T_ASSERT_EQ(WEXITSTATUS(status), EX_OK, "child should have exited with success"); | |
87 | } | |
88 | ||
89 | printf("wait returned with pid %d, status %d\n", ret, status); | |
90 | } | |
91 | ||
92 | T_DECL(suspended_spawn_continue, "Tests spawning a suspended process and continuing it", T_META_TIMEOUT(2)) | |
93 | { | |
94 | spawn_and_signal(SIGCONT); | |
95 | } | |
96 | ||
97 | T_DECL(suspended_spawn_kill, "Tests spawning a suspended process and killing it", T_META_TIMEOUT(2)) | |
98 | { | |
99 | spawn_and_signal(SIGKILL); | |
100 | } |