]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
split: prefer posix_spawn to fork and execl
authorCollin Funk <collin.funk1@gmail.com>
Thu, 23 Oct 2025 08:11:50 +0000 (01:11 -0700)
committerCollin Funk <collin.funk1@gmail.com>
Thu, 23 Oct 2025 18:24:06 +0000 (11:24 -0700)
* NEWS: Mention the change.
* bootstrap.conf (gnulib_modules): Add posix_spawn,
posix_spawnattr_setsigdefault, posix_spawn_file_actions_addclose,
posix_spawn_file_actions_adddup2, and posix_spawn_file_actions_init.
* src/split.c: Include spawn.h.
(create): Use posix_spawn instead of fork and execl.

NEWS
bootstrap.conf
src/split.c

diff --git a/NEWS b/NEWS
index a5471f37567d495b990c12fd882e2f1d1d00f3f6..ff684fb648ee305bd17e1644dc44a640c4090290 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -54,6 +54,9 @@ GNU coreutils NEWS                                    -*- outline -*-
    - supports a multi-byte --delimiter character
    - no longer processes input indefinitely in the presence of write errors
 
+  'split' now uses posix_spawn() to invoke the shell command specified by
+  --filter more efficiently.
+
   wc -l now operates 10% faster on hosts that support AVX512 instructions.
 
 
index 523c4923ac754c397730e097c0ceecb561bfa8f0..fdab0725f1c1444621c7c3bb90641e6387f404a7 100644 (file)
@@ -213,9 +213,14 @@ gnulib_modules="
   pipe-posix
   pipe2
   posix-shell
+  posix_spawn
   posix_spawnattr_destroy
   posix_spawnattr_init
   posix_spawnattr_setflags
+  posix_spawnattr_setsigdefault
+  posix_spawn_file_actions_addclose
+  posix_spawn_file_actions_adddup2
+  posix_spawn_file_actions_init
   posix_spawnp
   posixtm
   posixver
index e58e0db54ea1e070d1b307c1f8c10dd5c2e13383..077e30f0d76b8b9abb5bdd606556b931ba5e728b 100644 (file)
@@ -27,6 +27,7 @@
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <spawn.h>
 
 #include "system.h"
 #include "alignalloc.h"
@@ -497,50 +498,69 @@ create (char const *name)
     }
   else
     {
-      int fd_pair[2];
-      pid_t child_pid;
-      char const *shell_prog = getenv ("SHELL");
-      if (shell_prog == nullptr)
-        shell_prog = "/bin/sh";
       if (setenv ("FILE", name, 1) != 0)
         error (EXIT_FAILURE, errno,
                _("failed to set FILE environment variable"));
       if (verbose)
         fprintf (stdout, _("executing with FILE=%s\n"), quotef (name));
+
+      int result;
+      int fd_pair[2];
+      pid_t child_pid;
+
+      posix_spawnattr_t attr;
+      posix_spawn_file_actions_t actions;
+
+      sigset_t set;
+      sigemptyset (&set);
+      if (default_SIGPIPE)
+        sigaddset (&set, SIGPIPE);
+
+      if (   (result = posix_spawnattr_init (&attr))
+          || (result = posix_spawnattr_setflags (&attr,
+                                                 (POSIX_SPAWN_USEVFORK
+                                                  | POSIX_SPAWN_SETSIGDEF)))
+          || (result = posix_spawnattr_setsigdefault (&attr, &set))
+          || (result = posix_spawn_file_actions_init (&actions))
+         )
+        error (EXIT_FAILURE, result, _("posix_spawn initialization failed"));
+
       if (pipe (fd_pair) != 0)
         error (EXIT_FAILURE, errno, _("failed to create pipe"));
-      child_pid = fork ();
-      if (child_pid == 0)
-        {
-          /* This is the child process.  If an error occurs here, the
-             parent will eventually learn about it after doing a wait,
-             at which time it will emit its own error message.  */
-          int j;
-          /* We have to close any pipes that were opened during an
-             earlier call, otherwise this process will be holding a
-             write-pipe that will prevent the earlier process from
-             reading an EOF on the corresponding read-pipe.  */
-          for (j = 0; j < n_open_pipes; ++j)
-            if (close (open_pipes[j]) != 0)
-              error (EXIT_FAILURE, errno, _("closing prior pipe"));
-          if (close (fd_pair[1]))
-            error (EXIT_FAILURE, errno, _("closing output pipe"));
-          if (fd_pair[0] != STDIN_FILENO)
-            {
-              if (dup2 (fd_pair[0], STDIN_FILENO) != STDIN_FILENO)
-                error (EXIT_FAILURE, errno, _("moving input pipe"));
-              if (close (fd_pair[0]) != 0)
-                error (EXIT_FAILURE, errno, _("closing input pipe"));
-            }
-          if (default_SIGPIPE)
-            signal (SIGPIPE, SIG_DFL);
-          execl (shell_prog, last_component (shell_prog), "-c",
-                 filter_command, (char *) nullptr);
-          error (EXIT_FAILURE, errno, _("failed to run command: \"%s -c %s\""),
-                 shell_prog, filter_command);
-        }
-      if (child_pid < 0)
-        error (EXIT_FAILURE, errno, _("fork system call failed"));
+
+      /* We have to close any pipes that were opened during an
+         earlier call, otherwise this process will be holding a
+         write-pipe that will prevent the earlier process from
+         reading an EOF on the corresponding read-pipe.  */
+      for (int i = 0; i < n_open_pipes; ++i)
+        if ((result = posix_spawn_file_actions_addclose (&actions,
+                                                         open_pipes[i])))
+          break;
+
+      if (   result
+          || (result = posix_spawn_file_actions_addclose (&actions, fd_pair[1]))
+          || (fd_pair[0] != STDIN_FILENO
+              && (   (result = posix_spawn_file_actions_adddup2 (&actions,
+                                                                 fd_pair[0],
+                                                                 STDIN_FILENO))
+                  || (result = posix_spawn_file_actions_addclose (&actions,
+                                                                  fd_pair[0]))))
+         )
+        error (EXIT_FAILURE, result, _("posix_spawn setup failed"));
+
+
+      char const *shell_prog = getenv ("SHELL");
+      if (shell_prog == nullptr)
+        shell_prog = "/bin/sh";
+      char const *const argv[] = { last_component (shell_prog), "-c",
+                                   filter_command, nullptr };
+
+      result = posix_spawn (&child_pid, shell_prog, &actions, &attr,
+                            (char * const *) argv, environ);
+      if (result != 0)
+        error (EXIT_FAILURE, errno, _("failed to run command: \"%s -c %s\""),
+               shell_prog, filter_command);
+
       if (close (fd_pair[0]) != 0)
         error (EXIT_FAILURE, errno, _("failed to close input pipe"));
       filter_pid = child_pid;