#include <support/test-driver.h>
#include <support/check.h>
#include <support/temp_file-internal.h>
+#include <support/support.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
+#include <xstdio.h>
+
static const struct option default_options[] =
{
TEST_DEFAULT_OPTIONS
exit (1);
}
+/* This must be volatile as it will be modified by the debugger. */
+static volatile int wait_for_debugger = 0;
+
/* Run test_function or test_function_argv. */
static int
run_test_function (int argc, char **argv, const struct test_config *config)
{
+ const char *wfd = getenv("WAIT_FOR_DEBUGGER");
+ if (wfd != NULL)
+ wait_for_debugger = atoi (wfd);
+ if (wait_for_debugger)
+ {
+ pid_t mypid;
+ FILE *gdb_script;
+ char *gdb_script_name;
+ int inside_container = 0;
+
+ mypid = getpid();
+ if (mypid < 3)
+ {
+ const char *outside_pid = getenv("PID_OUTSIDE_CONTAINER");
+ if (outside_pid)
+ {
+ mypid = atoi (outside_pid);
+ inside_container = 1;
+ }
+ }
+
+ gdb_script_name = (char *) xmalloc (strlen (argv[0]) + strlen (".gdb") + 1);
+ sprintf (gdb_script_name, "%s.gdb", argv[0]);
+ gdb_script = xfopen (gdb_script_name, "w");
+
+ fprintf (stderr, "Waiting for debugger, test process is pid %d\n", mypid);
+ fprintf (stderr, "gdb -x %s\n", gdb_script_name);
+ if (inside_container)
+ fprintf (gdb_script, "set sysroot %s/testroot.root\n", support_objdir_root);
+ fprintf (gdb_script, "file\n");
+ fprintf (gdb_script, "file %s\n", argv[0]);
+ fprintf (gdb_script, "symbol-file %s\n", argv[0]);
+ fprintf (gdb_script, "exec-file %s\n", argv[0]);
+ fprintf (gdb_script, "attach %ld\n", (long int) mypid);
+ fprintf (gdb_script, "set wait_for_debugger = 0\n");
+ fclose (gdb_script);
+ }
+
+ /* Wait for the debugger to set wait_for_debugger to zero. */
+ while (wait_for_debugger)
+ usleep (1000);
+
if (config->test_function != NULL)
return config->test_function ();
else if (config->test_function_argv != NULL)
unsigned int timeoutfactor = 1;
pid_t termpid;
+ /* If we're debugging the test, we need to disable timeouts and use
+ the initial pid (esp if we're running inside a container). */
+ if (getenv("WAIT_FOR_DEBUGGER") != NULL)
+ direct = 1;
+
if (!config->no_mallopt)
{
/* Make uses of freed and uninitialized memory known. Do not
char *so_base;
int do_postclean = 0;
+ int pipes[2];
+ char pid_buf[20];
+
uid_t original_uid;
gid_t original_gid;
/* If set, the test runs as root instead of the user running the testsuite. */
if (chdir (new_cwd_path) < 0)
FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
+ /* This is to pass the "outside" PID to the child, which will be PID
+ 1. */
+ if (pipe2 (pipes, O_CLOEXEC) < 0)
+ FAIL_EXIT1 ("Can't create pid pipe");
+
/* To complete the containerization, we need to fork () at least
once. We can't exec, nor can we somehow link the new child to
our parent. So we run the child and propogate it's exit status
{
/* Parent. */
int status;
+
+ /* Send the child's "outside" pid to it. */
+ write (pipes[1], &child, sizeof(child));
+ close (pipes[0]);
+ close (pipes[1]);
+
waitpid (child, &status, 0);
if (WIFEXITED (status))
/* The rest is the child process, which is now PID 1 and "in" the
new root. */
+ /* Get our "outside" pid from our parent. We use this to help with
+ debugging from outside the container. */
+ read (pipes[0], &child, sizeof(child));
+ close (pipes[0]);
+ close (pipes[1]);
+ sprintf (pid_buf, "%lu", (long unsigned)child);
+ setenv ("PID_OUTSIDE_CONTAINER", pid_buf, 0);
+
maybe_xmkdir ("/tmp", 0755);
/* Now that we're pid 1 (effectively "root") we can mount /proc */