diff options
author | Philipp Stephani <phst@google.com> | 2020-12-26 12:20:51 +0100 |
---|---|---|
committer | Philipp Stephani <phst@google.com> | 2020-12-26 12:20:51 +0100 |
commit | e24f15c562e39f051dc963f9b3744b9a12b4c839 (patch) | |
tree | 9cae1005f6aaf3931c38f6e8be4049de13cf3e60 | |
parent | 3497c02d936ccb99fd61daf4409343d13cc88630 (diff) | |
download | emacs-scratch/posix-spawn.tar.gz |
Revert "Revert "Use posix_spawn if possible.""scratch/posix-spawn
This reverts commit e387371497d313f05b94e3bf42fe6685184605d1.
-rw-r--r-- | src/Makefile.in | 4 | ||||
-rw-r--r-- | src/callproc.c | 148 |
2 files changed, 150 insertions, 2 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index 19304cca040..06922379412 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -239,6 +239,8 @@ LCMS2_CFLAGS = @LCMS2_CFLAGS@ LIBZ = @LIBZ@ +LIB_POSIX_SPAWN = @LIB_POSIX_SPAWN@ + ## system-specific libs for dynamic modules, else empty LIBMODULES = @LIBMODULES@ ## dynlib.o emacs-module.o if modules enabled, else empty @@ -535,7 +537,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \ $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \ - $(JSON_LIBS) $(LIBGMP) + $(JSON_LIBS) $(LIBGMP) $(LIB_POSIX_SPAWN) ## FORCE it so that admin/unidata can decide whether this file is ## up-to-date. Although since charprop depends on bootstrap-emacs, diff --git a/src/callproc.c b/src/callproc.c index 3ecd6880274..a5056531e0b 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -27,6 +27,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <sys/file.h> #include <fcntl.h> +#include <spawn.h> #include "lisp.h" @@ -1228,12 +1229,156 @@ int emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, char **argv, char **envp, const char *cwd, const char *pty) { + /* `posix_spawn' is available on all Unix systems, either natively + or through Gnulib. Gnulib defines `posix_spawn' on Windows as + well, but doesn't implement it yet. So we fall back to our own + code on Windows. Because Gnulib always defines `posix_spawn', we + don't need to use conditional compilation here. */ + +#ifdef DOS_NT + enum { can_use_posix_spawn = false }; +#else + enum { can_use_posix_spawn = true }; +#endif + + /* `posix_spawn' doesn't yet support setting up pseudoterminals, so + we fall back to `vfork' if we're supposed to use a + pseudoterminal. */ + + bool use_posix_spawn = can_use_posix_spawn && pty == NULL; + + posix_spawn_file_actions_t actions; + posix_spawnattr_t attributes; + + if (use_posix_spawn) + { + /* Initialize optional attributes before blocking. */ + bool destroy_actions = false; + bool destroy_attributes = false; + + int error = posix_spawn_file_actions_init (&actions); + if (error != 0) + goto posix_spawn_init_failed; + destroy_actions = true; + + error = posix_spawnattr_init (&attributes); + if (error != 0) + goto posix_spawn_init_failed; + destroy_attributes = true; + + error = posix_spawn_file_actions_adddup2 (&actions, std_in, + STDIN_FILENO); + if (error != 0) + goto posix_spawn_init_failed; + + error = posix_spawn_file_actions_adddup2 (&actions, std_out, + STDOUT_FILENO); + if (error != 0) + goto posix_spawn_init_failed; + + error = posix_spawn_file_actions_adddup2 (&actions, + std_err < 0 ? std_out + : std_err, + STDERR_FILENO); + if (error != 0) + goto posix_spawn_init_failed; + + error = posix_spawn_file_actions_addchdir (&actions, cwd); + if (error != 0) + goto posix_spawn_init_failed; + + error = posix_spawnattr_setflags (&attributes, + POSIX_SPAWN_SETPGROUP + | POSIX_SPAWN_SETSIGDEF + | POSIX_SPAWN_SETSIGMASK); + if (error != 0) + goto posix_spawn_init_failed; + + error = posix_spawnattr_setpgroup (&attributes, 0); + if (error != 0) + goto posix_spawn_init_failed; + + sigset_t sigdefault; + sigemptyset (&sigdefault); + +#ifdef DARWIN_OS + /* Work around a macOS bug, where SIGCHLD is apparently + delivered to a vforked child instead of to its parent. See: + https://lists.gnu.org/r/emacs-devel/2017-05/msg00342.html + */ + sigaddset (&sigdefault, SIGCHLD); +#endif + + sigaddset (&sigdefault, SIGINT); + sigaddset (&sigdefault, SIGQUIT); +#ifdef SIGPROF + sigaddset (&sigdefault, SIGPROF); +#endif + + /* Emacs ignores SIGPIPE, but the child should not. */ + sigaddset (&sigdefault, SIGPIPE); + /* Likewise for SIGPROF. */ +#ifdef SIGPROF + sigaddset (&sigdefault, SIGPROF); +#endif + + error + = posix_spawnattr_setsigdefault (&attributes, &sigdefault); + if (error != 0) + goto posix_spawn_init_failed; + + /* Stop blocking SIGCHLD in the child. */ + sigset_t oldset; + error = pthread_sigmask (SIG_SETMASK, NULL, &oldset); + if (error != 0) + goto posix_spawn_init_failed; + error = posix_spawnattr_setsigmask (&attributes, &oldset); + if (error != 0) + goto posix_spawn_init_failed; + + goto next; + + posix_spawn_init_failed: + if (destroy_actions) + posix_spawn_file_actions_destroy (&actions); + if (destroy_attributes) + posix_spawnattr_destroy (&attributes); + eassert (0 < error); + return error; + } + sigset_t oldset; int pid; + int vfork_error; + next: block_input (); block_child_signal (&oldset); + if (use_posix_spawn) + { + vfork_error = posix_spawn (&pid, argv[0], &actions, &attributes, + argv, envp); + if (vfork_error != 0) + pid = -1; + + int error = posix_spawn_file_actions_destroy (&actions); + if (error != 0) + { + errno = error; + emacs_perror ("posix_spawn_file_actions_destroy"); + } + + error = posix_spawnattr_destroy (&attributes); + if (error != 0) + { + errno = error; + emacs_perror ("posix_spawnattr_destroy"); + } + + goto fork_done; + } + #ifndef WINDOWSNT /* vfork, and prevent local vars from being clobbered by the vfork. */ pid_t *volatile newpid_volatile = newpid; @@ -1375,8 +1520,9 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, /* Back in the parent process. */ - int vfork_error = pid < 0 ? errno : 0; + vfork_error = pid < 0 ? errno : 0; + fork_done: /* Stop blocking in the parent. */ unblock_child_signal (&oldset); unblock_input (); |