LIBCx, which is a kLIBC extension library, provides spawn2() to support
this feature. If LIBCx is available, then support a directory argument
using spawn2() of LIBCx.

* lib/os2-spawn.c (spawnpvech): New function.
* lib/os2-spawn.h (spawnpvech): New declaration.
* lib/spawn-pipe.c (create_pipe) [kLIBC]: Reimplement with spawnpvech().
* m4/spawn-pipe.m4 (gl_SPAWN_PIPE) [HAVE_LIBCX_SPAWN2_H]: Check if
libcx/spawn2.h is available.
---
 lib/os2-spawn.c  | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/os2-spawn.h  |  6 ++++
 lib/spawn-pipe.c | 61 +++++++++------------------------
 m4/spawn-pipe.m4 | 10 +++++-
 4 files changed, 121 insertions(+), 45 deletions(-)

diff --git a/lib/os2-spawn.c b/lib/os2-spawn.c
index 05bcd1ee07..42b2454908 100644
--- a/lib/os2-spawn.c
+++ b/lib/os2-spawn.c
@@ -28,6 +28,12 @@
 #include <unistd.h>
 #include <errno.h>
 
+#include <process.h>
+#include <dlfcn.h>
+#if HAVE_LIBCX_SPAWN2_H
+# include <libcx/spawn2.h>
+#endif
+
 #include "cloexec.h"
 #include <error.h>
 #include "gettext.h"
@@ -152,3 +158,86 @@ prepare_spawn (const char * const *argv, char 
**mem_to_free)
 
   return new_argv;
 }
+
+int
+spawnpvech (int mode,
+            const char *progname, const char * const *argv,
+            const char * const *envp,
+            const char *currdir,
+            int new_stdin, int new_stdout, int new_stderr)
+{
+#if HAVE_LIBCX_SPAWN2_H
+  static int (*libcx_spawn2) (int mode,
+                              const char *name, const char * const argv[],
+                              const char *cwd, const char * const envp[],
+                              const int stdfds[]) = NULL;
+  static int libcx_spawn2_loaded = -1;
+#else
+  static int libcx_spawn2_loaded = 0;
+#endif
+
+  int saved_stdin = STDIN_FILENO;
+  int saved_stdout = STDOUT_FILENO;
+  int saved_stderr = STDERR_FILENO;
+  int ret = -1;
+
+#if HAVE_LIBCX_SPAWN2_H
+  if (libcx_spawn2_loaded == -1)
+    {
+      void *libcx_handle;
+
+      libcx_handle = dlopen ("libcx0", RTLD_LAZY);
+      if (libcx_handle != NULL)
+        libcx_spawn2 = dlsym (libcx_handle, "_spawn2");
+
+      libcx_spawn2_loaded = libcx_handle != NULL && libcx_spawn2 != NULL;
+    }
+#endif
+
+  if (!(libcx_spawn2_loaded
+        || (currdir == NULL || strcmp (currdir, ".") == 0)))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Save standard file handles.  */
+  /* 0 means no changes. This is a behavior of spawn2().  */
+  if (new_stdin != 0)
+    saved_stdin = dup_safer_noinherit (STDIN_FILENO);
+
+  if (!(new_stdout == 0 || new_stdout == 1))
+    saved_stdout = dup_safer_noinherit (STDOUT_FILENO);
+
+  if (!(new_stderr == 0 || new_stderr == 2))
+    saved_stderr = dup_safer_noinherit (STDERR_FILENO);
+
+  if ((saved_stdin == STDIN_FILENO || dup2 (new_stdin, STDIN_FILENO) >= 0)
+      && (saved_stdout == STDOUT_FILENO
+          || dup2 (new_stdout, STDOUT_FILENO) >= 0)
+      && (saved_stderr == STDERR_FILENO
+          || dup2 (new_stderr, STDERR_FILENO) >= 0))
+    {
+      if (!libcx_spawn2_loaded
+          || (currdir == NULL || strcmp (currdir, ".") == 0))
+        ret = spawnvpe (mode, progname, (char * const *) argv,
+                        (char * const *) envp);
+#if HAVE_LIBCX_SPAWN2_H
+      else
+        ret = libcx_spawn2 (mode | P_2_THREADSAFE, progname, argv, currdir,
+                            envp, NULL);
+#endif
+    }
+
+  /* Restores standard file handles.  */
+  if (saved_stderr > STDERR_FILENO)
+    undup_safer_noinherit (saved_stderr, STDERR_FILENO);
+
+  if (saved_stdout > STDOUT_FILENO)
+    undup_safer_noinherit (saved_stdout, STDOUT_FILENO);
+
+  if (saved_stdin > STDIN_FILENO)
+    undup_safer_noinherit (saved_stdin, STDIN_FILENO);
+
+  return ret;
+}
diff --git a/lib/os2-spawn.h b/lib/os2-spawn.h
index c8b8e33505..e71791f52c 100644
--- a/lib/os2-spawn.h
+++ b/lib/os2-spawn.h
@@ -30,4 +30,10 @@ extern void undup_safer_noinherit (int tempfd, int origfd);
 extern const char ** prepare_spawn (const char * const *argv,
                                     char **mem_to_free);
 
+/* Creates a subprocess.  */
+extern int spawnpvech (int mode, const char *progname,
+                       const char * const *argv, const char * const *envp,
+                       const char *currdir,
+                       int stdin_fd, int stdout_fd, int stderr_fd);
+
 #endif /* _OS2_SPAWN_H */
diff --git a/lib/spawn-pipe.c b/lib/spawn-pipe.c
index 0f03926e9f..28e15a117a 100644
--- a/lib/spawn-pipe.c
+++ b/lib/spawn-pipe.c
@@ -356,62 +356,43 @@ create_pipe (const char *progname,
     }
 
 # 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;
-
-  /* Save standard file handles of parent process.  */
-  if (pipe_stdin || prog_stdin != NULL)
-    orig_stdin = dup_safer_noinherit (STDIN_FILENO);
-  if (pipe_stdout || prog_stdout != NULL)
-    orig_stdout = dup_safer_noinherit (STDOUT_FILENO);
-  if (null_stderr)
-    orig_stderr = dup_safer_noinherit (STDERR_FILENO);
+  int new_stdin = STDIN_FILENO;
+  int new_stdout = STDOUT_FILENO;
+  int new_stderr = STDERR_FILENO;
 
   /* Create standard file handles of child process.  */
   nulloutfd = -1;
   stdinfd = -1;
   stdoutfd = -1;
-  if ((!pipe_stdin || dup2 (ofd[0], STDIN_FILENO) >= 0)
-      && (!pipe_stdout || dup2 (ifd[1], STDOUT_FILENO) >= 0)
+  if ((!pipe_stdin || (new_stdin = ofd[0]) >= 0)
+      && (!pipe_stdout || (new_stdout = ifd[1]) >= 0)
       && (!null_stderr
-          || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
-              && (nulloutfd == STDERR_FILENO
-                  || (dup2 (nulloutfd, STDERR_FILENO) >= 0
-                      && close (nulloutfd) >= 0))))
+          || ((nulloutfd = open ("NUL", O_RDWR | O_CLOEXEC, 0)) >= 0
+              && (new_stderr = nulloutfd) >= 0))
       && (pipe_stdin
           || prog_stdin == NULL
-          || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
-              && (stdinfd == STDIN_FILENO
-                  || (dup2 (stdinfd, STDIN_FILENO) >= 0
-                      && close (stdinfd) >= 0))))
+          || ((stdinfd = open (prog_stdin, O_RDONLY | O_CLOEXEC, 0)) >= 0
+              && (new_stdin = stdinfd) >= 0))
       && (pipe_stdout
           || prog_stdout == NULL
-          || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0
-              && (stdoutfd == STDOUT_FILENO
-                  || (dup2 (stdoutfd, STDOUT_FILENO) >= 0
-                      && close (stdoutfd) >= 0)))))
+          || ((stdoutfd = open (prog_stdout, O_WRONLY | O_CLOEXEC, 0)) >= 0
+              && (new_stdout = stdoutfd) >= 0)))
     /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1],
        but it inherits all open()ed or dup2()ed file handles (which is what
        we want in the case of STD*_FILENO).  */
     {
-      child = _spawnvpe (P_NOWAIT, prog_path, argv + 1,
-                         (const char **) environ);
+      child = spawnpvech (P_NOWAIT, prog_path, argv + 1,
+                          (const char **) environ, directory,
+                          new_stdin, new_stdout, new_stderr);
 #  if 0 /* Executing arbitrary files as shell scripts is unsecure.  */
       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 argv.  */
-          child = _spawnvpe (P_NOWAIT, argv[0], argv,
-                             (const char **) environ);
+          child = spawnpvech (P_NOWAIT, argv[0], argv,
+                              (const char **) environ, directory,
+                              new_stdin, new_stdout, new_stderr);
         }
 #  endif
     }
@@ -424,14 +405,6 @@ create_pipe (const char *progname,
   if (nulloutfd >= 0)
     close (nulloutfd);
 
-  /* Restore standard file handles of parent process.  */
-  if (null_stderr)
-    undup_safer_noinherit (orig_stderr, STDERR_FILENO);
-  if (pipe_stdout || prog_stdout != NULL)
-    undup_safer_noinherit (orig_stdout, STDOUT_FILENO);
-  if (pipe_stdin || prog_stdin != NULL)
-    undup_safer_noinherit (orig_stdin, STDIN_FILENO);
-
   if (pipe_stdin)
     close (ofd[0]);
   if (pipe_stdout)
diff --git a/m4/spawn-pipe.m4 b/m4/spawn-pipe.m4
index 6539bdbb95..87c7a1e7c3 100644
--- a/m4/spawn-pipe.m4
+++ b/m4/spawn-pipe.m4
@@ -1,5 +1,5 @@
 # spawn-pipe.m4
-# serial 2
+# serial 3
 dnl Copyright (C) 2004, 2008-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -9,4 +9,12 @@ AC_DEFUN([gl_SPAWN_PIPE],
 [
   dnl Prerequisites of lib/spawn-pipe.c.
   AC_REQUIRE([AC_TYPE_MODE_T])
+
+  AC_CHECK_HEADERS_ONCE([libcx/spawn2.h])
+  if test $ac_cv_header_libcx_spawn2_h = yes; then
+    HAVE_LIBCX_SPAWN2_H=1
+  else
+    HAVE_LIBCX_SPAWN2_H=0
+  fi
+  AC_SUBST(HAVE_LIBCX_SPAWN2_H)
 ])
-- 
2.42.0


Reply via email to