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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
+
+ 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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
+ 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 <[email protected]>
+
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