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