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