(root)/
m4-1.4.19/
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  }