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