root/maint/gnulib/lib/execute.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nonintr_close
  2. nonintr_open
  3. execute

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */