]>
Commit | Line | Data |
---|---|---|
c23de0aa | 1 | /* Main worker function for the test driver. |
bfff8b1b | 2 | Copyright (C) 1998-2017 Free Software Foundation, Inc. |
c23de0aa FW |
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 | <http://www.gnu.org/licenses/>. */ | |
18 | ||
19 | #include <support/test-driver.h> | |
5f0b8437 | 20 | #include <support/check.h> |
c23de0aa FW |
21 | #include <support/temp_file-internal.h> |
22 | ||
23 | #include <assert.h> | |
24 | #include <errno.h> | |
25 | #include <getopt.h> | |
26 | #include <malloc.h> | |
27 | #include <signal.h> | |
28 | #include <stdbool.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <sys/param.h> | |
32 | #include <sys/resource.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/wait.h> | |
35 | #include <time.h> | |
36 | #include <unistd.h> | |
37 | ||
38 | static const struct option default_options[] = | |
39 | { | |
f47ae518 | 40 | TEST_DEFAULT_OPTIONS |
c23de0aa FW |
41 | { NULL, 0, NULL, 0 } |
42 | }; | |
43 | ||
44 | /* Show people how to run the program. */ | |
45 | static void | |
46 | usage (const struct option *options) | |
47 | { | |
48 | size_t i; | |
49 | ||
50 | printf ("Usage: %s [options]\n" | |
51 | "\n" | |
52 | "Environment Variables:\n" | |
53 | " TIMEOUTFACTOR An integer used to scale the timeout\n" | |
54 | " TMPDIR Where to place temporary files\n" | |
55 | " TEST_COREDUMPS Do not disable coredumps if set\n" | |
56 | "\n", | |
57 | program_invocation_short_name); | |
58 | printf ("Options:\n"); | |
59 | for (i = 0; options[i].name; ++i) | |
60 | { | |
61 | int indent; | |
62 | ||
63 | indent = printf (" --%s", options[i].name); | |
64 | if (options[i].has_arg == required_argument) | |
65 | indent += printf (" <arg>"); | |
66 | printf ("%*s", 25 - indent, ""); | |
67 | switch (options[i].val) | |
68 | { | |
f47ae518 FW |
69 | case 'v': |
70 | printf ("Increase the output verbosity"); | |
71 | break; | |
c23de0aa FW |
72 | case OPT_DIRECT: |
73 | printf ("Run the test directly (instead of forking & monitoring)"); | |
74 | break; | |
75 | case OPT_TESTDIR: | |
76 | printf ("Override the TMPDIR env var"); | |
77 | break; | |
78 | } | |
79 | printf ("\n"); | |
80 | } | |
81 | } | |
82 | ||
83 | /* The PID of the test process. */ | |
84 | static pid_t test_pid; | |
85 | ||
86 | /* The cleanup handler passed to test_main. */ | |
87 | static void (*cleanup_function) (void); | |
88 | ||
89 | /* Timeout handler. We kill the child and exit with an error. */ | |
90 | static void | |
91 | __attribute__ ((noreturn)) | |
92 | signal_handler (int sig) | |
93 | { | |
94 | int killed; | |
95 | int status; | |
96 | ||
97 | assert (test_pid > 1); | |
98 | /* Kill the whole process group. */ | |
99 | kill (-test_pid, SIGKILL); | |
100 | /* In case setpgid failed in the child, kill it individually too. */ | |
101 | kill (test_pid, SIGKILL); | |
102 | ||
103 | /* Wait for it to terminate. */ | |
104 | int i; | |
105 | for (i = 0; i < 5; ++i) | |
106 | { | |
107 | killed = waitpid (test_pid, &status, WNOHANG|WUNTRACED); | |
108 | if (killed != 0) | |
109 | break; | |
110 | ||
111 | /* Delay, give the system time to process the kill. If the | |
112 | nanosleep() call return prematurely, all the better. We | |
113 | won't restart it since this probably means the child process | |
114 | finally died. */ | |
115 | struct timespec ts; | |
116 | ts.tv_sec = 0; | |
117 | ts.tv_nsec = 100000000; | |
118 | nanosleep (&ts, NULL); | |
119 | } | |
120 | if (killed != 0 && killed != test_pid) | |
121 | { | |
122 | printf ("Failed to kill test process: %m\n"); | |
123 | exit (1); | |
124 | } | |
125 | ||
126 | if (cleanup_function != NULL) | |
127 | cleanup_function (); | |
128 | ||
129 | if (sig == SIGINT) | |
130 | { | |
131 | signal (sig, SIG_DFL); | |
132 | raise (sig); | |
133 | } | |
134 | ||
135 | if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL)) | |
136 | puts ("Timed out: killed the child process"); | |
137 | else if (WIFSTOPPED (status)) | |
138 | printf ("Timed out: the child process was %s\n", | |
139 | strsignal (WSTOPSIG (status))); | |
140 | else if (WIFSIGNALED (status)) | |
141 | printf ("Timed out: the child process got signal %s\n", | |
142 | strsignal (WTERMSIG (status))); | |
143 | else | |
144 | printf ("Timed out: killed the child process but it exited %d\n", | |
145 | WEXITSTATUS (status)); | |
146 | ||
147 | /* Exit with an error. */ | |
148 | exit (1); | |
149 | } | |
150 | ||
151 | /* Run test_function or test_function_argv. */ | |
152 | static int | |
153 | run_test_function (int argc, char **argv, const struct test_config *config) | |
154 | { | |
155 | if (config->test_function != NULL) | |
156 | return config->test_function (); | |
157 | else if (config->test_function_argv != NULL) | |
158 | return config->test_function_argv (argc, argv); | |
159 | else | |
160 | { | |
161 | printf ("error: no test function defined\n"); | |
162 | exit (1); | |
163 | } | |
164 | } | |
165 | ||
166 | static bool test_main_called; | |
167 | ||
168 | const char *test_dir = NULL; | |
f47ae518 | 169 | unsigned int test_verbose = 0; |
5f0b8437 FW |
170 | |
171 | /* If test failure reporting has been linked in, it may contribute | |
172 | additional test failures. */ | |
173 | static int | |
174 | adjust_exit_status (int status) | |
175 | { | |
176 | if (support_report_failure != NULL) | |
177 | return support_report_failure (status); | |
178 | return status; | |
179 | } | |
180 | ||
c23de0aa FW |
181 | int |
182 | support_test_main (int argc, char **argv, const struct test_config *config) | |
183 | { | |
184 | if (test_main_called) | |
185 | { | |
186 | printf ("error: test_main called for a second time\n"); | |
187 | exit (1); | |
188 | } | |
189 | test_main_called = true; | |
190 | const struct option *options; | |
191 | if (config->options != NULL) | |
192 | options = config->options; | |
193 | else | |
194 | options = default_options; | |
195 | ||
196 | cleanup_function = config->cleanup_function; | |
197 | ||
198 | int direct = 0; /* Directly call the test function? */ | |
199 | int status; | |
200 | int opt; | |
201 | unsigned int timeoutfactor = 1; | |
202 | pid_t termpid; | |
203 | ||
204 | if (!config->no_mallopt) | |
205 | { | |
206 | /* Make uses of freed and uninitialized memory known. Do not | |
207 | pull in a definition for mallopt if it has not been defined | |
208 | already. */ | |
209 | extern __typeof__ (mallopt) mallopt __attribute__ ((weak)); | |
210 | if (mallopt != NULL) | |
211 | mallopt (M_PERTURB, 42); | |
212 | } | |
213 | ||
214 | while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1) | |
215 | switch (opt) | |
216 | { | |
217 | case '?': | |
218 | usage (options); | |
219 | exit (1); | |
f47ae518 FW |
220 | case 'v': |
221 | ++test_verbose; | |
222 | break; | |
c23de0aa FW |
223 | case OPT_DIRECT: |
224 | direct = 1; | |
225 | break; | |
226 | case OPT_TESTDIR: | |
227 | test_dir = optarg; | |
228 | break; | |
229 | default: | |
230 | if (config->cmdline_function != NULL) | |
231 | config->cmdline_function (opt); | |
232 | } | |
233 | ||
234 | /* If set, read the test TIMEOUTFACTOR value from the environment. | |
235 | This value is used to scale the default test timeout values. */ | |
236 | char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR"); | |
237 | if (envstr_timeoutfactor != NULL) | |
238 | { | |
239 | char *envstr_conv = envstr_timeoutfactor; | |
240 | unsigned long int env_fact; | |
241 | ||
242 | env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0); | |
243 | if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor) | |
244 | timeoutfactor = MAX (env_fact, 1); | |
245 | } | |
246 | ||
247 | /* Set TMPDIR to specified test directory. */ | |
248 | if (test_dir != NULL) | |
249 | { | |
250 | setenv ("TMPDIR", test_dir, 1); | |
251 | ||
252 | if (chdir (test_dir) < 0) | |
253 | { | |
254 | printf ("chdir: %m\n"); | |
255 | exit (1); | |
256 | } | |
257 | } | |
258 | else | |
259 | { | |
260 | test_dir = getenv ("TMPDIR"); | |
261 | if (test_dir == NULL || test_dir[0] == '\0') | |
262 | test_dir = "/tmp"; | |
263 | } | |
264 | if (support_set_test_dir != NULL) | |
265 | support_set_test_dir (test_dir); | |
266 | ||
267 | int timeout = config->timeout; | |
268 | if (timeout == 0) | |
269 | timeout = DEFAULT_TIMEOUT; | |
270 | ||
271 | /* Make sure we see all message, even those on stdout. */ | |
272 | setvbuf (stdout, NULL, _IONBF, 0); | |
273 | ||
274 | /* Make sure temporary files are deleted. */ | |
275 | if (support_delete_temp_files != NULL) | |
276 | atexit (support_delete_temp_files); | |
277 | ||
278 | /* Correct for the possible parameters. */ | |
279 | argv[optind - 1] = argv[0]; | |
280 | argv += optind - 1; | |
281 | argc -= optind - 1; | |
282 | ||
283 | /* Call the initializing function, if one is available. */ | |
284 | if (config->prepare_function != NULL) | |
285 | config->prepare_function (argc, argv); | |
286 | ||
287 | const char *envstr_direct = getenv ("TEST_DIRECT"); | |
288 | if (envstr_direct != NULL) | |
289 | { | |
290 | FILE *f = fopen (envstr_direct, "w"); | |
291 | if (f == NULL) | |
292 | { | |
293 | printf ("cannot open TEST_DIRECT output file '%s': %m\n", | |
294 | envstr_direct); | |
295 | exit (1); | |
296 | } | |
297 | ||
298 | fprintf (f, "timeout=%u\ntimeoutfactor=%u\n", | |
299 | config->timeout, timeoutfactor); | |
300 | if (config->expected_status != 0) | |
301 | fprintf (f, "exit=%u\n", config->expected_status); | |
302 | if (config->expected_signal != 0) | |
303 | fprintf (f, "signal=%s\n", strsignal (config->expected_signal)); | |
304 | ||
305 | if (support_print_temp_files != NULL) | |
306 | support_print_temp_files (f); | |
307 | ||
308 | fclose (f); | |
309 | direct = 1; | |
310 | } | |
311 | ||
312 | bool disable_coredumps; | |
313 | { | |
314 | const char *coredumps = getenv ("TEST_COREDUMPS"); | |
315 | disable_coredumps = coredumps == NULL || coredumps[0] == '\0'; | |
316 | } | |
317 | ||
318 | /* If we are not expected to fork run the function immediately. */ | |
319 | if (direct) | |
5f0b8437 | 320 | return adjust_exit_status (run_test_function (argc, argv, config)); |
c23de0aa FW |
321 | |
322 | /* Set up the test environment: | |
323 | - prevent core dumps | |
324 | - set up the timer | |
325 | - fork and execute the function. */ | |
326 | ||
67f779f1 | 327 | test_pid = fork (); |
c23de0aa FW |
328 | if (test_pid == 0) |
329 | { | |
330 | /* This is the child. */ | |
331 | if (disable_coredumps) | |
332 | { | |
333 | /* Try to avoid dumping core. This is necessary because we | |
334 | run the test from the source tree, and the coredumps | |
335 | would end up there (and not in the build tree). */ | |
336 | struct rlimit core_limit; | |
337 | core_limit.rlim_cur = 0; | |
338 | core_limit.rlim_max = 0; | |
339 | setrlimit (RLIMIT_CORE, &core_limit); | |
340 | } | |
341 | ||
342 | /* We put the test process in its own pgrp so that if it bogusly | |
343 | generates any job control signals, they won't hit the whole build. */ | |
344 | if (setpgid (0, 0) != 0) | |
345 | printf ("Failed to set the process group ID: %m\n"); | |
346 | ||
347 | /* Execute the test function and exit with the return value. */ | |
348 | exit (run_test_function (argc, argv, config)); | |
349 | } | |
350 | else if (test_pid < 0) | |
351 | { | |
352 | printf ("Cannot fork test program: %m\n"); | |
353 | exit (1); | |
354 | } | |
355 | ||
356 | /* Set timeout. */ | |
357 | signal (SIGALRM, signal_handler); | |
fea34d51 | 358 | alarm (timeout * timeoutfactor); |
c23de0aa FW |
359 | |
360 | /* Make sure we clean up if the wrapper gets interrupted. */ | |
361 | signal (SIGINT, signal_handler); | |
362 | ||
363 | /* Wait for the regular termination. */ | |
364 | termpid = TEMP_FAILURE_RETRY (waitpid (test_pid, &status, 0)); | |
365 | if (termpid == -1) | |
366 | { | |
367 | printf ("Waiting for test program failed: %m\n"); | |
368 | exit (1); | |
369 | } | |
370 | if (termpid != test_pid) | |
371 | { | |
372 | printf ("Oops, wrong test program terminated: expected %ld, got %ld\n", | |
373 | (long int) test_pid, (long int) termpid); | |
374 | exit (1); | |
375 | } | |
376 | ||
377 | /* Process terminated normaly without timeout etc. */ | |
378 | if (WIFEXITED (status)) | |
379 | { | |
380 | if (config->expected_status == 0) | |
381 | { | |
382 | if (config->expected_signal == 0) | |
5f0b8437 FW |
383 | /* Exit with the return value of the test. */ |
384 | return adjust_exit_status (WEXITSTATUS (status)); | |
c23de0aa FW |
385 | else |
386 | { | |
387 | printf ("Expected signal '%s' from child, got none\n", | |
388 | strsignal (config->expected_signal)); | |
389 | exit (1); | |
390 | } | |
391 | } | |
392 | else | |
393 | { | |
394 | /* Non-zero exit status is expected */ | |
395 | if (WEXITSTATUS (status) != config->expected_status) | |
396 | { | |
397 | printf ("Expected status %d, got %d\n", | |
398 | config->expected_status, WEXITSTATUS (status)); | |
399 | exit (1); | |
400 | } | |
401 | } | |
5f0b8437 | 402 | return adjust_exit_status (0); |
c23de0aa FW |
403 | } |
404 | /* Process was killed by timer or other signal. */ | |
405 | else | |
406 | { | |
407 | if (config->expected_signal == 0) | |
408 | { | |
409 | printf ("Didn't expect signal from child: got `%s'\n", | |
410 | strsignal (WTERMSIG (status))); | |
411 | exit (1); | |
412 | } | |
413 | else if (WTERMSIG (status) != config->expected_signal) | |
414 | { | |
415 | printf ("Incorrect signal from child: got `%s', need `%s'\n", | |
416 | strsignal (WTERMSIG (status)), | |
417 | strsignal (config->expected_signal)); | |
418 | exit (1); | |
419 | } | |
420 | ||
5f0b8437 | 421 | return adjust_exit_status (0); |
c23de0aa FW |
422 | } |
423 | } |