1 /*
2  * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
3  *
4  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
5  */
6 
7 #include <stdio.h>
8 #include <string.h>
9 #include <stddef.h>
10 #include <signal.h>
11 #include <unistd.h>
12 #include <sys/wait.h>
13 #include <stdlib.h>
14 #ifdef __UCLIBC_HAS_THREADS_NATIVE__
15 #include <sched.h>
16 #include <errno.h>
17 #include <bits/libc-lock.h>
18 #include <sysdep-cancel.h>
19 #endif
20 
21 extern __typeof(system) __libc_system;
22 
23 /* TODO: the cancellable version breaks on sparc currently,
24  * need to figure out why still
25  */
26 #if !defined __UCLIBC_HAS_THREADS_NATIVE__ || defined __sparc__
27 
__libc_system(const char * command)28 int __libc_system(const char *command)
29 {
30 	int wait_val, pid;
31 	struct sigaction sa, save_quit, save_int;
32 	sigset_t save_mask;
33 
34 	if (command == 0)
35 		return 1;
36 
37 	memset(&sa, 0, sizeof(sa));
38 	sa.sa_handler = SIG_IGN;
39 	/* __sigemptyset(&sa.sa_mask); - done by memset() */
40 	/* sa.sa_flags = 0; - done by memset() */
41 
42 	sigaction(SIGQUIT, &sa, &save_quit);
43 	sigaction(SIGINT, &sa, &save_int);
44 	__sigaddset(&sa.sa_mask, SIGCHLD);
45 	sigprocmask(SIG_BLOCK, &sa.sa_mask, &save_mask);
46 
47 	if ((pid = vfork()) < 0) {
48 		wait_val = -1;
49 		goto out;
50 	}
51 	if (pid == 0) {
52 		sigaction(SIGQUIT, &save_quit, NULL);
53 		sigaction(SIGINT, &save_int, NULL);
54 		sigprocmask(SIG_SETMASK, &save_mask, NULL);
55 
56 		execl("/bin/sh", "sh", "-c", command, (char *) 0);
57 		_exit(127);
58 	}
59 
60 #if 0
61 	__printf("Waiting for child %d\n", pid);
62 #endif
63 
64 	if (__wait4_nocancel(pid, &wait_val, 0, 0) == -1)
65 		wait_val = -1;
66 
67 out:
68 	sigaction(SIGQUIT, &save_quit, NULL);
69 	sigaction(SIGINT, &save_int, NULL);
70 	sigprocmask(SIG_SETMASK, &save_mask, NULL);
71 	return wait_val;
72 }
73 #else
74 /* We have to and actually can handle cancelable system().  The big
75    problem: we have to kill the child process if necessary.  To do
76    this a cleanup handler has to be registered and is has to be able
77    to find the PID of the child.  The main problem is to reliable have
78    the PID when needed.  It is not necessary for the parent thread to
79    return.  It might still be in the kernel when the cancellation
80    request comes.  Therefore we have to use the clone() calls ability
81    to have the kernel write the PID into the user-level variable.  */
82 
83 libc_hidden_proto(sigaction)
84 libc_hidden_proto(waitpid)
85 
86 #if defined __ia64__
87 # define FORK() \
88   INLINE_SYSCALL (clone2, 6, CLONE_PARENT_SETTID | SIGCHLD, NULL, 0, \
89 		  &pid, NULL, NULL)
90 #elif defined __sparc__
91 # define FORK() \
92   INLINE_CLONE_SYSCALL (CLONE_PARENT_SETTID | SIGCHLD, 0, &pid, NULL, NULL)
93 #elif defined __s390__
94 # define FORK() \
95   INLINE_SYSCALL (clone, 3, 0, CLONE_PARENT_SETTID | SIGCHLD, &pid)
96 #else
97 # define FORK() \
98   INLINE_SYSCALL (clone, 3, CLONE_PARENT_SETTID | SIGCHLD, 0, &pid)
99 #endif
100 
101 static void cancel_handler (void *arg);
102 
103 # define CLEANUP_HANDLER \
104   __libc_cleanup_region_start (1, cancel_handler, &pid)
105 
106 # define CLEANUP_RESET \
107   __libc_cleanup_region_end (0)
108 
109 static struct sigaction intr, quit;
110 static int sa_refcntr;
111 __libc_lock_define_initialized (static, lock);
112 
113 # define DO_LOCK() __libc_lock_lock (lock)
114 # define DO_UNLOCK() __libc_lock_unlock (lock)
115 # define INIT_LOCK() ({ __libc_lock_init (lock); sa_refcntr = 0; })
116 # define ADD_REF() sa_refcntr++
117 # define SUB_REF() --sa_refcntr
118 
119 /* Execute LINE as a shell command, returning its status.  */
120 static int
do_system(const char * line)121 do_system (const char *line)
122 {
123   int status, save;
124   pid_t pid;
125   struct sigaction sa;
126   sigset_t omask;
127 
128   memset(&sa, 0, sizeof(sa));
129   sa.sa_handler = SIG_IGN;
130   /*sa.sa_flags = 0; - done by memset */
131   /*__sigemptyset (&sa.sa_mask); - done by memset */
132 
133   DO_LOCK ();
134   if (ADD_REF () == 0)
135     {
136       if (sigaction (SIGINT, &sa, &intr) < 0)
137 	{
138 	  SUB_REF ();
139 	  goto out;
140 	}
141       if (sigaction (SIGQUIT, &sa, &quit) < 0)
142 	{
143 	  save = errno;
144 	  SUB_REF ();
145 	  goto out_restore_sigint;
146 	}
147     }
148   DO_UNLOCK ();
149 
150   /* We reuse the bitmap in the 'sa' structure.  */
151   __sigaddset (&sa.sa_mask, SIGCHLD);
152   save = errno;
153   if (sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0)
154     {
155 	{
156 	  DO_LOCK ();
157 	  if (SUB_REF () == 0)
158 	    {
159 	      save = errno;
160 	      (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
161 	    out_restore_sigint:
162 	      (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
163 	      __set_errno (save);
164 	    }
165 	out:
166 	  DO_UNLOCK ();
167 	  return -1;
168 	}
169     }
170 
171   CLEANUP_HANDLER;
172 
173   pid = FORK ();
174   if (pid == (pid_t) 0)
175     {
176       /* Child side.  */
177       const char *new_argv[4];
178       new_argv[0] = "/bin/sh";
179       new_argv[1] = "-c";
180       new_argv[2] = line;
181       new_argv[3] = NULL;
182 
183       /* Restore the signals.  */
184       (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
185       (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
186       (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
187       INIT_LOCK ();
188 
189       /* Exec the shell.  */
190       (void) execve ("/bin/sh", (char *const *) new_argv, __environ);
191       _exit (127);
192     }
193   else if (pid < (pid_t) 0)
194     /* The fork failed.  */
195     status = -1;
196   else
197     /* Parent side.  */
198     {
199       /* Note the system() is a cancellation point.  But since we call
200 	 waitpid() which itself is a cancellation point we do not
201 	 have to do anything here.  */
202       if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
203 	status = -1;
204     }
205 
206   CLEANUP_RESET;
207 
208   save = errno;
209   DO_LOCK ();
210   if ((SUB_REF () == 0
211        && (sigaction (SIGINT, &intr, (struct sigaction *) NULL)
212 	   | sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)
213       || sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)
214     {
215 	status = -1;
216     }
217   DO_UNLOCK ();
218 
219   return status;
220 }
221 
222 
223 int
__libc_system(const char * line)224 __libc_system (const char *line)
225 {
226   if (line == NULL)
227     /* Check that we have a command processor available.  It might
228        not be available after a chroot(), for example.  */
229     return do_system ("exit 0") == 0;
230 
231   if (SINGLE_THREAD_P)
232     return do_system (line);
233 
234   int oldtype = LIBC_CANCEL_ASYNC ();
235 
236   int result = do_system (line);
237 
238   LIBC_CANCEL_RESET (oldtype);
239 
240   return result;
241 }
242 
243 
244 /* The cancellation handler.  */
245 static void
cancel_handler(void * arg)246 cancel_handler (void *arg)
247 {
248   pid_t child = *(pid_t *) arg;
249 
250   INTERNAL_SYSCALL_DECL (err);
251   INTERNAL_SYSCALL (kill, err, 2, child, SIGKILL);
252 
253   TEMP_FAILURE_RETRY (waitpid (child, NULL, 0));
254 
255   DO_LOCK ();
256 
257   if (SUB_REF () == 0)
258     {
259       (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
260       (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
261     }
262 
263   DO_UNLOCK ();
264 }
265 #endif
266 #ifdef IS_IN_libc
267 weak_alias(__libc_system,system)
268 #endif
269