]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/posix/system.c
posix: Fix system blocks SIGCHLD erroneously [BZ #30163]
[thirdparty/glibc.git] / sysdeps / posix / system.c
CommitLineData
6d7e8eda 1/* Copyright (C) 1991-2023 Free Software Foundation, Inc.
993b3242
UD
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
993b3242
UD
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 12 Lesser General Public License for more details.
993b3242 13
41bdb6e2 14 You should have received a copy of the GNU Lesser General Public
59ba27a6 15 License along with the GNU C Library; if not, see
5a82c748 16 <https://www.gnu.org/licenses/>. */
28f540f4 17
918bd861
UD
18#include <errno.h>
19#include <signal.h>
28f540f4
RM
20#include <stdlib.h>
21#include <unistd.h>
5fb7fc96
AZ
22#include <sigsetops.h>
23#include <spawn.h>
24#include <pthread.h>
28f540f4 25#include <sys/types.h>
918bd861 26#include <sys/wait.h>
5fb7fc96 27#include <stdio.h>
28f540f4 28
5fb7fc96
AZ
29#include <libc-lock.h>
30#include <not-errno.h>
31#include <not-cancel.h>
32#include <internal-signals.h>
28f540f4 33
28f540f4
RM
34#define SHELL_PATH "/bin/sh" /* Path of the shell. */
35#define SHELL_NAME "sh" /* Name to give it. */
36
918bd861 37
5fb7fc96
AZ
38/* This system implementation aims to be thread-safe, which requires to
39 restore the signal dispositions for SIGINT and SIGQUIT correctly and to
40 deal with cancellation by terminating the child process.
41
42 The signal disposition restoration on the single-thread case is
43 straighfoward. For multithreaded case, a reference-counter with a lock
44 is used, so the first thread will set the SIGINT/SIGQUIT dispositions and
45 last thread will restore them.
46
47 Cancellation handling is done with thread cancellation clean-up handlers
48 on waitpid call. */
49
918bd861
UD
50#ifdef _LIBC_REENTRANT
51static struct sigaction intr, quit;
52static int sa_refcntr;
53__libc_lock_define_initialized (static, lock);
54
55# define DO_LOCK() __libc_lock_lock (lock)
56# define DO_UNLOCK() __libc_lock_unlock (lock)
57# define INIT_LOCK() ({ __libc_lock_init (lock); sa_refcntr = 0; })
58# define ADD_REF() sa_refcntr++
59# define SUB_REF() --sa_refcntr
60#else
61# define DO_LOCK()
62# define DO_UNLOCK()
63# define INIT_LOCK()
43cd9933 64# define ADD_REF() 0
918bd861
UD
65# define SUB_REF() 0
66#endif
67
68
5fb7fc96
AZ
69#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
70struct cancel_handler_args
71{
72 struct sigaction *quit;
73 struct sigaction *intr;
74 pid_t pid;
75};
76
77static void
78cancel_handler (void *arg)
79{
80 struct cancel_handler_args *args = (struct cancel_handler_args *) (arg);
81
82 __kill_noerrno (args->pid, SIGKILL);
83
478f7078 84 int state;
93d78ec1 85 __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
478f7078 86 TEMP_FAILURE_RETRY (__waitpid (args->pid, NULL, 0));
93d78ec1 87 __pthread_setcancelstate (state, NULL);
5fb7fc96
AZ
88
89 DO_LOCK ();
90 if (SUB_REF () == 0)
91 {
92 __sigaction (SIGQUIT, args->quit, NULL);
93 __sigaction (SIGINT, args->intr, NULL);
94 }
95 DO_UNLOCK ();
96}
97#endif
98
28f540f4 99/* Execute LINE as a shell command, returning its status. */
a585ba22
RM
100static int
101do_system (const char *line)
28f540f4 102{
f09542c5
AZ
103 int status = -1;
104 int ret;
28f540f4 105 pid_t pid;
918bd861
UD
106 struct sigaction sa;
107#ifndef _LIBC_REENTRANT
108 struct sigaction intr, quit;
109#endif
71302f5f 110 sigset_t omask;
5fb7fc96 111 sigset_t reset;
28f540f4 112
28f540f4
RM
113 sa.sa_handler = SIG_IGN;
114 sa.sa_flags = 0;
115 __sigemptyset (&sa.sa_mask);
116
918bd861
UD
117 DO_LOCK ();
118 if (ADD_REF () == 0)
28f540f4 119 {
5fb7fc96
AZ
120 /* sigaction can not fail with SIGINT/SIGQUIT used with SIG_IGN. */
121 __sigaction (SIGINT, &sa, &intr);
122 __sigaction (SIGQUIT, &sa, &quit);
28f540f4 123 }
918bd861 124 DO_UNLOCK ();
28f540f4 125
71302f5f 126 __sigaddset (&sa.sa_mask, SIGCHLD);
5fb7fc96
AZ
127 /* sigprocmask can not fail with SIG_BLOCK used with valid input
128 arguments. */
129 __sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask);
130
131 __sigemptyset (&reset);
132 if (intr.sa_handler != SIG_IGN)
133 __sigaddset(&reset, SIGINT);
134 if (quit.sa_handler != SIG_IGN)
135 __sigaddset(&reset, SIGQUIT);
136
137 posix_spawnattr_t spawn_attr;
138 /* None of the posix_spawnattr_* function returns an error, including
139 posix_spawnattr_setflags for the follow specific usage (using valid
140 flags). */
141 __posix_spawnattr_init (&spawn_attr);
142 __posix_spawnattr_setsigmask (&spawn_attr, &omask);
143 __posix_spawnattr_setsigdefault (&spawn_attr, &reset);
144 __posix_spawnattr_setflags (&spawn_attr,
145 POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);
146
f09542c5
AZ
147 ret = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr,
148 (char *const[]){ (char *) SHELL_NAME,
149 (char *) "-c",
150 (char *) line, NULL },
151 __environ);
5fb7fc96
AZ
152 __posix_spawnattr_destroy (&spawn_attr);
153
f09542c5 154 if (ret == 0)
28f540f4 155 {
5fb7fc96
AZ
156 /* Cancellation results in cleanup handlers running as exceptions in
157 the block where they were installed, so it is safe to reference
158 stack variable allocate in the broader scope. */
159#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
160 struct cancel_handler_args cancel_args =
161 {
162 .quit = &quit,
163 .intr = &intr,
164 .pid = pid
165 };
166 __libc_cleanup_region_start (1, cancel_handler, &cancel_args);
3ca97e8f 167#endif
7a114794
UD
168 /* Note the system() is a cancellation point. But since we call
169 waitpid() which itself is a cancellation point we do not
170 have to do anything here. */
20f3b127 171 if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
ce40141c 172 status = -1;
5fb7fc96
AZ
173#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
174 __libc_cleanup_region_end (0);
3ca97e8f 175#endif
5fb7fc96 176 }
42dda89d
AZ
177 else
178 /* POSIX states that failure to execute the shell should return
179 as if the shell had terminated using _exit(127). */
180 status = W_EXITCODE (127, 0);
3ca97e8f 181
436a604b
AY
182 /* sigaction can not fail with SIGINT/SIGQUIT used with old
183 disposition. Same applies for sigprocmask. */
918bd861 184 DO_LOCK ();
5fb7fc96 185 if (SUB_REF () == 0)
28f540f4 186 {
5fb7fc96
AZ
187 __sigaction (SIGINT, &intr, NULL);
188 __sigaction (SIGQUIT, &quit, NULL);
28f540f4 189 }
918bd861 190 DO_UNLOCK ();
436a604b 191 __sigprocmask (SIG_SETMASK, &omask, NULL);
28f540f4 192
f09542c5
AZ
193 if (ret != 0)
194 __set_errno (ret);
195
28f540f4
RM
196 return status;
197}
a585ba22
RM
198
199int
200__libc_system (const char *line)
201{
202 if (line == NULL)
203 /* Check that we have a command processor available. It might
204 not be available after a chroot(), for example. */
205 return do_system ("exit 0") == 0;
206
c4dd57c3 207 return do_system (line);
a585ba22 208}
c4029823 209weak_alias (__libc_system, system)