(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
spawni.c
       1  /* Guts of POSIX spawn interface.  Generic POSIX.1 version.
       2     Copyright (C) 2000-2006, 2008-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file 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 Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include <spawn.h>
      22  #include "spawn_int.h"
      23  
      24  #include <alloca.h>
      25  #include <errno.h>
      26  
      27  #include <fcntl.h>
      28  #ifndef O_LARGEFILE
      29  # define O_LARGEFILE 0
      30  #endif
      31  
      32  #if _LIBC || HAVE_PATHS_H
      33  # include <paths.h>
      34  #else
      35  # define _PATH_BSHELL BOURNE_SHELL
      36  #endif
      37  
      38  #include <signal.h>
      39  #include <stdlib.h>
      40  #include <string.h>
      41  #include <unistd.h>
      42  
      43  #if _LIBC
      44  # include <not-cancel.h>
      45  #else
      46  # define close_not_cancel close
      47  # define open_not_cancel open
      48  #endif
      49  
      50  #if _LIBC
      51  # include <local-setxid.h>
      52  #else
      53  # if !HAVE_SETEUID
      54  #  define seteuid(id) setresuid (-1, id, -1)
      55  # endif
      56  # if !HAVE_SETEGID
      57  #  define setegid(id) setresgid (-1, id, -1)
      58  # endif
      59  # define local_seteuid(id) seteuid (id)
      60  # define local_setegid(id) setegid (id)
      61  #endif
      62  
      63  #if _LIBC
      64  # define alloca __alloca
      65  # define execve __execve
      66  # define dup2 __dup2
      67  # define fork __fork
      68  # define getgid __getgid
      69  # define getuid __getuid
      70  # define sched_setparam __sched_setparam
      71  # define sched_setscheduler __sched_setscheduler
      72  # define setpgid __setpgid
      73  # define sigaction __sigaction
      74  # define sigismember __sigismember
      75  # define sigprocmask __sigprocmask
      76  # define strchrnul __strchrnul
      77  # define vfork __vfork
      78  #endif
      79  
      80  
      81  /* The Unix standard contains a long explanation of the way to signal
      82     an error after the fork() was successful.  Since no new wait status
      83     was wanted there is no way to signal an error using one of the
      84     available methods.  The committee chose to signal an error by a
      85     normal program exit with the exit code 127.  */
      86  #define SPAWN_ERROR     127
      87  
      88  
      89  #if defined _WIN32 && ! defined __CYGWIN__
      90  /* Native Windows API.  */
      91  
      92  /* Define to 1 to enable DuplicateHandle optimization.
      93     Define to 0 to disable this optimization.  */
      94  # ifndef SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
      95  #  define SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE 1
      96  # endif
      97  
      98  /* Get declarations of the native Windows API functions.  */
      99  # define WIN32_LEAN_AND_MEAN
     100  # include <windows.h>
     101  
     102  # include <stdio.h>
     103  
     104  # include "filename.h"
     105  # include "concat-filename.h"
     106  # include "findprog.h"
     107  # include "malloca.h"
     108  # include "windows-spawn.h"
     109  
     110  /* Don't assume that UNICODE is not defined.  */
     111  # undef CreateFile
     112  # define CreateFile CreateFileA
     113  # undef STARTUPINFO
     114  # define STARTUPINFO STARTUPINFOA
     115  # undef CreateProcess
     116  # define CreateProcess CreateProcessA
     117  
     118  /* Grows inh_handles->count so that it becomes > newfd.
     119     Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
     120   */
     121  static int
     122  grow_inheritable_handles (struct inheritable_handles *inh_handles, int newfd)
     123  {
     124    if (inh_handles->allocated <= newfd)
     125      {
     126        size_t new_allocated = 2 * inh_handles->allocated + 1;
     127        if (new_allocated <= newfd)
     128          new_allocated = newfd + 1;
     129        struct IHANDLE *new_ih =
     130          (struct IHANDLE *)
     131          realloc (inh_handles->ih, new_allocated * sizeof (struct IHANDLE));
     132        if (new_ih == NULL)
     133          {
     134            errno = ENOMEM;
     135            return -1;
     136          }
     137        inh_handles->allocated = new_allocated;
     138        inh_handles->ih = new_ih;
     139      }
     140  
     141    struct IHANDLE *ih = inh_handles->ih;
     142  
     143    for (; inh_handles->count <= newfd; inh_handles->count++)
     144      ih[inh_handles->count].handle = INVALID_HANDLE_VALUE;
     145  
     146    return 0;
     147  }
     148  
     149  # if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
     150  
     151  /* Assuming inh_handles->ih[newfd].handle != INVALID_HANDLE_VALUE
     152     and      (inh_handles->ih[newfd].flags & DELAYED_DUP2_NEWFD) != 0,
     153     actually performs the delayed dup2 (oldfd, newfd).
     154     Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
     155   */
     156  static int
     157  do_delayed_dup2 (int newfd, struct inheritable_handles *inh_handles,
     158                   HANDLE curr_process)
     159  {
     160    int oldfd = inh_handles->ih[newfd].linked_fd;
     161    /* Check invariants.  */
     162    if (!((inh_handles->ih[oldfd].flags & DELAYED_DUP2_OLDFD) != 0
     163          && newfd == inh_handles->ih[oldfd].linked_fd
     164          && inh_handles->ih[newfd].handle == inh_handles->ih[oldfd].handle))
     165      abort ();
     166    /* Duplicate the handle now.  */
     167    if (!DuplicateHandle (curr_process, inh_handles->ih[oldfd].handle,
     168                          curr_process, &inh_handles->ih[newfd].handle,
     169                          0, TRUE, DUPLICATE_SAME_ACCESS))
     170      {
     171        errno = EBADF; /* arbitrary */
     172        return -1;
     173      }
     174    inh_handles->ih[oldfd].flags &= ~DELAYED_DUP2_OLDFD;
     175    inh_handles->ih[newfd].flags =
     176      (unsigned char) inh_handles->ih[oldfd].flags | KEEP_OPEN_IN_CHILD;
     177    return 0;
     178  }
     179  
     180  /* Performs the remaining delayed dup2 (oldfd, newfd).
     181     Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
     182   */
     183  static int
     184  do_remaining_delayed_dup2 (struct inheritable_handles *inh_handles,
     185                             HANDLE curr_process)
     186  {
     187    size_t handles_count = inh_handles->count;
     188    int newfd;
     189  
     190    for (newfd = 0; newfd < handles_count; newfd++)
     191      if (inh_handles->ih[newfd].handle != INVALID_HANDLE_VALUE
     192          && (inh_handles->ih[newfd].flags & DELAYED_DUP2_NEWFD) != 0)
     193        if (do_delayed_dup2 (newfd, inh_handles, curr_process) < 0)
     194          return -1;
     195    return 0;
     196  }
     197  
     198  # endif
     199  
     200  /* Closes the handles in inh_handles that are not meant to be preserved in the
     201     child process, and reduces inh_handles->count to the minimum needed.  */
     202  static void
     203  shrink_inheritable_handles (struct inheritable_handles *inh_handles)
     204  {
     205    struct IHANDLE *ih = inh_handles->ih;
     206    size_t handles_count = inh_handles->count;
     207    unsigned int fd;
     208  
     209    for (fd = 0; fd < handles_count; fd++)
     210      {
     211        HANDLE handle = ih[fd].handle;
     212  
     213        if (handle != INVALID_HANDLE_VALUE
     214            && (ih[fd].flags & KEEP_OPEN_IN_CHILD) == 0)
     215          {
     216            if (!(ih[fd].flags & KEEP_OPEN_IN_PARENT))
     217              CloseHandle (handle);
     218            ih[fd].handle = INVALID_HANDLE_VALUE;
     219          }
     220      }
     221  
     222    while (handles_count > 3
     223           && ih[handles_count - 1].handle == INVALID_HANDLE_VALUE)
     224      handles_count--;
     225  
     226    inh_handles->count = handles_count;
     227  }
     228  
     229  /* Closes all handles in inh_handles.  */
     230  static void
     231  close_inheritable_handles (struct inheritable_handles *inh_handles)
     232  {
     233    struct IHANDLE *ih = inh_handles->ih;
     234    size_t handles_count = inh_handles->count;
     235    unsigned int fd;
     236  
     237    for (fd = 0; fd < handles_count; fd++)
     238      {
     239        HANDLE handle = ih[fd].handle;
     240  
     241        if (handle != INVALID_HANDLE_VALUE
     242            && !(ih[fd].flags & DELAYED_DUP2_NEWFD)
     243            && !(ih[fd].flags & KEEP_OPEN_IN_PARENT))
     244          CloseHandle (handle);
     245      }
     246  }
     247  
     248  /* Tests whether a memory region, starting at P and N bytes long, contains only
     249     zeroes.  */
     250  static bool
     251  memiszero (const void *p, size_t n)
     252  {
     253    const char *cp = p;
     254    for (; n > 0; cp++, n--)
     255      if (*cp != 0)
     256        return 0;
     257    return 1;
     258  }
     259  
     260  /* Tests whether *S contains no signals.  */
     261  static bool
     262  sigisempty (const sigset_t *s)
     263  {
     264    return memiszero (s, sizeof (sigset_t));
     265  }
     266  
     267  /* Executes a 'close' action.
     268     Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
     269   */
     270  static int
     271  do_close (struct inheritable_handles *inh_handles, int fd, bool ignore_EBADF)
     272  {
     273    if (!(fd >= 0 && fd < inh_handles->count
     274          && inh_handles->ih[fd].handle != INVALID_HANDLE_VALUE))
     275      {
     276        if (ignore_EBADF)
     277          return 0;
     278        else
     279          {
     280            errno = EBADF;
     281            return -1;
     282          }
     283      }
     284  
     285  # if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
     286    if ((inh_handles->ih[fd].flags & DELAYED_DUP2_NEWFD) != 0)
     287      {
     288        int dup2_oldfd = inh_handles->ih[fd].linked_fd;
     289        /* Check invariants.  */
     290        if (!((inh_handles->ih[dup2_oldfd].flags & DELAYED_DUP2_OLDFD) != 0
     291              && fd == inh_handles->ih[dup2_oldfd].linked_fd
     292              && inh_handles->ih[fd].handle == inh_handles->ih[dup2_oldfd].handle))
     293          abort ();
     294        /* Annihilate a delayed dup2 (..., fd) call.  */
     295        inh_handles->ih[dup2_oldfd].flags &= ~DELAYED_DUP2_OLDFD;
     296      }
     297    else if ((inh_handles->ih[fd].flags & DELAYED_DUP2_OLDFD) != 0)
     298      {
     299        int dup2_newfd = inh_handles->ih[fd].linked_fd;
     300        /* Check invariants.  */
     301        if (!((inh_handles->ih[dup2_newfd].flags & DELAYED_DUP2_NEWFD) != 0
     302              && fd == inh_handles->ih[dup2_newfd].linked_fd
     303              && inh_handles->ih[fd].handle == inh_handles->ih[dup2_newfd].handle))
     304          abort ();
     305        /* Optimize a delayed dup2 (fd, ...) call.  */
     306        inh_handles->ih[dup2_newfd].flags =
     307          (inh_handles->ih[fd].flags & ~DELAYED_DUP2_OLDFD) | KEEP_OPEN_IN_CHILD;
     308      }
     309    else
     310  # endif
     311      {
     312        if (!(inh_handles->ih[fd].flags & KEEP_OPEN_IN_PARENT)
     313            && !CloseHandle (inh_handles->ih[fd].handle))
     314          {
     315            inh_handles->ih[fd].handle = INVALID_HANDLE_VALUE;
     316            errno = EIO;
     317            return -1;
     318          }
     319      }
     320    inh_handles->ih[fd].handle = INVALID_HANDLE_VALUE;
     321  
     322    return 0;
     323  }
     324  
     325  /* Opens an inheritable HANDLE to a file.
     326     Upon failure, returns INVALID_HANDLE_VALUE with errno set.  */
     327  static HANDLE
     328  open_handle (const char *name, int flags, mode_t mode)
     329  {
     330    /* To ease portability.  Like in open.c.  */
     331    if (strcmp (name, "/dev/null") == 0)
     332      name = "NUL";
     333  
     334    /* POSIX <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>
     335       specifies: "More than two leading <slash> characters shall be treated as
     336       a single <slash> character."  */
     337    if (ISSLASH (name[0]) && ISSLASH (name[1]) && ISSLASH (name[2]))
     338      {
     339        name += 2;
     340        while (ISSLASH (name[1]))
     341          name++;
     342      }
     343  
     344    size_t len = strlen (name);
     345    size_t drive_prefix_len = (HAS_DEVICE (name) ? 2 : 0);
     346  
     347    /* Remove trailing slashes (except the very first one, at position
     348       drive_prefix_len), but remember their presence.  */
     349    size_t rlen;
     350    bool check_dir = false;
     351  
     352    rlen = len;
     353    while (rlen > drive_prefix_len && ISSLASH (name[rlen-1]))
     354      {
     355        check_dir = true;
     356        if (rlen == drive_prefix_len + 1)
     357          break;
     358        rlen--;
     359      }
     360  
     361    /* Handle '' and 'C:'.  */
     362    if (!check_dir && rlen == drive_prefix_len)
     363      {
     364        errno = ENOENT;
     365        return INVALID_HANDLE_VALUE;
     366      }
     367  
     368    /* Handle '\\'.  */
     369    if (rlen == 1 && ISSLASH (name[0]) && len >= 2)
     370      {
     371        errno = ENOENT;
     372        return INVALID_HANDLE_VALUE;
     373      }
     374  
     375    const char *rname;
     376    char *malloca_rname;
     377    if (rlen == len)
     378      {
     379        rname = name;
     380        malloca_rname = NULL;
     381      }
     382    else
     383      {
     384        malloca_rname = malloca (rlen + 1);
     385        if (malloca_rname == NULL)
     386          {
     387            errno = ENOMEM;
     388            return INVALID_HANDLE_VALUE;
     389          }
     390        memcpy (malloca_rname, name, rlen);
     391        malloca_rname[rlen] = '\0';
     392        rname = malloca_rname;
     393      }
     394  
     395    /* For the meaning of the flags, see
     396       <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open-wopen>  */
     397    /* Open a handle to the file.
     398       CreateFile
     399       <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea>
     400       <https://docs.microsoft.com/en-us/windows/desktop/FileIO/creating-and-opening-files>  */
     401    SECURITY_ATTRIBUTES sec_attr;
     402    sec_attr.nLength = sizeof (SECURITY_ATTRIBUTES);
     403    sec_attr.lpSecurityDescriptor = NULL;
     404    sec_attr.bInheritHandle = TRUE;
     405    HANDLE handle =
     406      CreateFile (rname,
     407                  ((flags & (O_WRONLY | O_RDWR)) != 0
     408                   ? GENERIC_READ | GENERIC_WRITE
     409                   : GENERIC_READ),
     410                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
     411                  &sec_attr,
     412                  ((flags & O_CREAT) != 0
     413                   ? ((flags & O_EXCL) != 0
     414                      ? CREATE_NEW
     415                      : ((flags & O_TRUNC) != 0 ? CREATE_ALWAYS : OPEN_ALWAYS))
     416                   : ((flags & O_TRUNC) != 0
     417                      ? TRUNCATE_EXISTING
     418                      : OPEN_EXISTING)),
     419                  /* FILE_FLAG_BACKUP_SEMANTICS is useful for opening directories,
     420                     which is out-of-scope here.  */
     421                  /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
     422                     in case as different) makes sense only when applied to *all*
     423                     filesystem operations.  */
     424                  /* FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS */
     425                  FILE_ATTRIBUTE_NORMAL
     426                  | ((flags & O_TEMPORARY) != 0 ? FILE_FLAG_DELETE_ON_CLOSE : 0)
     427                  | ((flags & O_SEQUENTIAL ) != 0 ? FILE_FLAG_SEQUENTIAL_SCAN : 0)
     428                  | ((flags & O_RANDOM) != 0 ? FILE_FLAG_RANDOM_ACCESS : 0),
     429                  NULL);
     430    if (handle == INVALID_HANDLE_VALUE)
     431      switch (GetLastError ())
     432        {
     433        /* Some of these errors probably cannot happen with the specific flags
     434           that we pass to CreateFile.  But who knows...  */
     435        case ERROR_FILE_NOT_FOUND: /* The last component of rname does not exist.  */
     436        case ERROR_PATH_NOT_FOUND: /* Some directory component in rname does not exist.  */
     437        case ERROR_BAD_PATHNAME:   /* rname is such as '\\server'.  */
     438        case ERROR_BAD_NETPATH:    /* rname is such as '\\nonexistentserver\share'.  */
     439        case ERROR_BAD_NET_NAME:   /* rname is such as '\\server\nonexistentshare'.  */
     440        case ERROR_INVALID_NAME:   /* rname contains wildcards, misplaced colon, etc.  */
     441        case ERROR_DIRECTORY:
     442          errno = ENOENT;
     443          break;
     444  
     445        case ERROR_ACCESS_DENIED:  /* rname is such as 'C:\System Volume Information\foo'.  */
     446        case ERROR_SHARING_VIOLATION: /* rname is such as 'C:\pagefile.sys'.  */
     447                                      /* XXX map to EACCES or EPERM? */
     448          errno = EACCES;
     449          break;
     450  
     451        case ERROR_OUTOFMEMORY:
     452          errno = ENOMEM;
     453          break;
     454  
     455        case ERROR_WRITE_PROTECT:
     456          errno = EROFS;
     457          break;
     458  
     459        case ERROR_WRITE_FAULT:
     460        case ERROR_READ_FAULT:
     461        case ERROR_GEN_FAILURE:
     462          errno = EIO;
     463          break;
     464  
     465        case ERROR_BUFFER_OVERFLOW:
     466        case ERROR_FILENAME_EXCED_RANGE:
     467          errno = ENAMETOOLONG;
     468          break;
     469  
     470        case ERROR_DELETE_PENDING: /* XXX map to EACCES or EPERM? */
     471          errno = EPERM;
     472          break;
     473  
     474        default:
     475          errno = EINVAL;
     476          break;
     477        }
     478  
     479    if (malloca_rname != NULL)
     480      {
     481        int saved_errno = errno;
     482        freea (malloca_rname);
     483        errno = saved_errno;
     484      }
     485    return handle;
     486  }
     487  
     488  /* Executes an 'open' action.
     489     Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
     490   */
     491  static int
     492  do_open (struct inheritable_handles *inh_handles, int newfd,
     493           const char *filename, const char *directory,
     494           int flags, mode_t mode)
     495  {
     496    if (!(newfd >= 0 && newfd < _getmaxstdio ()))
     497      {
     498        errno = EBADF;
     499        return -1;
     500      }
     501    if (grow_inheritable_handles (inh_handles, newfd) < 0)
     502      return -1;
     503    if (do_close (inh_handles, newfd, true) < 0)
     504      return -1;
     505    if (filename == NULL)
     506      {
     507        errno = EINVAL;
     508        return -1;
     509      }
     510    char *filename_to_free = NULL;
     511    if (directory != NULL && IS_RELATIVE_FILE_NAME (filename))
     512      {
     513        char *real_filename = concatenated_filename (directory, filename, NULL);
     514        if (real_filename == NULL)
     515          {
     516            errno = ENOMEM;
     517            return -1;
     518          }
     519        filename = real_filename;
     520        filename_to_free = real_filename;
     521      }
     522    HANDLE handle = open_handle (filename, flags, mode);
     523    if (handle == INVALID_HANDLE_VALUE)
     524      {
     525        free (filename_to_free);
     526        return -1;
     527      }
     528    free (filename_to_free);
     529    inh_handles->ih[newfd].handle = handle;
     530    inh_handles->ih[newfd].flags =
     531      ((flags & O_APPEND) != 0 ? 32 : 0) | KEEP_OPEN_IN_CHILD;
     532    return 0;
     533  }
     534  
     535  /* Executes a 'dup2' action.
     536     Returns 0 upon success.  In case of failure, -1 is returned, with errno set.
     537   */
     538  static int
     539  do_dup2 (struct inheritable_handles *inh_handles, int oldfd, int newfd,
     540           HANDLE curr_process)
     541  {
     542    if (!(oldfd >= 0 && oldfd < inh_handles->count
     543          && inh_handles->ih[oldfd].handle != INVALID_HANDLE_VALUE))
     544      {
     545        errno = EBADF;
     546        return -1;
     547      }
     548    if (!(newfd >= 0 && newfd < _getmaxstdio ()))
     549      {
     550        errno = EBADF;
     551        return -1;
     552      }
     553    if (newfd != oldfd)
     554      {
     555        if (grow_inheritable_handles (inh_handles, newfd) < 0)
     556          return -1;
     557        if (do_close (inh_handles, newfd, true) < 0)
     558          return -1;
     559        /* We may need to duplicate the handle, so that a forthcoming do_close
     560           action on oldfd has no effect on newfd.  */
     561  # if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
     562        /* But try to not do it now; delay it if possible.  In many cases, the
     563           DuplicateHandle call can be optimized away.  */
     564        if ((inh_handles->ih[oldfd].flags & DELAYED_DUP2_NEWFD) != 0)
     565          if (do_delayed_dup2 (oldfd, inh_handles, curr_process) < 0)
     566            return -1;
     567        if ((inh_handles->ih[oldfd].flags & DELAYED_DUP2_OLDFD) != 0)
     568          {
     569            /* Check invariants.  */
     570            int dup2_newfd = inh_handles->ih[oldfd].linked_fd;
     571            if (!((inh_handles->ih[dup2_newfd].flags & DELAYED_DUP2_NEWFD) != 0
     572                  && oldfd == inh_handles->ih[dup2_newfd].linked_fd
     573                  && inh_handles->ih[oldfd].handle == inh_handles->ih[dup2_newfd].handle))
     574              abort ();
     575            /* We can't delay two or more dup2 calls from the same oldfd.  */
     576            if (!DuplicateHandle (curr_process, inh_handles->ih[oldfd].handle,
     577                                  curr_process, &inh_handles->ih[newfd].handle,
     578                                  0, TRUE, DUPLICATE_SAME_ACCESS))
     579              {
     580                errno = EBADF; /* arbitrary */
     581                return -1;
     582              }
     583            inh_handles->ih[newfd].flags =
     584              (unsigned char) inh_handles->ih[oldfd].flags | KEEP_OPEN_IN_CHILD;
     585          }
     586        else
     587          {
     588            /* Delay the dup2 (oldfd, newfd) action.  */
     589            inh_handles->ih[oldfd].flags |= DELAYED_DUP2_OLDFD;
     590            inh_handles->ih[oldfd].linked_fd = newfd;
     591            inh_handles->ih[newfd].handle = inh_handles->ih[oldfd].handle;
     592            inh_handles->ih[newfd].flags = DELAYED_DUP2_NEWFD;
     593            inh_handles->ih[newfd].linked_fd = oldfd;
     594          }
     595  # else
     596        if (!DuplicateHandle (curr_process, inh_handles->ih[oldfd].handle,
     597                              curr_process, &inh_handles->ih[newfd].handle,
     598                              0, TRUE, DUPLICATE_SAME_ACCESS))
     599          {
     600            errno = EBADF; /* arbitrary */
     601            return -1;
     602          }
     603        inh_handles->ih[newfd].flags =
     604          (unsigned char) inh_handles->ih[oldfd].flags | KEEP_OPEN_IN_CHILD;
     605  # endif
     606      }
     607    return 0;
     608  }
     609  
     610  int
     611  __spawni (pid_t *pid, const char *prog_filename,
     612            const posix_spawn_file_actions_t *file_actions,
     613            const posix_spawnattr_t *attrp, const char *const prog_argv[],
     614            const char *const envp[], int use_path)
     615  {
     616    /* Validate the arguments.  */
     617    if (prog_filename == NULL
     618        || (attrp != NULL
     619            && ((attrp->_flags & ~POSIX_SPAWN_SETPGROUP) != 0
     620                || attrp->_pgrp != 0
     621                || ! sigisempty (&attrp->_sd)
     622                || ! sigisempty (&attrp->_ss)
     623                || attrp->_sp.sched_priority != 0
     624                || attrp->_policy != 0)))
     625      return EINVAL;
     626  
     627    /* Process group handling:
     628       Native Windows does not have the concept of process group, but it has the
     629       concept of a console attached to a process.
     630       So, we interpret the three cases as follows:
     631         - Flag POSIX_SPAWN_SETPGROUP not set: Means, the child process is in the
     632           same process group as the parent process.  We interpret this as a
     633           request to reuse the same console.
     634         - Flag POSIX_SPAWN_SETPGROUP set with attrp->_pgrp == 0: Means the child
     635           process starts a process group of its own.  See
     636           <https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html>
     637           <https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgrp.html>
     638           We interpret this as a request to detach from the current console.
     639         - Flag POSIX_SPAWN_SETPGROUP set with attrp->_pgrp != 0: Means the child
     640           process joins another, existing process group.  See
     641           <https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html>
     642           <https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html>
     643           We don't support this case; it produces error EINVAL above.  */
     644    /* <https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags>  */
     645    DWORD process_creation_flags =
     646      (attrp != NULL && (attrp->_flags & POSIX_SPAWN_SETPGROUP) != 0 ? DETACHED_PROCESS : 0);
     647  
     648    char *argv_mem_to_free;
     649    const char **argv = prepare_spawn (prog_argv, &argv_mem_to_free);
     650    if (argv == NULL)
     651      return errno; /* errno is set here */
     652    argv++;
     653  
     654    /* Compose the command.  */
     655    char *command = compose_command (argv);
     656    if (command == NULL)
     657      {
     658        free (argv_mem_to_free);
     659        return ENOMEM;
     660      }
     661  
     662    /* Copy *ENVP into a contiguous block of memory.  */
     663    char *envblock;
     664    if (envp == NULL)
     665      envblock = NULL;
     666    else
     667      {
     668        envblock = compose_envblock (envp);
     669        if (envblock == NULL)
     670          {
     671            free (command);
     672            free (argv_mem_to_free);
     673            return ENOMEM;
     674          }
     675      }
     676  
     677    /* Set up the array of handles to inherit.
     678       Duplicate each handle, so that a spawn_do_close action (below) has no
     679       effect on the file descriptors of the current process.  Alternatively,
     680       we could store, for each handle, a bit that tells whether it is shared
     681       with the current process.  But this is simpler.  */
     682    struct inheritable_handles inh_handles;
     683    if (init_inheritable_handles (&inh_handles, true) < 0)
     684      goto failed_1;
     685  
     686    /* Directory in which to execute the new process.  */
     687    const char *directory = NULL;
     688  
     689    /* Execute the file_actions, modifying the inh_handles instead of the
     690       file descriptors of the current process.  */
     691    if (file_actions != NULL)
     692      {
     693        HANDLE curr_process = GetCurrentProcess ();
     694        int cnt;
     695  
     696        for (cnt = 0; cnt < file_actions->_used; ++cnt)
     697          {
     698            struct __spawn_action *action = &file_actions->_actions[cnt];
     699  
     700            switch (action->tag)
     701              {
     702              case spawn_do_close:
     703                {
     704                  int fd = action->action.close_action.fd;
     705                  if (do_close (&inh_handles, fd, false) < 0)
     706                    goto failed_2;
     707                }
     708                break;
     709  
     710              case spawn_do_open:
     711                {
     712                  int newfd = action->action.open_action.fd;
     713                  const char *filename = action->action.open_action.path;
     714                  int flags = action->action.open_action.oflag;
     715                  mode_t mode = action->action.open_action.mode;
     716                  if (do_open (&inh_handles, newfd, filename, directory,
     717                               flags, mode)
     718                      < 0)
     719                    goto failed_2;
     720                }
     721                break;
     722  
     723              case spawn_do_dup2:
     724                {
     725                  int oldfd = action->action.dup2_action.fd;
     726                  int newfd = action->action.dup2_action.newfd;
     727                  if (do_dup2 (&inh_handles, oldfd, newfd, curr_process) < 0)
     728                    goto failed_2;
     729                }
     730                break;
     731  
     732              case spawn_do_chdir:
     733                {
     734                  char *newdir = action->action.chdir_action.path;
     735                  if (directory != NULL && IS_RELATIVE_FILE_NAME (newdir))
     736                    {
     737                      newdir = concatenated_filename (directory, newdir, NULL);
     738                      if (newdir == NULL)
     739                        {
     740                          errno = ENOMEM;
     741                          goto failed_2;
     742                        }
     743                    }
     744                  directory = newdir;
     745                }
     746                break;
     747  
     748              case spawn_do_fchdir:
     749                /* Not supported in this implementation.  */
     750                errno = EINVAL;
     751                goto failed_2;
     752              }
     753          }
     754  
     755  # if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
     756        /* Do the remaining delayed dup2 invocations.  */
     757        if (do_remaining_delayed_dup2 (&inh_handles, curr_process) < 0)
     758          goto failed_2;
     759  # endif
     760      }
     761  
     762    /* Close the handles in inh_handles that are not meant to be preserved in the
     763       child process, and reduce inh_handles.count to the minimum needed.  */
     764    shrink_inheritable_handles (&inh_handles);
     765  
     766    /* CreateProcess
     767       <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa>  */
     768    /* STARTUPINFO
     769       <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa>  */
     770    STARTUPINFO sinfo;
     771    sinfo.cb = sizeof (STARTUPINFO);
     772    sinfo.lpReserved = NULL;
     773    sinfo.lpDesktop = NULL;
     774    sinfo.lpTitle = NULL;
     775    if (compose_handles_block (&inh_handles, &sinfo) < 0)
     776      goto failed_2;
     777  
     778    /* Perform the PATH search now, considering the final DIRECTORY.  */
     779    char *resolved_prog_filename_to_free = NULL;
     780    {
     781      const char *resolved_prog_filename =
     782        find_in_given_path (prog_filename, use_path ? getenv ("PATH") : "",
     783                            directory, false);
     784      if (resolved_prog_filename == NULL)
     785        goto failed_3;
     786      if (resolved_prog_filename != prog_filename)
     787        resolved_prog_filename_to_free = (char *) resolved_prog_filename;
     788      prog_filename = resolved_prog_filename;
     789    }
     790  
     791    PROCESS_INFORMATION pinfo;
     792    if (!CreateProcess (prog_filename, command, NULL, NULL, TRUE,
     793                        process_creation_flags, envblock, directory, &sinfo,
     794                        &pinfo))
     795      {
     796        DWORD error = GetLastError ();
     797  
     798        free (resolved_prog_filename_to_free);
     799        free (sinfo.lpReserved2);
     800        close_inheritable_handles (&inh_handles);
     801        free_inheritable_handles (&inh_handles);
     802        free (envblock);
     803        free (command);
     804        free (argv_mem_to_free);
     805  
     806        return convert_CreateProcess_error (error);
     807      }
     808  
     809    if (pinfo.hThread)
     810      CloseHandle (pinfo.hThread);
     811  
     812    free (resolved_prog_filename_to_free);
     813    free (sinfo.lpReserved2);
     814    close_inheritable_handles (&inh_handles);
     815    free_inheritable_handles (&inh_handles);
     816    free (envblock);
     817    free (command);
     818    free (argv_mem_to_free);
     819  
     820    if (pid != NULL)
     821      *pid = (intptr_t) pinfo.hProcess;
     822    return 0;
     823  
     824   failed_3:
     825    {
     826      int saved_errno = errno;
     827      free (sinfo.lpReserved2);
     828      close_inheritable_handles (&inh_handles);
     829      free_inheritable_handles (&inh_handles);
     830      free (envblock);
     831      free (command);
     832      free (argv_mem_to_free);
     833      return saved_errno;
     834    }
     835  
     836   failed_2:
     837    {
     838      int saved_errno = errno;
     839      close_inheritable_handles (&inh_handles);
     840      free_inheritable_handles (&inh_handles);
     841      free (envblock);
     842      free (command);
     843      free (argv_mem_to_free);
     844      return saved_errno;
     845    }
     846  
     847   failed_1:
     848    free (envblock);
     849    free (command);
     850    free (argv_mem_to_free);
     851    return errno;
     852  }
     853  
     854  #else
     855  
     856  
     857  /* The warning "warning: 'vfork' is deprecated: Use posix_spawn or fork" seen
     858     on macOS 12 is pointless, as we use vfork only when it is safe or when the
     859     user has explicitly requested it.  Silence this warning.  */
     860  #if _GL_GNUC_PREREQ (4, 2)
     861  # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
     862  #endif
     863  
     864  /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
     865     Before running the process perform the actions described in FILE-ACTIONS. */
     866  int
     867  __spawni (pid_t *pid, const char *file,
     868            const posix_spawn_file_actions_t *file_actions,
     869            const posix_spawnattr_t *attrp, const char *const argv[],
     870            const char *const envp[], int use_path)
     871  {
     872    pid_t new_pid;
     873    char *path, *p, *name;
     874    size_t len;
     875    size_t pathlen;
     876  
     877    /* Do this once.  */
     878    short int flags = attrp == NULL ? 0 : attrp->_flags;
     879  
     880    /* Avoid gcc warning
     881         "variable 'flags' might be clobbered by 'longjmp' or 'vfork'"  */
     882    (void) &flags;
     883  
     884    /* Generate the new process.  */
     885  #if HAVE_VFORK
     886    if ((flags & POSIX_SPAWN_USEVFORK) != 0
     887        /* If no major work is done, allow using vfork.  Note that we
     888           might perform the path searching.  But this would be done by
     889           a call to execvp(), too, and such a call must be OK according
     890           to POSIX.  */
     891        || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF
     892                      | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER
     893                      | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0
     894            && file_actions == NULL))
     895      new_pid = vfork ();
     896    else
     897  #endif
     898      new_pid = fork ();
     899  
     900    if (new_pid != 0)
     901      {
     902        if (new_pid < 0)
     903          return errno;
     904  
     905        /* The call was successful.  Store the PID if necessary.  */
     906        if (pid != NULL)
     907          *pid = new_pid;
     908  
     909        return 0;
     910      }
     911  
     912    /* Set signal mask.  */
     913    if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
     914        && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0)
     915      _exit (SPAWN_ERROR);
     916  
     917    /* Set signal default action.  */
     918    if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
     919      {
     920        /* We have to iterate over all signals.  This could possibly be
     921           done better but it requires system specific solutions since
     922           the sigset_t data type can be very different on different
     923           architectures.  */
     924        int sig;
     925        struct sigaction sa;
     926  
     927        memset (&sa, '\0', sizeof (sa));
     928        sa.sa_handler = SIG_DFL;
     929  
     930        for (sig = 1; sig <= NSIG; ++sig)
     931          if (sigismember (&attrp->_sd, sig) != 0
     932              && sigaction (sig, &sa, NULL) != 0)
     933            _exit (SPAWN_ERROR);
     934  
     935      }
     936  
     937  #if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER)
     938    /* Set the scheduling algorithm and parameters.  */
     939    if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
     940        == POSIX_SPAWN_SETSCHEDPARAM)
     941      {
     942        if (sched_setparam (0, &attrp->_sp) == -1)
     943          _exit (SPAWN_ERROR);
     944      }
     945    else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
     946      {
     947        if (sched_setscheduler (0, attrp->_policy,
     948                                (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
     949                                ? &attrp->_sp : NULL) == -1)
     950          _exit (SPAWN_ERROR);
     951      }
     952  #endif
     953  
     954    /* Set the process group ID.  */
     955    if ((flags & POSIX_SPAWN_SETPGROUP) != 0
     956        && setpgid (0, attrp->_pgrp) != 0)
     957      _exit (SPAWN_ERROR);
     958  
     959    /* Set the effective user and group IDs.  */
     960    if ((flags & POSIX_SPAWN_RESETIDS) != 0
     961        && (local_seteuid (getuid ()) != 0
     962            || local_setegid (getgid ()) != 0))
     963      _exit (SPAWN_ERROR);
     964  
     965    /* Execute the file actions.  */
     966    if (file_actions != NULL)
     967      {
     968        int cnt;
     969  
     970        for (cnt = 0; cnt < file_actions->_used; ++cnt)
     971          {
     972            struct __spawn_action *action = &file_actions->_actions[cnt];
     973  
     974            switch (action->tag)
     975              {
     976              case spawn_do_close:
     977                if (close_not_cancel (action->action.close_action.fd) != 0)
     978                  /* Signal the error.  */
     979                  _exit (SPAWN_ERROR);
     980                break;
     981  
     982              case spawn_do_open:
     983                {
     984                  int new_fd = open_not_cancel (action->action.open_action.path,
     985                                                action->action.open_action.oflag
     986                                                | O_LARGEFILE,
     987                                                action->action.open_action.mode);
     988  
     989                  if (new_fd == -1)
     990                    /* The 'open' call failed.  */
     991                    _exit (SPAWN_ERROR);
     992  
     993                  /* Make sure the desired file descriptor is used.  */
     994                  if (new_fd != action->action.open_action.fd)
     995                    {
     996                      if (dup2 (new_fd, action->action.open_action.fd)
     997                          != action->action.open_action.fd)
     998                        /* The 'dup2' call failed.  */
     999                        _exit (SPAWN_ERROR);
    1000  
    1001                      if (close_not_cancel (new_fd) != 0)
    1002                        /* The 'close' call failed.  */
    1003                        _exit (SPAWN_ERROR);
    1004                    }
    1005                }
    1006                break;
    1007  
    1008              case spawn_do_dup2:
    1009                if (dup2 (action->action.dup2_action.fd,
    1010                          action->action.dup2_action.newfd)
    1011                    != action->action.dup2_action.newfd)
    1012                  /* The 'dup2' call failed.  */
    1013                  _exit (SPAWN_ERROR);
    1014                break;
    1015  
    1016              case spawn_do_chdir:
    1017                if (chdir (action->action.chdir_action.path) < 0)
    1018                  /* The 'chdir' call failed.  */
    1019                  _exit (SPAWN_ERROR);
    1020                break;
    1021  
    1022              case spawn_do_fchdir:
    1023                if (fchdir (action->action.fchdir_action.fd) < 0)
    1024                  /* The 'fchdir' call failed.  */
    1025                  _exit (SPAWN_ERROR);
    1026                break;
    1027              }
    1028          }
    1029      }
    1030  
    1031    if (! use_path || strchr (file, '/') != NULL)
    1032      {
    1033        /* The FILE parameter is actually a path.  */
    1034        execve (file, (char * const *) argv, (char * const *) envp);
    1035  
    1036        /* Oh, oh.  'execve' returns.  This is bad.  */
    1037        _exit (SPAWN_ERROR);
    1038      }
    1039  
    1040    /* We have to search for FILE on the path.  */
    1041    path = getenv ("PATH");
    1042    if (path == NULL)
    1043      {
    1044  #if HAVE_CONFSTR
    1045        /* There is no 'PATH' in the environment.
    1046           The default search path is the current directory
    1047           followed by the path 'confstr' returns for '_CS_PATH'.  */
    1048        len = confstr (_CS_PATH, (char *) NULL, 0);
    1049        path = (char *) alloca (1 + len);
    1050        path[0] = ':';
    1051        (void) confstr (_CS_PATH, path + 1, len);
    1052  #else
    1053        /* Pretend that the PATH contains only the current directory.  */
    1054        path = "";
    1055  #endif
    1056      }
    1057  
    1058    len = strlen (file) + 1;
    1059    pathlen = strlen (path);
    1060    name = alloca (pathlen + len + 1);
    1061    /* Copy the file name at the top.  */
    1062    name = (char *) memcpy (name + pathlen + 1, file, len);
    1063    /* And add the slash.  */
    1064    *--name = '/';
    1065  
    1066    p = path;
    1067    do
    1068      {
    1069        char *startp;
    1070  
    1071        path = p;
    1072        p = strchrnul (path, ':');
    1073  
    1074        if (p == path)
    1075          /* Two adjacent colons, or a colon at the beginning or the end
    1076             of 'PATH' means to search the current directory.  */
    1077          startp = name + 1;
    1078        else
    1079          startp = (char *) memcpy (name - (p - path), path, p - path);
    1080  
    1081        /* Try to execute this name.  If it works, execv will not return.  */
    1082        execve (startp, (char * const *) argv, (char * const *) envp);
    1083  
    1084        switch (errno)
    1085          {
    1086          case EACCES:
    1087          case ENOENT:
    1088          case ESTALE:
    1089          case ENOTDIR:
    1090            /* Those errors indicate the file is missing or not executable
    1091               by us, in which case we want to just try the next path
    1092               directory.  */
    1093            break;
    1094  
    1095          default:
    1096            /* Some other error means we found an executable file, but
    1097               something went wrong executing it; return the error to our
    1098               caller.  */
    1099            _exit (SPAWN_ERROR);
    1100          }
    1101      }
    1102    while (*p++ != '\0');
    1103  
    1104    /* Return with an error.  */
    1105    _exit (SPAWN_ERROR);
    1106  }
    1107  
    1108  #endif