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 
34 static void
test_child(bool setgroup)35 test_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.  */
55 static sigset_t chldset;
56 
57 #ifdef SA_SIGINFO
58 static void
sigchld(int signo,siginfo_t * info,void * ctx)59 sigchld (int signo, siginfo_t *info, void *ctx)
60 {
61 }
62 #endif
63 
64 static void
check_sigchld(int code,int status,pid_t pid)65 check_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 
78 static int
do_test_waitd_common(idtype_t type,pid_t pid)79 do_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, 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 (P_PID, pid, &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 (P_PID, pid, &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, 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, 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 (P_PID, pid, &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 (P_PID, pid, &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 (P_PID, pid, &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, 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, stop_state);
165 
166   fail = waitid (type, pid, &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, 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, support_process_state_sleeping);
180 
181   check_sigchld (CLD_CONTINUED, SIGCONT, pid);
182 
183   fail = waitid (type, pid, &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, 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 (type, pid, &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 (type, pid, &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 (P_PID, pid, &info, WEXITED);
223   TEST_COMPARE (fail, -1);
224   TEST_COMPARE (errno, ECHILD);
225 
226   return 0;
227 }
228 
229 static int
do_test_waitid(idtype_t type)230 do_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, &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, &chldset, NULL);
248 
249   pid_t pid = xfork ();
250   if (pid == 0)
251     {
252       test_child (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, SIGKILL);		/* Make sure it's dead if we bailed early.  */
260   return ret;
261 }
262 
263 static int
do_test(void)264 do_test (void)
265 {
266   int ret = 0;
267 
268   ret |= do_test_waitid (P_PID);
269   ret |= do_test_waitid (P_PGID);
270   ret |= do_test_waitid (P_ALL);
271 
272   return ret;
273 }
274 
275 #include <support/test-driver.c>
276