]> Savannah Git Hosting - gnulib.git/commitdiff
spawn-pipe: Allow caller to specify directory for the subprocess.
authorBruno Haible <bruno@clisp.org>
Wed, 2 Dec 2020 16:52:00 +0000 (17:52 +0100)
committerBruno Haible <bruno@clisp.org>
Wed, 2 Dec 2020 16:52:00 +0000 (17:52 +0100)
* 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
NEWS
lib/csharpcomp.c
lib/javacomp.c
lib/javaversion.c
lib/pipe-filter-gi.c
lib/pipe-filter-ii.c
lib/spawn-pipe.c
lib/spawn-pipe.h
modules/spawn-pipe
tests/test-spawn-pipe-main.c

index b5ea083ff5585b91eee2f765cade3d5060dc8d6e..b32f1909cdd453166e3d0e06551a82571d25c00a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2020-12-02  Bruno Haible  <bruno@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  <bruno@clisp.org>
 
        execute: Allow caller to specify directory for the subprocess.
diff --git a/NEWS b/NEWS
index 445266d6e864da5d3c4af064e3158b8c321f5152..747165a265216cf3034e45354d85d3b42f0e3233 100644 (file)
--- 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.
index cf68c5a82bbb3b2d6493914b95e7501687d8aea9..57d2084b0ab1a210d04b96e56f17516d9f6c6735 100644 (file)
@@ -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)
         {
index fec51803940131a141dea1ae438fff5841b140f1..c24bb69166b0eb1289932628fe2fc98b63d8c643 100644 (file)
@@ -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)
         {
index fd99291c4927a90c7e0786d82bf4eeb211f1454e..e50c499c9b7aa492b0ec46335a2387f09eaf86cb 100644 (file)
@@ -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;
index f7f8f6ec83b4d75e02a2889cbfdbf61a04397519..7528053c6b178bea2be3e51244ca4a053e10f422 100644 (file)
@@ -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;
index b29f802b648d39c81812278bf64595cf8f8a666a..384a59bf686ec3cd2aeb76c9ea8f7db57ec12804 100644 (file)
@@ -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;
index a4c4d39c8c340001c7d22c7d45566ebfd8cb317a..209bbf57d06b19cd79c6cf4b12d5f45f19ac4d1b 100644 (file)
 #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);
index be0f1c8c717c92cd5faaa91ba44a03a554298784..02856a81b73e9b7614b54fb382ec2d7c2846aa08 100644 (file)
@@ -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]);
index 2e7817078a181ccb6ee0df426e9810e1f57acd8e..9d80193d07f5f9545601ebee21fee6493183e04f 100644 (file)
@@ -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
index 946871b0cae8e58f4c2318b9dfe15e810c26e59d..fe4f94ecaa27ac07c87f38677aa8f687bdbfe366 100644 (file)
@@ -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]);