Bump to m4 1.4.19
[platform/upstream/m4.git] / lib / execute.c
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
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
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
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 }