summaryrefslogtreecommitdiff
path: root/lib/spawni.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spawni.c')
-rw-r--r--lib/spawni.c303
1 files changed, 223 insertions, 80 deletions
diff --git a/lib/spawni.c b/lib/spawni.c
index 9bca2002b..cc9511fdd 100644
--- a/lib/spawni.c
+++ b/lib/spawni.c
@@ -1,5 +1,5 @@
/* Guts of POSIX spawn interface. Generic POSIX.1 version.
- Copyright (C) 2000-2006, 2008-2022 Free Software Foundation, Inc.
+ Copyright (C) 2000-2006, 2008-2023 Free Software Foundation, Inc.
This file is part of the GNU C Library.
This file is free software: you can redistribute it and/or modify
@@ -89,11 +89,16 @@
#if defined _WIN32 && ! defined __CYGWIN__
/* Native Windows API. */
+/* Define to 1 to enable DuplicateHandle optimization.
+ Define to 0 to disable this optimization. */
+# ifndef SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
+# define SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE 1
+# endif
+
/* Get declarations of the native Windows API functions. */
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
-# include <stdbool.h>
# include <stdio.h>
# include "filename.h"
@@ -121,60 +126,121 @@ grow_inheritable_handles (struct inheritable_handles *inh_handles, int newfd)
size_t new_allocated = 2 * inh_handles->allocated + 1;
if (new_allocated <= newfd)
new_allocated = newfd + 1;
- HANDLE *new_handles_array =
- (HANDLE *)
- realloc (inh_handles->handles, new_allocated * sizeof (HANDLE));
- if (new_handles_array == NULL)
+ struct IHANDLE *new_ih =
+ (struct IHANDLE *)
+ realloc (inh_handles->ih, new_allocated * sizeof (struct IHANDLE));
+ if (new_ih == NULL)
{
errno = ENOMEM;
return -1;
}
- unsigned char *new_flags_array =
- (unsigned char *)
- realloc (inh_handles->flags, new_allocated * sizeof (unsigned char));
- if (new_flags_array == NULL)
- {
- free (new_handles_array);
- errno = ENOMEM;
- return -1;
- }
inh_handles->allocated = new_allocated;
- inh_handles->handles = new_handles_array;
- inh_handles->flags = new_flags_array;
+ inh_handles->ih = new_ih;
}
- HANDLE *handles = inh_handles->handles;
+ struct IHANDLE *ih = inh_handles->ih;
for (; inh_handles->count <= newfd; inh_handles->count++)
- handles[inh_handles->count] = INVALID_HANDLE_VALUE;
+ ih[inh_handles->count].handle = INVALID_HANDLE_VALUE;
+
+ return 0;
+}
+
+# if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
+
+/* Assuming inh_handles->ih[newfd].handle != INVALID_HANDLE_VALUE
+ and (inh_handles->ih[newfd].flags & DELAYED_DUP2_NEWFD) != 0,
+ actually performs the delayed dup2 (oldfd, newfd).
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+static int
+do_delayed_dup2 (int newfd, struct inheritable_handles *inh_handles,
+ HANDLE curr_process)
+{
+ int oldfd = inh_handles->ih[newfd].linked_fd;
+ /* Check invariants. */
+ if (!((inh_handles->ih[oldfd].flags & DELAYED_DUP2_OLDFD) != 0
+ && newfd == inh_handles->ih[oldfd].linked_fd
+ && inh_handles->ih[newfd].handle == inh_handles->ih[oldfd].handle))
+ abort ();
+ /* Duplicate the handle now. */
+ if (!DuplicateHandle (curr_process, inh_handles->ih[oldfd].handle,
+ curr_process, &inh_handles->ih[newfd].handle,
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ errno = EBADF; /* arbitrary */
+ return -1;
+ }
+ inh_handles->ih[oldfd].flags &= ~DELAYED_DUP2_OLDFD;
+ inh_handles->ih[newfd].flags =
+ (unsigned char) inh_handles->ih[oldfd].flags | KEEP_OPEN_IN_CHILD;
+ return 0;
+}
+
+/* Performs the remaining delayed dup2 (oldfd, newfd).
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+static int
+do_remaining_delayed_dup2 (struct inheritable_handles *inh_handles,
+ HANDLE curr_process)
+{
+ size_t handles_count = inh_handles->count;
+ int newfd;
+ for (newfd = 0; newfd < handles_count; newfd++)
+ if (inh_handles->ih[newfd].handle != INVALID_HANDLE_VALUE
+ && (inh_handles->ih[newfd].flags & DELAYED_DUP2_NEWFD) != 0)
+ if (do_delayed_dup2 (newfd, inh_handles, curr_process) < 0)
+ return -1;
return 0;
}
-/* Reduces inh_handles->count to the minimum needed. */
+# endif
+
+/* Closes the handles in inh_handles that are not meant to be preserved in the
+ child process, and reduces inh_handles->count to the minimum needed. */
static void
shrink_inheritable_handles (struct inheritable_handles *inh_handles)
{
- HANDLE *handles = inh_handles->handles;
+ struct IHANDLE *ih = inh_handles->ih;
+ size_t handles_count = inh_handles->count;
+ unsigned int fd;
+
+ for (fd = 0; fd < handles_count; fd++)
+ {
+ HANDLE handle = ih[fd].handle;
- while (inh_handles->count > 3
- && handles[inh_handles->count - 1] == INVALID_HANDLE_VALUE)
- inh_handles->count--;
+ if (handle != INVALID_HANDLE_VALUE
+ && (ih[fd].flags & KEEP_OPEN_IN_CHILD) == 0)
+ {
+ if (!(ih[fd].flags & KEEP_OPEN_IN_PARENT))
+ CloseHandle (handle);
+ ih[fd].handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ while (handles_count > 3
+ && ih[handles_count - 1].handle == INVALID_HANDLE_VALUE)
+ handles_count--;
+
+ inh_handles->count = handles_count;
}
/* Closes all handles in inh_handles. */
static void
close_inheritable_handles (struct inheritable_handles *inh_handles)
{
- HANDLE *handles = inh_handles->handles;
+ struct IHANDLE *ih = inh_handles->ih;
size_t handles_count = inh_handles->count;
unsigned int fd;
for (fd = 0; fd < handles_count; fd++)
{
- HANDLE handle = handles[fd];
+ HANDLE handle = ih[fd].handle;
- if (handle != INVALID_HANDLE_VALUE)
+ if (handle != INVALID_HANDLE_VALUE
+ && !(ih[fd].flags & DELAYED_DUP2_NEWFD)
+ && !(ih[fd].flags & KEEP_OPEN_IN_PARENT))
CloseHandle (handle);
}
}
@@ -198,7 +264,65 @@ sigisempty (const sigset_t *s)
return memiszero (s, sizeof (sigset_t));
}
-/* Opens a HANDLE to a file.
+/* Executes a 'close' action.
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+static int
+do_close (struct inheritable_handles *inh_handles, int fd, bool ignore_EBADF)
+{
+ if (!(fd >= 0 && fd < inh_handles->count
+ && inh_handles->ih[fd].handle != INVALID_HANDLE_VALUE))
+ {
+ if (ignore_EBADF)
+ return 0;
+ else
+ {
+ errno = EBADF;
+ return -1;
+ }
+ }
+
+# if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
+ if ((inh_handles->ih[fd].flags & DELAYED_DUP2_NEWFD) != 0)
+ {
+ int dup2_oldfd = inh_handles->ih[fd].linked_fd;
+ /* Check invariants. */
+ if (!((inh_handles->ih[dup2_oldfd].flags & DELAYED_DUP2_OLDFD) != 0
+ && fd == inh_handles->ih[dup2_oldfd].linked_fd
+ && inh_handles->ih[fd].handle == inh_handles->ih[dup2_oldfd].handle))
+ abort ();
+ /* Annihilate a delayed dup2 (..., fd) call. */
+ inh_handles->ih[dup2_oldfd].flags &= ~DELAYED_DUP2_OLDFD;
+ }
+ else if ((inh_handles->ih[fd].flags & DELAYED_DUP2_OLDFD) != 0)
+ {
+ int dup2_newfd = inh_handles->ih[fd].linked_fd;
+ /* Check invariants. */
+ if (!((inh_handles->ih[dup2_newfd].flags & DELAYED_DUP2_NEWFD) != 0
+ && fd == inh_handles->ih[dup2_newfd].linked_fd
+ && inh_handles->ih[fd].handle == inh_handles->ih[dup2_newfd].handle))
+ abort ();
+ /* Optimize a delayed dup2 (fd, ...) call. */
+ inh_handles->ih[dup2_newfd].flags =
+ (inh_handles->ih[fd].flags & ~DELAYED_DUP2_OLDFD) | KEEP_OPEN_IN_CHILD;
+ }
+ else
+# endif
+ {
+ if (!(inh_handles->ih[fd].flags & KEEP_OPEN_IN_PARENT)
+ && !CloseHandle (inh_handles->ih[fd].handle))
+ {
+ inh_handles->ih[fd].handle = INVALID_HANDLE_VALUE;
+ errno = EIO;
+ return -1;
+ }
+ }
+ inh_handles->ih[fd].handle = INVALID_HANDLE_VALUE;
+
+ return 0;
+}
+
+/* Opens an inheritable HANDLE to a file.
Upon failure, returns INVALID_HANDLE_VALUE with errno set. */
static HANDLE
open_handle (const char *name, int flags, mode_t mode)
@@ -274,13 +398,17 @@ open_handle (const char *name, int flags, mode_t mode)
CreateFile
<https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea>
<https://docs.microsoft.com/en-us/windows/desktop/FileIO/creating-and-opening-files> */
+ SECURITY_ATTRIBUTES sec_attr;
+ sec_attr.nLength = sizeof (SECURITY_ATTRIBUTES);
+ sec_attr.lpSecurityDescriptor = NULL;
+ sec_attr.bInheritHandle = TRUE;
HANDLE handle =
CreateFile (rname,
((flags & (O_WRONLY | O_RDWR)) != 0
? GENERIC_READ | GENERIC_WRITE
: GENERIC_READ),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
+ &sec_attr,
((flags & O_CREAT) != 0
? ((flags & O_EXCL) != 0
? CREATE_NEW
@@ -363,7 +491,7 @@ open_handle (const char *name, int flags, mode_t mode)
static int
do_open (struct inheritable_handles *inh_handles, int newfd,
const char *filename, const char *directory,
- int flags, mode_t mode, HANDLE curr_process)
+ int flags, mode_t mode)
{
if (!(newfd >= 0 && newfd < _getmaxstdio ()))
{
@@ -372,12 +500,8 @@ do_open (struct inheritable_handles *inh_handles, int newfd,
}
if (grow_inheritable_handles (inh_handles, newfd) < 0)
return -1;
- if (inh_handles->handles[newfd] != INVALID_HANDLE_VALUE
- && !CloseHandle (inh_handles->handles[newfd]))
- {
- errno = EIO;
- return -1;
- }
+ if (do_close (inh_handles, newfd, true) < 0)
+ return -1;
if (filename == NULL)
{
errno = EINVAL;
@@ -402,16 +526,9 @@ do_open (struct inheritable_handles *inh_handles, int newfd,
return -1;
}
free (filename_to_free);
- /* Duplicate the handle, so that it becomes inheritable. */
- if (!DuplicateHandle (curr_process, handle,
- curr_process, &inh_handles->handles[newfd],
- 0, TRUE,
- DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
- {
- errno = EBADF; /* arbitrary */
- return -1;
- }
- inh_handles->flags[newfd] = ((flags & O_APPEND) != 0 ? 32 : 0);
+ inh_handles->ih[newfd].handle = handle;
+ inh_handles->ih[newfd].flags =
+ ((flags & O_APPEND) != 0 ? 32 : 0) | KEEP_OPEN_IN_CHILD;
return 0;
}
@@ -423,7 +540,7 @@ do_dup2 (struct inheritable_handles *inh_handles, int oldfd, int newfd,
HANDLE curr_process)
{
if (!(oldfd >= 0 && oldfd < inh_handles->count
- && inh_handles->handles[oldfd] != INVALID_HANDLE_VALUE))
+ && inh_handles->ih[oldfd].handle != INVALID_HANDLE_VALUE))
{
errno = EBADF;
return -1;
@@ -437,44 +554,56 @@ do_dup2 (struct inheritable_handles *inh_handles, int oldfd, int newfd,
{
if (grow_inheritable_handles (inh_handles, newfd) < 0)
return -1;
- if (inh_handles->handles[newfd] != INVALID_HANDLE_VALUE
- && !CloseHandle (inh_handles->handles[newfd]))
- {
- errno = EIO;
+ if (do_close (inh_handles, newfd, true) < 0)
+ return -1;
+ /* We may need to duplicate the handle, so that a forthcoming do_close
+ action on oldfd has no effect on newfd. */
+# if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
+ /* But try to not do it now; delay it if possible. In many cases, the
+ DuplicateHandle call can be optimized away. */
+ if ((inh_handles->ih[oldfd].flags & DELAYED_DUP2_NEWFD) != 0)
+ if (do_delayed_dup2 (oldfd, inh_handles, curr_process) < 0)
return -1;
+ if ((inh_handles->ih[oldfd].flags & DELAYED_DUP2_OLDFD) != 0)
+ {
+ /* Check invariants. */
+ int dup2_newfd = inh_handles->ih[oldfd].linked_fd;
+ if (!((inh_handles->ih[dup2_newfd].flags & DELAYED_DUP2_NEWFD) != 0
+ && oldfd == inh_handles->ih[dup2_newfd].linked_fd
+ && inh_handles->ih[oldfd].handle == inh_handles->ih[dup2_newfd].handle))
+ abort ();
+ /* We can't delay two or more dup2 calls from the same oldfd. */
+ if (!DuplicateHandle (curr_process, inh_handles->ih[oldfd].handle,
+ curr_process, &inh_handles->ih[newfd].handle,
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ errno = EBADF; /* arbitrary */
+ return -1;
+ }
+ inh_handles->ih[newfd].flags =
+ (unsigned char) inh_handles->ih[oldfd].flags | KEEP_OPEN_IN_CHILD;
}
- /* Duplicate the handle, so that it a forthcoming do_close action on oldfd
- has no effect on newfd. */
- if (!DuplicateHandle (curr_process, inh_handles->handles[oldfd],
- curr_process, &inh_handles->handles[newfd],
+ else
+ {
+ /* Delay the dup2 (oldfd, newfd) action. */
+ inh_handles->ih[oldfd].flags |= DELAYED_DUP2_OLDFD;
+ inh_handles->ih[oldfd].linked_fd = newfd;
+ inh_handles->ih[newfd].handle = inh_handles->ih[oldfd].handle;
+ inh_handles->ih[newfd].flags = DELAYED_DUP2_NEWFD;
+ inh_handles->ih[newfd].linked_fd = oldfd;
+ }
+# else
+ if (!DuplicateHandle (curr_process, inh_handles->ih[oldfd].handle,
+ curr_process, &inh_handles->ih[newfd].handle,
0, TRUE, DUPLICATE_SAME_ACCESS))
{
errno = EBADF; /* arbitrary */
return -1;
}
- inh_handles->flags[newfd] = 0;
- }
- return 0;
-}
-
-/* Executes a 'close' action.
- Returns 0 upon success. In case of failure, -1 is returned, with errno set.
- */
-static int
-do_close (struct inheritable_handles *inh_handles, int fd)
-{
- if (!(fd >= 0 && fd < inh_handles->count
- && inh_handles->handles[fd] != INVALID_HANDLE_VALUE))
- {
- errno = EBADF;
- return -1;
- }
- if (!CloseHandle (inh_handles->handles[fd]))
- {
- errno = EIO;
- return -1;
+ inh_handles->ih[newfd].flags =
+ (unsigned char) inh_handles->ih[oldfd].flags | KEEP_OPEN_IN_CHILD;
+# endif
}
- inh_handles->handles[fd] = INVALID_HANDLE_VALUE;
return 0;
}
@@ -573,7 +702,7 @@ __spawni (pid_t *pid, const char *prog_filename,
case spawn_do_close:
{
int fd = action->action.close_action.fd;
- if (do_close (&inh_handles, fd) < 0)
+ if (do_close (&inh_handles, fd, false) < 0)
goto failed_2;
}
break;
@@ -585,7 +714,7 @@ __spawni (pid_t *pid, const char *prog_filename,
int flags = action->action.open_action.oflag;
mode_t mode = action->action.open_action.mode;
if (do_open (&inh_handles, newfd, filename, directory,
- flags, mode, curr_process)
+ flags, mode)
< 0)
goto failed_2;
}
@@ -622,9 +751,16 @@ __spawni (pid_t *pid, const char *prog_filename,
goto failed_2;
}
}
+
+# if SPAWN_INTERNAL_OPTIMIZE_DUPLICATEHANDLE
+ /* Do the remaining delayed dup2 invocations. */
+ if (do_remaining_delayed_dup2 (&inh_handles, curr_process) < 0)
+ goto failed_2;
+# endif
}
- /* Reduce inh_handles.count to the minimum needed. */
+ /* Close the handles in inh_handles that are not meant to be preserved in the
+ child process, and reduce inh_handles.count to the minimum needed. */
shrink_inheritable_handles (&inh_handles);
/* CreateProcess
@@ -718,6 +854,13 @@ __spawni (pid_t *pid, const char *prog_filename,
#else
+/* The warning "warning: 'vfork' is deprecated: Use posix_spawn or fork" seen
+ on macOS 12 is pointless, as we use vfork only when it is safe or when the
+ user has explicitly requested it. Silence this warning. */
+#if __GNUC__ >= 3
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
/* Spawn a new process executing PATH with the attributes describes in *ATTRP.
Before running the process perform the actions described in FILE-ACTIONS. */
int