OSDN Git Service

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