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