[1/2] support: Add support_capture_subprogram
Commit Message
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
Comments
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.
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.
@@ -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
@@ -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 *);
new file mode 100644
@@ -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
@@ -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;
}
new file mode 100644
@@ -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;
+}
@@ -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>
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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