1 /* Creation of autonomous subprocesses.
2 Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18
19
20 #include <config.h>
21
22 /* Specification. */
23 #include "execute.h"
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <signal.h>
30 #include <unistd.h>
31
32 #include "error.h"
33 #include "exit.h"
34 #include "fatal-signal.h"
35 #include "wait-process.h"
36 #include "gettext.h"
37
38 #define _(str) gettext (str)
39
40 #if defined _MSC_VER || defined __MINGW32__
41
42 /* Native Woe32 API. */
43 # include <process.h>
44 # include "w32spawn.h"
45
46 #else
47
48 /* Unix API. */
49 # ifdef HAVE_POSIX_SPAWN
50 # include <spawn.h>
51 # else
52 # ifdef HAVE_VFORK_H
53 # include <vfork.h>
54 # endif
55 # endif
56
57 #endif
58
59 #ifndef HAVE_ENVIRON_DECL
60 extern char **environ;
61 #endif
62
63 #ifndef STDIN_FILENO
64 # define STDIN_FILENO 0
65 #endif
66 #ifndef STDOUT_FILENO
67 # define STDOUT_FILENO 1
68 #endif
69 #ifndef STDERR_FILENO
70 # define STDERR_FILENO 2
71 #endif
72
73
74 #ifdef EINTR
75
76 /* EINTR handling for close(), open().
77 These functions can return -1/EINTR even though we don't have any
78 signal handlers set up, namely when we get interrupted via SIGSTOP. */
79
80 static inline int
nonintr_close(int fd)81 nonintr_close (int fd)
82 {
83 int retval;
84
85 do
86 retval = close (fd);
87 while (retval < 0 && errno == EINTR);
88
89 return retval;
90 }
91 #define close nonintr_close
92
93 static inline int
nonintr_open(const char * pathname,int oflag,mode_t mode)94 nonintr_open (const char *pathname, int oflag, mode_t mode)
95 {
96 int retval;
97
98 do
99 retval = open (pathname, oflag, mode);
100 while (retval < 0 && errno == EINTR);
101
102 return retval;
103 }
104 #undef open /* avoid warning on VMS */
105 #define open nonintr_open
106
107 #endif
108
109
110 /* Execute a command, optionally redirecting any of the three standard file
111 descriptors to /dev/null. Return its exit code.
112 If it didn't terminate correctly, exit if exit_on_error is true, otherwise
113 return 127.
114 If slave_process is true, the child process will be terminated when its
115 creator receives a catchable fatal signal. */
116 int
execute(const char * progname,const char * prog_path,char ** prog_argv,bool ignore_sigpipe,bool null_stdin,bool null_stdout,bool null_stderr,bool slave_process,bool exit_on_error)117 execute (const char *progname,
118 const char *prog_path, char **prog_argv,
119 bool ignore_sigpipe,
120 bool null_stdin, bool null_stdout, bool null_stderr,
121 bool slave_process, bool exit_on_error)
122 {
123 #if defined _MSC_VER || defined __MINGW32__
124
125 /* Native Woe32 API. */
126 int orig_stdin;
127 int orig_stdout;
128 int orig_stderr;
129 int exitcode;
130 int nullinfd;
131 int nulloutfd;
132
133 prog_argv = prepare_spawn (prog_argv);
134
135 /* Save standard file handles of parent process. */
136 if (null_stdin)
137 orig_stdin = dup_noinherit (STDIN_FILENO);
138 if (null_stdout)
139 orig_stdout = dup_noinherit (STDOUT_FILENO);
140 if (null_stderr)
141 orig_stderr = dup_noinherit (STDERR_FILENO);
142 exitcode = -1;
143
144 /* Create standard file handles of child process. */
145 nullinfd = -1;
146 nulloutfd = -1;
147 if ((!null_stdin
148 || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0
149 && (nullinfd == STDIN_FILENO
150 || (dup2 (nullinfd, STDIN_FILENO) >= 0
151 && close (nullinfd) >= 0))))
152 && (!(null_stdout || null_stderr)
153 || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
154 && (!null_stdout
155 || nulloutfd == STDOUT_FILENO
156 || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
157 && (!null_stderr
158 || nulloutfd == STDERR_FILENO
159 || dup2 (nulloutfd, STDERR_FILENO) >= 0)
160 && ((null_stdout && nulloutfd == STDOUT_FILENO)
161 || (null_stderr && nulloutfd == STDERR_FILENO)
162 || close (nulloutfd) >= 0))))
163 exitcode = spawnvp (P_WAIT, prog_path, prog_argv);
164 if (nulloutfd >= 0)
165 close (nulloutfd);
166 if (nullinfd >= 0)
167 close (nullinfd);
168
169 /* Restore standard file handles of parent process. */
170 if (null_stderr)
171 dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
172 if (null_stdout)
173 dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
174 if (null_stdin)
175 dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);
176
177 if (exitcode == -1)
178 {
179 if (exit_on_error || !null_stderr)
180 error (exit_on_error ? EXIT_FAILURE : 0, errno,
181 _("%s subprocess failed"), progname);
182 return 127;
183 }
184
185 return exitcode;
186
187 #else
188
189 /* Unix API. */
190 /* Note about 127: Some errors during posix_spawnp() cause the function
191 posix_spawnp() to return an error code; some other errors cause the
192 subprocess to exit with return code 127. It is implementation
193 dependent which error is reported which way. We treat both cases as
194 equivalent. */
195 #if HAVE_POSIX_SPAWN
196 sigset_t blocked_signals;
197 posix_spawn_file_actions_t actions;
198 bool actions_allocated;
199 posix_spawnattr_t attrs;
200 bool attrs_allocated;
201 int err;
202 pid_t child;
203 #else
204 int child;
205 #endif
206
207 #if HAVE_POSIX_SPAWN
208 if (slave_process)
209 {
210 sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
211 block_fatal_signals ();
212 }
213 actions_allocated = false;
214 attrs_allocated = false;
215 if ((err = posix_spawn_file_actions_init (&actions)) != 0
216 || (actions_allocated = true,
217 (null_stdin
218 && (err = posix_spawn_file_actions_addopen (&actions,
219 STDIN_FILENO,
220 "/dev/null", O_RDONLY,
221 0))
222 != 0)
223 || (null_stdout
224 && (err = posix_spawn_file_actions_addopen (&actions,
225 STDOUT_FILENO,
226 "/dev/null", O_RDWR,
227 0))
228 != 0)
229 || (null_stderr
230 && (err = posix_spawn_file_actions_addopen (&actions,
231 STDERR_FILENO,
232 "/dev/null", O_RDWR,
233 0))
234 != 0)
235 || (slave_process
236 && ((err = posix_spawnattr_init (&attrs)) != 0
237 || (attrs_allocated = true,
238 (err = posix_spawnattr_setsigmask (&attrs,
239 &blocked_signals))
240 != 0
241 || (err = posix_spawnattr_setflags (&attrs,
242 POSIX_SPAWN_SETSIGMASK))
243 != 0)))
244 || (err = posix_spawnp (&child, prog_path, &actions,
245 attrs_allocated ? &attrs : NULL, prog_argv,
246 environ))
247 != 0))
248 {
249 if (actions_allocated)
250 posix_spawn_file_actions_destroy (&actions);
251 if (attrs_allocated)
252 posix_spawnattr_destroy (&attrs);
253 if (slave_process)
254 unblock_fatal_signals ();
255 if (exit_on_error || !null_stderr)
256 error (exit_on_error ? EXIT_FAILURE : 0, err,
257 _("%s subprocess failed"), progname);
258 return 127;
259 }
260 posix_spawn_file_actions_destroy (&actions);
261 if (attrs_allocated)
262 posix_spawnattr_destroy (&attrs);
263 #else
264 if (slave_process)
265 block_fatal_signals ();
266 /* Use vfork() instead of fork() for efficiency. */
267 if ((child = vfork ()) == 0)
268 {
269 /* Child process code. */
270 int nullinfd;
271 int nulloutfd;
272
273 if ((!null_stdin
274 || ((nullinfd = open ("/dev/null", O_RDONLY, 0)) >= 0
275 && (nullinfd == STDIN_FILENO
276 || (dup2 (nullinfd, STDIN_FILENO) >= 0
277 && close (nullinfd) >= 0))))
278 && (!(null_stdout || null_stderr)
279 || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
280 && (!null_stdout
281 || nulloutfd == STDOUT_FILENO
282 || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
283 && (!null_stderr
284 || nulloutfd == STDERR_FILENO
285 || dup2 (nulloutfd, STDERR_FILENO) >= 0)
286 && ((null_stdout && nulloutfd == STDOUT_FILENO)
287 || (null_stderr && nulloutfd == STDERR_FILENO)
288 || close (nulloutfd) >= 0)))
289 && (!slave_process || (unblock_fatal_signals (), true)))
290 execvp (prog_path, prog_argv);
291 _exit (127);
292 }
293 if (child == -1)
294 {
295 if (slave_process)
296 unblock_fatal_signals ();
297 if (exit_on_error || !null_stderr)
298 error (exit_on_error ? EXIT_FAILURE : 0, errno,
299 _("%s subprocess failed"), progname);
300 return 127;
301 }
302 #endif
303 if (slave_process)
304 {
305 register_slave_subprocess (child);
306 unblock_fatal_signals ();
307 }
308
309 return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
310 slave_process, exit_on_error);
311
312 #endif
313 }
314