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