OSDN Git Service

Merge commit 'origin/master' into nptl
[uclinux-h8/uClibc.git] / libc / stdlib / system.c
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 <stddef.h>
9 #include <signal.h>
10 #include <unistd.h>
11 #include <sys/wait.h>
12 #include <stdlib.h>
13 #ifdef __UCLIBC_HAS_THREADS_NATIVE__
14 #include <sched.h>
15 #include <errno.h>
16 #include <bits/libc-lock.h>
17 #include <sysdep-cancel.h>
18 #endif
19
20
21 /* TODO: the cancellable version breaks on sparc currently,
22  * need to figure out why still
23  */
24 #if !defined __UCLIBC_HAS_THREADS_NATIVE__ || defined __sparc__
25 /* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
26 #include <sys/syscall.h>
27 #ifndef __NR_vfork
28 # define vfork fork
29 #endif
30
31 extern __typeof(system) __libc_system;
32 int __libc_system(const char *command)
33 {
34         int wait_val, pid;
35         __sighandler_t save_quit, save_int, save_chld;
36
37         if (command == 0)
38                 return 1;
39
40         save_quit = signal(SIGQUIT, SIG_IGN);
41         save_int = signal(SIGINT, SIG_IGN);
42         save_chld = signal(SIGCHLD, SIG_DFL);
43
44         if ((pid = vfork()) < 0) {
45                 signal(SIGQUIT, save_quit);
46                 signal(SIGINT, save_int);
47                 signal(SIGCHLD, save_chld);
48                 return -1;
49         }
50         if (pid == 0) {
51                 signal(SIGQUIT, SIG_DFL);
52                 signal(SIGINT, SIG_DFL);
53                 signal(SIGCHLD, SIG_DFL);
54
55                 execl("/bin/sh", "sh", "-c", command, (char *) 0);
56                 _exit(127);
57         }
58         /* Signals are not absolutly guarenteed with vfork */
59         signal(SIGQUIT, SIG_IGN);
60         signal(SIGINT, SIG_IGN);
61
62 #if 0
63         __printf("Waiting for child %d\n", pid);
64 #endif
65
66         if (wait4(pid, &wait_val, 0, 0) == -1)
67                 wait_val = -1;
68
69         signal(SIGQUIT, save_quit);
70         signal(SIGINT, save_int);
71         signal(SIGCHLD, save_chld);
72         return wait_val;
73 }
74 #else
75 /* We have to and actually can handle cancelable system().  The big
76    problem: we have to kill the child process if necessary.  To do
77    this a cleanup handler has to be registered and is has to be able
78    to find the PID of the child.  The main problem is to reliable have
79    the PID when needed.  It is not necessary for the parent thread to
80    return.  It might still be in the kernel when the cancellation
81    request comes.  Therefore we have to use the clone() calls ability
82    to have the kernel write the PID into the user-level variable.  */
83
84 libc_hidden_proto(sigaction)
85 libc_hidden_proto(waitpid)
86
87 #if defined __ia64__
88 # define FORK() \
89   INLINE_SYSCALL (clone2, 6, CLONE_PARENT_SETTID | SIGCHLD, NULL, 0, \
90                   &pid, NULL, NULL)
91 #elif defined __sparc__
92 # define FORK() \
93   INLINE_CLONE_SYSCALL (CLONE_PARENT_SETTID | SIGCHLD, 0, &pid, NULL, NULL)
94 #elif defined __s390__
95 # define FORK() \
96   INLINE_SYSCALL (clone, 3, 0, CLONE_PARENT_SETTID | SIGCHLD, &pid)
97 #else
98 # define FORK() \
99   INLINE_SYSCALL (clone, 3, CLONE_PARENT_SETTID | SIGCHLD, 0, &pid)
100 #endif
101
102 static void cancel_handler (void *arg);
103
104 # define CLEANUP_HANDLER \
105   __libc_cleanup_region_start (1, cancel_handler, &pid)
106
107 # define CLEANUP_RESET \
108   __libc_cleanup_region_end (0)
109
110 static struct sigaction intr, quit;
111 static int sa_refcntr;
112 __libc_lock_define_initialized (static, lock);
113
114 # define DO_LOCK() __libc_lock_lock (lock)
115 # define DO_UNLOCK() __libc_lock_unlock (lock)
116 # define INIT_LOCK() ({ __libc_lock_init (lock); sa_refcntr = 0; })
117 # define ADD_REF() sa_refcntr++
118 # define SUB_REF() --sa_refcntr
119
120 /* Execute LINE as a shell command, returning its status.  */
121 static int
122 do_system (const char *line)
123 {
124   int status, save;
125   pid_t pid;
126   struct sigaction sa;
127   sigset_t omask;
128
129   sa.sa_handler = SIG_IGN;
130   sa.sa_flags = 0;
131   __sigemptyset (&sa.sa_mask);
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
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
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