]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/libc/stdlib/system.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / 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
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
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
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