Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / execute.c
1 /* Creation of autonomous subprocesses.
2    Copyright (C) 2001-2004, 2006-2015 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 _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
39
40 /* Native Windows API.  */
41 # include <process.h>
42 # include "w32spawn.h"
43
44 #else
45
46 /* Unix API.  */
47 # include <spawn.h>
48
49 #endif
50
51 /* environ is the exported symbol referencing the internal
52    __cygwin_environ variable on cygwin64:
53    <https://cygwin.com/ml/cygwin/2013-06/msg00228.html>.  */
54 #if defined __CYGWIN__ && defined __x86_64__
55 extern DLL_VARIABLE char **environ;
56 #endif
57
58
59 #if defined EINTR && ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
60
61 /* EINTR handling for close(), open().
62    These functions can return -1/EINTR even though we don't have any
63    signal handlers set up, namely when we get interrupted via SIGSTOP.  */
64
65 static int
66 nonintr_close (int fd)
67 {
68   int retval;
69
70   do
71     retval = close (fd);
72   while (retval < 0 && errno == EINTR);
73
74   return retval;
75 }
76 #define close nonintr_close
77
78 static int
79 nonintr_open (const char *pathname, int oflag, mode_t mode)
80 {
81   int retval;
82
83   do
84     retval = open (pathname, oflag, mode);
85   while (retval < 0 && errno == EINTR);
86
87   return retval;
88 }
89 #undef open /* avoid warning on VMS */
90 #define open nonintr_open
91
92 #endif
93
94
95 /* Execute a command, optionally redirecting any of the three standard file
96    descriptors to /dev/null.  Return its exit code.
97    If it didn't terminate correctly, exit if exit_on_error is true, otherwise
98    return 127.
99    If slave_process is true, the child process will be terminated when its
100    creator receives a catchable fatal signal.  */
101 int
102 execute (const char *progname,
103          const char *prog_path, char **prog_argv,
104          bool ignore_sigpipe,
105          bool null_stdin, bool null_stdout, bool null_stderr,
106          bool slave_process, bool exit_on_error,
107          int *termsigp)
108 {
109 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
110
111   /* Native Windows API.  */
112   int orig_stdin;
113   int orig_stdout;
114   int orig_stderr;
115   int exitcode;
116   int nullinfd;
117   int nulloutfd;
118
119   /* FIXME: Need to free memory allocated by prepare_spawn.  */
120   prog_argv = prepare_spawn (prog_argv);
121
122   /* Save standard file handles of parent process.  */
123   if (null_stdin)
124     orig_stdin = dup_safer_noinherit (STDIN_FILENO);
125   if (null_stdout)
126     orig_stdout = dup_safer_noinherit (STDOUT_FILENO);
127   if (null_stderr)
128     orig_stderr = dup_safer_noinherit (STDERR_FILENO);
129   exitcode = -1;
130
131   /* Create standard file handles of child process.  */
132   nullinfd = -1;
133   nulloutfd = -1;
134   if ((!null_stdin
135        || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0
136            && (nullinfd == STDIN_FILENO
137                || (dup2 (nullinfd, STDIN_FILENO) >= 0
138                    && close (nullinfd) >= 0))))
139       && (!(null_stdout || null_stderr)
140           || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
141               && (!null_stdout
142                   || nulloutfd == STDOUT_FILENO
143                   || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
144               && (!null_stderr
145                   || nulloutfd == STDERR_FILENO
146                   || dup2 (nulloutfd, STDERR_FILENO) >= 0)
147               && ((null_stdout && nulloutfd == STDOUT_FILENO)
148                   || (null_stderr && nulloutfd == STDERR_FILENO)
149                   || close (nulloutfd) >= 0))))
150     /* Use spawnvpe and pass the environment explicitly.  This is needed if
151        the program has modified the environment using putenv() or [un]setenv().
152        On Windows, programs have two environments, one in the "environment
153        block" of the process and managed through SetEnvironmentVariable(), and
154        one inside the process, in the location retrieved by the 'environ'
155        macro.  When using spawnvp() without 'e', the child process inherits a
156        copy of the environment block - ignoring the effects of putenv() and
157        [un]setenv().  */
158     {
159       exitcode = spawnvpe (P_WAIT, prog_path, (const char **) prog_argv,
160                            (const char **) environ);
161       if (exitcode < 0 && errno == ENOEXEC)
162         {
163           /* prog is not a native executable.  Try to execute it as a
164              shell script.  Note that prepare_spawn() has already prepended
165              a hidden element "sh.exe" to prog_argv.  */
166           --prog_argv;
167           exitcode = spawnvpe (P_WAIT, prog_argv[0], (const char **) prog_argv,
168                                (const char **) environ);
169         }
170     }
171   if (nulloutfd >= 0)
172     close (nulloutfd);
173   if (nullinfd >= 0)
174     close (nullinfd);
175
176   /* Restore standard file handles of parent process.  */
177   if (null_stderr)
178     undup_safer_noinherit (orig_stderr, STDERR_FILENO);
179   if (null_stdout)
180     undup_safer_noinherit (orig_stdout, STDOUT_FILENO);
181   if (null_stdin)
182     undup_safer_noinherit (orig_stdin, STDIN_FILENO);
183
184   if (termsigp != NULL)
185     *termsigp = 0;
186
187   if (exitcode == -1)
188     {
189       if (exit_on_error || !null_stderr)
190         error (exit_on_error ? EXIT_FAILURE : 0, errno,
191                _("%s subprocess failed"), progname);
192       return 127;
193     }
194
195   return exitcode;
196
197 #else
198
199   /* Unix API.  */
200   /* Note about 127: Some errors during posix_spawnp() cause the function
201      posix_spawnp() to return an error code; some other errors cause the
202      subprocess to exit with return code 127.  It is implementation
203      dependent which error is reported which way.  We treat both cases as
204      equivalent.  */
205   sigset_t blocked_signals;
206   posix_spawn_file_actions_t actions;
207   bool actions_allocated;
208   posix_spawnattr_t attrs;
209   bool attrs_allocated;
210   int err;
211   pid_t child;
212
213   if (slave_process)
214     {
215       sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
216       block_fatal_signals ();
217     }
218   actions_allocated = false;
219   attrs_allocated = false;
220   if ((err = posix_spawn_file_actions_init (&actions)) != 0
221       || (actions_allocated = true,
222           (null_stdin
223             && (err = posix_spawn_file_actions_addopen (&actions,
224                                                         STDIN_FILENO,
225                                                         "/dev/null", O_RDONLY,
226                                                         0))
227                != 0)
228           || (null_stdout
229               && (err = posix_spawn_file_actions_addopen (&actions,
230                                                           STDOUT_FILENO,
231                                                           "/dev/null", O_RDWR,
232                                                           0))
233                  != 0)
234           || (null_stderr
235               && (err = posix_spawn_file_actions_addopen (&actions,
236                                                           STDERR_FILENO,
237                                                           "/dev/null", O_RDWR,
238                                                           0))
239                  != 0)
240           || (slave_process
241               && ((err = posix_spawnattr_init (&attrs)) != 0
242                   || (attrs_allocated = true,
243                       (err = posix_spawnattr_setsigmask (&attrs,
244                                                          &blocked_signals))
245                       != 0
246                       || (err = posix_spawnattr_setflags (&attrs,
247                                                         POSIX_SPAWN_SETSIGMASK))
248                          != 0)))
249           || (err = posix_spawnp (&child, prog_path, &actions,
250                                   attrs_allocated ? &attrs : NULL, prog_argv,
251                                   environ))
252              != 0))
253     {
254       if (actions_allocated)
255         posix_spawn_file_actions_destroy (&actions);
256       if (attrs_allocated)
257         posix_spawnattr_destroy (&attrs);
258       if (slave_process)
259         unblock_fatal_signals ();
260       if (termsigp != NULL)
261         *termsigp = 0;
262       if (exit_on_error || !null_stderr)
263         error (exit_on_error ? EXIT_FAILURE : 0, err,
264                _("%s subprocess failed"), progname);
265       return 127;
266     }
267   posix_spawn_file_actions_destroy (&actions);
268   if (attrs_allocated)
269     posix_spawnattr_destroy (&attrs);
270   if (slave_process)
271     {
272       register_slave_subprocess (child);
273       unblock_fatal_signals ();
274     }
275
276   return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
277                           slave_process, exit_on_error, termsigp);
278
279 #endif
280 }