]> git.eshelyaron.com Git - emacs.git/commitdiff
Revert "Revert "Use posix_spawn if possible."" scratch/posix-spawn
authorPhilipp Stephani <phst@google.com>
Sat, 26 Dec 2020 11:20:51 +0000 (12:20 +0100)
committerPhilipp Stephani <phst@google.com>
Sat, 26 Dec 2020 11:20:51 +0000 (12:20 +0100)
This reverts commit e387371497d313f05b94e3bf42fe6685184605d1.

src/Makefile.in
src/callproc.c

index 19304cca040c921ed542125866b36943eaee63ea..06922379412c5ad33226c5d81c5a4a26f1547219 100644 (file)
@@ -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,
index 3ecd68802749ae9dcc2ebbf4369e0d95bb4d4d64..a5056531e0b6b30f5d2302d615cf4f3e6d9335ec 100644 (file)
@@ -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 ();