summaryrefslogtreecommitdiff
path: root/src/callproc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/callproc.c')
-rw-r--r--src/callproc.c191
1 files changed, 190 insertions, 1 deletions
diff --git a/src/callproc.c b/src/callproc.c
index fa43f973844..c949fff4db9 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -28,6 +28,20 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <sys/file.h>
#include <fcntl.h>
+/* In order to be able to use `posix_spawn', it needs to support some
+ variant of `chdir' as well as `setsid'. */
+#if defined HAVE_SPAWN_H && defined HAVE_POSIX_SPAWN \
+ && defined HAVE_POSIX_SPAWNATTR_SETFLAGS \
+ && (defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR \
+ || defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) \
+ && defined HAVE_DECL_POSIX_SPAWN_SETSID \
+ && HAVE_DECL_POSIX_SPAWN_SETSID == 1
+# include <spawn.h>
+# define USABLE_POSIX_SPAWN 1
+#else
+# define USABLE_POSIX_SPAWN 0
+#endif
+
#include "lisp.h"
#ifdef SETUP_SLAVE_PTY
@@ -1247,6 +1261,130 @@ child_setup (int in, int out, int err, char **new_argv, char **env,
#endif /* not WINDOWSNT */
}
+#if USABLE_POSIX_SPAWN
+
+/* Set up ACTIONS and ATTRIBUTES for `posix_spawn'. Return an error
+ number. */
+
+static int
+emacs_posix_spawn_init_actions (posix_spawn_file_actions_t *actions,
+ int std_in, int std_out, int std_err,
+ const char *cwd)
+{
+ int error = posix_spawn_file_actions_init (actions);
+ if (error != 0)
+ return error;
+
+ error = posix_spawn_file_actions_adddup2 (actions, std_in,
+ STDIN_FILENO);
+ if (error != 0)
+ goto out;
+
+ error = posix_spawn_file_actions_adddup2 (actions, std_out,
+ STDOUT_FILENO);
+ if (error != 0)
+ goto out;
+
+ error = posix_spawn_file_actions_adddup2 (actions,
+ std_err < 0 ? std_out
+ : std_err,
+ STDERR_FILENO);
+ if (error != 0)
+ goto out;
+
+ error =
+#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR
+ posix_spawn_file_actions_addchdir
+#else
+ posix_spawn_file_actions_addchdir_np
+#endif
+ (actions, cwd);
+ if (error != 0)
+ goto out;
+
+ out:
+ if (error != 0)
+ posix_spawn_file_actions_destroy (actions);
+ return error;
+}
+
+static int
+emacs_posix_spawn_init_attributes (posix_spawnattr_t *attributes)
+{
+ int error = posix_spawnattr_init (attributes);
+ if (error != 0)
+ return error;
+
+ error = posix_spawnattr_setflags (attributes,
+ POSIX_SPAWN_SETSID
+ | POSIX_SPAWN_SETSIGDEF
+ | POSIX_SPAWN_SETSIGMASK);
+ if (error != 0)
+ goto out;
+
+ 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 out;
+
+ /* Stop blocking SIGCHLD in the child. */
+ sigset_t oldset;
+ error = pthread_sigmask (SIG_SETMASK, NULL, &oldset);
+ if (error != 0)
+ goto out;
+ error = posix_spawnattr_setsigmask (attributes, &oldset);
+ if (error != 0)
+ goto out;
+
+ out:
+ if (error != 0)
+ posix_spawnattr_destroy (attributes);
+
+ return error;
+}
+
+static int
+emacs_posix_spawn_init (posix_spawn_file_actions_t *actions,
+ posix_spawnattr_t *attributes, int std_in,
+ int std_out, int std_err, const char *cwd)
+{
+ int error = emacs_posix_spawn_init_actions (actions, std_in,
+ std_out, std_err, cwd);
+ if (error != 0)
+ return error;
+
+ error = emacs_posix_spawn_init_attributes (attributes);
+ if (error != 0)
+ return error;
+
+ return 0;
+}
+
+#endif
+
/* Start a new asynchronous subprocess. If successful, return zero
and store the process identifier of the new process in *NEWPID.
Use STDIN, STDOUT, and STDERR as standard streams for the new
@@ -1266,10 +1404,58 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err,
char **argv, char **envp, const char *cwd,
const char *pty, const sigset_t *oldset)
{
+#if USABLE_POSIX_SPAWN
+ /* Prefer the simpler `posix_spawn' if available. `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 = pty == NULL;
+
+ posix_spawn_file_actions_t actions;
+ posix_spawnattr_t attributes;
+
+ if (use_posix_spawn)
+ {
+ /* Initialize optional attributes before blocking. */
+ int error
+ = emacs_posix_spawn_init (&actions, &attributes, std_in,
+ std_out, std_err, cwd);
+ if (error != 0)
+ return error;
+ }
+#endif
+
int pid;
+ int vfork_error;
eassert (input_blocked_p ());
+#if USABLE_POSIX_SPAWN
+ 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;
+ }
+#endif
+
#ifndef WINDOWSNT
/* vfork, and prevent local vars from being clobbered by the vfork. */
pid_t *volatile newpid_volatile = newpid;
@@ -1413,8 +1599,11 @@ 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;
+#if USABLE_POSIX_SPAWN
+ fork_done:
+#endif
if (pid < 0)
{
eassert (0 < vfork_error);