OSDN Git Service

libc: silence missing prototype warning
[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 #if !defined __UCLIBC_HAS_THREADS_NATIVE__
23 /* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
24 #include <sys/syscall.h>
25 #ifndef __NR_vfork
26 # define vfork fork
27 #endif
28
29 int __libc_system(const char *command)
30 {
31         int wait_val, pid;
32         struct sigaction sa, save_quit, save_int;
33         sigset_t save_mask;
34
35         if (command == 0)
36                 return 1;
37
38         memset(&sa, 0, sizeof(sa));
39         sa.sa_handler = SIG_IGN;
40         /* __sigemptyset(&sa.sa_mask); - done by memset() */
41         /* sa.sa_flags = 0; - done by memset() */
42
43         sigaction(SIGQUIT, &sa, &save_quit);
44         sigaction(SIGINT, &sa, &save_int);
45         __sigaddset(&sa.sa_mask, SIGCHLD);
46         sigprocmask(SIG_BLOCK, &sa.sa_mask, &save_mask);
47
48         if ((pid = vfork()) < 0) {
49                 wait_val = -1;
50                 goto out;
51         }
52         if (pid == 0) {
53                 sigaction(SIGQUIT, &save_quit, NULL);
54                 sigaction(SIGINT, &save_int, NULL);
55                 sigprocmask(SIG_SETMASK, &save_mask, NULL);
56
57                 execl("/bin/sh", "sh", "-c", command, (char *) 0);
58                 _exit(127);
59         }
60
61 #if 0
62         __printf("Waiting for child %d\n", pid);
63 #endif
64
65         if (__wait4_nocancel(pid, &wait_val, 0, 0) == -1)
66                 wait_val = -1;
67
68 out:
69         sigaction(SIGQUIT, &save_quit, NULL);
70         sigaction(SIGINT, &save_int, NULL);
71         sigprocmask(SIG_SETMASK, &save_mask, NULL);
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   memset(&sa, 0, sizeof(sa));
130   sa.sa_handler = SIG_IGN;
131   /*sa.sa_flags = 0; - done by memset */
132   /*__sigemptyset (&sa.sa_mask); - done by memset */
133
134   DO_LOCK ();
135   if (ADD_REF () == 0)
136     {
137       if (sigaction (SIGINT, &sa, &intr) < 0)
138         {
139           SUB_REF ();
140           goto out;
141         }
142       if (sigaction (SIGQUIT, &sa, &quit) < 0)
143         {
144           save = errno;
145           SUB_REF ();
146           goto out_restore_sigint;
147         }
148     }
149   DO_UNLOCK ();
150
151   /* We reuse the bitmap in the 'sa' structure.  */
152   __sigaddset (&sa.sa_mask, SIGCHLD);
153   save = errno;
154   if (sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0)
155     {
156         {
157           DO_LOCK ();
158           if (SUB_REF () == 0)
159             {
160               save = errno;
161               (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
162             out_restore_sigint:
163               (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
164               __set_errno (save);
165             }
166         out:
167           DO_UNLOCK ();
168           return -1;
169         }
170     }
171
172   CLEANUP_HANDLER;
173
174   pid = FORK ();
175   if (pid == (pid_t) 0)
176     {
177       /* Child side.  */
178       const char *new_argv[4];
179       new_argv[0] = "/bin/sh";
180       new_argv[1] = "-c";
181       new_argv[2] = line;
182       new_argv[3] = NULL;
183
184       /* Restore the signals.  */
185       (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
186       (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
187       (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
188       INIT_LOCK ();
189
190       /* Exec the shell.  */
191       (void) execve ("/bin/sh", (char *const *) new_argv, __environ);
192       _exit (127);
193     }
194   else if (pid < (pid_t) 0)
195     /* The fork failed.  */
196     status = -1;
197   else
198     /* Parent side.  */
199     {
200       /* Note the system() is a cancellation point.  But since we call
201          waitpid() which itself is a cancellation point we do not
202          have to do anything here.  */
203       if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
204         status = -1;
205     }
206
207   CLEANUP_RESET;
208
209   save = errno;
210   DO_LOCK ();
211   if ((SUB_REF () == 0
212        && (sigaction (SIGINT, &intr, (struct sigaction *) NULL)
213            | sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)
214       || sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)
215     {
216         status = -1;
217     }
218   DO_UNLOCK ();
219
220   return status;
221 }
222
223
224 int
225 __libc_system (const char *line)
226 {
227   if (line == NULL)
228     /* Check that we have a command processor available.  It might
229        not be available after a chroot(), for example.  */
230     return do_system ("exit 0") == 0;
231
232   if (SINGLE_THREAD_P)
233     return do_system (line);
234
235   int oldtype = LIBC_CANCEL_ASYNC ();
236
237   int result = do_system (line);
238
239   LIBC_CANCEL_RESET (oldtype);
240
241   return result;
242 }
243
244
245 /* The cancellation handler.  */
246 static void
247 cancel_handler (void *arg)
248 {
249   pid_t child = *(pid_t *) arg;
250
251   INTERNAL_SYSCALL_DECL (err);
252   INTERNAL_SYSCALL (kill, err, 2, child, SIGKILL);
253
254   TEMP_FAILURE_RETRY (waitpid (child, NULL, 0));
255
256   DO_LOCK ();
257
258   if (SUB_REF () == 0)
259     {
260       (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
261       (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
262     }
263
264   DO_UNLOCK ();
265 }
266 #endif
267 #ifdef IS_IN_libc
268 weak_alias(__libc_system,system)
269 #endif