It has been noted [1] that when spawning a child process, it is useful to be able to specify in which directory to execute this child process.
The "obvious" solution, to do a chdir() before spawning the child process and chdir() or fchdir() back afterwards, has two drawbacks: - It is not multithread-safe. - It interferes with the search of the program in $PATH, if $PATH contains non-absolute directories (which is a bad practice for security reasons but not forbidden). This patch adds a 'directory' parameter to the functions 'execute' and create_pipe_*. I need this, in particular, in order to complete the Ruby support for GNU gettext. Note the added NEWS entries: Date Modules Changes 2020-12-02 spawn-pipe The functions 'create_pipe_out', 'create_pipe_in', 'create_pipe_bidi' now take a 4th argument 'const char *directory'. To maintain the previous behaviour, insert NULL as additional 4th argument. 2020-12-02 execute The function 'execute' now takes a 4th argument 'const char *directory'. To maintain the previous behaviour, insert NULL as additional 4th argument. [1] https://www.austingroupbugs.net/view.php?id=1208 2020-12-02 Bruno Haible <br...@clisp.org> spawn-pipe: Allow caller to specify directory for the subprocess. * lib/spawn-pipe.h (create_pipe_out, create_pipe_in, create_pipe_bidi): Add directory argument. * lib/spawn-pipe.c: Include canonicalize.h, filename.h, findprog.h. (create_pipe): Add directory argument. If specified, resolve the program file name and make it absolute, first. Pass the directory to spawnpvech and posix_spawn_file_actions_addchdir. (create_pipe_bidi, create_pipe_in, create_pipe_out): Add directory argument. * modules/spawn-pipe (Depends-on): Add canonicalize, filename, findprog-in, posix_spawn, posix_spawn_file_actions_addchdir. * tests/test-spawn-pipe-main.c (test_pipe): Update. * NEWS: Mention the change. * lib/csharpcomp.c (compile_csharp_using_mono, compile_csharp_using_sscli): Update. * lib/javacomp.c (is_envjavac_gcj, is_envjavac_gcj43, is_gcj_present, is_gcj_43): Update. * lib/javaversion.c (execute_and_read_line): Update. * lib/pipe-filter-gi.c (pipe_filter_gi_create): Update. * lib/pipe-filter-ii.c (pipe_filter_ii_execute): Update. 2020-12-02 Bruno Haible <br...@clisp.org> execute: Allow caller to specify directory for the subprocess. * lib/execute.h (execute): Add directory argument. * lib/execute.c: Include canonicalize.h, filename.h, findprog.h. (execute): Add directory argument. If specified, resolve the program file name and make it absolute, first. Pass the directory to spawnpvech and posix_spawn_file_actions_addchdir. * modules/execute (Depends-on): Add canonicalize, filename, findprog-in, posix_spawn, posix_spawn_file_actions_addchdir. * tests/test-execute-main.c: Add test for passing a directory. * tests/test-execute-child.c: Likewise. * tests/test-execute.sh: Update. * modules/execute-tests (Depends-on): Add mkdir. * NEWS: Mention the change. * lib/csharpcomp.c (compile_csharp_using_sscli): Update. * lib/csharpexec.c (execute_csharp_using_mono, execute_csharp_using_sscli): Update. * lib/javacomp.c (compile_using_envjavac, compile_using_gcj, compile_using_javac, compile_using_jikes, is_javac_present, is_jikes_present): Update. * lib/javaexec.c (execute_java_class): Update.
>From 702cba00f4ff7b22f0684a23db0fb66aea2c4086 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 2 Dec 2020 17:44:04 +0100 Subject: [PATCH 1/2] execute: Allow caller to specify directory for the subprocess. * lib/execute.h (execute): Add directory argument. * lib/execute.c: Include canonicalize.h, filename.h, findprog.h. (execute): Add directory argument. If specified, resolve the program file name and make it absolute, first. Pass the directory to spawnpvech and posix_spawn_file_actions_addchdir. * modules/execute (Depends-on): Add canonicalize, filename, findprog-in, posix_spawn, posix_spawn_file_actions_addchdir. * tests/test-execute-main.c: Add test for passing a directory. * tests/test-execute-child.c: Likewise. * tests/test-execute.sh: Update. * modules/execute-tests (Depends-on): Add mkdir. * NEWS: Mention the change. * lib/csharpcomp.c (compile_csharp_using_sscli): Update. * lib/csharpexec.c (execute_csharp_using_mono, execute_csharp_using_sscli): Update. * lib/javacomp.c (compile_using_envjavac, compile_using_gcj, compile_using_javac, compile_using_jikes, is_javac_present, is_jikes_present): Update. * lib/javaexec.c (execute_java_class): Update. --- ChangeLog | 23 +++++++++++ NEWS | 4 ++ lib/csharpcomp.c | 3 +- lib/csharpexec.c | 6 ++- lib/execute.c | 98 ++++++++++++++++++++++++++++++++++++++-------- lib/execute.h | 4 ++ lib/javacomp.c | 22 +++++++---- lib/javaexec.c | 12 ++++-- modules/execute | 5 +++ modules/execute-tests | 1 + tests/test-execute-child.c | 20 +++++++++- tests/test-execute-main.c | 66 ++++++++++++++++++++----------- tests/test-execute.sh | 2 +- 13 files changed, 210 insertions(+), 56 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8e9e032..b5ea083 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2020-12-02 Bruno Haible <br...@clisp.org> + + execute: Allow caller to specify directory for the subprocess. + * lib/execute.h (execute): Add directory argument. + * lib/execute.c: Include canonicalize.h, filename.h, findprog.h. + (execute): Add directory argument. If specified, resolve the program + file name and make it absolute, first. Pass the directory to spawnpvech + and posix_spawn_file_actions_addchdir. + * modules/execute (Depends-on): Add canonicalize, filename, findprog-in, + posix_spawn, posix_spawn_file_actions_addchdir. + * tests/test-execute-main.c: Add test for passing a directory. + * tests/test-execute-child.c: Likewise. + * tests/test-execute.sh: Update. + * modules/execute-tests (Depends-on): Add mkdir. + * NEWS: Mention the change. + * lib/csharpcomp.c (compile_csharp_using_sscli): Update. + * lib/csharpexec.c (execute_csharp_using_mono, + execute_csharp_using_sscli): Update. + * lib/javacomp.c (compile_using_envjavac, compile_using_gcj, + compile_using_javac, compile_using_jikes, is_javac_present, + is_jikes_present): Update. + * lib/javaexec.c (execute_java_class): Update. + 2020-12-01 Bruno Haible <br...@clisp.org> vma-iter: Add support for macOS11/arm64. diff --git a/NEWS b/NEWS index d76e3e7..445266d 100644 --- a/NEWS +++ b/NEWS @@ -60,6 +60,10 @@ User visible incompatible changes Date Modules Changes +2020-12-02 execute The function 'execute' now takes a 4th argument + 'const char *directory'. To maintain the previous + behaviour, insert NULL as additional 4th argument. + 2020-10-16 hash This module deprecates the 'hash_delete' function using gcc's "deprecated" attribute. Use the better- named 'hash_remove' equivalent. diff --git a/lib/csharpcomp.c b/lib/csharpcomp.c index fac9a89..cf68c5a 100644 --- a/lib/csharpcomp.c +++ b/lib/csharpcomp.c @@ -378,7 +378,8 @@ compile_csharp_using_sscli (const char * const *sources, free (command); } - exitstatus = execute ("csc", "csc", argv, false, false, false, false, + exitstatus = execute ("csc", "csc", argv, NULL, + false, false, false, false, true, true, NULL); for (i = 2; i < 3 + libdirs_count + libraries_count; i++) diff --git a/lib/csharpexec.c b/lib/csharpexec.c index b54f14e..5e2aa2c 100644 --- a/lib/csharpexec.c +++ b/lib/csharpexec.c @@ -106,7 +106,8 @@ execute_csharp_using_mono (const char *assembly_path, argv[0] = "mono"; argv[1] = "--version"; argv[2] = NULL; - exitstatus = execute ("mono", "mono", argv, false, false, true, true, + exitstatus = execute ("mono", "mono", argv, NULL, + false, false, true, true, true, false, NULL); mono_present = (exitstatus == 0); mono_tested = true; @@ -167,7 +168,8 @@ execute_csharp_using_sscli (const char *assembly_path, argv[0] = "clix"; argv[1] = NULL; - exitstatus = execute ("clix", "clix", argv, false, false, true, true, + exitstatus = execute ("clix", "clix", argv, NULL, + false, false, true, true, true, false, NULL); clix_present = (exitstatus == 0 || exitstatus == 1); clix_tested = true; diff --git a/lib/execute.c b/lib/execute.c index 15556cf..03fb6ec 100644 --- a/lib/execute.c +++ b/lib/execute.c @@ -28,8 +28,11 @@ #include <signal.h> #include <unistd.h> +#include "canonicalize.h" #include "error.h" #include "fatal-signal.h" +#include "filename.h" +#include "findprog.h" #include "wait-process.h" #include "gettext.h" @@ -94,17 +97,66 @@ nonintr_open (const char *pathname, int oflag, mode_t mode) int execute (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, bool ignore_sigpipe, bool null_stdin, bool null_stdout, bool null_stderr, bool slave_process, bool exit_on_error, int *termsigp) { + int saved_errno; + char *prog_path_to_free = NULL; + + if (directory != NULL) + { + /* If a change of directory is requested, make sure PROG_PATH is absolute + before we do so. This is needed because + - posix_spawn and posix_spawnp are required to resolve a relative + PROG_PATH *after* changing the directory. See + <https://www.austingroupbugs.net/view.php?id=1208>: + "if this pathname does not start with a <slash> it shall be + interpreted relative to the working directory of the child + process _after_ all file_actions have been performed." + But this would be a surprising application behaviour, possibly + even security relevant. + - For the Windows CreateProcess() function, it is unspecified whether + a relative file name is interpreted to the parent's current + directory or to the specified directory. See + <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */ + if (! IS_ABSOLUTE_FILE_NAME (prog_path)) + { + const char *resolved_prog = + find_in_given_path (prog_path, getenv ("PATH"), false); + if (resolved_prog == NULL) + goto fail_with_errno; + if (resolved_prog != prog_path) + prog_path_to_free = (char *) resolved_prog; + prog_path = resolved_prog; + + if (! IS_ABSOLUTE_FILE_NAME (prog_path)) + { + char *absolute_prog = + canonicalize_filename_mode (prog_path, + CAN_MISSING | CAN_NOLINKS); + if (absolute_prog == NULL) + { + saved_errno = errno; + free (prog_path_to_free); + goto fail_with_saved_errno; + } + free (prog_path_to_free); + prog_path_to_free = absolute_prog; + prog_path = absolute_prog; + + if (! IS_ABSOLUTE_FILE_NAME (prog_path)) + abort (); + } + } + } + #if defined _WIN32 && ! defined __CYGWIN__ /* Native Windows API. */ - int saved_errno; - /* FIXME: Need to free memory allocated by prepare_spawn. */ prog_argv = prepare_spawn (prog_argv); @@ -133,16 +185,17 @@ execute (const char *progname, (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO); exitcode = spawnpvech (P_WAIT, prog_path, (const char **) prog_argv, - (const char **) environ, NULL, + (const char **) environ, directory, stdin_handle, stdout_handle, stderr_handle); if (exitcode == -1 && errno == ENOEXEC) { /* prog is not a native executable. Try to execute it as a shell script. Note that prepare_spawn() has already prepended a hidden element "sh.exe" to prog_argv. */ + prog_argv[0] = prog_path; --prog_argv; exitcode = spawnpvech (P_WAIT, prog_argv[0], (const char **) prog_argv, - (const char **) environ, NULL, + (const char **) environ, directory, stdin_handle, stdout_handle, stderr_handle); } } @@ -152,17 +205,13 @@ execute (const char *progname, close (nulloutfd); if (nullinfd >= 0) close (nullinfd); + free (prog_path_to_free); if (termsigp != NULL) *termsigp = 0; if (exitcode == -1) - { - if (exit_on_error || !null_stderr) - error (exit_on_error ? EXIT_FAILURE : 0, saved_errno, - _("%s subprocess failed"), progname); - return 127; - } + goto fail_with_saved_errno; return exitcode; @@ -209,6 +258,9 @@ execute (const char *progname, "/dev/null", O_RDWR, 0)) != 0) + || (directory != NULL + && (err = posix_spawn_file_actions_addchdir (&actions, + directory))) || (slave_process && ((err = posix_spawnattr_init (&attrs)) != 0 || (attrs_allocated = true, @@ -218,9 +270,13 @@ execute (const char *progname, || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0))) - || (err = posix_spawnp (&child, prog_path, &actions, - attrs_allocated ? &attrs : NULL, prog_argv, - environ)) + || (err = (directory != NULL + ? posix_spawn (&child, prog_path, &actions, + attrs_allocated ? &attrs : NULL, prog_argv, + environ) + : posix_spawnp (&child, prog_path, &actions, + attrs_allocated ? &attrs : NULL, prog_argv, + environ))) != 0)) { if (actions_allocated) @@ -229,12 +285,11 @@ execute (const char *progname, posix_spawnattr_destroy (&attrs); if (slave_process) unblock_fatal_signals (); + free (prog_path_to_free); if (termsigp != NULL) *termsigp = 0; - if (exit_on_error || !null_stderr) - error (exit_on_error ? EXIT_FAILURE : 0, err, - _("%s subprocess failed"), progname); - return 127; + saved_errno = err; + goto fail_with_saved_errno; } posix_spawn_file_actions_destroy (&actions); if (attrs_allocated) @@ -244,9 +299,18 @@ execute (const char *progname, register_slave_subprocess (child); unblock_fatal_signals (); } + free (prog_path_to_free); return wait_subprocess (child, progname, ignore_sigpipe, null_stderr, slave_process, exit_on_error, termsigp); #endif + + fail_with_errno: + saved_errno = errno; + fail_with_saved_errno: + if (exit_on_error || !null_stderr) + error (exit_on_error ? EXIT_FAILURE : 0, saved_errno, + _("%s subprocess failed"), progname); + return 127; } diff --git a/lib/execute.h b/lib/execute.h index b31c4d1..34fc989 100644 --- a/lib/execute.h +++ b/lib/execute.h @@ -32,6 +32,9 @@ prog_argv is the array of strings that the subprocess shall receive in argv[]. It is a NULL-terminated array. prog_argv[0] should normally be identical to prog_path. + If directory is not NULL, the command is executed in that directory. If + prog_path is a relative file name, it resolved before changing to that + directory. The current directory of the current process remains unchanged. If ignore_sigpipe is true, consider a subprocess termination due to SIGPIPE as equivalent to a success. This is suitable for processes whose only purpose is to write to standard output. @@ -44,6 +47,7 @@ is called. See spawn-pipe.h for the reason. */ extern int execute (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, bool ignore_sigpipe, bool null_stdin, bool null_stdout, bool null_stderr, bool slave_process, bool exit_on_error, diff --git a/lib/javacomp.c b/lib/javacomp.c index 63efc2d..fec5180 100644 --- a/lib/javacomp.c +++ b/lib/javacomp.c @@ -343,8 +343,9 @@ compile_using_envjavac (const char *javac, argv[1] = "-c"; argv[2] = command; argv[3] = NULL; - exitstatus = execute (javac, BOURNE_SHELL, argv, false, false, false, - null_stderr, true, true, NULL); + exitstatus = execute (javac, BOURNE_SHELL, argv, NULL, + false, false, false, null_stderr, + true, true, NULL); err = (exitstatus != 0); freea (command); @@ -425,7 +426,8 @@ compile_using_gcj (const char * const *java_sources, free (command); } - exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr, + exitstatus = execute ("gcj", "gcj", argv, NULL, + false, false, false, null_stderr, true, true, NULL); err = (exitstatus != 0); @@ -496,7 +498,8 @@ compile_using_javac (const char * const *java_sources, free (command); } - exitstatus = execute ("javac", "javac", argv, false, false, false, + exitstatus = execute ("javac", "javac", argv, NULL, + false, false, false, null_stderr, true, true, NULL); err = (exitstatus != 0); @@ -551,8 +554,9 @@ compile_using_jikes (const char * const *java_sources, free (command); } - exitstatus = execute ("jikes", "jikes", argv, false, false, false, - null_stderr, true, true, NULL); + exitstatus = execute ("jikes", "jikes", argv, NULL, + false, false, false, null_stderr, + true, true, NULL); err = (exitstatus != 0); freea (argv); @@ -1872,7 +1876,8 @@ is_javac_present (void) argv[0] = "javac"; argv[1] = NULL; - exitstatus = execute ("javac", "javac", argv, false, false, true, true, + exitstatus = execute ("javac", "javac", argv, NULL, + false, false, true, true, true, false, NULL); javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2); javac_tested = true; @@ -2138,7 +2143,8 @@ is_jikes_present (void) argv[0] = "jikes"; argv[1] = NULL; - exitstatus = execute ("jikes", "jikes", argv, false, false, true, true, + exitstatus = execute ("jikes", "jikes", argv, NULL, + false, false, true, true, true, false, NULL); jikes_present = (exitstatus == 0 || exitstatus == 1); jikes_tested = true; diff --git a/lib/javaexec.c b/lib/javaexec.c index a374bff..d486fb8 100644 --- a/lib/javaexec.c +++ b/lib/javaexec.c @@ -208,7 +208,8 @@ execute_java_class (const char *class_name, argv[0] = "gij"; argv[1] = "--version"; argv[2] = NULL; - exitstatus = execute ("gij", "gij", argv, false, false, true, true, + exitstatus = execute ("gij", "gij", argv, NULL, + false, false, true, true, true, false, NULL); gij_present = (exitstatus == 0); gij_tested = true; @@ -261,7 +262,8 @@ execute_java_class (const char *class_name, argv[0] = "java"; argv[1] = "-version"; argv[2] = NULL; - exitstatus = execute ("java", "java", argv, false, false, true, true, + exitstatus = execute ("java", "java", argv, NULL, + false, false, true, true, true, false, NULL); java_present = (exitstatus == 0); java_tested = true; @@ -315,7 +317,8 @@ execute_java_class (const char *class_name, argv[0] = "jre"; argv[1] = NULL; - exitstatus = execute ("jre", "jre", argv, false, false, true, true, + exitstatus = execute ("jre", "jre", argv, NULL, + false, false, true, true, true, false, NULL); jre_present = (exitstatus == 0 || exitstatus == 1); jre_tested = true; @@ -372,7 +375,8 @@ execute_java_class (const char *class_name, argv[0] = "jview"; argv[1] = "-?"; argv[2] = NULL; - exitstatus = execute ("jview", "jview", argv, false, false, true, true, + exitstatus = execute ("jview", "jview", argv, NULL, + false, false, true, true, true, false, NULL); jview_present = (exitstatus == 0 || exitstatus == 1); jview_tested = true; diff --git a/modules/execute b/modules/execute index 52eee5f..cb04003 100644 --- a/modules/execute +++ b/modules/execute @@ -8,15 +8,20 @@ m4/execute.m4 Depends-on: dup2 +canonicalize environ error fatal-signal +filename +findprog-in msvc-nothrow gettext-h spawn +posix_spawn posix_spawnp posix_spawn_file_actions_init posix_spawn_file_actions_addopen +posix_spawn_file_actions_addchdir posix_spawn_file_actions_destroy posix_spawnattr_init posix_spawnattr_setsigmask diff --git a/modules/execute-tests b/modules/execute-tests index 854b1ad..9562182 100644 --- a/modules/execute-tests +++ b/modules/execute-tests @@ -7,6 +7,7 @@ tests/macros.h Depends-on: dup2 fcntl +mkdir msvc-inval read-file stdint diff --git a/tests/test-execute-child.c b/tests/test-execute-child.c index 77f99ae..8197c30 100644 --- a/tests/test-execute-child.c +++ b/tests/test-execute-child.c @@ -29,7 +29,7 @@ /* Get declarations of the native Windows API functions. */ # define WIN32_LEAN_AND_MEAN # include <windows.h> -/* Get _get_osfhandle, _isatty. */ +/* Get _get_osfhandle, _isatty, _chdir, _getcwd. */ # include <io.h> #endif @@ -41,6 +41,7 @@ #undef fprintf #undef fputs #undef fstat +#undef getcwd #undef isatty #undef raise #undef read @@ -201,6 +202,23 @@ main (int argc, char *argv[]) return 4 + 2 * (isatty (10) != 0) + (isatty (11) != 0); #endif } + case 21: + /* Check execution in a different directory. */ + { + char cwd[1024]; + #if defined _WIN32 && ! defined __CYGWIN__ + if (_chdir ("..") != 0) + return 1; + if (_getcwd (cwd, sizeof (cwd)) == NULL) + return 2; + #else + if (chdir ("..") != 0) + return 1; + if (getcwd (cwd, sizeof (cwd)) == NULL) + return 2; + #endif + return (argc == 3 && strcmp (argv[2], cwd) == 0 ? 0 : 3); + } default: abort (); } diff --git a/tests/test-execute-main.c b/tests/test-execute-main.c index 62357c1..755209e 100644 --- a/tests/test-execute-main.c +++ b/tests/test-execute-main.c @@ -26,9 +26,10 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <sys/stat.h> #if defined _WIN32 && ! defined __CYGWIN__ -/* Get _isatty. */ +/* Get _isatty, _getcwd. */ # include <io.h> #endif @@ -63,7 +64,7 @@ main (int argc, char *argv[]) { /* Check an invocation without arguments. Check the exit code. */ char *prog_argv[2] = { prog_path, NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 40); } @@ -72,7 +73,7 @@ main (int argc, char *argv[]) { /* Check an invocation of a non-existent program. */ char *prog_argv[3] = { "./non-existent", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 127); } @@ -96,7 +97,7 @@ main (int argc, char *argv[]) (char *) "", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); } @@ -107,7 +108,7 @@ main (int argc, char *argv[]) /* Check SIGPIPE handling with ignore_sigpipe = false. */ char *prog_argv[3] = { prog_path, (char *) "3", NULL }; int termsig = 0xDEADBEEF; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, &termsig); ASSERT (ret == 127); ASSERT (termsig == SIGPIPE); @@ -120,7 +121,7 @@ main (int argc, char *argv[]) /* Check SIGPIPE handling with ignore_sigpipe = true. */ char *prog_argv[3] = { prog_path, (char *) "4", NULL }; int termsig = 0xDEADBEEF; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, true, false, false, false, true, false, &termsig); ASSERT (ret == 0); ASSERT (termsig == SIGPIPE); @@ -132,7 +133,7 @@ main (int argc, char *argv[]) /* Check other signal. */ char *prog_argv[3] = { prog_path, (char *) "5", NULL }; int termsig = 0xDEADBEEF; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, &termsig); #if defined _WIN32 && !defined __CYGWIN__ ASSERT (ret == 3); @@ -154,7 +155,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "6", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); @@ -173,7 +174,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "7", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, true, false, false, true, false, NULL); ASSERT (ret == 0); @@ -188,7 +189,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "8", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); @@ -208,7 +209,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "9", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); } @@ -220,7 +221,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "10", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, true, false, true, false, NULL); ASSERT (ret == 0); @@ -240,7 +241,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "11", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); @@ -260,7 +261,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "12", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); } @@ -272,7 +273,7 @@ main (int argc, char *argv[]) ASSERT (fp != NULL); char *prog_argv[3] = { prog_path, (char *) "13", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, true, true, false, NULL); ASSERT (ret == 0); @@ -290,7 +291,7 @@ main (int argc, char *argv[]) /* Check file descriptors >= 3 can be inherited. */ ASSERT (dup2 (STDOUT_FILENO, 10) >= 0); char *prog_argv[3] = { prog_path, (char *) "14", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, true, false, false, false, true, false, NULL); ASSERT (ret == 0); } @@ -300,7 +301,7 @@ main (int argc, char *argv[]) /* Check file descriptors >= 3 can be inherited. */ ASSERT (fcntl (STDOUT_FILENO, F_DUPFD, 10) >= 0); char *prog_argv[3] = { prog_path, (char *) "15", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, true, false, false, false, true, false, NULL); ASSERT (ret == 0); } @@ -310,7 +311,7 @@ main (int argc, char *argv[]) /* Check file descriptors >= 3 with O_CLOEXEC bit are not inherited. */ ASSERT (fcntl (STDOUT_FILENO, F_DUPFD_CLOEXEC, 10) >= 0); char *prog_argv[3] = { prog_path, (char *) "16", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, true, false, false, false, true, false, NULL); ASSERT (ret == 0); } @@ -335,7 +336,7 @@ main (int argc, char *argv[]) /* The file position is now 2. */ char *prog_argv[3] = { prog_path, (char *) "17", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); @@ -359,7 +360,7 @@ main (int argc, char *argv[]) /* The file position is now 3. */ char *prog_argv[3] = { prog_path, (char *) "18", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); ASSERT (ret == 0); @@ -395,7 +396,7 @@ main (int argc, char *argv[]) fd_out = 11; char *prog_argv[3] = { prog_path, (char *) "19", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); #if defined _WIN32 && ! defined __CYGWIN__ ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0)); @@ -419,7 +420,7 @@ main (int argc, char *argv[]) int fd_out = 11; char *prog_argv[3] = { prog_path, (char *) "20", NULL }; - int ret = execute (progname, prog_argv[0], prog_argv, + int ret = execute (progname, prog_argv[0], prog_argv, NULL, false, false, false, false, true, false, NULL); #if defined _WIN32 && ! defined __CYGWIN__ ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0)); @@ -431,6 +432,27 @@ main (int argc, char *argv[]) close (fd_out); } break; + case 21: + { + /* Check execution in a different directory. */ + rmdir (BASE ".sub"); + ASSERT (mkdir (BASE ".sub", 0700) == 0); + + char cwd[1024]; + #if defined _WIN32 && ! defined __CYGWIN__ + ASSERT (_getcwd (cwd, sizeof (cwd)) != NULL); + #else + ASSERT (getcwd (cwd, sizeof (cwd)) != NULL); + #endif + + char *prog_argv[4] = { prog_path, (char *) "21", cwd, NULL }; + int ret = execute (progname, prog_argv[0], prog_argv, BASE ".sub", + false, false, false, false, true, false, NULL); + ASSERT (ret == 0); + + ASSERT (rmdir (BASE ".sub") == 0); + } + break; default: ASSERT (false); } diff --git a/tests/test-execute.sh b/tests/test-execute.sh index 1320e76..15c8b47 100755 --- a/tests/test-execute.sh +++ b/tests/test-execute.sh @@ -1,7 +1,7 @@ #!/bin/sh st=0 -for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do +for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ; do ${CHECKER} ./test-execute-main${EXEEXT} ./test-execute-child${EXEEXT} $i \ || { echo test-execute.sh: test case $i failed >&2; st=1; } done -- 2.7.4
>From 0949c47c03456dae91c15740731b1350296b0497 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 2 Dec 2020 17:52:00 +0100 Subject: [PATCH 2/2] spawn-pipe: Allow caller to specify directory for the subprocess. * lib/spawn-pipe.h (create_pipe_out, create_pipe_in, create_pipe_bidi): Add directory argument. * lib/spawn-pipe.c: Include canonicalize.h, filename.h, findprog.h. (create_pipe): Add directory argument. If specified, resolve the program file name and make it absolute, first. Pass the directory to spawnpvech and posix_spawn_file_actions_addchdir. (create_pipe_bidi, create_pipe_in, create_pipe_out): Add directory argument. * modules/spawn-pipe (Depends-on): Add canonicalize, filename, findprog-in, posix_spawn, posix_spawn_file_actions_addchdir. * tests/test-spawn-pipe-main.c (test_pipe): Update. * NEWS: Mention the change. * lib/csharpcomp.c (compile_csharp_using_mono, compile_csharp_using_sscli): Update. * lib/javacomp.c (is_envjavac_gcj, is_envjavac_gcj43, is_gcj_present, is_gcj_43): Update. * lib/javaversion.c (execute_and_read_line): Update. * lib/pipe-filter-gi.c (pipe_filter_gi_create): Update. * lib/pipe-filter-ii.c (pipe_filter_ii_execute): Update. --- ChangeLog | 23 +++++++++ NEWS | 5 ++ lib/csharpcomp.c | 11 +++-- lib/javacomp.c | 16 +++--- lib/javaversion.c | 4 +- lib/pipe-filter-gi.c | 2 +- lib/pipe-filter-ii.c | 2 +- lib/spawn-pipe.c | 114 +++++++++++++++++++++++++++++++++++-------- lib/spawn-pipe.h | 7 +++ modules/spawn-pipe | 5 ++ tests/test-spawn-pipe-main.c | 2 +- 11 files changed, 154 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index b5ea083..b32f190 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,28 @@ 2020-12-02 Bruno Haible <br...@clisp.org> + spawn-pipe: Allow caller to specify directory for the subprocess. + * lib/spawn-pipe.h (create_pipe_out, create_pipe_in, create_pipe_bidi): + Add directory argument. + * lib/spawn-pipe.c: Include canonicalize.h, filename.h, findprog.h. + (create_pipe): Add directory argument. If specified, resolve the program + file name and make it absolute, first. Pass the directory to spawnpvech + and posix_spawn_file_actions_addchdir. + (create_pipe_bidi, create_pipe_in, create_pipe_out): Add directory + argument. + * modules/spawn-pipe (Depends-on): Add canonicalize, filename, + findprog-in, posix_spawn, posix_spawn_file_actions_addchdir. + * tests/test-spawn-pipe-main.c (test_pipe): Update. + * NEWS: Mention the change. + * lib/csharpcomp.c (compile_csharp_using_mono, + compile_csharp_using_sscli): Update. + * lib/javacomp.c (is_envjavac_gcj, is_envjavac_gcj43, is_gcj_present, + is_gcj_43): Update. + * lib/javaversion.c (execute_and_read_line): Update. + * lib/pipe-filter-gi.c (pipe_filter_gi_create): Update. + * lib/pipe-filter-ii.c (pipe_filter_ii_execute): Update. + +2020-12-02 Bruno Haible <br...@clisp.org> + execute: Allow caller to specify directory for the subprocess. * lib/execute.h (execute): Add directory argument. * lib/execute.c: Include canonicalize.h, filename.h, findprog.h. diff --git a/NEWS b/NEWS index 445266d..747165a 100644 --- a/NEWS +++ b/NEWS @@ -60,6 +60,11 @@ User visible incompatible changes Date Modules Changes +2020-12-02 spawn-pipe The functions 'create_pipe_out', 'create_pipe_in', + 'create_pipe_bidi' now take a 4th argument + 'const char *directory'. To maintain the previous + behaviour, insert NULL as additional 4th argument. + 2020-12-02 execute The function 'execute' now takes a 4th argument 'const char *directory'. To maintain the previous behaviour, insert NULL as additional 4th argument. diff --git a/lib/csharpcomp.c b/lib/csharpcomp.c index cf68c5a..57d2084 100644 --- a/lib/csharpcomp.c +++ b/lib/csharpcomp.c @@ -84,8 +84,8 @@ compile_csharp_using_mono (const char * const *sources, argv[0] = "mcs"; argv[1] = "--version"; argv[2] = NULL; - child = create_pipe_in ("mcs", "mcs", argv, DEV_NULL, true, true, false, - fd); + child = create_pipe_in ("mcs", "mcs", argv, NULL, + DEV_NULL, true, true, false, fd); mcs_present = false; if (child != -1) { @@ -193,7 +193,8 @@ compile_csharp_using_mono (const char * const *sources, free (command); } - child = create_pipe_in ("mcs", "mcs", argv, NULL, false, true, true, fd); + child = create_pipe_in ("mcs", "mcs", argv, NULL, + NULL, false, true, true, fd); /* Read the subprocess output, copying it to stderr. Drop the last line if it starts with "Compilation succeeded". */ @@ -270,8 +271,8 @@ compile_csharp_using_sscli (const char * const *sources, argv[0] = "csc"; argv[1] = "-help"; argv[2] = NULL; - child = create_pipe_in ("csc", "csc", argv, DEV_NULL, true, true, false, - fd); + child = create_pipe_in ("csc", "csc", argv, NULL, + DEV_NULL, true, true, false, fd); csc_present = false; if (child != -1) { diff --git a/lib/javacomp.c b/lib/javacomp.c index fec5180..c24bb69 100644 --- a/lib/javacomp.c +++ b/lib/javacomp.c @@ -664,8 +664,8 @@ is_envjavac_gcj (const char *javac) argv[1] = "-c"; argv[2] = command; argv[3] = NULL; - child = create_pipe_in (javac, BOURNE_SHELL, argv, DEV_NULL, true, true, - false, fd); + child = create_pipe_in (javac, BOURNE_SHELL, argv, NULL, + DEV_NULL, true, true, false, fd); if (child == -1) goto failed; @@ -746,8 +746,8 @@ is_envjavac_gcj43 (const char *javac) argv[1] = "-c"; argv[2] = command; argv[3] = NULL; - child = create_pipe_in (javac, BOURNE_SHELL, argv, DEV_NULL, true, true, - false, fd); + child = create_pipe_in (javac, BOURNE_SHELL, argv, NULL, + DEV_NULL, true, true, false, fd); if (child == -1) goto failed; @@ -1414,8 +1414,8 @@ is_gcj_present (void) argv[0] = "gcj"; argv[1] = "--version"; argv[2] = NULL; - child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true, - false, fd); + child = create_pipe_in ("gcj", "gcj", argv, NULL, + DEV_NULL, true, true, false, fd); gcj_present = false; if (child != -1) { @@ -1530,8 +1530,8 @@ is_gcj_43 (void) argv[0] = "gcj"; argv[1] = "--version"; argv[2] = NULL; - child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true, - false, fd); + child = create_pipe_in ("gcj", "gcj", argv, NULL, + DEV_NULL, true, true, false, fd); gcj_43 = false; if (child != -1) { diff --git a/lib/javaversion.c b/lib/javaversion.c index fd99291..e50c499 100644 --- a/lib/javaversion.c +++ b/lib/javaversion.c @@ -65,8 +65,8 @@ execute_and_read_line (const char *progname, int exitstatus; /* Open a pipe to the JVM. */ - child = create_pipe_in (progname, prog_path, prog_argv, DEV_NULL, false, - true, false, fd); + child = create_pipe_in (progname, prog_path, prog_argv, NULL, + DEV_NULL, false, true, false, fd); if (child == -1) return false; diff --git a/lib/pipe-filter-gi.c b/lib/pipe-filter-gi.c index f7f8f6e..7528053 100644 --- a/lib/pipe-filter-gi.c +++ b/lib/pipe-filter-gi.c @@ -498,7 +498,7 @@ pipe_filter_gi_create (const char *progname, /* Open a bidirectional pipe to a subprocess. */ filter->child = create_pipe_bidi (progname, prog_path, (char **) prog_argv, - null_stderr, true, exit_on_error, + NULL, null_stderr, true, exit_on_error, filter->fd); filter->progname = progname; filter->null_stderr = null_stderr; diff --git a/lib/pipe-filter-ii.c b/lib/pipe-filter-ii.c index b29f802..384a59b 100644 --- a/lib/pipe-filter-ii.c +++ b/lib/pipe-filter-ii.c @@ -271,7 +271,7 @@ pipe_filter_ii_execute (const char *progname, /* Open a bidirectional pipe to a subprocess. */ child = create_pipe_bidi (progname, prog_path, (char **) prog_argv, - null_stderr, true, exit_on_error, + NULL, null_stderr, true, exit_on_error, fd); if (child == -1) return -1; diff --git a/lib/spawn-pipe.c b/lib/spawn-pipe.c index a4c4d39..209bbf5 100644 --- a/lib/spawn-pipe.c +++ b/lib/spawn-pipe.c @@ -32,8 +32,11 @@ #include <signal.h> #include <unistd.h> +#include "canonicalize.h" #include "error.h" #include "fatal-signal.h" +#include "filename.h" +#include "findprog.h" #include "unistd-safer.h" #include "wait-process.h" #include "gettext.h" @@ -120,12 +123,62 @@ nonintr_open (const char *pathname, int oflag, mode_t mode) static pid_t create_pipe (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, bool pipe_stdin, bool pipe_stdout, const char *prog_stdin, const char *prog_stdout, bool null_stderr, bool slave_process, bool exit_on_error, int fd[2]) { + int saved_errno; + char *prog_path_to_free = NULL; + + if (directory != NULL) + { + /* If a change of directory is requested, make sure PROG_PATH is absolute + before we do so. This is needed because + - posix_spawn and posix_spawnp are required to resolve a relative + PROG_PATH *after* changing the directory. See + <https://www.austingroupbugs.net/view.php?id=1208>: + "if this pathname does not start with a <slash> it shall be + interpreted relative to the working directory of the child + process _after_ all file_actions have been performed." + But this would be a surprising application behaviour, possibly + even security relevant. + - For the Windows CreateProcess() function, it is unspecified whether + a relative file name is interpreted to the parent's current + directory or to the specified directory. See + <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */ + if (! IS_ABSOLUTE_FILE_NAME (prog_path)) + { + const char *resolved_prog = + find_in_given_path (prog_path, getenv ("PATH"), false); + if (resolved_prog == NULL) + goto fail_with_errno; + if (resolved_prog != prog_path) + prog_path_to_free = (char *) resolved_prog; + prog_path = resolved_prog; + + if (! IS_ABSOLUTE_FILE_NAME (prog_path)) + { + char *absolute_prog = + canonicalize_filename_mode (prog_path, CAN_MISSING | CAN_NOLINKS); + if (absolute_prog == NULL) + { + saved_errno = errno; + free (prog_path_to_free); + goto fail_with_saved_errno; + } + free (prog_path_to_free); + prog_path_to_free = absolute_prog; + prog_path = absolute_prog; + + if (! IS_ABSOLUTE_FILE_NAME (prog_path)) + abort (); + } + } + } + #if (defined _WIN32 && ! defined __CYGWIN__) || defined __KLIBC__ /* Native Windows API. @@ -139,7 +192,6 @@ create_pipe (const char *progname, int nulloutfd; int stdinfd; int stdoutfd; - int saved_errno; /* FIXME: Need to free memory allocated by prepare_spawn. */ prog_argv = prepare_spawn (prog_argv); @@ -227,16 +279,17 @@ create_pipe (const char *progname, (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO); child = spawnpvech (P_NOWAIT, prog_path, (const char **) prog_argv, - (const char **) environ, NULL, + (const char **) environ, directory, stdin_handle, stdout_handle, stderr_handle); if (child == -1 && errno == ENOEXEC) { /* prog is not a native executable. Try to execute it as a shell script. Note that prepare_spawn() has already prepended a hidden element "sh.exe" to prog_argv. */ + prog_argv[0] = prog_path; --prog_argv; child = spawnpvech (P_NOWAIT, prog_argv[0], (const char **) prog_argv, - (const char **) environ, NULL, + (const char **) environ, directory, stdin_handle, stdout_handle, stderr_handle); } } @@ -256,6 +309,13 @@ create_pipe (const char *progname, close (ifd[1]); # else /* __KLIBC__ */ + if (!(directory == NULL && strcmp (directory, ".") == 0)) + { + /* A directory argument is not supported in this implementation. */ + saved_errno = EINVAL; + goto fail_with_saved_errno; + } + int orig_stdin; int orig_stdout; int orig_stderr; @@ -330,17 +390,15 @@ create_pipe (const char *progname, close (ifd[1]); # endif + free (prog_path_to_free); + if (child == -1) { - if (exit_on_error || !null_stderr) - error (exit_on_error ? EXIT_FAILURE : 0, saved_errno, - _("%s subprocess failed"), progname); if (pipe_stdout) close (ifd[0]); if (pipe_stdin) close (ofd[1]); - errno = saved_errno; - return -1; + goto fail_with_saved_errno; } if (pipe_stdout) @@ -426,6 +484,9 @@ create_pipe (const char *progname, prog_stdout, O_WRONLY, 0)) != 0) + || (directory != NULL + && (err = posix_spawn_file_actions_addchdir (&actions, + directory))) || (slave_process && ((err = posix_spawnattr_init (&attrs)) != 0 || (attrs_allocated = true, @@ -435,9 +496,13 @@ create_pipe (const char *progname, || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0))) - || (err = posix_spawnp (&child, prog_path, &actions, - attrs_allocated ? &attrs : NULL, prog_argv, - environ)) + || (err = (directory != NULL + ? posix_spawn (&child, prog_path, &actions, + attrs_allocated ? &attrs : NULL, prog_argv, + environ) + : posix_spawnp (&child, prog_path, &actions, + attrs_allocated ? &attrs : NULL, prog_argv, + environ))) != 0)) { if (actions_allocated) @@ -446,9 +511,6 @@ create_pipe (const char *progname, posix_spawnattr_destroy (&attrs); if (slave_process) unblock_fatal_signals (); - if (exit_on_error || !null_stderr) - error (exit_on_error ? EXIT_FAILURE : 0, err, - _("%s subprocess failed"), progname); if (pipe_stdout) { close (ifd[0]); @@ -459,8 +521,9 @@ create_pipe (const char *progname, close (ofd[0]); close (ofd[1]); } - errno = err; - return -1; + free (prog_path_to_free); + saved_errno = err; + goto fail_with_saved_errno; } posix_spawn_file_actions_destroy (&actions); if (attrs_allocated) @@ -474,6 +537,7 @@ create_pipe (const char *progname, close (ofd[0]); if (pipe_stdout) close (ifd[1]); + free (prog_path_to_free); if (pipe_stdout) fd[0] = ifd[0]; @@ -482,6 +546,15 @@ create_pipe (const char *progname, return child; #endif + + fail_with_errno: + saved_errno = errno; + fail_with_saved_errno: + if (exit_on_error || !null_stderr) + error (exit_on_error ? EXIT_FAILURE : 0, saved_errno, + _("%s subprocess failed"), progname); + errno = saved_errno; + return -1; } /* Open a bidirectional pipe. @@ -495,11 +568,12 @@ create_pipe (const char *progname, pid_t create_pipe_bidi (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, bool null_stderr, bool slave_process, bool exit_on_error, int fd[2]) { - pid_t result = create_pipe (progname, prog_path, prog_argv, + pid_t result = create_pipe (progname, prog_path, prog_argv, directory, true, true, NULL, NULL, null_stderr, slave_process, exit_on_error, fd); @@ -516,12 +590,13 @@ create_pipe_bidi (const char *progname, pid_t create_pipe_in (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, const char *prog_stdin, bool null_stderr, bool slave_process, bool exit_on_error, int fd[1]) { int iofd[2]; - pid_t result = create_pipe (progname, prog_path, prog_argv, + pid_t result = create_pipe (progname, prog_path, prog_argv, directory, false, true, prog_stdin, NULL, null_stderr, slave_process, exit_on_error, iofd); @@ -540,12 +615,13 @@ create_pipe_in (const char *progname, pid_t create_pipe_out (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, const char *prog_stdout, bool null_stderr, bool slave_process, bool exit_on_error, int fd[1]) { int iofd[2]; - pid_t result = create_pipe (progname, prog_path, prog_argv, + pid_t result = create_pipe (progname, prog_path, prog_argv, directory, true, false, NULL, prog_stdout, null_stderr, slave_process, exit_on_error, iofd); diff --git a/lib/spawn-pipe.h b/lib/spawn-pipe.h index be0f1c8..02856a8 100644 --- a/lib/spawn-pipe.h +++ b/lib/spawn-pipe.h @@ -51,6 +51,10 @@ extern "C" { argv[]. It is a NULL-terminated array. prog_argv[0] should normally be identical to prog_path. + If directory is not NULL, the subprocess is started in that directory. If + prog_path is a relative file name, it resolved before changing to that + directory. The current directory of the current process remains unchanged. + If slave_process is true, the child process will be terminated when its creator receives a catchable fatal signal or exits normally. If slave_process is false, the child process will continue running in this @@ -93,6 +97,7 @@ extern "C" { */ extern pid_t create_pipe_out (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, const char *prog_stdout, bool null_stderr, bool slave_process, bool exit_on_error, int fd[1]); @@ -106,6 +111,7 @@ extern pid_t create_pipe_out (const char *progname, */ extern pid_t create_pipe_in (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, const char *prog_stdin, bool null_stderr, bool slave_process, bool exit_on_error, int fd[1]); @@ -134,6 +140,7 @@ extern pid_t create_pipe_in (const char *progname, */ extern pid_t create_pipe_bidi (const char *progname, const char *prog_path, char **prog_argv, + const char *directory, bool null_stderr, bool slave_process, bool exit_on_error, int fd[2]); diff --git a/modules/spawn-pipe b/modules/spawn-pipe index 2e78170..9d80193 100644 --- a/modules/spawn-pipe +++ b/modules/spawn-pipe @@ -10,20 +10,25 @@ m4/spawn-pipe.m4 Depends-on: dup2 +canonicalize environ error fatal-signal +filename +findprog-in gettext-h msvc-nothrow open pipe2 pipe2-safer spawn +posix_spawn posix_spawnp posix_spawn_file_actions_init posix_spawn_file_actions_addclose posix_spawn_file_actions_adddup2 posix_spawn_file_actions_addopen +posix_spawn_file_actions_addchdir posix_spawn_file_actions_destroy posix_spawnattr_init posix_spawnattr_setsigmask diff --git a/tests/test-spawn-pipe-main.c b/tests/test-spawn-pipe-main.c index 946871b..fe4f94e 100644 --- a/tests/test-spawn-pipe-main.c +++ b/tests/test-spawn-pipe-main.c @@ -52,7 +52,7 @@ test_pipe (const char *prog, bool stderr_closed) argv[0] = (char *) prog; argv[1] = (char *) (stderr_closed ? "1" : "0"); argv[2] = NULL; - pid = create_pipe_bidi (prog, prog, argv, false, true, true, fd); + pid = create_pipe_bidi (prog, prog, argv, NULL, false, true, true, fd); ASSERT (0 <= pid); ASSERT (STDERR_FILENO < fd[0]); ASSERT (STDERR_FILENO < fd[1]); -- 2.7.4