1 | /* Test framework for wait3 and wait4. |
2 | Copyright (C) 2020-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <errno.h> |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <unistd.h> |
23 | #include <sys/wait.h> |
24 | #include <sys/resource.h> |
25 | #include <signal.h> |
26 | #include <time.h> |
27 | #include <stdatomic.h> |
28 | #include <stdbool.h> |
29 | |
30 | #include <support/xsignal.h> |
31 | #include <support/xunistd.h> |
32 | #include <support/check.h> |
33 | #include <support/process_state.h> |
34 | |
35 | static void |
36 | test_child (void) |
37 | { |
38 | /* First thing, we stop ourselves. */ |
39 | raise (SIGSTOP); |
40 | |
41 | /* Hey, we got continued! */ |
42 | while (1) |
43 | pause (); |
44 | } |
45 | |
46 | #ifndef WEXITED |
47 | # define WEXITED 0 |
48 | # define WCONTINUED 0 |
49 | # define WSTOPPED WUNTRACED |
50 | #endif |
51 | |
52 | /* Set with only SIGCHLD on do_test_waitid. */ |
53 | static sigset_t chldset; |
54 | |
55 | #ifdef SA_SIGINFO |
56 | static void |
57 | sigchld (int signo, siginfo_t *info, void *ctx) |
58 | { |
59 | } |
60 | #endif |
61 | |
62 | static void |
63 | check_sigchld (int code, int status, pid_t pid) |
64 | { |
65 | #ifdef SA_SIGINFO |
66 | siginfo_t siginfo; |
67 | TEST_COMPARE (sigwaitinfo (&chldset, &siginfo), SIGCHLD); |
68 | |
69 | TEST_COMPARE (siginfo.si_signo, SIGCHLD); |
70 | TEST_COMPARE (siginfo.si_code, code); |
71 | TEST_COMPARE (siginfo.si_status, status); |
72 | TEST_COMPARE (siginfo.si_pid, pid); |
73 | #endif |
74 | } |
75 | |
76 | static int |
77 | do_test_wait (pid_t pid) |
78 | { |
79 | /* Adding process_state_tracing_stop ('t') allows the test to work under |
80 | trace programs such as ptrace. */ |
81 | enum support_process_state stop_state = support_process_state_stopped |
82 | | support_process_state_tracing_stop; |
83 | |
84 | support_process_state_wait (pid, state: stop_state); |
85 | |
86 | check_sigchld (CLD_STOPPED, SIGSTOP, pid); |
87 | |
88 | pid_t ret; |
89 | int wstatus; |
90 | struct rusage rusage; |
91 | |
92 | ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WCONTINUED|WNOHANG, NULL); |
93 | if (ret == -1 && errno == ENOTSUP) |
94 | FAIL_RET ("waitid WNOHANG on stopped: %m" ); |
95 | TEST_COMPARE (ret, pid); |
96 | TEST_VERIFY (WIFSTOPPED (wstatus)); |
97 | |
98 | /* Issue again but with struct rusage input. */ |
99 | ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WCONTINUED|WNOHANG, &rusage); |
100 | /* With WNOHANG and WUNTRACED, if the children has not changes its state |
101 | since previous call the expected result it 0. */ |
102 | TEST_COMPARE (ret, 0); |
103 | |
104 | /* Some sanity tests to check if 'wtatus' and 'rusage' possible |
105 | input values. */ |
106 | ret = WAIT_CALL (pid, NULL, WUNTRACED|WCONTINUED|WNOHANG, &rusage); |
107 | TEST_COMPARE (ret, 0); |
108 | ret = WAIT_CALL (pid, NULL, WUNTRACED|WCONTINUED|WNOHANG, NULL); |
109 | TEST_COMPARE (ret, 0); |
110 | |
111 | if (kill (pid: pid, SIGCONT) != 0) |
112 | FAIL_RET ("kill (%d, SIGCONT): %m\n" , pid); |
113 | |
114 | /* Wait for the child to have continued. */ |
115 | support_process_state_wait (pid, state: support_process_state_sleeping); |
116 | |
117 | #if WCONTINUED != 0 |
118 | check_sigchld (CLD_CONTINUED, SIGCONT, pid); |
119 | |
120 | ret = WAIT_CALL (pid, &wstatus, WCONTINUED|WNOHANG, NULL); |
121 | TEST_COMPARE (ret, pid); |
122 | TEST_VERIFY (WIFCONTINUED (wstatus)); |
123 | |
124 | /* Issue again but with struct rusage input. */ |
125 | ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WCONTINUED|WNOHANG, &rusage); |
126 | /* With WNOHANG and WUNTRACED, if the children has not changes its state |
127 | since previous call the expected result it 0. */ |
128 | TEST_COMPARE (ret, 0); |
129 | |
130 | /* Now stop him again and test waitpid with WCONTINUED. */ |
131 | if (kill (pid: pid, SIGSTOP) != 0) |
132 | FAIL_RET ("kill (%d, SIGSTOP): %m\n" , pid); |
133 | |
134 | /* Wait the child stop. The waitid call below will block until it has |
135 | stopped, but if we are real quick and enter the waitid system call |
136 | before the SIGCHLD has been generated, then it will be discarded and |
137 | never delivered. */ |
138 | support_process_state_wait (pid, state: stop_state); |
139 | |
140 | ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WNOHANG, &rusage); |
141 | TEST_COMPARE (ret, pid); |
142 | |
143 | check_sigchld (CLD_STOPPED, SIGSTOP, pid); |
144 | |
145 | if (kill (pid: pid, SIGCONT) != 0) |
146 | FAIL_RET ("kill (%d, SIGCONT): %m\n" , pid); |
147 | |
148 | /* Wait for the child to have continued. */ |
149 | support_process_state_wait (pid, state: support_process_state_sleeping); |
150 | |
151 | check_sigchld (CLD_CONTINUED, SIGCONT, pid); |
152 | |
153 | ret = WAIT_CALL (pid, &wstatus, WCONTINUED|WNOHANG, NULL); |
154 | TEST_COMPARE (ret, pid); |
155 | TEST_VERIFY (WIFCONTINUED (wstatus)); |
156 | #endif |
157 | |
158 | /* Die, child, die! */ |
159 | if (kill (pid: pid, SIGKILL) != 0) |
160 | FAIL_RET ("kill (%d, SIGKILL): %m\n" , pid); |
161 | |
162 | support_process_state_wait (pid, state: support_process_state_zombie); |
163 | |
164 | ret = WAIT_CALL (pid, &wstatus, 0, &rusage); |
165 | TEST_COMPARE (ret, pid); |
166 | TEST_VERIFY (WIFSIGNALED (wstatus)); |
167 | TEST_VERIFY (WTERMSIG (wstatus) == SIGKILL); |
168 | |
169 | check_sigchld (CLD_KILLED, SIGKILL, pid); |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | static int |
175 | do_test (void) |
176 | { |
177 | #ifdef SA_SIGINFO |
178 | { |
179 | struct sigaction sa; |
180 | sa.sa_flags = SA_SIGINFO | SA_RESTART; |
181 | sa.sa_sigaction = sigchld; |
182 | sigemptyset (&sa.sa_mask); |
183 | xsigaction (SIGCHLD, newact: &sa, NULL); |
184 | } |
185 | #endif |
186 | |
187 | sigemptyset (&chldset); |
188 | sigaddset (&chldset, SIGCHLD); |
189 | |
190 | /* The SIGCHLD shall has blocked at the time of the call to sigwait; |
191 | otherwise, the behavior is undefined. */ |
192 | sigprocmask (SIG_BLOCK, set: &chldset, NULL); |
193 | |
194 | pid_t pid = xfork (); |
195 | if (pid == 0) |
196 | { |
197 | test_child (); |
198 | _exit (127); |
199 | } |
200 | |
201 | do_test_wait (pid); |
202 | |
203 | xsignal (SIGCHLD, SIG_IGN); |
204 | kill (pid: pid, SIGKILL); /* Make sure it's dead if we bailed early. */ |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | #include <support/test-driver.c> |
210 | |