1/* Tests for waitid.
2 Copyright (C) 2004-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 <signal.h>
25#include <time.h>
26#include <stdatomic.h>
27#include <stdbool.h>
28
29#include <support/xsignal.h>
30#include <support/xunistd.h>
31#include <support/check.h>
32#include <support/process_state.h>
33
34static void
35test_child (bool setgroup)
36{
37 if (setgroup)
38 TEST_COMPARE (setpgid (0, 0), 0);
39
40 /* First thing, we stop ourselves. */
41 raise (SIGSTOP);
42
43 /* Hey, we got continued! */
44 while (1)
45 pause ();
46}
47
48#ifndef WEXITED
49# define WEXITED 0
50# define WCONTINUED 0
51# define WSTOPPED WUNTRACED
52#endif
53
54/* Set with only SIGCHLD on do_test_waitid. */
55static sigset_t chldset;
56
57#ifdef SA_SIGINFO
58static void
59sigchld (int signo, siginfo_t *info, void *ctx)
60{
61}
62#endif
63
64static void
65check_sigchld (int code, int status, pid_t pid)
66{
67#ifdef SA_SIGINFO
68 siginfo_t siginfo;
69 TEST_COMPARE (sigwaitinfo (&chldset, &siginfo), SIGCHLD);
70
71 TEST_COMPARE (siginfo.si_signo, SIGCHLD);
72 TEST_COMPARE (siginfo.si_code, code);
73 TEST_COMPARE (siginfo.si_status, status);
74 TEST_COMPARE (siginfo.si_pid, pid);
75#endif
76}
77
78static int
79do_test_waitd_common (idtype_t type, pid_t pid)
80{
81 /* Adding process_state_tracing_stop ('t') allows the test to work under
82 trace programs such as ptrace. */
83 enum support_process_state stop_state = support_process_state_stopped
84 | support_process_state_tracing_stop;
85
86 support_process_state_wait (pid, state: stop_state);
87
88 check_sigchld (CLD_STOPPED, SIGSTOP, pid);
89
90 /* Now try a wait that should not succeed. */
91 siginfo_t info;
92 int fail;
93
94 info.si_signo = 0; /* A successful call sets it to SIGCHLD. */
95 fail = waitid (idtype: P_PID, id: pid, infop: &info, WEXITED|WCONTINUED|WNOHANG);
96 if (fail == -1 && errno == ENOTSUP)
97 FAIL_RET ("waitid WNOHANG on stopped: %m");
98 TEST_COMPARE (fail, 0);
99 TEST_COMPARE (info.si_signo, 0);
100
101 /* Next the wait that should succeed right away. */
102 info.si_signo = 0; /* A successful call sets it to SIGCHLD. */
103 info.si_pid = -1;
104 info.si_status = -1;
105 fail = waitid (idtype: P_PID, id: pid, infop: &info, WSTOPPED|WNOHANG);
106 if (fail == -1 && errno == ENOTSUP)
107 FAIL_RET ("waitid WNOHANG on stopped: %m");
108 TEST_COMPARE (fail, 0);
109 TEST_COMPARE (info.si_signo, SIGCHLD);
110 TEST_COMPARE (info.si_code, CLD_STOPPED);
111 TEST_COMPARE (info.si_status, SIGSTOP);
112 TEST_COMPARE (info.si_pid, pid);
113
114 if (kill (pid: pid, SIGCONT) != 0)
115 FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
116
117 /* Wait for the child to have continued. */
118 support_process_state_wait (pid, state: support_process_state_sleeping);
119
120#if WCONTINUED != 0
121 check_sigchld (CLD_CONTINUED, SIGCONT, pid);
122
123 info.si_signo = 0; /* A successful call sets it to SIGCHLD. */
124 info.si_pid = -1;
125 info.si_status = -1;
126 fail = waitid (idtype: P_PID, id: pid, infop: &info, WCONTINUED|WNOWAIT);
127 if (fail == -1 && errno == ENOTSUP)
128 FAIL_RET ("waitid WCONTINUED|WNOWAIT on continued: %m");
129 TEST_COMPARE (fail, 0);
130 TEST_COMPARE (info.si_signo, SIGCHLD);
131 TEST_COMPARE (info.si_code, CLD_CONTINUED);
132 TEST_COMPARE (info.si_status, SIGCONT);
133 TEST_COMPARE (info.si_pid, pid);
134
135 /* That should leave the CLD_CONTINUED state waiting to be seen again. */
136 info.si_signo = 0; /* A successful call sets it to SIGCHLD. */
137 info.si_pid = -1;
138 info.si_status = -1;
139 fail = waitid (idtype: P_PID, id: pid, infop: &info, WCONTINUED);
140 if (fail == -1 && errno == ENOTSUP)
141 FAIL_RET ("waitid WCONTINUED on continued: %m");
142 TEST_COMPARE (fail, 0);
143 TEST_COMPARE (info.si_signo, SIGCHLD);
144 TEST_COMPARE (info.si_code, CLD_CONTINUED);
145 TEST_COMPARE (info.si_status, SIGCONT);
146 TEST_COMPARE (info.si_pid, pid);
147
148 /* Now try a wait that should not succeed. */
149 info.si_signo = 0; /* A successful call sets it to SIGCHLD. */
150 fail = waitid (idtype: P_PID, id: pid, infop: &info, WCONTINUED|WNOHANG);
151 if (fail == -1 && errno == ENOTSUP)
152 FAIL_RET ("waitid WCONTINUED|WNOHANG on waited continued: %m");
153 TEST_COMPARE (fail, 0);
154 TEST_COMPARE (info.si_signo, 0);
155
156 /* Now stop him again and test waitpid with WCONTINUED. */
157 if (kill (pid: pid, SIGSTOP) != 0)
158 FAIL_RET ("kill (%d, SIGSTOP): %m\n", pid);
159
160 /* Wait the child stop. The waitid call below will block until it has
161 stopped, but if we are real quick and enter the waitid system call
162 before the SIGCHLD has been generated, then it will be discarded and
163 never delivered. */
164 support_process_state_wait (pid, state: stop_state);
165
166 fail = waitid (idtype: type, id: pid, infop: &info, WEXITED|WSTOPPED);
167 TEST_COMPARE (fail, 0);
168 TEST_COMPARE (info.si_signo, SIGCHLD);
169 TEST_COMPARE (info.si_code, CLD_STOPPED);
170 TEST_COMPARE (info.si_status, SIGSTOP);
171 TEST_COMPARE (info.si_pid, pid);
172
173 check_sigchld (CLD_STOPPED, SIGSTOP, pid);
174
175 if (kill (pid: pid, SIGCONT) != 0)
176 FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
177
178 /* Wait for the child to have continued. */
179 support_process_state_wait (pid, state: support_process_state_sleeping);
180
181 check_sigchld (CLD_CONTINUED, SIGCONT, pid);
182
183 fail = waitid (idtype: type, id: pid, infop: &info, WCONTINUED);
184 TEST_COMPARE (fail, 0);
185 TEST_COMPARE (info.si_signo, SIGCHLD);
186 TEST_COMPARE (info.si_code, CLD_CONTINUED);
187 TEST_COMPARE (info.si_status, SIGCONT);
188 TEST_COMPARE (info.si_pid, pid);
189#endif
190
191 /* Die, child, die! */
192 if (kill (pid: pid, SIGKILL) != 0)
193 FAIL_RET ("kill (%d, SIGKILL): %m\n", pid);
194
195#ifdef WNOWAIT
196 info.si_signo = 0; /* A successful call sets it to SIGCHLD. */
197 info.si_pid = -1;
198 info.si_status = -1;
199 fail = waitid (idtype: type, id: pid, infop: &info, WEXITED|WNOWAIT);
200 if (fail == -1 && errno == ENOTSUP)
201 FAIL_RET ("waitid WNOHANG on killed: %m");
202 TEST_COMPARE (fail, 0);
203 TEST_COMPARE (info.si_signo, SIGCHLD);
204 TEST_COMPARE (info.si_code, CLD_KILLED);
205 TEST_COMPARE (info.si_status, SIGKILL);
206 TEST_COMPARE (info.si_pid, pid);
207#else
208 support_process_state_wait (pid, support_process_state_zombie);
209#endif
210 check_sigchld (CLD_KILLED, SIGKILL, pid);
211
212 info.si_signo = 0; /* A successful call sets it to SIGCHLD. */
213 info.si_pid = -1;
214 info.si_status = -1;
215 fail = waitid (idtype: type, id: pid, infop: &info, WEXITED | WNOHANG);
216 TEST_COMPARE (fail, 0);
217 TEST_COMPARE (info.si_signo, SIGCHLD);
218 TEST_COMPARE (info.si_code, CLD_KILLED);
219 TEST_COMPARE (info.si_status, SIGKILL);
220 TEST_COMPARE (info.si_pid, pid);
221
222 fail = waitid (idtype: P_PID, id: pid, infop: &info, WEXITED);
223 TEST_COMPARE (fail, -1);
224 TEST_COMPARE (errno, ECHILD);
225
226 return 0;
227}
228
229static int
230do_test_waitid (idtype_t type)
231{
232#ifdef SA_SIGINFO
233 {
234 struct sigaction sa;
235 sa.sa_flags = SA_SIGINFO | SA_RESTART;
236 sa.sa_sigaction = sigchld;
237 sigemptyset (&sa.sa_mask);
238 xsigaction (SIGCHLD, newact: &sa, NULL);
239 }
240#endif
241
242 sigemptyset (&chldset);
243 sigaddset (&chldset, SIGCHLD);
244
245 /* The SIGCHLD shall has blocked at the time of the call to sigwait;
246 otherwise, the behavior is undefined. */
247 sigprocmask (SIG_BLOCK, set: &chldset, NULL);
248
249 pid_t pid = xfork ();
250 if (pid == 0)
251 {
252 test_child (setgroup: type == P_PGID || type == P_ALL);
253 _exit (127);
254 }
255
256 int ret = do_test_waitd_common (type, pid);
257
258 xsignal (SIGCHLD, SIG_IGN);
259 kill (pid: pid, SIGKILL); /* Make sure it's dead if we bailed early. */
260 return ret;
261}
262
263static int
264do_test (void)
265{
266 int ret = 0;
267
268 ret |= do_test_waitid (type: P_PID);
269 ret |= do_test_waitid (type: P_PGID);
270 ret |= do_test_waitid (type: P_ALL);
271
272 return ret;
273}
274
275#include <support/test-driver.c>
276

source code of glibc/posix/tst-waitid.c