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

source code of glibc/support/support_test_main.c