1 /* Creation of autonomous subprocesses.
2 Copyright (C) 2001-2004, 2006-2021 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 <https://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 <sys/types.h>
32 #include <sys/wait.h>
33
34 #include "canonicalize.h"
35 #include "error.h"
36 #include "fatal-signal.h"
37 #include "filename.h"
38 #include "findprog.h"
39 #include "wait-process.h"
40 #include "xalloc.h"
41 #include "gettext.h"
42
43 #define _(str) gettext (str)
44
45
46 /* Choice of implementation for native Windows.
47 - Define to 0 to use the posix_spawn facility (modules 'posix_spawn' and
48 'posix_spawnp'), that is based on the module 'windows-spawn'.
49 - Define to 1 to use the older code, that uses the module 'windows-spawn'
50 directly.
51 You can set this macro from a Makefile or at configure time, from the
52 CPPFLAGS. */
53 #ifndef EXECUTE_IMPL_AVOID_POSIX_SPAWN
54 # define EXECUTE_IMPL_AVOID_POSIX_SPAWN 0
55 #endif
56
57
58 #if (defined _WIN32 && !defined __CYGWIN__) && EXECUTE_IMPL_AVOID_POSIX_SPAWN
59
60 /* Native Windows API. */
61 # if GNULIB_MSVC_NOTHROW
62 # include "msvc-nothrow.h"
63 # else
64 # include <io.h>
65 # endif
66 # include <process.h>
67 # include "windows-spawn.h"
68
69 #else
70
71 /* Unix API. */
72 # include <spawn.h>
73
74 #endif
75
76
77 #if defined EINTR && (defined _WIN32 && !defined __CYGWIN__) && EXECUTE_IMPL_AVOID_POSIX_SPAWN
78
79 /* EINTR handling for close(), open().
80 These functions can return -1/EINTR even though we don't have any
81 signal handlers set up, namely when we get interrupted via SIGSTOP. */
82
83 static int
nonintr_close(int fd)84 nonintr_close (int fd)
85 {
86 int retval;
87
88 do
89 retval = close (fd);
90 while (retval < 0 && errno == EINTR);
91
92 return retval;
93 }
94 #undef close /* avoid warning related to gnulib module unistd */
95 #define close nonintr_close
96
97 static int
nonintr_open(const char * pathname,int oflag,mode_t mode)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 int
execute(const char * progname,const char * prog_path,const char * const * prog_argv,const char * directory,bool ignore_sigpipe,bool null_stdin,bool null_stdout,bool null_stderr,bool slave_process,bool exit_on_error,int * termsigp)115 execute (const char *progname,
116 const char *prog_path, const char * const *prog_argv,
117 const char *directory,
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 int saved_errno;
124 char *prog_path_to_free = NULL;
125
126 if (directory != NULL)
127 {
128 /* If a change of directory is requested, make sure PROG_PATH is absolute
129 before we do so. This is needed because
130 - posix_spawn and posix_spawnp are required to resolve a relative
131 PROG_PATH *after* changing the directory. See
132 <https://www.austingroupbugs.net/view.php?id=1208>:
133 "if this pathname does not start with a <slash> it shall be
134 interpreted relative to the working directory of the child
135 process _after_ all file_actions have been performed."
136 But this would be a surprising application behaviour, possibly
137 even security relevant.
138 - For the Windows CreateProcess() function, it is unspecified whether
139 a relative file name is interpreted to the parent's current
140 directory or to the specified directory. See
141 <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */
142 if (! IS_ABSOLUTE_FILE_NAME (prog_path))
143 {
144 const char *resolved_prog =
145 find_in_given_path (prog_path, getenv ("PATH"), NULL, false);
146 if (resolved_prog == NULL)
147 goto fail_with_errno;
148 if (resolved_prog != prog_path)
149 prog_path_to_free = (char *) resolved_prog;
150 prog_path = resolved_prog;
151
152 if (! IS_ABSOLUTE_FILE_NAME (prog_path))
153 {
154 char *absolute_prog =
155 canonicalize_filename_mode (prog_path,
156 CAN_MISSING | CAN_NOLINKS);
157 if (absolute_prog == NULL)
158 {
159 free (prog_path_to_free);
160 goto fail_with_errno;
161 }
162 free (prog_path_to_free);
163 prog_path_to_free = absolute_prog;
164 prog_path = absolute_prog;
165
166 if (! IS_ABSOLUTE_FILE_NAME (prog_path))
167 abort ();
168 }
169 }
170 }
171
172 #if (defined _WIN32 && !defined __CYGWIN__) && EXECUTE_IMPL_AVOID_POSIX_SPAWN
173
174 /* Native Windows API. */
175
176 char *argv_mem_to_free;
177
178 const char **argv = prepare_spawn (prog_argv, &argv_mem_to_free);
179 if (argv == NULL)
180 xalloc_die ();
181
182 int exitcode = -1;
183
184 /* Create standard file handles of child process. */
185 int nullinfd = -1;
186 int nulloutfd = -1;
187 if ((!null_stdin
188 || (nullinfd = open ("NUL", O_RDONLY, 0)) >= 0)
189 && (!(null_stdout || null_stderr)
190 || (nulloutfd = open ("NUL", O_RDWR, 0)) >= 0))
191 /* Pass the environment explicitly. This is needed if the program has
192 modified the environment using putenv() or [un]setenv(). On Windows,
193 processes have two environments, one in the "environment block" of the
194 process and managed through SetEnvironmentVariable(), and one inside the
195 process, in the location retrieved by the 'environ' macro. If we were
196 to pass NULL, the child process would inherit a copy of the environment
197 block - ignoring the effects of putenv() and [un]setenv(). */
198 {
199 HANDLE stdin_handle =
200 (HANDLE) _get_osfhandle (null_stdin ? nullinfd : STDIN_FILENO);
201 HANDLE stdout_handle =
202 (HANDLE) _get_osfhandle (null_stdout ? nulloutfd : STDOUT_FILENO);
203 HANDLE stderr_handle =
204 (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO);
205
206 exitcode = spawnpvech (P_WAIT, prog_path, argv + 1,
207 (const char * const *) environ, directory,
208 stdin_handle, stdout_handle, stderr_handle);
209 # if 0 /* Executing arbitrary files as shell scripts is unsecure. */
210 if (exitcode == -1 && errno == ENOEXEC)
211 {
212 /* prog is not a native executable. Try to execute it as a
213 shell script. Note that prepare_spawn() has already prepended
214 a hidden element "sh.exe" to argv. */
215 argv[1] = prog_path;
216 exitcode = spawnpvech (P_WAIT, argv[0], argv,
217 (const char * const *) environ, directory,
218 stdin_handle, stdout_handle, stderr_handle);
219 }
220 # endif
221 }
222 if (exitcode == -1)
223 saved_errno = errno;
224 if (nulloutfd >= 0)
225 close (nulloutfd);
226 if (nullinfd >= 0)
227 close (nullinfd);
228 free (argv);
229 free (argv_mem_to_free);
230 free (prog_path_to_free);
231
232 /* Treat failure and signalled child processes like wait_subprocess()
233 does. */
234 if (termsigp != NULL)
235 *termsigp = 0;
236
237 if (exitcode == -1)
238 goto fail_with_saved_errno;
239
240 if (WIFSIGNALED (exitcode))
241 {
242 if (termsigp != NULL)
243 *termsigp = WTERMSIG (exitcode);
244 saved_errno = 0;
245 goto fail_with_saved_errno;
246 }
247
248 return exitcode;
249
250 #else
251
252 /* Unix API. */
253 /* Note about 127: Some errors during posix_spawnp() cause the function
254 posix_spawnp() to return an error code; some other errors cause the
255 subprocess to exit with return code 127. It is implementation
256 dependent which error is reported which way. We treat both cases as
257 equivalent. */
258 sigset_t blocked_signals;
259 posix_spawn_file_actions_t actions;
260 bool actions_allocated;
261 posix_spawnattr_t attrs;
262 bool attrs_allocated;
263 int err;
264 pid_t child;
265
266 if (slave_process)
267 {
268 sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
269 block_fatal_signals ();
270 }
271 actions_allocated = false;
272 attrs_allocated = false;
273 if ((err = posix_spawn_file_actions_init (&actions)) != 0
274 || (actions_allocated = true,
275 (null_stdin
276 && (err = posix_spawn_file_actions_addopen (&actions,
277 STDIN_FILENO,
278 "/dev/null", O_RDONLY,
279 0))
280 != 0)
281 || (null_stdout
282 && (err = posix_spawn_file_actions_addopen (&actions,
283 STDOUT_FILENO,
284 "/dev/null", O_RDWR,
285 0))
286 != 0)
287 || (null_stderr
288 && (err = posix_spawn_file_actions_addopen (&actions,
289 STDERR_FILENO,
290 "/dev/null", O_RDWR,
291 0))
292 != 0)
293 || (directory != NULL
294 && (err = posix_spawn_file_actions_addchdir (&actions,
295 directory)))
296 # if !(defined _WIN32 && !defined __CYGWIN__)
297 || (slave_process
298 && ((err = posix_spawnattr_init (&attrs)) != 0
299 || (attrs_allocated = true,
300 (err = posix_spawnattr_setsigmask (&attrs,
301 &blocked_signals))
302 != 0
303 || (err = posix_spawnattr_setflags (&attrs,
304 POSIX_SPAWN_SETSIGMASK))
305 != 0)))
306 # endif
307 || (err = (directory != NULL
308 ? posix_spawn (&child, prog_path, &actions,
309 attrs_allocated ? &attrs : NULL,
310 (char * const *) prog_argv, environ)
311 : posix_spawnp (&child, prog_path, &actions,
312 attrs_allocated ? &attrs : NULL,
313 (char * const *) prog_argv, environ)))
314 != 0))
315 {
316 if (actions_allocated)
317 posix_spawn_file_actions_destroy (&actions);
318 if (attrs_allocated)
319 posix_spawnattr_destroy (&attrs);
320 if (slave_process)
321 unblock_fatal_signals ();
322 free (prog_path_to_free);
323 if (termsigp != NULL)
324 *termsigp = 0;
325 saved_errno = err;
326 goto fail_with_saved_errno;
327 }
328 posix_spawn_file_actions_destroy (&actions);
329 if (attrs_allocated)
330 posix_spawnattr_destroy (&attrs);
331 if (slave_process)
332 {
333 register_slave_subprocess (child);
334 unblock_fatal_signals ();
335 }
336 free (prog_path_to_free);
337
338 return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
339 slave_process, exit_on_error, termsigp);
340
341 #endif
342
343 fail_with_errno:
344 saved_errno = errno;
345 fail_with_saved_errno:
346 if (exit_on_error || !null_stderr)
347 error (exit_on_error ? EXIT_FAILURE : 0, saved_errno,
348 _("%s subprocess failed"), progname);
349 return 127;
350 }
351