diff mbox series

[1/2] support: Add support_capture_subprogram

Message ID 20190416212713.24659-1-adhemerval.zanella@linaro.org
State Accepted
Commit 0e169691290a6d2187a4ff41495fc5678cbfdcdc
Headers show
Series [1/2] support: Add support_capture_subprogram | expand

Commit Message

Adhemerval Zanella April 16, 2019, 9:27 p.m. UTC
Its API is similar to support_capture_subprogram, but rather creates a
new process based on the input path and arguments.  Under the hoods it
uses posix_spawn to create the new process.

It also allows the use of other support_capture_* functions to check
for expected results and free the resources.

Checked on x86_64-linux-gnu.

	* support/Makefile (libsupport-routines): Add support_subprocess,
	xposix_spawn, xposix_spawn_file_actions_addclose, and
	xposix_spawn_file_actions_adddup2.
	(tst-support_capture_subprocess-ARGS): New rule.
	* support/capture_subprocess.h (support_capture_subprogram): New
	prototype.
	* support/support_capture_subprocess.c (support_capture_subprocess):
	Refactor to use support_subprocess and support_capture_poll.
	(support_capture_subprogram): New function.
	* support/tst-support_capture_subprocess.c (write_mode_to_str,
	str_to_write_mode, test_common, parse_int, handle_restart,
	do_subprocess, do_subprogram, do_multiple_tests): New functions.
	(do_test): Add support_capture_subprogram tests.
	* support/subprocess.h: New file.
	* support/support_subprocess.c: Likewise.
	* support/xposix_spawn.c: Likewise.
	* support/xposix_spawn_file_actions_addclose.c: Likewise.
	* support/xposix_spawn_file_actions_adddup2.c: Likewise.
	* support/xspawn.h: Likewise.
---
 support/Makefile                             |   8 +-
 support/capture_subprocess.h                 |   6 +
 support/subprocess.h                         |  49 ++++++
 support/support_capture_subprocess.c         |  80 ++++-----
 support/support_subprocess.c                 | 152 ++++++++++++++++
 support/tst-support_capture_subprocess.c     | 175 ++++++++++++++++++-
 support/xposix_spawn.c                       |  32 ++++
 support/xposix_spawn_file_actions_addclose.c |  29 +++
 support/xposix_spawn_file_actions_adddup2.c  |  30 ++++
 support/xspawn.h                             |  34 ++++
 10 files changed, 544 insertions(+), 51 deletions(-)
 create mode 100644 support/subprocess.h
 create mode 100644 support/support_subprocess.c
 create mode 100644 support/xposix_spawn.c
 create mode 100644 support/xposix_spawn_file_actions_addclose.c
 create mode 100644 support/xposix_spawn_file_actions_adddup2.c
 create mode 100644 support/xspawn.h

-- 
2.17.1

Comments

Carlos O'Donell April 17, 2019, 3:49 a.m. UTC | #1
On 4/16/19 5:27 PM, Adhemerval Zanella wrote:> Its API is similar to support_capture_subprogram, but rather creates a> new process based on the input path and arguments.  Under the hoods it> uses posix_spawn to create the new process.
Do you mean similar to support_capture_subprocess?

LGTM if:
- you adjust commit message.
- drop unrelated change.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

  
> It also allows the use of other support_capture_* functions to check

> for expected results and free the resources.


OK.

> Checked on x86_64-linux-gnu.

> 

> 	* support/Makefile (libsupport-routines): Add support_subprocess,

> 	xposix_spawn, xposix_spawn_file_actions_addclose, and

> 	xposix_spawn_file_actions_adddup2.

> 	(tst-support_capture_subprocess-ARGS): New rule.

> 	* support/capture_subprocess.h (support_capture_subprogram): New

> 	prototype.

> 	* support/support_capture_subprocess.c (support_capture_subprocess):

> 	Refactor to use support_subprocess and support_capture_poll.

> 	(support_capture_subprogram): New function.

> 	* support/tst-support_capture_subprocess.c (write_mode_to_str,

> 	str_to_write_mode, test_common, parse_int, handle_restart,

> 	do_subprocess, do_subprogram, do_multiple_tests): New functions.

> 	(do_test): Add support_capture_subprogram tests.

> 	* support/subprocess.h: New file.

> 	* support/support_subprocess.c: Likewise.

> 	* support/xposix_spawn.c: Likewise.

> 	* support/xposix_spawn_file_actions_addclose.c: Likewise.

> 	* support/xposix_spawn_file_actions_adddup2.c: Likewise.

> 	* support/xspawn.h: Likewise.

> ---

>   support/Makefile                             |   8 +-

>   support/capture_subprocess.h                 |   6 +

>   support/subprocess.h                         |  49 ++++++

>   support/support_capture_subprocess.c         |  80 ++++-----

>   support/support_subprocess.c                 | 152 ++++++++++++++++

>   support/tst-support_capture_subprocess.c     | 175 ++++++++++++++++++-

>   support/xposix_spawn.c                       |  32 ++++

>   support/xposix_spawn_file_actions_addclose.c |  29 +++

>   support/xposix_spawn_file_actions_adddup2.c  |  30 ++++

>   support/xspawn.h                             |  34 ++++

>   10 files changed, 544 insertions(+), 51 deletions(-)

>   create mode 100644 support/subprocess.h

>   create mode 100644 support/support_subprocess.c

>   create mode 100644 support/xposix_spawn.c

>   create mode 100644 support/xposix_spawn_file_actions_addclose.c

>   create mode 100644 support/xposix_spawn_file_actions_adddup2.c

>   create mode 100644 support/xspawn.h

> 

> diff --git a/support/Makefile b/support/Makefile

> index f173565202..4daf3f46fb 100644

> --- a/support/Makefile

> +++ b/support/Makefile

> @@ -63,6 +63,7 @@ libsupport-routines = \

>     support_record_failure \

>     support_run_diff \

>     support_shared_allocate \

> +  support_subprocess \


OK.

>     support_test_compare_blob \

>     support_test_compare_failure \

>     support_test_compare_string \

> @@ -151,6 +152,9 @@ libsupport-routines = \

>     xsignal \

>     xsigstack \

>     xsocket \

> +  xposix_spawn \

> +  xposix_spawn_file_actions_addclose \

> +  xposix_spawn_file_actions_adddup2 \


OK.

>     xstrdup \

>     xstrndup \

>     xsymlink \

> @@ -210,7 +214,7 @@ tests = \

>     tst-test_compare_blob \

>     tst-test_compare_string \

>     tst-xreadlink \

> -  tst-xsigstack \

> +  tst-xsigstack


Drop unrelated changes.

>   

>   ifeq ($(run-built-tests),yes)

>   tests-special = \

> @@ -226,4 +230,6 @@ endif

>   

>   $(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so

>   

> +tst-support_capture_subprocess-ARGS = -- $(host-test-program-cmd)

> +


OK.

>   include ../Rules

> diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h

> index 2dce42e3a3..2832cfc635 100644

> --- a/support/capture_subprocess.h

> +++ b/support/capture_subprocess.h

> @@ -35,6 +35,12 @@ struct support_capture_subprocess

>   struct support_capture_subprocess support_capture_subprocess

>     (void (*callback) (void *), void *closure);

>   

> +/* Issue FILE with ARGV arguments by using posix_spawn and capture standard

> +   output, standard error, and the exit status.  The out.buffer and err.buffer

> +   are handle as support_capture_subprocess.  */

> +struct support_capture_subprocess support_capture_subprogram

> +  (const char *file, char *const argv[]);


OK.

> +

>   /* Deallocate the subprocess data captured by

>      support_capture_subprocess.  */

>   void support_capture_subprocess_free (struct support_capture_subprocess *);

> diff --git a/support/subprocess.h b/support/subprocess.h

> new file mode 100644

> index 0000000000..002319cbad

> --- /dev/null

> +++ b/support/subprocess.h

> @@ -0,0 +1,49 @@

> +/* Create a subprocess.


OK.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   This file is part of the GNU C Library.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU Lesser General Public

> +   License along with the GNU C Library; if not, see

> +   <http://www.gnu.org/licenses/>.  */

> +

> +#ifndef SUPPORT_SUBPROCESS_H

> +#define SUPPORT_SUBPROCESS_H

> +

> +#include <sys/types.h>

> +

> +struct support_subprocess

> +{

> +  int stdout_pipe[2];

> +  int stderr_pipe[2];

> +  pid_t pid;

> +};

> +

> +/* Invoke CALLBACK (CLOSURE) in a subprocess created with fork and return

> +   its PID, a pipe redirected to STDOUT, and a pipe redirected to STDERR.  */

> +struct support_subprocess support_subprocess

> +  (void (*callback) (void *), void *closure);


OK.

> +

> +/* Issue FILE with ARGV arguments by using posix_spawn and return is PID, a

> +   pipe redirected to STDOUT, and a pipe redirected to STDERR.  */

> +struct support_subprocess support_subprogram

> +  (const char *file, char *const argv[]);


OK.

> +

> +/* Wait for the subprocess indicated by PROC::PID.  Return the status

> +   indicate by waitpid call.  */

> +int support_process_wait (struct support_subprocess *proc);


OK.

> +

> +/* Terminate the subprocess indicated by PROC::PID, first with a SIGTERM and

> +   then with a SIGKILL.  Return the status as for waitpid call.  */

> +int support_process_terminate (struct support_subprocess *proc);


OK.

> +

> +#endif

> diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c

> index 167514faf1..0d7a9baa85 100644

> --- a/support/support_capture_subprocess.c

> +++ b/support/support_capture_subprocess.c

> @@ -16,6 +16,7 @@

>      License along with the GNU C Library; if not, see

>      <http://www.gnu.org/licenses/>.  */

>   

> +#include <support/subprocess.h>


OK.

>   #include <support/capture_subprocess.h>

>   

>   #include <errno.h>

> @@ -23,6 +24,7 @@

>   #include <support/check.h>

>   #include <support/xunistd.h>

>   #include <support/xsocket.h>

> +#include <support/xspawn.h>


OK.

>   

>   static void

>   transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)

> @@ -50,59 +52,53 @@ transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)

>       }

>   }

>   

> -struct support_capture_subprocess

> -support_capture_subprocess (void (*callback) (void *), void *closure)

> +static void

> +support_capture_poll (struct support_capture_subprocess *result,

> +		      struct support_subprocess *proc)

>   {

> -  struct support_capture_subprocess result;

> -  xopen_memstream (&result.out);

> -  xopen_memstream (&result.err);

> -

> -  int stdout_pipe[2];

> -  xpipe (stdout_pipe);

> -  TEST_VERIFY (stdout_pipe[0] > STDERR_FILENO);

> -  TEST_VERIFY (stdout_pipe[1] > STDERR_FILENO);

> -  int stderr_pipe[2];

> -  xpipe (stderr_pipe);

> -  TEST_VERIFY (stderr_pipe[0] > STDERR_FILENO);

> -  TEST_VERIFY (stderr_pipe[1] > STDERR_FILENO);

> -

> -  TEST_VERIFY (fflush (stdout) == 0);

> -  TEST_VERIFY (fflush (stderr) == 0);

> -

> -  pid_t pid = xfork ();

> -  if (pid == 0)

> -    {

> -      xclose (stdout_pipe[0]);

> -      xclose (stderr_pipe[0]);

> -      xdup2 (stdout_pipe[1], STDOUT_FILENO);

> -      xdup2 (stderr_pipe[1], STDERR_FILENO);

> -      xclose (stdout_pipe[1]);

> -      xclose (stderr_pipe[1]);

> -      callback (closure);

> -      _exit (0);

> -    }

> -  xclose (stdout_pipe[1]);

> -  xclose (stderr_pipe[1]);

> -

>     struct pollfd fds[2] =

>       {

> -      { .fd = stdout_pipe[0], .events = POLLIN },

> -      { .fd = stderr_pipe[0], .events = POLLIN },

> +      { .fd = proc->stdout_pipe[0], .events = POLLIN },

> +      { .fd = proc->stderr_pipe[0], .events = POLLIN },

>       };

>   

>     do

>       {

>         xpoll (fds, 2, -1);

> -      transfer ("stdout", &fds[0], &result.out);

> -      transfer ("stderr", &fds[1], &result.err);

> +      transfer ("stdout", &fds[0], &result->out);

> +      transfer ("stderr", &fds[1], &result->err);

>       }

>     while (fds[0].events != 0 || fds[1].events != 0);

> -  xclose (stdout_pipe[0]);

> -  xclose (stderr_pipe[0]);

>   

> -  xfclose_memstream (&result.out);

> -  xfclose_memstream (&result.err);

> -  xwaitpid (pid, &result.status, 0);

> +  xfclose_memstream (&result->out);

> +  xfclose_memstream (&result->err);

> +

> +  result->status = support_process_wait (proc);

> +}


OK.

> +

> +struct support_capture_subprocess

> +support_capture_subprocess (void (*callback) (void *), void *closure)

> +{

> +  struct support_capture_subprocess result;

> +  xopen_memstream (&result.out);

> +  xopen_memstream (&result.err);

> +

> +  struct support_subprocess proc = support_subprocess (callback, closure);

> +

> +  support_capture_poll (&result, &proc);

> +  return result;

> +}


OK.

> +

> +struct support_capture_subprocess

> +support_capture_subprogram (const char *file, char *const argv[])

> +{

> +  struct support_capture_subprocess result;

> +  xopen_memstream (&result.out);

> +  xopen_memstream (&result.err);

> +

> +  struct support_subprocess proc = support_subprogram (file, argv);

> +

> +  support_capture_poll (&result, &proc);

>     return result;

>   }


OK.

>   

> diff --git a/support/support_subprocess.c b/support/support_subprocess.c

> new file mode 100644

> index 0000000000..f847fa5e89

> --- /dev/null

> +++ b/support/support_subprocess.c

> @@ -0,0 +1,152 @@

> +/* Create subprocess.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   This file is part of the GNU C Library.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU Lesser General Public

> +   License along with the GNU C Library; if not, see

> +   <http://www.gnu.org/licenses/>.  */

> +

> +#include <stdio.h>

> +#include <signal.h>

> +#include <time.h>

> +#include <sys/wait.h>

> +#include <stdbool.h>

> +#include <support/xspawn.h>

> +#include <support/check.h>

> +#include <support/xunistd.h>

> +#include <support/subprocess.h>

> +

> +static struct support_subprocess

> +support_suprocess_init (void)

> +{

> +  struct support_subprocess result;

> +

> +  xpipe (result.stdout_pipe);

> +  TEST_VERIFY (result.stdout_pipe[0] > STDERR_FILENO);

> +  TEST_VERIFY (result.stdout_pipe[1] > STDERR_FILENO);

> +

> +  xpipe (result.stderr_pipe);

> +  TEST_VERIFY (result.stderr_pipe[0] > STDERR_FILENO);

> +  TEST_VERIFY (result.stderr_pipe[1] > STDERR_FILENO);

> +

> +  TEST_VERIFY (fflush (stdout) == 0);

> +  TEST_VERIFY (fflush (stderr) == 0);

> +

> +  return result;

> +}


OK.

> +

> +struct support_subprocess

> +support_subprocess (void (*callback) (void *), void *closure)

> +{

> +  struct support_subprocess result = support_suprocess_init ();

> +

> +  result.pid = xfork ();

> +  if (result.pid == 0)

> +    {

> +      xclose (result.stdout_pipe[0]);

> +      xclose (result.stderr_pipe[0]);

> +      xdup2 (result.stdout_pipe[1], STDOUT_FILENO);

> +      xdup2 (result.stderr_pipe[1], STDERR_FILENO);

> +      xclose (result.stdout_pipe[1]);

> +      xclose (result.stderr_pipe[1]);

> +      callback (closure);

> +      _exit (0);

> +    }

> +  xclose (result.stdout_pipe[1]);

> +  xclose (result.stderr_pipe[1]);

> +

> +  return result;

> +}


OK.

> +

> +struct support_subprocess

> +support_subprogram (const char *file, char *const argv[])

> +{

> +  struct support_subprocess result = support_suprocess_init ();

> +

> +  posix_spawn_file_actions_t fa;

> +  /* posix_spawn_file_actions_init does not fail.  */

> +  posix_spawn_file_actions_init (&fa);

> +

> +  xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[0]);

> +  xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[0]);

> +  xposix_spawn_file_actions_adddup2 (&fa, result.stdout_pipe[1], STDOUT_FILENO);

> +  xposix_spawn_file_actions_adddup2 (&fa, result.stderr_pipe[1], STDERR_FILENO);

> +  xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]);

> +  xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]);

> +

> +  result.pid = xposix_spawn (file, &fa, NULL, argv, NULL);

> +

> +  xclose (result.stdout_pipe[1]);

> +  xclose (result.stderr_pipe[1]);

> +

> +  return result;

> +}


OK.

> +

> +int

> +support_process_wait (struct support_subprocess *proc)

> +{

> +  xclose (proc->stdout_pipe[0]);

> +  xclose (proc->stderr_pipe[0]);

> +

> +  int status;

> +  xwaitpid (proc->pid, &status, 0);

> +  return status;

> +}


OK.

> +

> +

> +static bool

> +support_process_kill (int pid, int signo, int *status)

> +{

> +  /* Kill the whole process group.  */

> +  kill (-pid, signo);

> +  /* In case setpgid failed in the child, kill it individually too.  */

> +  kill (pid, signo);

> +

> +  /* Wait for it to terminate.  */

> +  pid_t killed;

> +  for (int i = 0; i < 5; ++i)

> +    {

> +      int status;

> +      killed = xwaitpid (pid, &status, WNOHANG|WUNTRACED);

> +      if (killed != 0)

> +        break;

> +

> +      /* Delay, give the system time to process the kill.  If the

> +         nanosleep() call return prematurely, all the better.  We

> +         won't restart it since this probably means the child process

> +         finally died.  */

> +      nanosleep (&((struct timespec) { 0, 100000000 }), NULL);

> +    }

> +  if (killed != 0 && killed != pid)

> +    return false;

> +

> +  return true;

> +}


OK.

> +

> +int

> +support_process_terminate (struct support_subprocess *proc)

> +{

> +  xclose (proc->stdout_pipe[0]);

> +  xclose (proc->stderr_pipe[0]);

> +

> +  int status;

> +  pid_t killed = xwaitpid (proc->pid, &status, WNOHANG|WUNTRACED);

> +  if (killed != 0 && killed == proc->pid)

> +    return status;

> +

> +  /* Subprocess is already running, terminate it.  */

> +  if (!support_process_kill (proc->pid, SIGTERM, &status) )

> +    support_process_kill (proc->pid, SIGKILL, &status);

> +

> +  return status;

> +}


OK.

> diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c

> index d8ba42ea8b..0f04dc541c 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;


OK.

> +

> +/* Hold the four initial argument used to respawn the process.  */

> +static char *initial_argv[5];


OK.

>   

>   /* 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";

> +    }

> +}


OK.

> +

> +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;

> +}


OK.

> +

>   /* 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);

>   }


OK.

>   

> +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;

> +}


OK.

> +

> +/* 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);


OK.

> +}

> +

> +/* For use with support_capture_subprocess.  */

> +_Noreturn static void

> +callback (void *closure)

> +{

> +  const struct test *test = closure;

> +  test_common (test);

> +}


OK.

> +

>   /* Create a heap-allocated random string of letters.  */

>   static char *

>   random_string (size_t length)

> @@ -130,12 +198,51 @@ 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);

> +}


OK.

> +

> +static struct support_capture_subprocess

> +do_subprogram (const struct test *test)

> +{

> +  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;

> +  char *args[11];

> +

> +  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;

> +

> +  return support_capture_subprogram (args[0], args);


OK.

> +}

> +

> +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}.


OK.

>   

>        length_idx_stdout: Index into the lengths array above,

>          controls how many bytes are written by the subprocess to

> @@ -164,8 +271,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);


OK.

> +

>                 check_stream ("stdout", &result.out, test.out);

>                 check_stream ("stderr", &result.err, test.err);

>   

> @@ -199,4 +308,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;

> +}


OK.

> +

> +#define CMDLINE_OPTIONS \

> +  { "restart", no_argument, &restart, 1 },

> +#define TEST_FUNCTION_ARGV do_test

>   #include <support/test-driver.c>


OK.

> diff --git a/support/xposix_spawn.c b/support/xposix_spawn.c

> new file mode 100644

> index 0000000000..e846017632

> --- /dev/null

> +++ b/support/xposix_spawn.c

> @@ -0,0 +1,32 @@

> +/* xposix_spawn implementation.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   This file is part of the GNU C Library.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU Lesser General Public

> +   License along with the GNU C Library; if not, see

> +   <http://www.gnu.org/licenses/>.  */

> +

> +#include <support/xspawn.h>

> +#include <support/check.h>

> +

> +pid_t

> +xposix_spawn (const char *file, const posix_spawn_file_actions_t *fa,

> +	      const posix_spawnattr_t *attr, char *const args[],

> +	      char *const envp[])

> +{

> +  pid_t pid;

> +  int status = posix_spawn (&pid, file, fa, attr, args, envp);

> +  if (status != 0)

> +    FAIL_EXIT1 ("posix_spawn to %s file failed: %m", file);

> +  return pid;

> +}


OK.

> diff --git a/support/xposix_spawn_file_actions_addclose.c b/support/xposix_spawn_file_actions_addclose.c

> new file mode 100644

> index 0000000000..eed54a6514

> --- /dev/null

> +++ b/support/xposix_spawn_file_actions_addclose.c

> @@ -0,0 +1,29 @@

> +/* xposix_spawn_file_actions_addclose implementation.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   This file is part of the GNU C Library.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU Lesser General Public

> +   License along with the GNU C Library; if not, see

> +   <http://www.gnu.org/licenses/>.  */

> +

> +#include <support/xspawn.h>

> +#include <support/check.h>

> +

> +int

> +xposix_spawn_file_actions_addclose (posix_spawn_file_actions_t *fa, int fd)

> +{

> +  int status = posix_spawn_file_actions_addclose (fa, fd);

> +  if (status == -1)

> +    FAIL_EXIT1 ("posix_spawn_file_actions_addclose failed: %m\n");

> +  return status;

> +}


OK.

> diff --git a/support/xposix_spawn_file_actions_adddup2.c b/support/xposix_spawn_file_actions_adddup2.c

> new file mode 100644

> index 0000000000..a43b6490be

> --- /dev/null

> +++ b/support/xposix_spawn_file_actions_adddup2.c

> @@ -0,0 +1,30 @@

> +/* xposix_spawn_file_actions_adddup2 implementation.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   This file is part of the GNU C Library.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU Lesser General Public

> +   License along with the GNU C Library; if not, see

> +   <http://www.gnu.org/licenses/>.  */

> +

> +#include <support/xspawn.h>

> +#include <support/check.h>

> +

> +int

> +xposix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *fa, int fd,

> +				   int newfd)

> +{

> +  int status = posix_spawn_file_actions_adddup2 (fa, fd, newfd);

> +  if (status == -1)

> +    FAIL_EXIT1 ("posix_spawn_file_actions_adddup2 failed: %m\n");

> +  return status;

> +}


OK.

> diff --git a/support/xspawn.h b/support/xspawn.h

> new file mode 100644

> index 0000000000..bbf89132e4

> --- /dev/null

> +++ b/support/xspawn.h

> @@ -0,0 +1,34 @@

> +/* posix_spawn with support checks.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   This file is part of the GNU C Library.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU Lesser General Public

> +   License along with the GNU C Library; if not, see

> +   <http://www.gnu.org/licenses/>.  */

> +

> +#ifndef SUPPORT_XSPAWN_H

> +#define SUPPORT_XSPAWN_H

> +

> +#include <spawn.h>

> +

> +__BEGIN_DECLS

> +

> +int xposix_spawn_file_actions_addclose (posix_spawn_file_actions_t *, int);

> +int xposix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *, int, int);

> +

> +pid_t xposix_spawn (const char *, const posix_spawn_file_actions_t *,

> +		    const posix_spawnattr_t *, char *const [], char *const []);

> +

> +__END_DECLS

> +

> +#endif

> 


OK.

-- 
Cheers,
Carlos.
Adhemerval Zanella April 17, 2019, 1:27 p.m. UTC | #2
On 17/04/2019 00:49, Carlos O'Donell wrote:
> On 4/16/19 5:27 PM, Adhemerval Zanella wrote:> Its API is similar to support_capture_subprogram, but rather creates a> new process based on the input path and arguments.  Under the hoods it> uses posix_spawn to create the new process.

> Do you mean similar to support_capture_subprocess?


Oops, yes I meant support_capture_sub*process*.

> 

> LGTM if:

> - you adjust commit message.

> - drop unrelated change.


Fixed both.

> 

> Reviewed-by: Carlos O'Donell <carlos@redhat.com>


>> diff --git a/support/Makefile b/support/Makefile

>> index f173565202..4daf3f46fb 100644

>> --- a/support/Makefile

>> +++ b/support/Makefile

>> @@ -63,6 +63,7 @@ libsupport-routines = \

>>     support_record_failure \

>>     support_run_diff \

>>     support_shared_allocate \

>> +  support_subprocess \

> 

> OK.

> 

>>     support_test_compare_blob \

>>     support_test_compare_failure \

>>     support_test_compare_string \

>> @@ -151,6 +152,9 @@ libsupport-routines = \

>>     xsignal \

>>     xsigstack \

>>     xsocket \

>> +  xposix_spawn \

>> +  xposix_spawn_file_actions_addclose \

>> +  xposix_spawn_file_actions_adddup2 \

> 

> OK.

> 

>>     xstrdup \

>>     xstrndup \

>>     xsymlink \

>> @@ -210,7 +214,7 @@ tests = \

>>     tst-test_compare_blob \

>>     tst-test_compare_string \

>>     tst-xreadlink \

>> -  tst-xsigstack \

>> +  tst-xsigstack

> 

> Drop unrelated changes.


Ack.
diff mbox series

Patch

diff --git a/support/Makefile b/support/Makefile
index f173565202..4daf3f46fb 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -63,6 +63,7 @@  libsupport-routines = \
   support_record_failure \
   support_run_diff \
   support_shared_allocate \
+  support_subprocess \
   support_test_compare_blob \
   support_test_compare_failure \
   support_test_compare_string \
@@ -151,6 +152,9 @@  libsupport-routines = \
   xsignal \
   xsigstack \
   xsocket \
+  xposix_spawn \
+  xposix_spawn_file_actions_addclose \
+  xposix_spawn_file_actions_adddup2 \
   xstrdup \
   xstrndup \
   xsymlink \
@@ -210,7 +214,7 @@  tests = \
   tst-test_compare_blob \
   tst-test_compare_string \
   tst-xreadlink \
-  tst-xsigstack \
+  tst-xsigstack
 
 ifeq ($(run-built-tests),yes)
 tests-special = \
@@ -226,4 +230,6 @@  endif
 
 $(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so
 
+tst-support_capture_subprocess-ARGS = -- $(host-test-program-cmd)
+
 include ../Rules
diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h
index 2dce42e3a3..2832cfc635 100644
--- a/support/capture_subprocess.h
+++ b/support/capture_subprocess.h
@@ -35,6 +35,12 @@  struct support_capture_subprocess
 struct support_capture_subprocess support_capture_subprocess
   (void (*callback) (void *), void *closure);
 
+/* Issue FILE with ARGV arguments by using posix_spawn and capture standard
+   output, standard error, and the exit status.  The out.buffer and err.buffer
+   are handle as support_capture_subprocess.  */
+struct support_capture_subprocess support_capture_subprogram
+  (const char *file, char *const argv[]);
+
 /* Deallocate the subprocess data captured by
    support_capture_subprocess.  */
 void support_capture_subprocess_free (struct support_capture_subprocess *);
diff --git a/support/subprocess.h b/support/subprocess.h
new file mode 100644
index 0000000000..002319cbad
--- /dev/null
+++ b/support/subprocess.h
@@ -0,0 +1,49 @@ 
+/* Create a subprocess.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef SUPPORT_SUBPROCESS_H
+#define SUPPORT_SUBPROCESS_H
+
+#include <sys/types.h>
+
+struct support_subprocess
+{
+  int stdout_pipe[2];
+  int stderr_pipe[2];
+  pid_t pid;
+};
+
+/* Invoke CALLBACK (CLOSURE) in a subprocess created with fork and return
+   its PID, a pipe redirected to STDOUT, and a pipe redirected to STDERR.  */
+struct support_subprocess support_subprocess
+  (void (*callback) (void *), void *closure);
+
+/* Issue FILE with ARGV arguments by using posix_spawn and return is PID, a 
+   pipe redirected to STDOUT, and a pipe redirected to STDERR.  */
+struct support_subprocess support_subprogram
+  (const char *file, char *const argv[]);
+
+/* Wait for the subprocess indicated by PROC::PID.  Return the status
+   indicate by waitpid call.  */
+int support_process_wait (struct support_subprocess *proc);
+
+/* Terminate the subprocess indicated by PROC::PID, first with a SIGTERM and
+   then with a SIGKILL.  Return the status as for waitpid call.  */
+int support_process_terminate (struct support_subprocess *proc);
+
+#endif
diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c
index 167514faf1..0d7a9baa85 100644
--- a/support/support_capture_subprocess.c
+++ b/support/support_capture_subprocess.c
@@ -16,6 +16,7 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <support/subprocess.h>
 #include <support/capture_subprocess.h>
 
 #include <errno.h>
@@ -23,6 +24,7 @@ 
 #include <support/check.h>
 #include <support/xunistd.h>
 #include <support/xsocket.h>
+#include <support/xspawn.h>
 
 static void
 transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
@@ -50,59 +52,53 @@  transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
     }
 }
 
-struct support_capture_subprocess
-support_capture_subprocess (void (*callback) (void *), void *closure)
+static void
+support_capture_poll (struct support_capture_subprocess *result,
+		      struct support_subprocess *proc)
 {
-  struct support_capture_subprocess result;
-  xopen_memstream (&result.out);
-  xopen_memstream (&result.err);
-
-  int stdout_pipe[2];
-  xpipe (stdout_pipe);
-  TEST_VERIFY (stdout_pipe[0] > STDERR_FILENO);
-  TEST_VERIFY (stdout_pipe[1] > STDERR_FILENO);
-  int stderr_pipe[2];
-  xpipe (stderr_pipe);
-  TEST_VERIFY (stderr_pipe[0] > STDERR_FILENO);
-  TEST_VERIFY (stderr_pipe[1] > STDERR_FILENO);
-
-  TEST_VERIFY (fflush (stdout) == 0);
-  TEST_VERIFY (fflush (stderr) == 0);
-
-  pid_t pid = xfork ();
-  if (pid == 0)
-    {
-      xclose (stdout_pipe[0]);
-      xclose (stderr_pipe[0]);
-      xdup2 (stdout_pipe[1], STDOUT_FILENO);
-      xdup2 (stderr_pipe[1], STDERR_FILENO);
-      xclose (stdout_pipe[1]);
-      xclose (stderr_pipe[1]);
-      callback (closure);
-      _exit (0);
-    }
-  xclose (stdout_pipe[1]);
-  xclose (stderr_pipe[1]);
-
   struct pollfd fds[2] =
     {
-      { .fd = stdout_pipe[0], .events = POLLIN },
-      { .fd = stderr_pipe[0], .events = POLLIN },
+      { .fd = proc->stdout_pipe[0], .events = POLLIN },
+      { .fd = proc->stderr_pipe[0], .events = POLLIN },
     };
 
   do
     {
       xpoll (fds, 2, -1);
-      transfer ("stdout", &fds[0], &result.out);
-      transfer ("stderr", &fds[1], &result.err);
+      transfer ("stdout", &fds[0], &result->out);
+      transfer ("stderr", &fds[1], &result->err);
     }
   while (fds[0].events != 0 || fds[1].events != 0);
-  xclose (stdout_pipe[0]);
-  xclose (stderr_pipe[0]);
 
-  xfclose_memstream (&result.out);
-  xfclose_memstream (&result.err);
-  xwaitpid (pid, &result.status, 0);
+  xfclose_memstream (&result->out);
+  xfclose_memstream (&result->err);
+ 
+  result->status = support_process_wait (proc);
+}
+
+struct support_capture_subprocess
+support_capture_subprocess (void (*callback) (void *), void *closure)
+{
+  struct support_capture_subprocess result;
+  xopen_memstream (&result.out);
+  xopen_memstream (&result.err);
+
+  struct support_subprocess proc = support_subprocess (callback, closure);
+
+  support_capture_poll (&result, &proc);
+  return result;
+}
+
+struct support_capture_subprocess
+support_capture_subprogram (const char *file, char *const argv[])
+{
+  struct support_capture_subprocess result;
+  xopen_memstream (&result.out);
+  xopen_memstream (&result.err);
+
+  struct support_subprocess proc = support_subprogram (file, argv);
+
+  support_capture_poll (&result, &proc);
   return result;
 }
 
diff --git a/support/support_subprocess.c b/support/support_subprocess.c
new file mode 100644
index 0000000000..f847fa5e89
--- /dev/null
+++ b/support/support_subprocess.c
@@ -0,0 +1,152 @@ 
+/* Create subprocess.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <stdbool.h>
+#include <support/xspawn.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <support/subprocess.h>
+
+static struct support_subprocess
+support_suprocess_init (void)
+{
+  struct support_subprocess result;
+
+  xpipe (result.stdout_pipe);
+  TEST_VERIFY (result.stdout_pipe[0] > STDERR_FILENO);
+  TEST_VERIFY (result.stdout_pipe[1] > STDERR_FILENO);
+
+  xpipe (result.stderr_pipe);
+  TEST_VERIFY (result.stderr_pipe[0] > STDERR_FILENO);
+  TEST_VERIFY (result.stderr_pipe[1] > STDERR_FILENO);
+
+  TEST_VERIFY (fflush (stdout) == 0);
+  TEST_VERIFY (fflush (stderr) == 0);
+
+  return result;
+}
+
+struct support_subprocess
+support_subprocess (void (*callback) (void *), void *closure)
+{
+  struct support_subprocess result = support_suprocess_init ();
+
+  result.pid = xfork ();
+  if (result.pid == 0)
+    {
+      xclose (result.stdout_pipe[0]);
+      xclose (result.stderr_pipe[0]);
+      xdup2 (result.stdout_pipe[1], STDOUT_FILENO);
+      xdup2 (result.stderr_pipe[1], STDERR_FILENO);
+      xclose (result.stdout_pipe[1]);
+      xclose (result.stderr_pipe[1]);
+      callback (closure);
+      _exit (0);
+    }
+  xclose (result.stdout_pipe[1]);
+  xclose (result.stderr_pipe[1]);
+
+  return result;
+}
+
+struct support_subprocess
+support_subprogram (const char *file, char *const argv[])
+{
+  struct support_subprocess result = support_suprocess_init ();
+
+  posix_spawn_file_actions_t fa;
+  /* posix_spawn_file_actions_init does not fail.  */
+  posix_spawn_file_actions_init (&fa);
+  
+  xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[0]);
+  xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[0]);
+  xposix_spawn_file_actions_adddup2 (&fa, result.stdout_pipe[1], STDOUT_FILENO);
+  xposix_spawn_file_actions_adddup2 (&fa, result.stderr_pipe[1], STDERR_FILENO);
+  xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]);
+  xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]);
+
+  result.pid = xposix_spawn (file, &fa, NULL, argv, NULL);
+
+  xclose (result.stdout_pipe[1]);
+  xclose (result.stderr_pipe[1]);
+
+  return result;
+}
+
+int
+support_process_wait (struct support_subprocess *proc)
+{
+  xclose (proc->stdout_pipe[0]);
+  xclose (proc->stderr_pipe[0]);
+
+  int status;
+  xwaitpid (proc->pid, &status, 0);
+  return status;
+}
+
+
+static bool
+support_process_kill (int pid, int signo, int *status)
+{
+  /* Kill the whole process group.  */
+  kill (-pid, signo);
+  /* In case setpgid failed in the child, kill it individually too.  */
+  kill (pid, signo);
+
+  /* Wait for it to terminate.  */
+  pid_t killed;
+  for (int i = 0; i < 5; ++i)
+    {
+      int status;
+      killed = xwaitpid (pid, &status, WNOHANG|WUNTRACED);
+      if (killed != 0)
+        break;
+
+      /* Delay, give the system time to process the kill.  If the
+         nanosleep() call return prematurely, all the better.  We
+         won't restart it since this probably means the child process
+         finally died.  */
+      nanosleep (&((struct timespec) { 0, 100000000 }), NULL);
+    }
+  if (killed != 0 && killed != pid)
+    return false;
+
+  return true;
+}
+
+int
+support_process_terminate (struct support_subprocess *proc)
+{
+  xclose (proc->stdout_pipe[0]);
+  xclose (proc->stderr_pipe[0]);
+
+  int status;
+  pid_t killed = xwaitpid (proc->pid, &status, WNOHANG|WUNTRACED);
+  if (killed != 0 && killed == proc->pid)
+    return status;
+
+  /* Subprocess is already running, terminate it.  */
+  if (!support_process_kill (proc->pid, SIGTERM, &status) )
+    support_process_kill (proc->pid, SIGKILL, &status);
+
+  return status;
+}
diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c
index d8ba42ea8b..0f04dc541c 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,51 @@  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)
+{
+  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;
+  char *args[11];
+
+  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;
+
+  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 +271,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);
 
@@ -199,4 +308,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>
diff --git a/support/xposix_spawn.c b/support/xposix_spawn.c
new file mode 100644
index 0000000000..e846017632
--- /dev/null
+++ b/support/xposix_spawn.c
@@ -0,0 +1,32 @@ 
+/* xposix_spawn implementation.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/xspawn.h>
+#include <support/check.h>
+
+pid_t
+xposix_spawn (const char *file, const posix_spawn_file_actions_t *fa,
+	      const posix_spawnattr_t *attr, char *const args[],
+	      char *const envp[])
+{
+  pid_t pid;
+  int status = posix_spawn (&pid, file, fa, attr, args, envp);
+  if (status != 0)
+    FAIL_EXIT1 ("posix_spawn to %s file failed: %m", file);
+  return pid;
+}
diff --git a/support/xposix_spawn_file_actions_addclose.c b/support/xposix_spawn_file_actions_addclose.c
new file mode 100644
index 0000000000..eed54a6514
--- /dev/null
+++ b/support/xposix_spawn_file_actions_addclose.c
@@ -0,0 +1,29 @@ 
+/* xposix_spawn_file_actions_addclose implementation.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/xspawn.h>
+#include <support/check.h>
+
+int
+xposix_spawn_file_actions_addclose (posix_spawn_file_actions_t *fa, int fd)
+{
+  int status = posix_spawn_file_actions_addclose (fa, fd);
+  if (status == -1)
+    FAIL_EXIT1 ("posix_spawn_file_actions_addclose failed: %m\n");
+  return status;
+}
diff --git a/support/xposix_spawn_file_actions_adddup2.c b/support/xposix_spawn_file_actions_adddup2.c
new file mode 100644
index 0000000000..a43b6490be
--- /dev/null
+++ b/support/xposix_spawn_file_actions_adddup2.c
@@ -0,0 +1,30 @@ 
+/* xposix_spawn_file_actions_adddup2 implementation.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/xspawn.h>
+#include <support/check.h>
+
+int
+xposix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *fa, int fd,
+				   int newfd)
+{
+  int status = posix_spawn_file_actions_adddup2 (fa, fd, newfd);
+  if (status == -1)
+    FAIL_EXIT1 ("posix_spawn_file_actions_adddup2 failed: %m\n");
+  return status;
+}
diff --git a/support/xspawn.h b/support/xspawn.h
new file mode 100644
index 0000000000..bbf89132e4
--- /dev/null
+++ b/support/xspawn.h
@@ -0,0 +1,34 @@ 
+/* posix_spawn with support checks.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef SUPPORT_XSPAWN_H
+#define SUPPORT_XSPAWN_H
+
+#include <spawn.h>
+
+__BEGIN_DECLS
+
+int xposix_spawn_file_actions_addclose (posix_spawn_file_actions_t *, int);
+int xposix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *, int, int);
+
+pid_t xposix_spawn (const char *, const posix_spawn_file_actions_t *,
+		    const posix_spawnattr_t *, char *const [], char *const []);
+
+__END_DECLS
+
+#endif