| commit 5fb7fc96350575c9adb1316833e48ca11553be49 |
| Author: Adhemerval Zanella <adhemerval.zanella@linaro.org> |
| Date: Wed Oct 24 16:29:38 2018 -0300 |
| |
| posix: Use posix_spawn on system |
| |
| This patch uses posix_spawn on system implementation. On Linux this has |
| the advantage of much lower memory consumption (usually 32 Kb minimum for |
| the mmap stack area). |
| |
| Although POSIX does not require, glibc system implementation aims to be |
| thread and cancellation safe. The cancellation code is moved to generic |
| implementation and enabled iff SIGCANCEL is defined (similar on how the |
| cancellation handler is enabled on nptl-init.c). |
| |
| Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu, |
| arm-linux-gnueabihf, and powerpc64le-linux-gnu. |
| |
| * sysdeps/unix/sysv/linux/spawni.c (__spawni_child): Use |
| __sigismember instead of sigismember. |
| * sysdeps/posix/system.c [SIGCANCEL] (cancel_handler_args, |
| cancel_handler): New definitions. |
| (CLEANUP_HANDLER, CLEANUP_RESET): Likewise. |
| (DO_LOCK, DO_UNLOCK, INIT_LOCK, ADD_REF, SUB_REF): Remove. |
| (do_system): Use posix_spawn instead of fork and execl and remove |
| reentracy code. |
| * sysdeps/generic/not-errno.h (__kill_noerrno): New prototype. |
| * sysdeps/unix/sysv/linux/not-errno.h (__kill_noerrno): Likewise. |
| * sysdeps/unix/sysv/linux/ia64/system.c: Remove file. |
| * sysdeps/unix/sysv/linux/s390/system.c: Likewise. |
| * sysdeps/unix/sysv/linux/sparc/system.c: Likewise. |
| * sysdeps/unix/sysv/linux/system.c: Likewise. |
| |
| diff --git a/sysdeps/generic/not-errno.h b/sysdeps/generic/not-errno.h |
| index 93617a3266fd4aad..0fd66b5c5ed82315 100644 |
| |
| |
| @@ -17,3 +17,5 @@ |
| <http://www.gnu.org/licenses/>. */ |
| |
| extern __typeof (__access) __access_noerrno attribute_hidden; |
| + |
| +extern __typeof (__kill) __kill_noerrno attribute_hidden; |
| diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c |
| index d7594436ed59906f..8a51a6b9919ec39b 100644 |
| |
| |
| @@ -17,20 +17,36 @@ |
| |
| #include <errno.h> |
| #include <signal.h> |
| -#include <stddef.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| +#include <sigsetops.h> |
| +#include <spawn.h> |
| +#include <pthread.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| -#include <libc-lock.h> |
| -#include <sysdep-cancel.h> |
| -#include <sigsetops.h> |
| +#include <stdio.h> |
| |
| +#include <libc-lock.h> |
| +#include <not-errno.h> |
| +#include <not-cancel.h> |
| +#include <internal-signals.h> |
| |
| #define SHELL_PATH "/bin/sh" /* Path of the shell. */ |
| #define SHELL_NAME "sh" /* Name to give it. */ |
| |
| |
| +/* This system implementation aims to be thread-safe, which requires to |
| + restore the signal dispositions for SIGINT and SIGQUIT correctly and to |
| + deal with cancellation by terminating the child process. |
| + |
| + The signal disposition restoration on the single-thread case is |
| + straighfoward. For multithreaded case, a reference-counter with a lock |
| + is used, so the first thread will set the SIGINT/SIGQUIT dispositions and |
| + last thread will restore them. |
| + |
| + Cancellation handling is done with thread cancellation clean-up handlers |
| + on waitpid call. */ |
| + |
| #ifdef _LIBC_REENTRANT |
| static struct sigaction intr, quit; |
| static int sa_refcntr; |
| @@ -50,17 +66,45 @@ __libc_lock_define_initialized (static, lock); |
| #endif |
| |
| |
| +#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL) |
| +struct cancel_handler_args |
| +{ |
| + struct sigaction *quit; |
| + struct sigaction *intr; |
| + pid_t pid; |
| +}; |
| + |
| +static void |
| +cancel_handler (void *arg) |
| +{ |
| + struct cancel_handler_args *args = (struct cancel_handler_args *) (arg); |
| + |
| + __kill_noerrno (args->pid, SIGKILL); |
| + |
| + TEMP_FAILURE_RETRY (__waitpid_nocancel (args->pid, NULL, 0)); |
| + |
| + DO_LOCK (); |
| + if (SUB_REF () == 0) |
| + { |
| + __sigaction (SIGQUIT, args->quit, NULL); |
| + __sigaction (SIGINT, args->intr, NULL); |
| + } |
| + DO_UNLOCK (); |
| +} |
| +#endif |
| + |
| /* Execute LINE as a shell command, returning its status. */ |
| static int |
| do_system (const char *line) |
| { |
| - int status, save; |
| + int status; |
| pid_t pid; |
| struct sigaction sa; |
| #ifndef _LIBC_REENTRANT |
| struct sigaction intr, quit; |
| #endif |
| sigset_t omask; |
| + sigset_t reset; |
| |
| sa.sa_handler = SIG_IGN; |
| sa.sa_flags = 0; |
| @@ -69,105 +113,72 @@ do_system (const char *line) |
| DO_LOCK (); |
| if (ADD_REF () == 0) |
| { |
| - if (__sigaction (SIGINT, &sa, &intr) < 0) |
| - { |
| - (void) SUB_REF (); |
| - goto out; |
| - } |
| - if (__sigaction (SIGQUIT, &sa, &quit) < 0) |
| - { |
| - save = errno; |
| - (void) SUB_REF (); |
| - goto out_restore_sigint; |
| - } |
| + /* sigaction can not fail with SIGINT/SIGQUIT used with SIG_IGN. */ |
| + __sigaction (SIGINT, &sa, &intr); |
| + __sigaction (SIGQUIT, &sa, &quit); |
| } |
| DO_UNLOCK (); |
| |
| - /* We reuse the bitmap in the 'sa' structure. */ |
| __sigaddset (&sa.sa_mask, SIGCHLD); |
| - save = errno; |
| - if (__sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0) |
| + /* sigprocmask can not fail with SIG_BLOCK used with valid input |
| + arguments. */ |
| + __sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask); |
| + |
| + __sigemptyset (&reset); |
| + if (intr.sa_handler != SIG_IGN) |
| + __sigaddset(&reset, SIGINT); |
| + if (quit.sa_handler != SIG_IGN) |
| + __sigaddset(&reset, SIGQUIT); |
| + |
| + posix_spawnattr_t spawn_attr; |
| + /* None of the posix_spawnattr_* function returns an error, including |
| + posix_spawnattr_setflags for the follow specific usage (using valid |
| + flags). */ |
| + __posix_spawnattr_init (&spawn_attr); |
| + __posix_spawnattr_setsigmask (&spawn_attr, &omask); |
| + __posix_spawnattr_setsigdefault (&spawn_attr, &reset); |
| + __posix_spawnattr_setflags (&spawn_attr, |
| + POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK); |
| + |
| + status = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr, |
| + (char *const[]){ (char*) SHELL_NAME, |
| + (char*) "-c", |
| + (char *) line, NULL }, |
| + __environ); |
| + __posix_spawnattr_destroy (&spawn_attr); |
| + |
| + if (status == 0) |
| { |
| -#ifndef _LIBC |
| - if (errno == ENOSYS) |
| - __set_errno (save); |
| - else |
| -#endif |
| - { |
| - DO_LOCK (); |
| - if (SUB_REF () == 0) |
| - { |
| - save = errno; |
| - (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); |
| - out_restore_sigint: |
| - (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); |
| - __set_errno (save); |
| - } |
| - out: |
| - DO_UNLOCK (); |
| - return -1; |
| - } |
| - } |
| - |
| -#ifdef CLEANUP_HANDLER |
| - CLEANUP_HANDLER; |
| -#endif |
| - |
| -#ifdef FORK |
| - pid = FORK (); |
| -#else |
| - pid = __fork (); |
| + /* Cancellation results in cleanup handlers running as exceptions in |
| + the block where they were installed, so it is safe to reference |
| + stack variable allocate in the broader scope. */ |
| +#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL) |
| + struct cancel_handler_args cancel_args = |
| + { |
| + .quit = &quit, |
| + .intr = &intr, |
| + .pid = pid |
| + }; |
| + __libc_cleanup_region_start (1, cancel_handler, &cancel_args); |
| #endif |
| - if (pid == (pid_t) 0) |
| - { |
| - /* Child side. */ |
| - const char *new_argv[4]; |
| - new_argv[0] = SHELL_NAME; |
| - new_argv[1] = "-c"; |
| - new_argv[2] = line; |
| - new_argv[3] = NULL; |
| - |
| - /* Restore the signals. */ |
| - (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); |
| - (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); |
| - (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); |
| - INIT_LOCK (); |
| - |
| - /* Exec the shell. */ |
| - (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ); |
| - _exit (127); |
| - } |
| - else if (pid < (pid_t) 0) |
| - /* The fork failed. */ |
| - status = -1; |
| - else |
| - /* Parent side. */ |
| - { |
| /* Note the system() is a cancellation point. But since we call |
| waitpid() which itself is a cancellation point we do not |
| have to do anything here. */ |
| if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid) |
| status = -1; |
| - } |
| - |
| -#ifdef CLEANUP_HANDLER |
| - CLEANUP_RESET; |
| +#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL) |
| + __libc_cleanup_region_end (0); |
| #endif |
| + } |
| |
| - save = errno; |
| DO_LOCK (); |
| - if ((SUB_REF () == 0 |
| - && (__sigaction (SIGINT, &intr, (struct sigaction *) NULL) |
| - | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0) |
| - || __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0) |
| + if (SUB_REF () == 0) |
| { |
| -#ifndef _LIBC |
| - /* glibc cannot be used on systems without waitpid. */ |
| - if (errno == ENOSYS) |
| - __set_errno (save); |
| - else |
| -#endif |
| - status = -1; |
| + /* sigaction can not fail with SIGINT/SIGQUIT used with old |
| + disposition. Same applies for sigprocmask. */ |
| + __sigaction (SIGINT, &intr, NULL); |
| + __sigaction (SIGQUIT, &quit, NULL); |
| + __sigprocmask (SIG_SETMASK, &omask, NULL); |
| } |
| DO_UNLOCK (); |
| |
| diff --git a/sysdeps/unix/sysv/linux/ia64/system.c b/sysdeps/unix/sysv/linux/ia64/system.c |
| deleted file mode 100644 |
| index d09fefefe64753ab..0000000000000000 |
| |
| |
| @@ -1,30 +0,0 @@ |
| -/* Copyright (C) 2002-2018 Free Software Foundation, Inc. |
| - This file is part of the GNU C Library. |
| - |
| - The GNU C Library is free software; you can redistribute it and/or |
| - modify it under the terms of the GNU Lesser General Public |
| - License as published by the Free Software Foundation; either |
| - version 2.1 of the License, or (at your option) any later version. |
| - |
| - The GNU C Library is distributed in the hope that it will be useful, |
| - but WITHOUT ANY WARRANTY; without even the implied warranty of |
| - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| - Lesser General Public License for more details. |
| - |
| - You should have received a copy of the GNU Lesser General Public |
| - License along with the GNU C Library; if not, see |
| - <http://www.gnu.org/licenses/>. */ |
| - |
| -/* We have to and actually can handle cancelable system(). The big |
| - problem: we have to kill the child process if necessary. To do |
| - this a cleanup handler has to be registered and is has to be able |
| - to find the PID of the child. The main problem is to reliable have |
| - the PID when needed. It is not necessary for the parent thread to |
| - return. It might still be in the kernel when the cancellation |
| - request comes. Therefore we have to use the clone() calls ability |
| - to have the kernel write the PID into the user-level variable. */ |
| -#define FORK() \ |
| - INLINE_SYSCALL (clone2, 6, CLONE_PARENT_SETTID | SIGCHLD, NULL, 0, \ |
| - &pid, NULL, NULL) |
| - |
| -#include <sysdeps/unix/sysv/linux/system.c> |
| diff --git a/sysdeps/unix/sysv/linux/not-errno.h b/sysdeps/unix/sysv/linux/not-errno.h |
| index 106ba5c72e3d7dda..b2f72cfb3d412c56 100644 |
| |
| |
| @@ -16,6 +16,9 @@ |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| +#include <sysdep.h> |
| +#include <fcntl.h> |
| + |
| /* This function is used on maybe_enable_malloc_check (elf/dl-tunables.c) |
| and to avoid having to build/use multiple versions if stack protection |
| in enabled it is defined as inline. */ |
| @@ -33,3 +36,14 @@ __access_noerrno (const char *pathname, int mode) |
| return INTERNAL_SYSCALL_ERRNO (res, err); |
| return 0; |
| } |
| + |
| +static inline int |
| +__kill_noerrno (pid_t pid, int sig) |
| +{ |
| + int res; |
| + INTERNAL_SYSCALL_DECL (err); |
| + res = INTERNAL_SYSCALL_CALL (kill, err, pid, sig); |
| + if (INTERNAL_SYSCALL_ERROR_P (res, err)) |
| + return INTERNAL_SYSCALL_ERRNO (res, err); |
| + return 0; |
| +} |
| diff --git a/sysdeps/unix/sysv/linux/s390/system.c b/sysdeps/unix/sysv/linux/s390/system.c |
| deleted file mode 100644 |
| index d8ef46133419dd89..0000000000000000 |
| |
| |
| @@ -1,29 +0,0 @@ |
| -/* Copyright (C) 2003-2018 Free Software Foundation, Inc. |
| - This file is part of the GNU C Library. |
| - |
| - The GNU C Library is free software; you can redistribute it and/or |
| - modify it under the terms of the GNU Lesser General Public |
| - License as published by the Free Software Foundation; either |
| - version 2.1 of the License, or (at your option) any later version. |
| - |
| - The GNU C Library is distributed in the hope that it will be useful, |
| - but WITHOUT ANY WARRANTY; without even the implied warranty of |
| - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| - Lesser General Public License for more details. |
| - |
| - You should have received a copy of the GNU Lesser General Public |
| - License along with the GNU C Library; if not, see |
| - <http://www.gnu.org/licenses/>. */ |
| - |
| -/* We have to and actually can handle cancelable system(). The big |
| - problem: we have to kill the child process if necessary. To do |
| - this a cleanup handler has to be registered and is has to be able |
| - to find the PID of the child. The main problem is to reliable have |
| - the PID when needed. It is not necessary for the parent thread to |
| - return. It might still be in the kernel when the cancellation |
| - request comes. Therefore we have to use the clone() calls ability |
| - to have the kernel write the PID into the user-level variable. */ |
| -#define FORK() \ |
| - INLINE_SYSCALL (clone, 3, 0, CLONE_PARENT_SETTID | SIGCHLD, &pid) |
| - |
| -#include "../system.c" |
| diff --git a/sysdeps/unix/sysv/linux/sparc/system.c b/sysdeps/unix/sysv/linux/sparc/system.c |
| deleted file mode 100644 |
| index 1f65c83399f920d6..0000000000000000 |
| |
| |
| @@ -1,29 +0,0 @@ |
| -/* Copyright (C) 2003-2018 Free Software Foundation, Inc. |
| - This file is part of the GNU C Library. |
| - |
| - The GNU C Library is free software; you can redistribute it and/or |
| - modify it under the terms of the GNU Lesser General Public |
| - License as published by the Free Software Foundation; either |
| - version 2.1 of the License, or (at your option) any later version. |
| - |
| - The GNU C Library is distributed in the hope that it will be useful, |
| - but WITHOUT ANY WARRANTY; without even the implied warranty of |
| - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| - Lesser General Public License for more details. |
| - |
| - You should have received a copy of the GNU Lesser General Public |
| - License along with the GNU C Library; if not, see |
| - <http://www.gnu.org/licenses/>. */ |
| - |
| -/* We have to and actually can handle cancelable system(). The big |
| - problem: we have to kill the child process if necessary. To do |
| - this a cleanup handler has to be registered and is has to be able |
| - to find the PID of the child. The main problem is to reliable have |
| - the PID when needed. It is not necessary for the parent thread to |
| - return. It might still be in the kernel when the cancellation |
| - request comes. Therefore we have to use the clone() calls ability |
| - to have the kernel write the PID into the user-level variable. */ |
| -#define FORK() \ |
| - INLINE_CLONE_SYSCALL (CLONE_PARENT_SETTID | SIGCHLD, 0, &pid, NULL, NULL) |
| - |
| -#include "../system.c" |
| diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c |
| index 85239cedbf2a5ab5..6a8bd2ed2e1c29b7 100644 |
| |
| |
| @@ -138,11 +138,11 @@ __spawni_child (void *arguments) |
| for (int sig = 1; sig < _NSIG; ++sig) |
| { |
| if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) |
| - && sigismember (&attr->__sd, sig)) |
| + && __sigismember (&attr->__sd, sig)) |
| { |
| sa.sa_handler = SIG_DFL; |
| } |
| - else if (sigismember (&hset, sig)) |
| + else if (__sigismember (&hset, sig)) |
| { |
| if (__is_internal_signal (sig)) |
| sa.sa_handler = SIG_IGN; |
| diff --git a/sysdeps/unix/sysv/linux/system.c b/sysdeps/unix/sysv/linux/system.c |
| deleted file mode 100644 |
| index 7cc68a1528ee8f99..0000000000000000 |
| |
| |
| @@ -1,76 +0,0 @@ |
| -/* Copyright (C) 2002-2018 Free Software Foundation, Inc. |
| - This file is part of the GNU C Library. |
| - |
| - The GNU C Library is free software; you can redistribute it and/or |
| - modify it under the terms of the GNU Lesser General Public |
| - License as published by the Free Software Foundation; either |
| - version 2.1 of the License, or (at your option) any later version. |
| - |
| - The GNU C Library is distributed in the hope that it will be useful, |
| - but WITHOUT ANY WARRANTY; without even the implied warranty of |
| - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| - Lesser General Public License for more details. |
| - |
| - You should have received a copy of the GNU Lesser General Public |
| - License along with the GNU C Library; if not, see |
| - <http://www.gnu.org/licenses/>. */ |
| - |
| -#include <sched.h> |
| -#include <signal.h> |
| -#include <string.h> /* For the real memset prototype. */ |
| -#include <sysdep.h> |
| -#include <unistd.h> |
| -#include <sys/wait.h> |
| -#include <libc-lock.h> |
| - |
| -/* We have to and actually can handle cancelable system(). The big |
| - problem: we have to kill the child process if necessary. To do |
| - this a cleanup handler has to be registered and is has to be able |
| - to find the PID of the child. The main problem is to reliable have |
| - the PID when needed. It is not necessary for the parent thread to |
| - return. It might still be in the kernel when the cancellation |
| - request comes. Therefore we have to use the clone() calls ability |
| - to have the kernel write the PID into the user-level variable. */ |
| -#ifndef FORK |
| -# define FORK() \ |
| - INLINE_SYSCALL (clone, 3, CLONE_PARENT_SETTID | SIGCHLD, 0, &pid) |
| -#endif |
| - |
| -#ifdef _LIBC_REENTRANT |
| -static void cancel_handler (void *arg); |
| - |
| -# define CLEANUP_HANDLER \ |
| - __libc_cleanup_region_start (1, cancel_handler, &pid) |
| - |
| -# define CLEANUP_RESET \ |
| - __libc_cleanup_region_end (0) |
| -#endif |
| - |
| - |
| -/* Linux has waitpid(), so override the generic unix version. */ |
| -#include <sysdeps/posix/system.c> |
| - |
| - |
| -#ifdef _LIBC_REENTRANT |
| -/* The cancellation handler. */ |
| -static void |
| -cancel_handler (void *arg) |
| -{ |
| - pid_t child = *(pid_t *) arg; |
| - |
| - INTERNAL_SYSCALL_DECL (err); |
| - INTERNAL_SYSCALL (kill, err, 2, child, SIGKILL); |
| - |
| - TEMP_FAILURE_RETRY (__waitpid (child, NULL, 0)); |
| - |
| - DO_LOCK (); |
| - |
| - if (SUB_REF () == 0) |
| - { |
| - (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); |
| - (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); |
| - } |
| - |
| - DO_UNLOCK (); |
| -} |
| -#endif |