Add termsigp argument to execute() and wait_process().
[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   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 (termsigp != NULL)
178     *termsigp = 0;
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 (termsigp != NULL)
259         *termsigp = 0;
260       if (exit_on_error || !null_stderr)
261         error (exit_on_error ? EXIT_FAILURE : 0, err,
262                _("%s subprocess failed"), progname);
263       return 127;
264     }
265   posix_spawn_file_actions_destroy (&actions);
266   if (attrs_allocated)
267     posix_spawnattr_destroy (&attrs);
268 #else
269   if (slave_process)
270     block_fatal_signals ();
271   /* Use vfork() instead of fork() for efficiency.  */
272   if ((child = vfork ()) == 0)
273     {
274       /* Child process code.  */
275       int nullinfd;
276       int nulloutfd;
277
278       if ((!null_stdin
279            || ((nullinfd = open ("/dev/null", O_RDONLY, 0)) >= 0
280                && (nullinfd == STDIN_FILENO
281                    || (dup2 (nullinfd, STDIN_FILENO) >= 0
282                        && close (nullinfd) >= 0))))
283           && (!(null_stdout || null_stderr)
284               || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
285                   && (!null_stdout
286                       || nulloutfd == STDOUT_FILENO
287                       || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
288                   && (!null_stderr
289                       || nulloutfd == STDERR_FILENO
290                       || dup2 (nulloutfd, STDERR_FILENO) >= 0)
291                   && ((null_stdout && nulloutfd == STDOUT_FILENO)
292                       || (null_stderr && nulloutfd == STDERR_FILENO)
293                       || close (nulloutfd) >= 0)))
294           && (!slave_process || (unblock_fatal_signals (), true)))
295         execvp (prog_path, prog_argv);
296       _exit (127);
297     }
298   if (child == -1)
299     {
300       if (slave_process)
301         unblock_fatal_signals ();
302       if (termsigp != NULL)
303         *termsigp = 0;
304       if (exit_on_error || !null_stderr)
305         error (exit_on_error ? EXIT_FAILURE : 0, errno,
306                _("%s subprocess failed"), progname);
307       return 127;
308     }
309 #endif
310   if (slave_process)
311     {
312       register_slave_subprocess (child);
313       unblock_fatal_signals ();
314     }
315
316   return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
317                           slave_process, exit_on_error, termsigp);
318
319 #endif
320 }