[3/3] posix: Refactor posix_spawn

Message ID 1494876985-21990-3-git-send-email-adhemerval.zanella@linaro.org
State New
Headers show
Series
  • [1/3] posix: Adapt tst-spawn{2,3} to use libsupport.
Related show

Commit Message

Adhemerval Zanella May 15, 2017, 7:36 p.m.
This patch refactor both POSIX and Linux internal posix_spawn to use
a common implementation.  The POSIX one is parametrized to use define
hooks and Linux reimplements it on its file.  No functional change is
expected.

Checked on x86_64-linux-gnu.

	* sysdeps/posix/spawni.c (_POSIX_SPAWN_SYNC_ARGS): New define.
	(_POSIX_SPAWN_CHILD_SETUP_SIGNAL): Likewise.
	(_POSIX_SPAWN_CHILD_SYNC): Likewise.
	(_POSIX_SPAWN_CHILD_SYNC_END): Likewise.
	(_POSIX_SPAWN_PARENT_SYNC_START): Likewise.
	(_POSIX_SPAWN_PARENT_PROCESS): Likewise.
	(_POSIX_SPAWN_PARENT_BLOCK_SIGNALS): Likewise.
	(_POSIX_SPAWN_PARENT_RESTORE_SIGNALS): Likewise.
	(__spawni_child_setup_signals): New function.
	(__spawn_process): Likewise.
	(struct posix_spawn_args): Use _POSIX_SPAWN_SYNC_ARGS.
	(__spawni_child): Use _POSIX_SPAWN_CHILD_SYNC,
	_POSIX_SPAWN_CHILD_SETUP_SIGNAL, and
	_POSIX_SPAWN_CHILD_SYNC_PRE_EXEC.
	(__spawnix): Use _POSIX_SPAWN_PARENT_SYNC_START,
	_POSIX_SPAWN_PARENT_BLOCK_SIGNALS, _POSIX_SPAWN_PARENT_PROCESS,
	and _POSIX_SPAWN_PARENT_RESTORE_SIGNALS.
	* sysdeps/unix/sysv/linux/spawni.c (_POSIX_SPAWN_SYNC_ARGS): Define.
	(_POSIX_SPAWN_CHILD_SYNC): Likewise.
	(_POSIX_SPAWN_CHILD_SETUP_SIGNAL): Likewise.
	(_POSIX_SPAWN_CHILD_SYNC_PRE_EXEC): Likewise.
	(_POSIX_SPAWN_CHILD_SYNC_END): Likewise.
	(_POSIX_SPAWN_PARENT_SYNC_START: Likewise.
	(_POSIX_SPAWN_PARENT_PROCESS): Likewise.
	(_POSIX_SPAWN_PARENT_BLOCK_SIGNALS): Likewise.
	(_POSIX_SPAWN_PARENT_RESTORE_SIGNALS): Likewise.
	(__spawni_child_setup_signals): New function.
	(__spawn_process): Likewise.
	(maybe_script_execute): Remove function.
	(struct posix_spawn_args): Remove definition.
---
 ChangeLog                        |  31 ++++
 sysdeps/posix/spawni.c           | 190 ++++++++++++++++------
 sysdeps/unix/sysv/linux/spawni.c | 333 +++++++--------------------------------
 3 files changed, 233 insertions(+), 321 deletions(-)

-- 
2.7.4

Comments

Rasmus Villemoes June 28, 2017, 1:07 a.m. | #1
On Mon, May 15 2017, Adhemerval Zanella <adhemerval.zanella@linaro.org> wrote:

> This patch refactor both POSIX and Linux internal posix_spawn to use

> a common implementation.  The POSIX one is parametrized to use define

> hooks and Linux reimplements it on its file.  No functional change is

> expected.

>


Hm, I do think this makes the code quite a bit harder to understand,
since one has to skip back and forth between the #includer and the file
with the bulk of the implementation to see what actually happens.

I didn't read it all carefully, but there were at least a number of places
where your macro parameters have a double leading underscore, but then
you omit those in the macro body. I don't suppose that's intentional?

>  

> +/* errno should have an appropriate non-zero value; otherwise,

> +   there's a bug in glibc or the kernel.  For lack of an error code

> +   (EINTERNALBUG) describing that, use ECHILD.  Another option would

> +   be to set args->err to some negative sentinel and have the parent

> +   abort(), but that seems needlessly harsh.  */

> +#define _POSIX_SPAWN_CHILD_SYNC_END(__ret, __args) \

> +  args->err = errno ? : ECHILD


here

> +struct posix_spawn_args;

> +static int __spawn_process (pid_t *pid, ptrdiff_t argc,

> +			    struct posix_spawn_args *args);

> +#define _POSIX_SPAWN_PARENT_PROCESS(__pid, __argc, __args) \

> +  __spawn_process (__pid, __argc, __args)

> +

> +#define _POSIX_SPAWN_PARENT_BLOCK_SIGNALS(__args) \

> +  __libc_signal_block_all (&__args.oldmask);

> +

> +#define _POSIX_SPAWN_PARENT_RESTORE_SIGNALS(__args) \

> +  __libc_signal_restore_set (&args.oldmask);


here
Adhemerval Zanella June 28, 2017, 12:51 p.m. | #2
On 27/06/2017 22:07, Rasmus Villemoes wrote:
> On Mon, May 15 2017, Adhemerval Zanella <adhemerval.zanella@linaro.org> wrote:

> 

>> This patch refactor both POSIX and Linux internal posix_spawn to use

>> a common implementation.  The POSIX one is parametrized to use define

>> hooks and Linux reimplements it on its file.  No functional change is

>> expected.

>>

> 

> Hm, I do think this makes the code quite a bit harder to understand,

> since one has to skip back and forth between the #includer and the file

> with the bulk of the implementation to see what actually happens.

> 

> I didn't read it all carefully, but there were at least a number of places

> where your macro parameters have a double leading underscore, but then

> you omit those in the macro body. I don't suppose that's intentional?


I was in fact unsure about this change and thinking twice now I am not
sure if it indeed adds more readability. My idea was to try share as
much code is possible with the default posix_spawn implementation,
but it ended with a somewhat convoluted code with a lot of #includes.
I am tending to just drop this specific change and just push the
posix implementation (patch 2/3). 

> 

>>  

>> +/* errno should have an appropriate non-zero value; otherwise,

>> +   there's a bug in glibc or the kernel.  For lack of an error code

>> +   (EINTERNALBUG) describing that, use ECHILD.  Another option would

>> +   be to set args->err to some negative sentinel and have the parent

>> +   abort(), but that seems needlessly harsh.  */

>> +#define _POSIX_SPAWN_CHILD_SYNC_END(__ret, __args) \

>> +  args->err = errno ? : ECHILD

> 

> here

> 

>> +struct posix_spawn_args;

>> +static int __spawn_process (pid_t *pid, ptrdiff_t argc,

>> +			    struct posix_spawn_args *args);

>> +#define _POSIX_SPAWN_PARENT_PROCESS(__pid, __argc, __args) \

>> +  __spawn_process (__pid, __argc, __args)

>> +

>> +#define _POSIX_SPAWN_PARENT_BLOCK_SIGNALS(__args) \

>> +  __libc_signal_block_all (&__args.oldmask);

>> +

>> +#define _POSIX_SPAWN_PARENT_RESTORE_SIGNALS(__args) \

>> +  __libc_signal_restore_set (&args.oldmask);

> 

> here

>

Patch hide | download patch | download mbox

diff --git a/sysdeps/posix/spawni.c b/sysdeps/posix/spawni.c
index e096a42..35130d3 100644
--- a/sysdeps/posix/spawni.c
+++ b/sysdeps/posix/spawni.c
@@ -1,4 +1,4 @@ 
-/* Guts of POSIX spawn interface.  Generic POSIX.1 version.
+/* Generic POSIXX.1 posix_spawn implementation.
    Copyright (C) 2000-2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -42,6 +42,12 @@ 
    normal program exit with the exit code 127.  */
 #define SPAWN_ERROR	127
 
+/* Additional variable to synchronize the error value with parent.  For
+   default implementation it is pipe.  */
+#ifndef _POSIX_SPAWN_SYNC_ARGS
+# define _POSIX_SPAWN_SYNC_ARGS  int pipe[2]
+#endif
+
 struct posix_spawn_args
 {
   sigset_t oldmask;
@@ -53,9 +59,120 @@  struct posix_spawn_args
   ptrdiff_t argc;
   char *const *envp;
   int xflags;
-  int pipe[2];
+  _POSIX_SPAWN_SYNC_ARGS;
 };
 
+/* Any steps required to setup the signal disposition for
+   POSIX_SPAWN_SETSIGDEF flag.  */
+#ifndef _POSIX_SPAWN_CHILD_SETUP_SIGNAL
+static void
+__spawni_child_setup_signals (const posix_spawnattr_t *restrict attr)
+{
+  /* Set signal default action.  */
+  if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) != 0)
+    {
+      /* We have to iterate over all signals.  This could possibly be
+	 done better but it requires system specific solutions since
+	 the sigset_t data type can be very different on different
+	 architectures.  */
+      int sig;
+      struct sigaction sa;
+
+      memset (&sa, '\0', sizeof (sa));
+      sa.sa_handler = SIG_DFL;
+
+      for (sig = 1; sig <= _NSIG; ++sig)
+	if (__sigismember (&attr->__sd, sig) != 0)
+	  /* Ignore erros for invalid signals/handlers.  */
+	  __sigaction (sig, &sa, NULL);
+    }
+}
+# define _POSIX_SPAWN_CHILD_SETUP_SIGNAL(__attr) \
+ __spawni_child_setup_signals (__attr)
+#endif
+
+/* Any steps required to handle the error sync in the child.  */
+#ifndef _POSIX_SPAWN_CHILD_SYNC
+# define _POSIX_SPAWN_CHILD_SYNC(__args) \
+  __close (__args->pipe[0])
+#endif
+
+/* Any steps required to handle the error sync just before the call to
+   execve/execvep.  */
+#ifndef _POSIX_SPAWN_CHILD_SYNC_PRE_EXEC
+# define _POSIX_SPAWN_CHILD_SYNC_PRE_EXEC(__args)
+#endif
+
+/* Any steps required to handle the error sync after a failed execve/execvep
+   call.  */
+#ifndef _POSIX_SPAWN_CHILD_SYNC_END
+/* Write down the error on the pipe.  Since sizeof errno < PIPE_BUF, the write
+   is atomic. */
+# define _POSIX_SPAWN_CHILD_SYNC_END(__ret, __args)			    \
+  ({									    \
+    int __err = -ret;							    \
+    if (__err)								    \
+      while (write_not_cancel (args->pipe[1], &__err, sizeof (__err)) < 0); \
+  })
+#endif
+
+/* Any steps required to handle the erro sync in parent before child
+   creation.  */
+#ifndef _POSIX_SPAWN_PARENT_SYNC_START
+# define _POSIX_SPAWN_PARENT_SYNC_START(__args) \
+  if (__pipe2 (__args.pipe, O_CLOEXEC))		\
+    return errno;
+#endif
+
+static int __spawni_child (void *arguments);
+
+/* The routine to create the spawn process and synchronize the error handling
+   with parent.  */
+#ifndef _POSIX_SPAWN_PARENT_PROCESS
+/* For default implementation, create the spawn process using fork plus
+   execve and send any possible creation error using the pipe.  */
+static int
+__spawn_process (pid_t *pid, struct posix_spawn_args *args)
+{
+  int ec;
+
+  pid_t new_pid = __fork ();
+
+  if (new_pid == 0)
+    __spawni_child (args);
+  else if (new_pid > 0)
+    {
+      __close (args->pipe[1]);
+
+      if (__read (args->pipe[0], &ec, sizeof ec) != sizeof ec)
+	ec = 0;
+      else
+	__waitpid (new_pid, &(int) { 0 }, 0);
+    }
+  else
+    ec = -new_pid;
+
+  close (args->pipe[0]);
+
+  *pid = new_pid;
+  return ec;
+}
+# define _POSIX_SPAWN_PARENT_PROCESS(__pid, __argc, __args) \
+  __spawn_process (__pid, __args)
+#endif
+
+/* Block the signal before process spawn.  */
+#ifndef _POSIX_SPAWN_PARENT_BLOCK_SIGNALS
+# define _POSIX_SPAWN_PARENT_BLOCK_SIGNALS(__args) \
+  __sigprocmask (SIG_BLOCK, &SIGALL_SET, &__args.oldmask)
+#endif
+
+/* Restore the signal after process spawn.  */
+#ifndef _POSIX_SPAWN_PARENT_RESTORE_SIGNALS
+# define _POSIX_SPAWN_PARENT_RESTORE_SIGNALS(__args) \
+  __sigprocmask (SIG_SETMASK, &__args.oldmask, 0)
+#endif
+
 /* Older version requires that shell script without shebang definition
    to be called explicitly using /bin/sh (_PATH_BSHELL).  */
 static void
@@ -91,26 +208,9 @@  __spawni_child (void *arguments)
   const posix_spawn_file_actions_t *file_actions = args->fa;
   int ret;
 
-  __close (args->pipe[0]);
-
-  /* Set signal default action.  */
-  if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) != 0)
-    {
-      /* We have to iterate over all signals.  This could possibly be
-	 done better but it requires system specific solutions since
-	 the sigset_t data type can be very different on different
-	 architectures.  */
-      int sig;
-      struct sigaction sa;
-
-      memset (&sa, '\0', sizeof (sa));
-      sa.sa_handler = SIG_DFL;
+  _POSIX_SPAWN_CHILD_SYNC (args);
 
-      for (sig = 1; sig <= _NSIG; ++sig)
-	if (__sigismember (&attr->__sd, sig) != 0
-	    && __sigaction (sig, &sa, NULL) != 0)
-	  goto fail;
-    }
+  _POSIX_SPAWN_CHILD_SETUP_SIGNAL (attr);
 
 #ifdef _POSIX_PRIORITY_SCHEDULING
   /* Set the scheduling algorithm and parameters.  */
@@ -221,6 +321,7 @@  __spawni_child (void *arguments)
   __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
 		 ? &attr->__ss : &args->oldmask, 0);
 
+  _POSIX_SPAWN_CHILD_SYNC_PRE_EXEC (args);
   args->exec (args->file, args->argv, args->envp);
 
   /* This is compatibility function required to enable posix_spawn run
@@ -231,10 +332,7 @@  __spawni_child (void *arguments)
   ret = -errno;
 
 fail:
-  /* Since sizeof errno < PIPE_BUF, the write is atomic. */
-  ret = -ret;
-  if (ret)
-    while (write_not_cancel (args->pipe[1], &ret, sizeof (ret)) < 0);
+  _POSIX_SPAWN_CHILD_SYNC_END (ret, args);
 
   _exit (SPAWN_ERROR);
 }
@@ -251,15 +349,17 @@  __spawnix (pid_t *pid, const char *file,
   struct posix_spawn_args args;
   int ec;
 
-  if (__pipe2 (args.pipe, O_CLOEXEC))
-    return errno;
-
-  /* Disable asynchronous cancellation.  */
-  int state;
-  __libc_ptf_call (__pthread_setcancelstate,
-                   (PTHREAD_CANCEL_DISABLE, &state), 0);
+  _POSIX_SPAWN_PARENT_SYNC_START (args);
 
+  /* To avoid imposing hard limits on posix_spawn{p} the total number of
+     arguments is first calculated to allocate a mmap to hold all possible
+     values.  */
   ptrdiff_t argc = 0;
+  /* Linux allows at most max (0x7FFFFFFF, 1/4 stack size) arguments
+     to be used in a execve call.  We limit to INT_MAX minus one due the
+     compatiblity code that may execute a shell script (maybe_script_execute)
+     where it will construct another argument list with an additional
+     argument.  */
   ptrdiff_t limit = INT_MAX - 1;
   while (argv[argc++] != NULL)
     if (argc == limit)
@@ -268,6 +368,11 @@  __spawnix (pid_t *pid, const char *file,
 	return errno;
       }
 
+  /* Disable asynchronous cancellation.  */
+  int state;
+  __libc_ptf_call (__pthread_setcancelstate,
+                   (PTHREAD_CANCEL_DISABLE, &state), 0);
+
   args.file = file;
   args.exec = exec;
   args.fa = file_actions;
@@ -277,28 +382,17 @@  __spawnix (pid_t *pid, const char *file,
   args.envp = envp;
   args.xflags = xflags;
 
-  /* Generate the new process.  */
-  pid_t new_pid = __fork ();
-
-  if (new_pid == 0)
-    __spawni_child (&args);
-  else if (new_pid > 0)
-    {
-      __close (args.pipe[1]);
+  _POSIX_SPAWN_PARENT_BLOCK_SIGNALS (args);
 
-      if (__read (args.pipe[0], &ec, sizeof ec) != sizeof ec)
-	ec = 0;
-      else
-	__waitpid (new_pid, &(int) { 0 }, 0);
-    }
-  else
-    ec = -new_pid;
+  pid_t new_pid = 0;
 
-  __close (args.pipe[0]);
+  ec = _POSIX_SPAWN_PARENT_PROCESS (&new_pid, argc, &args);
 
   if ((ec == 0) && (pid != NULL))
     *pid = new_pid;
 
+  _POSIX_SPAWN_PARENT_RESTORE_SIGNALS (args);
+
   __libc_ptf_call (__pthread_setcancelstate, (state, NULL), 0);
 
   return ec;
diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c
index c56f894..2588046 100644
--- a/sysdeps/unix/sysv/linux/spawni.c
+++ b/sysdeps/unix/sysv/linux/spawni.c
@@ -17,22 +17,8 @@ 
    <http://www.gnu.org/licenses/>.  */
 
 #include <spawn.h>
-#include <assert.h>
-#include <fcntl.h>
-#include <paths.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <sys/wait.h>
-#include <sys/param.h>
-#include <sys/mman.h>
-#include <not-cancel.h>
-#include <local-setxid.h>
-#include <shlib-compat.h>
-#include <nptl/pthreadP.h>
-#include <dl-sysdep.h>
-#include <libc-pointer-arith.h>
-#include <ldsodefs.h>
-#include "spawn_int.h"
+#include <stddef.h>
+
 
 /* The Linux implementation of posix_spawn{p} uses the clone syscall directly
    with CLONE_VM and CLONE_VFORK flags and an allocated stack.  The new stack
@@ -52,79 +38,51 @@ 
    code. CLONE_VFORK ensures that the parent does not run until the
    child has either exec'ed successfully or exited.  */
 
+/* Due the CLONE_VFORK synchronization (where parent will resume execution
+   only when child ends its own) there is no need of external mechanism to
+   send the error value (such as a pipe for POSIX default implementation).  */
+#define _POSIX_SPAWN_SYNC_ARGS \
+  int err
 
-/* The Unix standard contains a long explanation of the way to signal
-   an error after the fork() was successful.  Since no new wait status
-   was wanted there is no way to signal an error using one of the
-   available methods.  The committee chose to signal an error by a
-   normal program exit with the exit code 127.  */
-#define SPAWN_ERROR	127
+#define _POSIX_SPAWN_CHILD_SYNC(__args)
 
-#ifdef __ia64__
-# define CLONE(__fn, __stackbase, __stacksize, __flags, __args) \
-  __clone2 (__fn, __stackbase, __stacksize, __flags, __args, 0, 0, 0)
-#else
-# define CLONE(__fn, __stack, __stacksize, __flags, __args) \
-  __clone (__fn, __stack, __flags, __args)
-#endif
+static void __spawni_child_setup_signals (const posix_spawnattr_t
+					  *restrict attr);
+#define _POSIX_SPAWN_CHILD_SETUP_SIGNAL(__attr) \
+  __spawni_child_setup_signals (__attr)
 
-/* Since ia64 wants the stackbase w/clone2, re-use the grows-up macro.  */
-#if _STACK_GROWS_UP || defined (__ia64__)
-# define STACK(__stack, __stack_size) (__stack)
-#elif _STACK_GROWS_DOWN
-# define STACK(__stack, __stack_size) (__stack + __stack_size)
-#endif
+/* Initialize the error value just before the execve/execvep call.  */
+#define _POSIX_SPAWN_CHILD_SYNC_PRE_EXEC(__args) \
+  __args->err = 0
 
+/* errno should have an appropriate non-zero value; otherwise,
+   there's a bug in glibc or the kernel.  For lack of an error code
+   (EINTERNALBUG) describing that, use ECHILD.  Another option would
+   be to set args->err to some negative sentinel and have the parent
+   abort(), but that seems needlessly harsh.  */
+#define _POSIX_SPAWN_CHILD_SYNC_END(__ret, __args) \
+  args->err = errno ? : ECHILD
 
-struct posix_spawn_args
-{
-  sigset_t oldmask;
-  const char *file;
-  int (*exec) (const char *, char *const *, char *const *);
-  const posix_spawn_file_actions_t *fa;
-  const posix_spawnattr_t *restrict attr;
-  char *const *argv;
-  ptrdiff_t argc;
-  char *const *envp;
-  int xflags;
-  int err;
-};
-
-/* Older version requires that shell script without shebang definition
-   to be called explicitly using /bin/sh (_PATH_BSHELL).  */
-static void
-maybe_script_execute (struct posix_spawn_args *args)
-{
-  if (SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15)
-      && (args->xflags & SPAWN_XFLAGS_TRY_SHELL) && errno == ENOEXEC)
-    {
-      char *const *argv = args->argv;
-      ptrdiff_t argc = args->argc;
-
-      /* Construct an argument list for the shell.  */
-      char *new_argv[argc + 1];
-      new_argv[0] = (char *) _PATH_BSHELL;
-      new_argv[1] = (char *) args->file;
-      if (argc > 1)
-	memcpy (new_argv + 2, argv + 1, argc * sizeof(char *));
-      else
-	new_argv[2] = NULL;
+#define _POSIX_SPAWN_PARENT_SYNC_START(__args)
 
-      /* Execute the shell.  */
-      args->exec (new_argv[0], new_argv, args->envp);
-    }
-}
+struct posix_spawn_args;
+static int __spawn_process (pid_t *pid, ptrdiff_t argc,
+			    struct posix_spawn_args *args);
+#define _POSIX_SPAWN_PARENT_PROCESS(__pid, __argc, __args) \
+  __spawn_process (__pid, __argc, __args)
+
+#define _POSIX_SPAWN_PARENT_BLOCK_SIGNALS(__args) \
+  __libc_signal_block_all (&__args.oldmask);
+
+#define _POSIX_SPAWN_PARENT_RESTORE_SIGNALS(__args) \
+  __libc_signal_restore_set (&args.oldmask);
+
+#include <sysdeps/posix/spawni.c>
 
-/* Function used in the clone call to setup the signals mask, posix_spawn
-   attributes, and file actions.  It run on its own stack (provided by the
-   posix_spawn call).  */
-static int
-__spawni_child (void *arguments)
-{
-  struct posix_spawn_args *args = arguments;
-  const posix_spawnattr_t *restrict attr = args->attr;
-  const posix_spawn_file_actions_t *file_actions = args->fa;
 
+static void
+__spawni_child_setup_signals (const posix_spawnattr_t *restrict attr)
+{
   /* The child must ensure that no signal handler are enabled because it shared
      memory with parent, so the signal disposition must be either SIG_DFL or
      SIG_IGN.  It does by iterating over all signals and although it could
@@ -160,162 +118,27 @@  __spawni_child (void *arguments)
 
       __libc_sigaction (sig, &sa, 0);
     }
+}
 
-#ifdef _POSIX_PRIORITY_SCHEDULING
-  /* Set the scheduling algorithm and parameters.  */
-  if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
-      == POSIX_SPAWN_SETSCHEDPARAM)
-    {
-      if (__sched_setparam (0, &attr->__sp) == -1)
-	goto fail;
-    }
-  else if ((attr->__flags & POSIX_SPAWN_SETSCHEDULER) != 0)
-    {
-      if (__sched_setscheduler (0, attr->__policy, &attr->__sp) == -1)
-	goto fail;
-    }
-#endif
-
-  if ((attr->__flags & POSIX_SPAWN_SETSID) != 0
-      && __setsid () < 0)
-    goto fail;
-
-  /* Set the process group ID.  */
-  if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0
-      && __setpgid (0, attr->__pgrp) != 0)
-    goto fail;
-
-  /* Set the effective user and group IDs.  */
-  if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0
-      && (local_seteuid (__getuid ()) != 0
-	  || local_setegid (__getgid ()) != 0))
-    goto fail;
-
-  /* Execute the file actions.  */
-  if (file_actions != 0)
-    {
-      int cnt;
-      struct rlimit64 fdlimit;
-      bool have_fdlimit = false;
-
-      for (cnt = 0; cnt < file_actions->__used; ++cnt)
-	{
-	  struct __spawn_action *action = &file_actions->__actions[cnt];
 
-	  switch (action->tag)
-	    {
-	    case spawn_do_close:
-	      if (close_not_cancel (action->action.close_action.fd) != 0)
-		{
-		  if (!have_fdlimit)
-		    {
-		      __getrlimit64 (RLIMIT_NOFILE, &fdlimit);
-		      have_fdlimit = true;
-		    }
-
-		  /* Signal errors only for file descriptors out of range.  */
-		  if (action->action.close_action.fd < 0
-		      || action->action.close_action.fd >= fdlimit.rlim_cur)
-		    goto fail;
-		}
-	      break;
-
-	    case spawn_do_open:
-	      {
-		/* POSIX states that if fildes was already an open file descriptor,
-		   it shall be closed before the new file is opened.  This avoid
-		   pontential issues when posix_spawn plus addopen action is called
-		   with the process already at maximum number of file descriptor
-		   opened and also for multiple actions on single-open special
-		   paths (like /dev/watchdog).  */
-		close_not_cancel (action->action.open_action.fd);
-
-		int ret = open_not_cancel (action->action.open_action.path,
-					   action->action.
-					   open_action.oflag | O_LARGEFILE,
-					   action->action.open_action.mode);
-
-		if (ret == -1)
-		  goto fail;
-
-		int new_fd = ret;
-
-		/* Make sure the desired file descriptor is used.  */
-		if (ret != action->action.open_action.fd)
-		  {
-		    if (__dup2 (new_fd, action->action.open_action.fd)
-			!= action->action.open_action.fd)
-		      goto fail;
-
-		    if (close_not_cancel (new_fd) != 0)
-		      goto fail;
-		  }
-	      }
-	      break;
-
-	    case spawn_do_dup2:
-	      if (__dup2 (action->action.dup2_action.fd,
-			  action->action.dup2_action.newfd)
-		  != action->action.dup2_action.newfd)
-		goto fail;
-	      break;
-	    }
-	}
-    }
+#ifdef __ia64__
+# define CLONE(__fn, __stackbase, __stacksize, __flags, __args) \
+  __clone2 (__fn, __stackbase, __stacksize, __flags, __args, 0, 0, 0)
+#else
+# define CLONE(__fn, __stack, __stacksize, __flags, __args) \
+  __clone (__fn, __stack, __flags, __args)
+#endif
 
-  /* Set the initial signal mask of the child if POSIX_SPAWN_SETSIGMASK
-     is set, otherwise restore the previous one.  */
-  __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
-		 ? &attr->__ss : &args->oldmask, 0);
-
-  args->err = 0;
-  args->exec (args->file, args->argv, args->envp);
-
-  /* This is compatibility function required to enable posix_spawn run
-     script without shebang definition for older posix_spawn versions
-     (2.15).  */
-  maybe_script_execute (args);
-
-fail:
-  /* errno should have an appropriate non-zero value; otherwise,
-     there's a bug in glibc or the kernel.  For lack of an error code
-     (EINTERNALBUG) describing that, use ECHILD.  Another option would
-     be to set args->err to some negative sentinel and have the parent
-     abort(), but that seems needlessly harsh.  */
-  args->err = errno ? : ECHILD;
-  _exit (SPAWN_ERROR);
-}
+/* Since ia64 wants the stackbase w/clone2, re-use the grows-up macro.  */
+#if _STACK_GROWS_UP || defined (__ia64__)
+# define STACK(__stack, __stack_size) (__stack)
+#elif _STACK_GROWS_DOWN
+# define STACK(__stack, __stack_size) (__stack + __stack_size)
+#endif
 
-/* Spawn a new process executing PATH with the attributes describes in *ATTRP.
-   Before running the process perform the actions described in FILE-ACTIONS. */
 static int
-__spawnix (pid_t * pid, const char *file,
-	   const posix_spawn_file_actions_t * file_actions,
-	   const posix_spawnattr_t * attrp, char *const argv[],
-	   char *const envp[], int xflags,
-	   int (*exec) (const char *, char *const *, char *const *))
+__spawn_process (pid_t *pid, ptrdiff_t argc, struct posix_spawn_args *args)
 {
-  pid_t new_pid;
-  struct posix_spawn_args args;
-  int ec;
-
-  /* To avoid imposing hard limits on posix_spawn{p} the total number of
-     arguments is first calculated to allocate a mmap to hold all possible
-     values.  */
-  ptrdiff_t argc = 0;
-  /* Linux allows at most max (0x7FFFFFFF, 1/4 stack size) arguments
-     to be used in a execve call.  We limit to INT_MAX minus one due the
-     compatiblity code that may execute a shell script (maybe_script_execute)
-     where it will construct another argument list with an additional
-     argument.  */
-  ptrdiff_t limit = INT_MAX - 1;
-  while (argv[argc++] != NULL)
-    if (argc == limit)
-      {
-	errno = E2BIG;
-	return errno;
-      }
-
   int prot = (PROT_READ | PROT_WRITE
 	     | ((GL (dl_stack_flags) & PF_X) ? PROT_EXEC : 0));
 
@@ -332,25 +155,6 @@  __spawnix (pid_t * pid, const char *file,
   if (__glibc_unlikely (stack == MAP_FAILED))
     return errno;
 
-  /* Disable asynchronous cancellation.  */
-  int state;
-  __libc_ptf_call (__pthread_setcancelstate,
-                   (PTHREAD_CANCEL_DISABLE, &state), 0);
-
-  /* Child must set args.err to something non-negative - we rely on
-     the parent and child sharing VM.  */
-  args.err = -1;
-  args.file = file;
-  args.exec = exec;
-  args.fa = file_actions;
-  args.attr = attrp ? attrp : &(const posix_spawnattr_t) { 0 };
-  args.argv = argv;
-  args.argc = argc;
-  args.envp = envp;
-  args.xflags = xflags;
-
-  __libc_signal_block_all (&args.oldmask);
-
   /* The clone flags used will create a new child that will run in the same
      memory space (CLONE_VM) and the execution of calling thread will be
      suspend until the child calls execve or _exit.
@@ -359,39 +163,22 @@  __spawnix (pid_t * pid, const char *file,
      need for CLONE_SETTLS.  Although parent and child share the same TLS
      namespace, there will be no concurrent access for TLS variables (errno
      for instance).  */
-  new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size,
-		   CLONE_VM | CLONE_VFORK | SIGCHLD, &args);
+  pid_t new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size,
+			 CLONE_VM | CLONE_VFORK | SIGCHLD, args);
 
+  int ec = 0;
   if (new_pid > 0)
     {
-      ec = args.err;
+      ec = args->err;
       assert (ec >= 0);
       if (ec != 0)
-	  __waitpid (new_pid, NULL, 0);
+	__waitpid (new_pid, NULL, 0);
     }
   else
     ec = -new_pid;
 
   __munmap (stack, stack_size);
 
-  if ((ec == 0) && (pid != NULL))
-    *pid = new_pid;
-
-  __libc_signal_restore_set (&args.oldmask);
-
-  __libc_ptf_call (__pthread_setcancelstate, (state, NULL), 0);
-
+  *pid = new_pid;
   return ec;
 }
-
-/* Spawn a new process executing PATH with the attributes describes in *ATTRP.
-   Before running the process perform the actions described in FILE-ACTIONS. */
-int
-__spawni (pid_t * pid, const char *file,
-	  const posix_spawn_file_actions_t * acts,
-	  const posix_spawnattr_t * attrp, char *const argv[],
-	  char *const envp[], int xflags)
-{
-  return __spawnix (pid, file, acts, attrp, argv, envp, xflags,
-		    xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve);
-}