1/* Main worker function for the test driver.
2 Copyright (C) 1998-2024 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 (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 const char *outside_pid = getenv(name: "PID_OUTSIDE_CONTAINER");
199 if (outside_pid)
200 {
201 mypid = atoi (outside_pid);
202 inside_container = 1;
203 }
204 else
205 mypid = getpid();
206
207 gdb_script_name = (char *) xmalloc (n: strlen (s: argv[0]) + strlen (s: ".gdb") + 1);
208 sprintf (s: gdb_script_name, format: "%s.gdb", argv[0]);
209 gdb_script = xfopen (path: gdb_script_name, mode: "w");
210
211 fprintf (stderr, format: "Waiting for debugger, test process is pid %d\n", mypid);
212 fprintf (stderr, format: "gdb -x %s\n", gdb_script_name);
213 if (inside_container)
214 fprintf (stream: gdb_script, format: "set sysroot %s/testroot.root\n", support_objdir_root);
215 fprintf (stream: gdb_script, format: "file\n");
216 fprintf (stream: gdb_script, format: "file %s\n", argv[0]);
217 fprintf (stream: gdb_script, format: "symbol-file %s\n", argv[0]);
218 fprintf (stream: gdb_script, format: "exec-file %s\n", argv[0]);
219 fprintf (stream: gdb_script, format: "attach %ld\n", (long int) mypid);
220 fprintf (stream: gdb_script, format: "set wait_for_debugger = 0\n");
221 fclose (stream: gdb_script);
222 free (ptr: gdb_script_name);
223 }
224
225 /* Wait for the debugger to set wait_for_debugger to zero. */
226 while (wait_for_debugger)
227 usleep (useconds: 1000);
228
229 if (config->run_command_mode)
230 {
231 /* In run-command-mode, the child process executes the command line
232 arguments as a new program. */
233 char **argv_ = xmalloc (n: sizeof (char *) * argc);
234 memcpy (dest: argv_, src: &argv[1], n: sizeof (char *) * (argc - 1));
235 argv_[argc - 1] = NULL;
236 execv (path: argv_[0], argv: argv_);
237 printf (format: "error: should not return here\n");
238 exit (status: 1);
239 }
240
241 if (config->test_function != NULL)
242 return config->test_function ();
243 else if (config->test_function_argv != NULL)
244 return config->test_function_argv (argc, argv);
245 else
246 {
247 printf (format: "error: no test function defined\n");
248 exit (status: 1);
249 }
250}
251
252static bool test_main_called;
253
254const char *test_dir = NULL;
255unsigned int test_verbose = 0;
256
257/* If test failure reporting has been linked in, it may contribute
258 additional test failures. */
259static int
260adjust_exit_status (int status)
261{
262 if (support_report_failure != NULL)
263 return support_report_failure (status);
264 return status;
265}
266
267int
268support_test_main (int argc, char **argv, const struct test_config *config)
269{
270 if (test_main_called)
271 {
272 printf (format: "error: test_main called for a second time\n");
273 exit (status: 1);
274 }
275 test_main_called = true;
276 const struct option *options;
277 if (config->options != NULL)
278 options = config->options;
279 else
280 options = default_options;
281
282 cleanup_function = config->cleanup_function;
283
284 int direct = 0; /* Directly call the test function? */
285 int status;
286 int opt;
287 unsigned int timeoutfactor = TIMEOUTFACTOR;
288 pid_t termpid;
289
290 /* If we're debugging the test, we need to disable timeouts and use
291 the initial pid (esp if we're running inside a container). */
292 if (getenv(name: "WAIT_FOR_DEBUGGER") != NULL)
293 direct = 1;
294
295 if (!config->no_mallopt)
296 {
297 /* Make uses of freed and uninitialized memory known. Do not
298 pull in a definition for mallopt if it has not been defined
299 already. */
300 extern __typeof__ (mallopt) mallopt __attribute__ ((weak));
301 if (mallopt != NULL)
302 mallopt (M_PERTURB, 42);
303 }
304
305 while ((opt = getopt_long (argc: argc, argv: argv, shortopts: config->optstring, longopts: options, NULL))
306 != -1)
307 switch (opt)
308 {
309 case '?':
310 usage (options);
311 exit (status: 1);
312 case 'v':
313 ++test_verbose;
314 break;
315 case OPT_DIRECT:
316 direct = 1;
317 break;
318 case OPT_TESTDIR:
319 test_dir = optarg;
320 break;
321 default:
322 if (config->cmdline_function != NULL)
323 config->cmdline_function (opt);
324 }
325
326 /* If set, read the test TIMEOUTFACTOR value from the environment.
327 This value is used to scale the default test timeout values. */
328 char *envstr_timeoutfactor = getenv (name: "TIMEOUTFACTOR");
329 if (envstr_timeoutfactor != NULL)
330 {
331 char *envstr_conv = envstr_timeoutfactor;
332 unsigned long int env_fact;
333
334 env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0);
335 if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor)
336 timeoutfactor = MAX (env_fact, 1);
337 }
338
339 /* Set TMPDIR to specified test directory. */
340 if (test_dir != NULL)
341 {
342 setenv (name: "TMPDIR", value: test_dir, replace: 1);
343
344 if (chdir (path: test_dir) < 0)
345 {
346 printf (format: "chdir: %m\n");
347 exit (status: 1);
348 }
349 }
350 else
351 {
352 test_dir = getenv (name: "TMPDIR");
353 if (test_dir == NULL || test_dir[0] == '\0')
354 test_dir = "/tmp";
355 }
356 if (support_set_test_dir != NULL)
357 support_set_test_dir (name: test_dir);
358
359 int timeout = config->timeout;
360 if (timeout == 0)
361 timeout = DEFAULT_TIMEOUT;
362
363 /* Make sure we see all message, even those on stdout. */
364 if (!config->no_setvbuf)
365 setvbuf (stdout, NULL, _IONBF, n: 0);
366
367 /* Make sure temporary files are deleted. */
368 if (support_delete_temp_files != NULL)
369 atexit (func: support_delete_temp_files);
370
371 /* Correct for the possible parameters. */
372 argv[optind - 1] = argv[0];
373 argv += optind - 1;
374 argc -= optind - 1;
375
376 /* Call the initializing function, if one is available. */
377 if (config->prepare_function != NULL)
378 config->prepare_function (argc, argv);
379
380 const char *envstr_direct = getenv (name: "TEST_DIRECT");
381 if (envstr_direct != NULL)
382 {
383 FILE *f = fopen (filename: envstr_direct, modes: "w");
384 if (f == NULL)
385 {
386 printf (format: "cannot open TEST_DIRECT output file '%s': %m\n",
387 envstr_direct);
388 exit (status: 1);
389 }
390
391 fprintf (stream: f, format: "timeout=%u\ntimeoutfactor=%u\n",
392 config->timeout, timeoutfactor);
393 if (config->expected_status != 0)
394 fprintf (stream: f, format: "exit=%u\n", config->expected_status);
395 if (config->expected_signal != 0)
396 fprintf (stream: f, format: "signal=%s\n", strsignal (sig: config->expected_signal));
397
398 if (support_print_temp_files != NULL)
399 support_print_temp_files (f);
400
401 fclose (stream: f);
402 direct = 1;
403 }
404
405 bool disable_coredumps;
406 {
407 const char *coredumps = getenv (name: "TEST_COREDUMPS");
408 disable_coredumps = coredumps == NULL || coredumps[0] == '\0';
409 }
410
411 /* If we are not expected to fork run the function immediately. */
412 if (direct)
413 return adjust_exit_status (status: run_test_function (argc, argv, config));
414
415 /* Set up the test environment:
416 - prevent core dumps
417 - set up the timer
418 - fork and execute the function. */
419
420 test_pid = fork ();
421 if (test_pid == 0)
422 {
423 /* This is the child. */
424 if (disable_coredumps)
425 {
426 /* Try to avoid dumping core. This is necessary because we
427 run the test from the source tree, and the coredumps
428 would end up there (and not in the build tree). */
429 struct rlimit core_limit;
430 core_limit.rlim_cur = 0;
431 core_limit.rlim_max = 0;
432 setrlimit (RLIMIT_CORE, rlimits: &core_limit);
433 }
434
435 /* We put the test process in its own pgrp so that if it bogusly
436 generates any job control signals, they won't hit the whole build. */
437 if (setpgid (pid: 0, pgid: 0) != 0)
438 printf (format: "Failed to set the process group ID: %m\n");
439
440 /* Execute the test function and exit with the return value. */
441 exit (status: run_test_function (argc, argv, config));
442 }
443 else if (test_pid < 0)
444 {
445 printf (format: "Cannot fork test program: %m\n");
446 exit (status: 1);
447 }
448
449 /* Set timeout. */
450 signal (SIGALRM, handler: signal_handler);
451 alarm (seconds: timeout * timeoutfactor);
452
453 /* Make sure we clean up if the wrapper gets interrupted. */
454 signal (SIGINT, handler: signal_handler);
455
456 /* Wait for the regular termination. */
457 termpid = TEMP_FAILURE_RETRY (waitpid (test_pid, &status, 0));
458 if (termpid == -1)
459 {
460 printf (format: "Waiting for test program failed: %m\n");
461 exit (status: 1);
462 }
463 if (termpid != test_pid)
464 {
465 printf (format: "Oops, wrong test program terminated: expected %ld, got %ld\n",
466 (long int) test_pid, (long int) termpid);
467 exit (status: 1);
468 }
469
470 /* Process terminated normally without timeout etc. */
471 if (WIFEXITED (status))
472 {
473 if (config->expected_status == 0)
474 {
475 if (config->expected_signal == 0)
476 /* Exit with the return value of the test. */
477 return adjust_exit_status (WEXITSTATUS (status));
478 else
479 {
480 printf (format: "Expected signal '%s' from child, got none\n",
481 strsignal (sig: config->expected_signal));
482 exit (status: 1);
483 }
484 }
485 else
486 {
487 /* Non-zero exit status is expected */
488 if (WEXITSTATUS (status) != config->expected_status)
489 {
490 printf (format: "Expected status %d, got %d\n",
491 config->expected_status, WEXITSTATUS (status));
492 exit (status: 1);
493 }
494 }
495 return adjust_exit_status (status: 0);
496 }
497 /* Process was killed by timer or other signal. */
498 else
499 {
500 if (config->expected_signal == 0)
501 {
502 printf (format: "Didn't expect signal from child: got `%s'\n",
503 strsignal (WTERMSIG (status)));
504 exit (status: 1);
505 }
506 else if (WTERMSIG (status) != config->expected_signal)
507 {
508 printf (format: "Incorrect signal from child: got `%s', need `%s'\n",
509 strsignal (WTERMSIG (status)),
510 strsignal (sig: config->expected_signal));
511 exit (status: 1);
512 }
513
514 return adjust_exit_status (status: 0);
515 }
516}
517

source code of glibc/support/support_test_main.c