1 /* Creation of autonomous subprocesses.
2 Copyright (C) 2001-2003 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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)
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
39 #include "fatal-signal.h"
40 #include "wait-process.h"
43 #define _(str) gettext (str)
45 #if defined _MSC_VER || defined __MINGW32__
47 /* Native Woe32 API. */
49 # include "w32spawn.h"
54 # ifdef HAVE_POSIX_SPAWN
65 # define STDIN_FILENO 0
68 # define STDOUT_FILENO 1
71 # define STDERR_FILENO 2
77 /* EINTR handling for close(), open().
78 These functions can return -1/EINTR even though we don't have any
79 signal handlers set up, namely when we get interrupted via SIGSTOP. */
82 nonintr_close (int fd)
88 while (retval < 0 && errno == EINTR);
92 #define close nonintr_close
95 nonintr_open (const char *pathname, int oflag, mode_t mode)
100 retval = open (pathname, oflag, mode);
101 while (retval < 0 && errno == EINTR);
105 #undef open /* avoid warning on VMS */
106 #define open nonintr_open
111 /* Execute a command, optionally redirecting any of the three standard file
112 descriptors to /dev/null. Return its exit code.
113 If it didn't terminate correctly, exit if exit_on_error is true, otherwise
115 If slave_process is true, the child process will be terminated when its
116 creator receives a catchable fatal signal. */
118 execute (const char *progname,
119 const char *prog_path, char **prog_argv,
121 bool null_stdin, bool null_stdout, bool null_stderr,
122 bool slave_process, bool exit_on_error)
124 #if defined _MSC_VER || defined __MINGW32__
126 /* Native Woe32 API. */
134 prog_argv = prepare_spawn (prog_argv);
136 /* Save standard file handles of parent process. */
138 orig_stdin = dup_noinherit (STDIN_FILENO);
140 orig_stdout = dup_noinherit (STDOUT_FILENO);
142 orig_stderr = dup_noinherit (STDERR_FILENO);
145 /* Create standard file handles of child process. */
149 || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0
150 && (nullinfd == STDIN_FILENO
151 || (dup2 (nullinfd, STDIN_FILENO) >= 0
152 && close (nullinfd) >= 0))))
153 && (!(null_stdout || null_stderr)
154 || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
156 || nulloutfd == STDOUT_FILENO
157 || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
159 || nulloutfd == STDERR_FILENO
160 || dup2 (nulloutfd, STDERR_FILENO) >= 0)
161 && ((null_stdout && nulloutfd == STDOUT_FILENO)
162 || (null_stderr && nulloutfd == STDERR_FILENO)
163 || close (nulloutfd) >= 0))))
164 exitcode = spawnvp (P_WAIT, prog_path, prog_argv);
170 /* Restore standard file handles of parent process. */
172 dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
174 dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
176 dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);
180 if (exit_on_error || !null_stderr)
181 error (exit_on_error ? EXIT_FAILURE : 0, errno,
182 _("%s subprocess failed"), progname);
191 /* Note about 127: Some errors during posix_spawnp() cause the function
192 posix_spawnp() to return an error code; some other errors cause the
193 subprocess to exit with return code 127. It is implementation
194 dependent which error is reported which way. We treat both cases as
197 sigset_t blocked_signals;
198 posix_spawn_file_actions_t actions;
199 bool actions_allocated;
200 posix_spawnattr_t attrs;
201 bool attrs_allocated;
211 sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
212 block_fatal_signals ();
214 actions_allocated = false;
215 attrs_allocated = false;
216 if ((err = posix_spawn_file_actions_init (&actions)) != 0
217 || (actions_allocated = true,
219 && (err = posix_spawn_file_actions_addopen (&actions,
221 "/dev/null", O_RDONLY,
225 && (err = posix_spawn_file_actions_addopen (&actions,
231 && (err = posix_spawn_file_actions_addopen (&actions,
237 && ((err = posix_spawnattr_init (&attrs)) != 0
238 || (attrs_allocated = true,
239 (err = posix_spawnattr_setsigmask (&attrs,
242 || (err = posix_spawnattr_setflags (&attrs,
243 POSIX_SPAWN_SETSIGMASK))
245 || (err = posix_spawnp (&child, prog_path, &actions,
246 attrs_allocated ? &attrs : NULL, prog_argv,
250 if (actions_allocated)
251 posix_spawn_file_actions_destroy (&actions);
253 posix_spawnattr_destroy (&attrs);
255 unblock_fatal_signals ();
256 if (exit_on_error || !null_stderr)
257 error (exit_on_error ? EXIT_FAILURE : 0, err,
258 _("%s subprocess failed"), progname);
261 posix_spawn_file_actions_destroy (&actions);
263 posix_spawnattr_destroy (&attrs);
266 block_fatal_signals ();
267 /* Use vfork() instead of fork() for efficiency. */
268 if ((child = vfork ()) == 0)
270 /* Child process code. */
275 || ((nullinfd = open ("/dev/null", O_RDONLY, 0)) >= 0
276 && (nullinfd == STDIN_FILENO
277 || (dup2 (nullinfd, STDIN_FILENO) >= 0
278 && close (nullinfd) >= 0))))
279 && (!(null_stdout || null_stderr)
280 || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
282 || nulloutfd == STDOUT_FILENO
283 || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
285 || nulloutfd == STDERR_FILENO
286 || dup2 (nulloutfd, STDERR_FILENO) >= 0)
287 && ((null_stdout && nulloutfd == STDOUT_FILENO)
288 || (null_stderr && nulloutfd == STDERR_FILENO)
289 || close (nulloutfd) >= 0)))
290 && (!slave_process || (unblock_fatal_signals (), true)))
291 execvp (prog_path, prog_argv);
297 unblock_fatal_signals ();
298 if (exit_on_error || !null_stderr)
299 error (exit_on_error ? EXIT_FAILURE : 0, errno,
300 _("%s subprocess failed"), progname);
306 register_slave_subprocess (child);
307 unblock_fatal_signals ();
310 return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
311 slave_process, exit_on_error);