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