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