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