1 /* Copyright (C) 1991-2022 Free Software Foundation, Inc.
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
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.
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
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <errno.h>
19 #include <signal.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <sigsetops.h>
23 #include <spawn.h>
24 #include <pthread.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <stdio.h>
28 
29 #include <libc-lock.h>
30 #include <not-errno.h>
31 #include <not-cancel.h>
32 #include <internal-signals.h>
33 
34 #define	SHELL_PATH	"/bin/sh"	/* Path of the shell.  */
35 #define	SHELL_NAME	"sh"		/* Name to give it.  */
36 
37 
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 
50 #ifdef _LIBC_REENTRANT
51 static struct sigaction intr, quit;
52 static 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()
64 # define ADD_REF() 0
65 # define SUB_REF() 0
66 #endif
67 
68 
69 #if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
70 struct cancel_handler_args
71 {
72   struct sigaction *quit;
73   struct sigaction *intr;
74   pid_t pid;
75 };
76 
77 static void
cancel_handler(void * arg)78 cancel_handler (void *arg)
79 {
80   struct cancel_handler_args *args = (struct cancel_handler_args *) (arg);
81 
82   __kill_noerrno (args->pid, SIGKILL);
83 
84   int state;
85   __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
86   TEMP_FAILURE_RETRY (__waitpid (args->pid, NULL, 0));
87   __pthread_setcancelstate (state, NULL);
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 
99 /* Execute LINE as a shell command, returning its status.  */
100 static int
do_system(const char * line)101 do_system (const char *line)
102 {
103   int status = -1;
104   int ret;
105   pid_t pid;
106   struct sigaction sa;
107 #ifndef _LIBC_REENTRANT
108   struct sigaction intr, quit;
109 #endif
110   sigset_t omask;
111   sigset_t reset;
112 
113   sa.sa_handler = SIG_IGN;
114   sa.sa_flags = 0;
115   __sigemptyset (&sa.sa_mask);
116 
117   DO_LOCK ();
118   if (ADD_REF () == 0)
119     {
120       /* sigaction can not fail with SIGINT/SIGQUIT used with SIG_IGN.  */
121       __sigaction (SIGINT, &sa, &intr);
122       __sigaction (SIGQUIT, &sa, &quit);
123     }
124   DO_UNLOCK ();
125 
126   __sigaddset (&sa.sa_mask, SIGCHLD);
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 
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);
152   __posix_spawnattr_destroy (&spawn_attr);
153 
154   if (ret == 0)
155     {
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);
167 #endif
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.  */
171       if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
172 	status = -1;
173 #if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
174       __libc_cleanup_region_end (0);
175 #endif
176     }
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);
181 
182   DO_LOCK ();
183   if (SUB_REF () == 0)
184     {
185       /* sigaction can not fail with SIGINT/SIGQUIT used with old
186 	 disposition.  Same applies for sigprocmask.  */
187       __sigaction (SIGINT, &intr, NULL);
188       __sigaction (SIGQUIT, &quit, NULL);
189       __sigprocmask (SIG_SETMASK, &omask, NULL);
190     }
191   DO_UNLOCK ();
192 
193   if (ret != 0)
194     __set_errno (ret);
195 
196   return status;
197 }
198 
199 int
__libc_system(const char * line)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 
207   return do_system (line);
208 }
209 weak_alias (__libc_system, system)
210