Blame support/support_test_main.c

Packit Service 82fcde
/* Main worker function for the test driver.
Packit Service 82fcde
   Copyright (C) 1998-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <support/test-driver.h>
Packit Service 82fcde
#include <support/check.h>
Packit Service 82fcde
#include <support/temp_file-internal.h>
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <getopt.h>
Packit Service 82fcde
#include <malloc.h>
Packit Service 82fcde
#include <signal.h>
Packit Service 82fcde
#include <stdbool.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <sys/param.h>
Packit Service 82fcde
#include <sys/resource.h>
Packit Service 82fcde
#include <sys/types.h>
Packit Service 82fcde
#include <sys/wait.h>
Packit Service 82fcde
#include <time.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
Packit Service 82fcde
static const struct option default_options[] =
Packit Service 82fcde
{
Packit Service 82fcde
  TEST_DEFAULT_OPTIONS
Packit Service 82fcde
  { NULL, 0, NULL, 0 }
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
/* Show people how to run the program.  */
Packit Service 82fcde
static void
Packit Service 82fcde
usage (const struct option *options)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t i;
Packit Service 82fcde
Packit Service 82fcde
  printf ("Usage: %s [options]\n"
Packit Service 82fcde
          "\n"
Packit Service 82fcde
          "Environment Variables:\n"
Packit Service 82fcde
          "  TIMEOUTFACTOR          An integer used to scale the timeout\n"
Packit Service 82fcde
          "  TMPDIR                 Where to place temporary files\n"
Packit Service 82fcde
          "  TEST_COREDUMPS         Do not disable coredumps if set\n"
Packit Service 82fcde
          "\n",
Packit Service 82fcde
          program_invocation_short_name);
Packit Service 82fcde
  printf ("Options:\n");
Packit Service 82fcde
  for (i = 0; options[i].name; ++i)
Packit Service 82fcde
    {
Packit Service 82fcde
      int indent;
Packit Service 82fcde
Packit Service 82fcde
      indent = printf ("  --%s", options[i].name);
Packit Service 82fcde
      if (options[i].has_arg == required_argument)
Packit Service 82fcde
        indent += printf (" <arg>");
Packit Service 82fcde
      printf ("%*s", 25 - indent, "");
Packit Service 82fcde
      switch (options[i].val)
Packit Service 82fcde
        {
Packit Service 82fcde
        case 'v':
Packit Service 82fcde
          printf ("Increase the output verbosity");
Packit Service 82fcde
          break;
Packit Service 82fcde
        case OPT_DIRECT:
Packit Service 82fcde
          printf ("Run the test directly (instead of forking & monitoring)");
Packit Service 82fcde
          break;
Packit Service 82fcde
        case OPT_TESTDIR:
Packit Service 82fcde
          printf ("Override the TMPDIR env var");
Packit Service 82fcde
          break;
Packit Service 82fcde
        }
Packit Service 82fcde
      printf ("\n");
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* The PID of the test process.  */
Packit Service 82fcde
static pid_t test_pid;
Packit Service 82fcde
Packit Service 82fcde
/* The cleanup handler passed to test_main.  */
Packit Service 82fcde
static void (*cleanup_function) (void);
Packit Service 82fcde
Packit Service 82fcde
/* Timeout handler.  We kill the child and exit with an error.  */
Packit Service 82fcde
static void
Packit Service 82fcde
__attribute__ ((noreturn))
Packit Service 82fcde
signal_handler (int sig)
Packit Service 82fcde
{
Packit Service 82fcde
  int killed;
Packit Service 82fcde
  int status;
Packit Service 82fcde
Packit Service 82fcde
  assert (test_pid > 1);
Packit Service 82fcde
  /* Kill the whole process group.  */
Packit Service 82fcde
  kill (-test_pid, SIGKILL);
Packit Service 82fcde
  /* In case setpgid failed in the child, kill it individually too.  */
Packit Service 82fcde
  kill (test_pid, SIGKILL);
Packit Service 82fcde
Packit Service 82fcde
  /* Wait for it to terminate.  */
Packit Service 82fcde
  int i;
Packit Service 82fcde
  for (i = 0; i < 5; ++i)
Packit Service 82fcde
    {
Packit Service 82fcde
      killed = waitpid (test_pid, &status, WNOHANG|WUNTRACED);
Packit Service 82fcde
      if (killed != 0)
Packit Service 82fcde
        break;
Packit Service 82fcde
Packit Service 82fcde
      /* Delay, give the system time to process the kill.  If the
Packit Service 82fcde
         nanosleep() call return prematurely, all the better.  We
Packit Service 82fcde
         won't restart it since this probably means the child process
Packit Service 82fcde
         finally died.  */
Packit Service 82fcde
      struct timespec ts;
Packit Service 82fcde
      ts.tv_sec = 0;
Packit Service 82fcde
      ts.tv_nsec = 100000000;
Packit Service 82fcde
      nanosleep (&ts, NULL);
Packit Service 82fcde
    }
Packit Service 82fcde
  if (killed != 0 && killed != test_pid)
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("Failed to kill test process: %m\n");
Packit Service 82fcde
      exit (1);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (cleanup_function != NULL)
Packit Service 82fcde
    cleanup_function ();
Packit Service 82fcde
Packit Service 82fcde
  if (sig == SIGINT)
Packit Service 82fcde
    {
Packit Service 82fcde
      signal (sig, SIG_DFL);
Packit Service 82fcde
      raise (sig);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL))
Packit Service 82fcde
    puts ("Timed out: killed the child process");
Packit Service 82fcde
  else if (WIFSTOPPED (status))
Packit Service 82fcde
    printf ("Timed out: the child process was %s\n",
Packit Service 82fcde
            strsignal (WSTOPSIG (status)));
Packit Service 82fcde
  else if (WIFSIGNALED (status))
Packit Service 82fcde
    printf ("Timed out: the child process got signal %s\n",
Packit Service 82fcde
            strsignal (WTERMSIG (status)));
Packit Service 82fcde
  else
Packit Service 82fcde
    printf ("Timed out: killed the child process but it exited %d\n",
Packit Service 82fcde
            WEXITSTATUS (status));
Packit Service 82fcde
Packit Service 82fcde
  /* Exit with an error.  */
Packit Service 82fcde
  exit (1);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Run test_function or test_function_argv.  */
Packit Service 82fcde
static int
Packit Service 82fcde
run_test_function (int argc, char **argv, const struct test_config *config)
Packit Service 82fcde
{
Packit Service 82fcde
  if (config->test_function != NULL)
Packit Service 82fcde
    return config->test_function ();
Packit Service 82fcde
  else if (config->test_function_argv != NULL)
Packit Service 82fcde
    return config->test_function_argv (argc, argv);
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("error: no test function defined\n");
Packit Service 82fcde
      exit (1);
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static bool test_main_called;
Packit Service 82fcde
Packit Service 82fcde
const char *test_dir = NULL;
Packit Service 82fcde
unsigned int test_verbose = 0;
Packit Service 82fcde
Packit Service 82fcde
/* If test failure reporting has been linked in, it may contribute
Packit Service 82fcde
   additional test failures.  */
Packit Service 82fcde
static int
Packit Service 82fcde
adjust_exit_status (int status)
Packit Service 82fcde
{
Packit Service 82fcde
  if (support_report_failure != NULL)
Packit Service 82fcde
    return support_report_failure (status);
Packit Service 82fcde
  return status;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
support_test_main (int argc, char **argv, const struct test_config *config)
Packit Service 82fcde
{
Packit Service 82fcde
  if (test_main_called)
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("error: test_main called for a second time\n");
Packit Service 82fcde
      exit (1);
Packit Service 82fcde
    }
Packit Service 82fcde
  test_main_called = true;
Packit Service 82fcde
  const struct option *options;
Packit Service 82fcde
  if (config->options != NULL)
Packit Service 82fcde
    options = config->options;
Packit Service 82fcde
  else
Packit Service 82fcde
    options = default_options;
Packit Service 82fcde
Packit Service 82fcde
  cleanup_function = config->cleanup_function;
Packit Service 82fcde
Packit Service 82fcde
  int direct = 0;       /* Directly call the test function?  */
Packit Service 82fcde
  int status;
Packit Service 82fcde
  int opt;
Packit Service 82fcde
  unsigned int timeoutfactor = 1;
Packit Service 82fcde
  pid_t termpid;
Packit Service 82fcde
Packit Service 82fcde
  if (!config->no_mallopt)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Make uses of freed and uninitialized memory known.  Do not
Packit Service 82fcde
         pull in a definition for mallopt if it has not been defined
Packit Service 82fcde
         already.  */
Packit Service 82fcde
      extern __typeof__ (mallopt) mallopt __attribute__ ((weak));
Packit Service 82fcde
      if (mallopt != NULL)
Packit Service 82fcde
        mallopt (M_PERTURB, 42);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  while ((opt = getopt_long (argc, argv, config->optstring, options, NULL))
Packit Service 82fcde
	 != -1)
Packit Service 82fcde
    switch (opt)
Packit Service 82fcde
      {
Packit Service 82fcde
      case '?':
Packit Service 82fcde
        usage (options);
Packit Service 82fcde
        exit (1);
Packit Service 82fcde
      case 'v':
Packit Service 82fcde
        ++test_verbose;
Packit Service 82fcde
        break;
Packit Service 82fcde
      case OPT_DIRECT:
Packit Service 82fcde
        direct = 1;
Packit Service 82fcde
        break;
Packit Service 82fcde
      case OPT_TESTDIR:
Packit Service 82fcde
        test_dir = optarg;
Packit Service 82fcde
        break;
Packit Service 82fcde
      default:
Packit Service 82fcde
        if (config->cmdline_function != NULL)
Packit Service 82fcde
          config->cmdline_function (opt);
Packit Service 82fcde
      }
Packit Service 82fcde
Packit Service 82fcde
  /* If set, read the test TIMEOUTFACTOR value from the environment.
Packit Service 82fcde
     This value is used to scale the default test timeout values. */
Packit Service 82fcde
  char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR");
Packit Service 82fcde
  if (envstr_timeoutfactor != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      char *envstr_conv = envstr_timeoutfactor;
Packit Service 82fcde
      unsigned long int env_fact;
Packit Service 82fcde
Packit Service 82fcde
      env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0);
Packit Service 82fcde
      if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor)
Packit Service 82fcde
        timeoutfactor = MAX (env_fact, 1);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Set TMPDIR to specified test directory.  */
Packit Service 82fcde
  if (test_dir != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      setenv ("TMPDIR", test_dir, 1);
Packit Service 82fcde
Packit Service 82fcde
      if (chdir (test_dir) < 0)
Packit Service 82fcde
        {
Packit Service 82fcde
          printf ("chdir: %m\n");
Packit Service 82fcde
          exit (1);
Packit Service 82fcde
        }
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      test_dir = getenv ("TMPDIR");
Packit Service 82fcde
      if (test_dir == NULL || test_dir[0] == '\0')
Packit Service 82fcde
        test_dir = "/tmp";
Packit Service 82fcde
    }
Packit Service 82fcde
  if (support_set_test_dir != NULL)
Packit Service 82fcde
    support_set_test_dir (test_dir);
Packit Service 82fcde
Packit Service 82fcde
  int timeout = config->timeout;
Packit Service 82fcde
  if (timeout == 0)
Packit Service 82fcde
    timeout =  DEFAULT_TIMEOUT;
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure we see all message, even those on stdout.  */
Packit Service 82fcde
  if (!config->no_setvbuf)
Packit Service 82fcde
    setvbuf (stdout, NULL, _IONBF, 0);
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure temporary files are deleted.  */
Packit Service 82fcde
  if (support_delete_temp_files != NULL)
Packit Service 82fcde
      atexit (support_delete_temp_files);
Packit Service 82fcde
Packit Service 82fcde
  /* Correct for the possible parameters.  */
Packit Service 82fcde
  argv[optind - 1] = argv[0];
Packit Service 82fcde
  argv += optind - 1;
Packit Service 82fcde
  argc -= optind - 1;
Packit Service 82fcde
Packit Service 82fcde
  /* Call the initializing function, if one is available.  */
Packit Service 82fcde
  if (config->prepare_function != NULL)
Packit Service 82fcde
    config->prepare_function (argc, argv);
Packit Service 82fcde
Packit Service 82fcde
  const char *envstr_direct = getenv ("TEST_DIRECT");
Packit Service 82fcde
  if (envstr_direct != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      FILE *f = fopen (envstr_direct, "w");
Packit Service 82fcde
      if (f == NULL)
Packit Service 82fcde
        {
Packit Service 82fcde
          printf ("cannot open TEST_DIRECT output file '%s': %m\n",
Packit Service 82fcde
                  envstr_direct);
Packit Service 82fcde
          exit (1);
Packit Service 82fcde
        }
Packit Service 82fcde
Packit Service 82fcde
      fprintf (f, "timeout=%u\ntimeoutfactor=%u\n",
Packit Service 82fcde
               config->timeout, timeoutfactor);
Packit Service 82fcde
      if (config->expected_status != 0)
Packit Service 82fcde
        fprintf (f, "exit=%u\n", config->expected_status);
Packit Service 82fcde
      if (config->expected_signal != 0)
Packit Service 82fcde
        fprintf (f, "signal=%s\n", strsignal (config->expected_signal));
Packit Service 82fcde
Packit Service 82fcde
      if (support_print_temp_files != NULL)
Packit Service 82fcde
        support_print_temp_files (f);
Packit Service 82fcde
Packit Service 82fcde
      fclose (f);
Packit Service 82fcde
      direct = 1;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  bool disable_coredumps;
Packit Service 82fcde
  {
Packit Service 82fcde
    const char *coredumps = getenv ("TEST_COREDUMPS");
Packit Service 82fcde
    disable_coredumps = coredumps == NULL || coredumps[0] == '\0';
Packit Service 82fcde
  }
Packit Service 82fcde
Packit Service 82fcde
  /* If we are not expected to fork run the function immediately.  */
Packit Service 82fcde
  if (direct)
Packit Service 82fcde
    return adjust_exit_status (run_test_function (argc, argv, config));
Packit Service 82fcde
Packit Service 82fcde
  /* Set up the test environment:
Packit Service 82fcde
     - prevent core dumps
Packit Service 82fcde
     - set up the timer
Packit Service 82fcde
     - fork and execute the function.  */
Packit Service 82fcde
Packit Service 82fcde
  test_pid = fork ();
Packit Service 82fcde
  if (test_pid == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* This is the child.  */
Packit Service 82fcde
      if (disable_coredumps)
Packit Service 82fcde
        {
Packit Service 82fcde
          /* Try to avoid dumping core.  This is necessary because we
Packit Service 82fcde
             run the test from the source tree, and the coredumps
Packit Service 82fcde
             would end up there (and not in the build tree).  */
Packit Service 82fcde
          struct rlimit core_limit;
Packit Service 82fcde
          core_limit.rlim_cur = 0;
Packit Service 82fcde
          core_limit.rlim_max = 0;
Packit Service 82fcde
          setrlimit (RLIMIT_CORE, &core_limit);
Packit Service 82fcde
        }
Packit Service 82fcde
Packit Service 82fcde
      /* We put the test process in its own pgrp so that if it bogusly
Packit Service 82fcde
         generates any job control signals, they won't hit the whole build.  */
Packit Service 82fcde
      if (setpgid (0, 0) != 0)
Packit Service 82fcde
        printf ("Failed to set the process group ID: %m\n");
Packit Service 82fcde
Packit Service 82fcde
      /* Execute the test function and exit with the return value.   */
Packit Service 82fcde
      exit (run_test_function (argc, argv, config));
Packit Service 82fcde
    }
Packit Service 82fcde
  else if (test_pid < 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("Cannot fork test program: %m\n");
Packit Service 82fcde
      exit (1);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Set timeout.  */
Packit Service 82fcde
  signal (SIGALRM, signal_handler);
Packit Service 82fcde
  alarm (timeout * timeoutfactor);
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure we clean up if the wrapper gets interrupted.  */
Packit Service 82fcde
  signal (SIGINT, signal_handler);
Packit Service 82fcde
Packit Service 82fcde
  /* Wait for the regular termination.  */
Packit Service 82fcde
  termpid = TEMP_FAILURE_RETRY (waitpid (test_pid, &status, 0));
Packit Service 82fcde
  if (termpid == -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("Waiting for test program failed: %m\n");
Packit Service 82fcde
      exit (1);
Packit Service 82fcde
    }
Packit Service 82fcde
  if (termpid != test_pid)
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("Oops, wrong test program terminated: expected %ld, got %ld\n",
Packit Service 82fcde
              (long int) test_pid, (long int) termpid);
Packit Service 82fcde
      exit (1);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Process terminated normaly without timeout etc.  */
Packit Service 82fcde
  if (WIFEXITED (status))
Packit Service 82fcde
    {
Packit Service 82fcde
      if (config->expected_status == 0)
Packit Service 82fcde
        {
Packit Service 82fcde
          if (config->expected_signal == 0)
Packit Service 82fcde
            /* Exit with the return value of the test.  */
Packit Service 82fcde
            return adjust_exit_status (WEXITSTATUS (status));
Packit Service 82fcde
          else
Packit Service 82fcde
            {
Packit Service 82fcde
              printf ("Expected signal '%s' from child, got none\n",
Packit Service 82fcde
                      strsignal (config->expected_signal));
Packit Service 82fcde
              exit (1);
Packit Service 82fcde
            }
Packit Service 82fcde
        }
Packit Service 82fcde
      else
Packit Service 82fcde
        {
Packit Service 82fcde
          /* Non-zero exit status is expected */
Packit Service 82fcde
          if (WEXITSTATUS (status) != config->expected_status)
Packit Service 82fcde
            {
Packit Service 82fcde
              printf ("Expected status %d, got %d\n",
Packit Service 82fcde
                      config->expected_status, WEXITSTATUS (status));
Packit Service 82fcde
              exit (1);
Packit Service 82fcde
            }
Packit Service 82fcde
        }
Packit Service 82fcde
      return adjust_exit_status (0);
Packit Service 82fcde
    }
Packit Service 82fcde
  /* Process was killed by timer or other signal.  */
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      if (config->expected_signal == 0)
Packit Service 82fcde
        {
Packit Service 82fcde
          printf ("Didn't expect signal from child: got `%s'\n",
Packit Service 82fcde
                  strsignal (WTERMSIG (status)));
Packit Service 82fcde
          exit (1);
Packit Service 82fcde
        }
Packit Service 82fcde
      else if (WTERMSIG (status) != config->expected_signal)
Packit Service 82fcde
        {
Packit Service 82fcde
          printf ("Incorrect signal from child: got `%s', need `%s'\n",
Packit Service 82fcde
                  strsignal (WTERMSIG (status)),
Packit Service 82fcde
                  strsignal (config->expected_signal));
Packit Service 82fcde
          exit (1);
Packit Service 82fcde
        }
Packit Service 82fcde
Packit Service 82fcde
      return adjust_exit_status (0);
Packit Service 82fcde
    }
Packit Service 82fcde
}