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