]>
Commit | Line | Data |
---|---|---|
c4d44194 | 1 | /* Tests for wait4. |
2b778ceb | 2 | Copyright (C) 2020-2021 Free Software Foundation, Inc. |
c4d44194 AF |
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_wait4 (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 = wait4 (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 = wait4 (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 = wait4 (pid, NULL, WUNTRACED|WCONTINUED|WNOHANG, &rusage); | |
107 | TEST_COMPARE (ret, 0); | |
108 | ret = wait4 (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 = wait4 (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 = wait4 (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 = wait4 (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 = wait4 (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 = wait4 (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, &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_wait4 (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> |