Propagate effects of putenv/setenv/unsetenv to child processes.
[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          int *termsigp)
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   /* FIXME: Need to free memory allocated by prepare_spawn.  */
134   prog_argv = prepare_spawn (prog_argv);
135
136   /* Save standard file handles of parent process.  */
137   if (null_stdin)
138     orig_stdin = dup_noinherit (STDIN_FILENO);
139   if (null_stdout)
140     orig_stdout = dup_noinherit (STDOUT_FILENO);
141   if (null_stderr)
142     orig_stderr = dup_noinherit (STDERR_FILENO);
143   exitcode = -1;
144
145   /* Create standard file handles of child process.  */
146   nullinfd = -1;
147   nulloutfd = -1;
148   if ((!null_stdin
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
155               && (!null_stdout
156                   || nulloutfd == STDOUT_FILENO
157                   || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
158               && (!null_stderr
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     /* Use spawnvpe and pass the environment explicitly.  This is needed if
165        the program has modified the environment using putenv() or [un]setenv().
166        On Windows, programs have two environments, one in the "environment
167        block" of the process and managed through SetEnvironmentVariable(), and
168        one inside the process, in the location retrieved by the 'environ'
169        macro.  When using spawnvp() without 'e', the child process inherits a
170        copy of the environment block - ignoring the effects of putenv() and
171        [un]setenv().  */
172     {
173       exitcode = spawnvpe (P_WAIT, prog_path, prog_argv, environ);
174       if (exitcode < 0 && errno == ENOEXEC)
175         {
176           /* prog is not an native executable.  Try to execute it as a
177              shell script.  Note that prepare_spawn() has already prepended
178              a hidden element "sh.exe" to prog_argv.  */
179           --prog_argv;
180           exitcode = spawnvpe (P_WAIT, prog_argv[0], prog_argv, environ);
181         }
182     }
183   if (nulloutfd >= 0)
184     close (nulloutfd);
185   if (nullinfd >= 0)
186     close (nullinfd);
187
188   /* Restore standard file handles of parent process.  */
189   if (null_stderr)
190     dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
191   if (null_stdout)
192     dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
193   if (null_stdin)
194     dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);
195
196   if (termsigp != NULL)
197     *termsigp = 0;
198
199   if (exitcode == -1)
200     {
201       if (exit_on_error || !null_stderr)
202         error (exit_on_error ? EXIT_FAILURE : 0, errno,
203                _("%s subprocess failed"), progname);
204       return 127;
205     }
206
207   return exitcode;
208
209 #else
210
211   /* Unix API.  */
212   /* Note about 127: Some errors during posix_spawnp() cause the function
213      posix_spawnp() to return an error code; some other errors cause the
214      subprocess to exit with return code 127.  It is implementation
215      dependent which error is reported which way.  We treat both cases as
216      equivalent.  */
217 #if HAVE_POSIX_SPAWN
218   sigset_t blocked_signals;
219   posix_spawn_file_actions_t actions;
220   bool actions_allocated;
221   posix_spawnattr_t attrs;
222   bool attrs_allocated;
223   int err;
224   pid_t child;
225 #else
226   int child;
227 #endif
228
229 #if HAVE_POSIX_SPAWN
230   if (slave_process)
231     {
232       sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
233       block_fatal_signals ();
234     }
235   actions_allocated = false;
236   attrs_allocated = false;
237   if ((err = posix_spawn_file_actions_init (&actions)) != 0
238       || (actions_allocated = true,
239           (null_stdin
240             && (err = posix_spawn_file_actions_addopen (&actions,
241                                                         STDIN_FILENO,
242                                                         "/dev/null", O_RDONLY,
243                                                         0))
244                != 0)
245           || (null_stdout
246               && (err = posix_spawn_file_actions_addopen (&actions,
247                                                           STDOUT_FILENO,
248                                                           "/dev/null", O_RDWR,
249                                                           0))
250                  != 0)
251           || (null_stderr
252               && (err = posix_spawn_file_actions_addopen (&actions,
253                                                           STDERR_FILENO,
254                                                           "/dev/null", O_RDWR,
255                                                           0))
256                  != 0)
257           || (slave_process
258               && ((err = posix_spawnattr_init (&attrs)) != 0
259                   || (attrs_allocated = true,
260                       (err = posix_spawnattr_setsigmask (&attrs,
261                                                          &blocked_signals))
262                       != 0
263                       || (err = posix_spawnattr_setflags (&attrs,
264                                                         POSIX_SPAWN_SETSIGMASK))
265                          != 0)))
266           || (err = posix_spawnp (&child, prog_path, &actions,
267                                   attrs_allocated ? &attrs : NULL, prog_argv,
268                                   environ))
269              != 0))
270     {
271       if (actions_allocated)
272         posix_spawn_file_actions_destroy (&actions);
273       if (attrs_allocated)
274         posix_spawnattr_destroy (&attrs);
275       if (slave_process)
276         unblock_fatal_signals ();
277       if (termsigp != NULL)
278         *termsigp = 0;
279       if (exit_on_error || !null_stderr)
280         error (exit_on_error ? EXIT_FAILURE : 0, err,
281                _("%s subprocess failed"), progname);
282       return 127;
283     }
284   posix_spawn_file_actions_destroy (&actions);
285   if (attrs_allocated)
286     posix_spawnattr_destroy (&attrs);
287 #else
288   if (slave_process)
289     block_fatal_signals ();
290   /* Use vfork() instead of fork() for efficiency.  */
291   if ((child = vfork ()) == 0)
292     {
293       /* Child process code.  */
294       int nullinfd;
295       int nulloutfd;
296
297       if ((!null_stdin
298            || ((nullinfd = open ("/dev/null", O_RDONLY, 0)) >= 0
299                && (nullinfd == STDIN_FILENO
300                    || (dup2 (nullinfd, STDIN_FILENO) >= 0
301                        && close (nullinfd) >= 0))))
302           && (!(null_stdout || null_stderr)
303               || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
304                   && (!null_stdout
305                       || nulloutfd == STDOUT_FILENO
306                       || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
307                   && (!null_stderr
308                       || nulloutfd == STDERR_FILENO
309                       || dup2 (nulloutfd, STDERR_FILENO) >= 0)
310                   && ((null_stdout && nulloutfd == STDOUT_FILENO)
311                       || (null_stderr && nulloutfd == STDERR_FILENO)
312                       || close (nulloutfd) >= 0)))
313           && (!slave_process || (unblock_fatal_signals (), true)))
314         execvp (prog_path, prog_argv);
315       _exit (127);
316     }
317   if (child == -1)
318     {
319       if (slave_process)
320         unblock_fatal_signals ();
321       if (termsigp != NULL)
322         *termsigp = 0;
323       if (exit_on_error || !null_stderr)
324         error (exit_on_error ? EXIT_FAILURE : 0, errno,
325                _("%s subprocess failed"), progname);
326       return 127;
327     }
328 #endif
329   if (slave_process)
330     {
331       register_slave_subprocess (child);
332       unblock_fatal_signals ();
333     }
334
335   return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
336                           slave_process, exit_on_error, termsigp);
337
338 #endif
339 }