doc: Prefer https urls where possible.
[gnulib.git] / lib / execute.c
blobc5ebf29690de3cf08d44c9a5de07cfcc078faad0
1 /* Creation of autonomous subprocesses.
2 Copyright (C) 2001-2004, 2006-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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.
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.
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/>. */
19 #include <config.h>
21 /* Specification. */
22 #include "execute.h"
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <signal.h>
28 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
33 #include "canonicalize.h"
34 #include <error.h>
35 #include "fatal-signal.h"
36 #include "filename.h"
37 #include "findprog.h"
38 #include "windows-path.h"
39 #include "wait-process.h"
40 #include "xalloc.h"
41 #include "gettext.h"
43 #define _(str) gettext (str)
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
58 #if (defined _WIN32 && !defined __CYGWIN__) && EXECUTE_IMPL_AVOID_POSIX_SPAWN
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"
69 #else
71 /* Unix API. */
72 # include <spawn.h>
74 #endif
77 #if defined EINTR && (defined _WIN32 && !defined __CYGWIN__) && EXECUTE_IMPL_AVOID_POSIX_SPAWN
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. */
83 static int
84 nonintr_close (int fd)
86 int retval;
89 retval = close (fd);
90 while (retval < 0 && errno == EINTR);
92 return retval;
94 #undef close /* avoid warning related to gnulib module unistd */
95 #define close nonintr_close
97 static int
98 nonintr_open (const char *pathname, int oflag, mode_t mode)
100 int retval;
103 retval = open (pathname, oflag, mode);
104 while (retval < 0 && errno == EINTR);
106 return retval;
108 #undef open /* avoid warning on VMS */
109 #define open nonintr_open
111 #endif
115 execute (const char *progname,
116 const char *prog_path, const char * const *prog_argv,
117 const char * const *dll_dirs,
118 const char *directory,
119 bool ignore_sigpipe,
120 bool null_stdin, bool null_stdout, bool null_stderr,
121 bool slave_process, bool exit_on_error,
122 int *termsigp)
124 int saved_errno;
125 char *prog_path_to_free = NULL;
127 if (directory != NULL)
129 /* If a change of directory is requested, make sure PROG_PATH is absolute
130 before we do so. This is needed because
131 - posix_spawn and posix_spawnp are required to resolve a relative
132 PROG_PATH *after* changing the directory. See
133 <https://www.austingroupbugs.net/view.php?id=1208>:
134 "if this pathname does not start with a <slash> it shall be
135 interpreted relative to the working directory of the child
136 process _after_ all file_actions have been performed."
137 But this would be a surprising application behaviour, possibly
138 even security relevant.
139 - For the Windows CreateProcess() function, it is unspecified whether
140 a relative file name is interpreted to the parent's current
141 directory or to the specified directory. See
142 <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */
143 if (! IS_ABSOLUTE_FILE_NAME (prog_path))
145 const char *resolved_prog =
146 find_in_given_path (prog_path, getenv ("PATH"), NULL, false);
147 if (resolved_prog == NULL)
148 goto fail_with_errno;
149 if (resolved_prog != prog_path)
150 prog_path_to_free = (char *) resolved_prog;
151 prog_path = resolved_prog;
153 if (! IS_ABSOLUTE_FILE_NAME (prog_path))
155 char *absolute_prog =
156 canonicalize_filename_mode (prog_path,
157 CAN_MISSING | CAN_NOLINKS);
158 if (absolute_prog == NULL)
160 free (prog_path_to_free);
161 goto fail_with_errno;
163 free (prog_path_to_free);
164 prog_path_to_free = absolute_prog;
165 prog_path = absolute_prog;
167 if (! IS_ABSOLUTE_FILE_NAME (prog_path))
168 abort ();
173 #if (defined _WIN32 && !defined __CYGWIN__) && EXECUTE_IMPL_AVOID_POSIX_SPAWN
175 /* Native Windows API. */
177 char *argv_mem_to_free;
179 const char **argv = prepare_spawn (prog_argv, &argv_mem_to_free);
180 if (argv == NULL)
181 xalloc_die ();
183 int exitcode = -1;
185 /* Create standard file handles of child process. */
186 int nullinfd = -1;
187 int nulloutfd = -1;
188 if ((!null_stdin
189 || (nullinfd = open ("NUL", O_RDONLY, 0)) >= 0)
190 && (!(null_stdout || null_stderr)
191 || (nulloutfd = open ("NUL", O_RDWR, 0)) >= 0))
192 /* Pass the environment explicitly. This is needed if the program has
193 modified the environment using putenv() or [un]setenv(). On Windows,
194 processes have two environments, one in the "environment block" of the
195 process and managed through SetEnvironmentVariable(), and one inside the
196 process, in the location retrieved by the 'environ' macro. If we were
197 to pass NULL, the child process would inherit a copy of the environment
198 block - ignoring the effects of putenv() and [un]setenv(). */
200 HANDLE stdin_handle =
201 (HANDLE) _get_osfhandle (null_stdin ? nullinfd : STDIN_FILENO);
202 HANDLE stdout_handle =
203 (HANDLE) _get_osfhandle (null_stdout ? nulloutfd : STDOUT_FILENO);
204 HANDLE stderr_handle =
205 (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO);
207 exitcode = spawnpvech (P_WAIT, prog_path, argv + 1,
208 (const char * const *) environ, dll_dirs,
209 directory,
210 stdin_handle, stdout_handle, stderr_handle);
211 # if 0 /* Executing arbitrary files as shell scripts is unsecure. */
212 if (exitcode == -1 && errno == ENOEXEC)
214 /* prog is not a native executable. Try to execute it as a
215 shell script. Note that prepare_spawn() has already prepended
216 a hidden element "sh.exe" to argv. */
217 argv[1] = prog_path;
218 exitcode = spawnpvech (P_WAIT, argv[0], argv,
219 (const char * const *) environ, dll_dirs,
220 directory,
221 stdin_handle, stdout_handle, stderr_handle);
223 # endif
225 if (exitcode == -1)
226 saved_errno = errno;
227 if (nulloutfd >= 0)
228 close (nulloutfd);
229 if (nullinfd >= 0)
230 close (nullinfd);
231 free (argv);
232 free (argv_mem_to_free);
233 free (prog_path_to_free);
235 /* Treat failure and signalled child processes like wait_subprocess()
236 does. */
237 if (termsigp != NULL)
238 *termsigp = 0;
240 if (exitcode == -1)
241 goto fail_with_saved_errno;
243 if (WIFSIGNALED (exitcode))
245 if (termsigp != NULL)
246 *termsigp = WTERMSIG (exitcode);
247 saved_errno = 0;
248 goto fail_with_saved_errno;
251 return exitcode;
253 #else
255 /* Unix API. */
256 /* Note about 127: Some errors during posix_spawnp() cause the function
257 posix_spawnp() to return an error code; some other errors cause the
258 subprocess to exit with return code 127. It is implementation
259 dependent which error is reported which way. We treat both cases as
260 equivalent. */
261 char **child_environ;
262 char **malloced_environ;
263 sigset_t blocked_signals;
264 posix_spawn_file_actions_t actions;
265 bool actions_allocated;
266 posix_spawnattr_t attrs;
267 bool attrs_allocated;
268 int err;
269 pid_t child;
271 child_environ = environ;
272 malloced_environ = NULL;
273 # if defined _WIN32 || defined __CYGWIN__
274 if (dll_dirs != NULL && dll_dirs[0] != NULL)
276 malloced_environ = extended_environ (dll_dirs);
277 if (malloced_environ == NULL)
278 goto fail_with_errno;
279 child_environ = malloced_environ;
281 # endif
283 if (slave_process)
285 sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
286 block_fatal_signals ();
288 actions_allocated = false;
289 attrs_allocated = false;
290 if ((err = posix_spawn_file_actions_init (&actions)) != 0
291 || (actions_allocated = true,
292 (null_stdin
293 && (err = posix_spawn_file_actions_addopen (&actions,
294 STDIN_FILENO,
295 "/dev/null", O_RDONLY,
297 != 0)
298 || (null_stdout
299 && (err = posix_spawn_file_actions_addopen (&actions,
300 STDOUT_FILENO,
301 "/dev/null", O_RDWR,
303 != 0)
304 || (null_stderr
305 && (err = posix_spawn_file_actions_addopen (&actions,
306 STDERR_FILENO,
307 "/dev/null", O_RDWR,
309 != 0)
310 || (directory != NULL
311 && (err = posix_spawn_file_actions_addchdir (&actions,
312 directory)))
313 # if !(defined _WIN32 && !defined __CYGWIN__)
314 || (slave_process
315 && ((err = posix_spawnattr_init (&attrs)) != 0
316 || (attrs_allocated = true,
317 (err = posix_spawnattr_setsigmask (&attrs,
318 &blocked_signals))
319 != 0
320 || (err = posix_spawnattr_setflags (&attrs,
321 POSIX_SPAWN_SETSIGMASK))
322 != 0)))
323 # endif
324 || (err = (directory != NULL
325 ? posix_spawn (&child, prog_path, &actions,
326 attrs_allocated ? &attrs : NULL,
327 (char * const *) prog_argv,
328 child_environ)
329 : posix_spawnp (&child, prog_path, &actions,
330 attrs_allocated ? &attrs : NULL,
331 (char * const *) prog_argv,
332 child_environ)))
333 != 0))
335 if (actions_allocated)
336 posix_spawn_file_actions_destroy (&actions);
337 if (attrs_allocated)
338 posix_spawnattr_destroy (&attrs);
339 if (slave_process)
340 unblock_fatal_signals ();
341 if (malloced_environ != NULL)
343 free (malloced_environ[0]);
344 free (malloced_environ);
346 free (prog_path_to_free);
347 if (termsigp != NULL)
348 *termsigp = 0;
349 saved_errno = err;
350 goto fail_with_saved_errno;
352 posix_spawn_file_actions_destroy (&actions);
353 if (attrs_allocated)
354 posix_spawnattr_destroy (&attrs);
355 if (slave_process)
357 register_slave_subprocess (child);
358 unblock_fatal_signals ();
360 if (malloced_environ != NULL)
362 free (malloced_environ[0]);
363 free (malloced_environ);
365 free (prog_path_to_free);
367 return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
368 slave_process, exit_on_error, termsigp);
370 #endif
372 fail_with_errno:
373 saved_errno = errno;
374 fail_with_saved_errno:
375 if (exit_on_error || !null_stderr)
376 error (exit_on_error ? EXIT_FAILURE : 0, saved_errno,
377 _("%s subprocess failed"), progname);
378 return 127;