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
test_child(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
sigchld(int signo,siginfo_t * info,void * ctx)57 sigchld (int signo, siginfo_t *info, void *ctx)
58 {
59 }
60 #endif
61 
62 static void
check_sigchld(int code,int status,pid_t pid)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
do_test_wait(pid_t pid)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, 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, 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, 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, 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, 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, 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, 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, SIGKILL) != 0)
160     FAIL_RET ("kill (%d, SIGKILL): %m\n", pid);
161 
162   support_process_state_wait (pid, 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
do_test(void)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, &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, &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, SIGKILL);          /* Make sure it's dead if we bailed early.  */
205 
206   return 0;
207 }
208 
209 #include <support/test-driver.c>
210