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