about summary refs log tree commit diff
path: root/support/tst-support_capture_subprocess.c
diff options
context:
space:
mode:
Diffstat (limited to 'support/tst-support_capture_subprocess.c')
-rw-r--r--support/tst-support_capture_subprocess.c183
1 files changed, 175 insertions, 8 deletions
diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c
index a685256091..bfa1c181c5 100644
--- a/support/tst-support_capture_subprocess.c
+++ b/support/tst-support_capture_subprocess.c
@@ -23,8 +23,20 @@
 #include <support/capture_subprocess.h>
 #include <support/check.h>
 #include <support/support.h>
+#include <support/temp_file.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <paths.h>
+#include <getopt.h>
+#include <limits.h>
+#include <errno.h>
+#include <array_length.h>
+
+/* Nonzero if the program gets called via 'exec'.  */
+static int restart;
+
+/* Hold the four initial argument used to respawn the process.  */
+static char *initial_argv[5];
 
 /* Write one byte at *P to FD and advance *P.  Do nothing if *P is
    '\0'.  */
@@ -42,6 +54,30 @@ transfer (const unsigned char **p, int fd)
 enum write_mode { out_first, err_first, interleave,
                   write_mode_last =  interleave };
 
+static const char *
+write_mode_to_str (enum write_mode mode)
+{
+  switch (mode)
+    {
+    case out_first:  return "out_first";
+    case err_first:  return "err_first";
+    case interleave: return "interleave";
+    default:         return "write_mode_last";
+    }
+}
+
+static enum write_mode
+str_to_write_mode (const char *mode)
+{
+  if (strcmp (mode, "out_first") == 0)
+    return out_first;
+  else if (strcmp (mode, "err_first") == 0)
+    return err_first;
+  else if (strcmp (mode, "interleave") == 0)
+    return interleave;
+  return write_mode_last;
+}
+
 /* Describe what to write in the subprocess.  */
 struct test
 {
@@ -52,11 +88,9 @@ struct test
   int status;
 };
 
-/* For use with support_capture_subprocess.  */
-static void
-callback (void *closure)
+_Noreturn static void
+test_common (const struct test *test)
 {
-  const struct test *test = closure;
   bool mode_ok = false;
   switch (test->write_mode)
     {
@@ -95,6 +129,40 @@ callback (void *closure)
   exit (test->status);
 }
 
+static int
+parse_int (const char *str)
+{
+  char *endptr;
+  long int ret = strtol (str, &endptr, 10);
+  TEST_COMPARE (errno, 0);
+  TEST_VERIFY (ret >= 0 && ret <= INT_MAX);
+  return ret;
+}
+
+/* For use with support_capture_subprogram.  */
+_Noreturn static void
+handle_restart (char *out, char *err, const char *write_mode,
+		const char *signal, const char *status)
+{
+  struct test test =
+    {
+      out,
+      err,
+      str_to_write_mode (write_mode),
+      parse_int (signal),
+      parse_int (status)
+    };
+  test_common (&test);
+}
+
+/* For use with support_capture_subprocess.  */
+_Noreturn static void
+callback (void *closure)
+{
+  const struct test *test = closure;
+  test_common (test);
+}
+
 /* Create a heap-allocated random string of letters.  */
 static char *
 random_string (size_t length)
@@ -130,12 +198,59 @@ check_stream (const char *what, const struct xmemstream *stream,
     }
 }
 
+static struct support_capture_subprocess
+do_subprocess (struct test *test)
+{
+  return support_capture_subprocess (callback, test);
+}
+
+static struct support_capture_subprocess
+do_subprogram (const struct test *test)
+{
+  /* Three digits per byte plus null terminator.  */
+  char signalstr[3 * sizeof(int) + 1];
+  snprintf (signalstr, sizeof (signalstr), "%d", test->signal);
+  char statusstr[3 * sizeof(int) + 1];
+  snprintf (statusstr, sizeof (statusstr), "%d", test->status);
+
+  int argc = 0;
+  enum {
+    /* 4 elements from initial_argv (path to ld.so, '--library-path', the
+       path', and application name'), 2 for restart argument ('--direct',
+       '--restart'), 5 arguments plus NULL.  */
+    argv_size = 12
+  };
+  char *args[argv_size];
+
+  for (char **arg = initial_argv; *arg != NULL; arg++)
+    args[argc++] = *arg;
+
+  args[argc++] = (char*) "--direct";
+  args[argc++] = (char*) "--restart";
+
+  args[argc++] = test->out;
+  args[argc++] = test->err;
+  args[argc++] = (char*) write_mode_to_str (test->write_mode);
+  args[argc++] = signalstr;
+  args[argc++] = statusstr;
+  args[argc]   = NULL;
+  TEST_VERIFY (argc < argv_size);
+
+  return support_capture_subprogram (args[0], args);
+}
+
+enum test_type
+{
+  subprocess,
+  subprogram,
+};
+
 static int
-do_test (void)
+do_multiple_tests (enum test_type type)
 {
   const int lengths[] = {0, 1, 17, 512, 20000, -1};
 
-  /* Test multiple combinations of support_capture_subprocess.
+  /* Test multiple combinations of support_capture_sub{process,program}.
 
      length_idx_stdout: Index into the lengths array above,
        controls how many bytes are written by the subprocess to
@@ -164,8 +279,10 @@ do_test (void)
               TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]);
               TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]);
 
-              struct support_capture_subprocess result
-                = support_capture_subprocess (callback, &test);
+	      struct support_capture_subprocess result
+		= type == subprocess ? do_subprocess (&test)
+				     : do_subprogram (&test);
+
               check_stream ("stdout", &result.out, test.out);
               check_stream ("stderr", &result.err, test.err);
               if (test.signal != 0)
@@ -185,4 +302,54 @@ do_test (void)
   return 0;
 }
 
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+
+     - one or four parameters if called initially:
+       + argv[1]: path for ld.so        optional
+       + argv[2]: "--library-path"      optional
+       + argv[3]: the library path      optional
+       + argv[4]: the application name
+
+     - six parameters left if called through re-execution:
+       + argv[1]: the application name
+       + argv[2]: the stdout to print
+       + argv[3]: the stderr to print
+       + argv[4]: the write mode to use
+       + argv[5]: the signal to issue
+       + argv[6]: the exit status code to use
+
+     * When built with --enable-hardcoded-path-in-tests or issued without
+       using the loader directly.
+  */
+
+  if (argc != (restart ? 6 : 5) && argc != (restart ? 6 : 2))
+    FAIL_EXIT1 ("wrong number of arguments (%d)", argc);
+
+  if (restart)
+    {
+      handle_restart (argv[1],  /* stdout  */
+		      argv[2],  /* stderr  */
+		      argv[3],  /* write_mode  */
+		      argv[4],  /* signal  */
+		      argv[5]); /* status  */
+    }
+
+  initial_argv[0] = argv[1]; /* path for ld.so  */
+  initial_argv[1] = argv[2]; /* "--library-path"  */
+  initial_argv[2] = argv[3]; /* the library path  */
+  initial_argv[3] = argv[4]; /* the application name  */
+  initial_argv[4] = NULL;
+
+  do_multiple_tests (subprocess);
+  do_multiple_tests (subprogram);
+
+  return 0;
+}
+
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+#define TEST_FUNCTION_ARGV do_test
 #include <support/test-driver.c>