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