From patchwork Mon May 15 19:36:25 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 20450 Received: (qmail 62172 invoked by alias); 15 May 2017 19:36:51 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 62113 invoked by uid 89); 15 May 2017 19:36:50 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.9 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=ham version=3.3.2 spammy=resume, Signal, disposition X-HELO: mail-qk0-f174.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=myFqOaTKeFthn7w9SarLBgF/DyfYgB0kDbo0FSPd1ls=; b=iCSwLn1aniEMA+ecTOoewQaL8Rh+E3dmrVXceL1r5Yt7e4iy4inbr9gNz+GRKlpPdt 2H94r/t5C8x2SZi2pbvT3k380zbZQoRqFQ+TPqZi4B2KHeFOlnUuv/10mJ/McJ8UQi4X fLsMYDGBuUvZTyGLzgUxb99hmWKGGM2fqs3uuxlKOCjfBmgDz0RE9wIPm39n+kGZmUzs nS7zcTOXR7j2MxPsxaOmBTRnEkK/ALzqYfg4hdDZXIM4c+xOYLA9/4BIl2pFqVzM5kul LCvHGmysb+NxqkgxyT4olz3pBqVWQKs+eLcoDOtBdsTL13abqjkF8gjcL20wXFx1zQ7Y a6Og== X-Gm-Message-State: AODbwcBZmWdoPQEpNW8om3p0m4Gp8rzaGWb4yM0DH2Jv4e70NaXU8yYR uEUHvWHDNEmiDGTBeR9P4A== X-Received: by 10.55.156.11 with SMTP id f11mr7565109qke.8.1494876995536; Mon, 15 May 2017 12:36:35 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Subject: [PATCH 3/3] posix: Refactor posix_spawn Date: Mon, 15 May 2017 16:36:25 -0300 Message-Id: <1494876985-21990-3-git-send-email-adhemerval.zanella@linaro.org> In-Reply-To: <1494876985-21990-1-git-send-email-adhemerval.zanella@linaro.org> References: <1494876985-21990-1-git-send-email-adhemerval.zanella@linaro.org> 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(-) 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 @@ . */ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "spawn_int.h" +#include + /* 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 -/* 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); -}