[glib: 5/7] glib/win32: implement fd passing with g_spawn_async_with_pipes_and_fds
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 5/7] glib/win32: implement fd passing with g_spawn_async_with_pipes_and_fds
- Date: Wed, 9 Feb 2022 11:19:17 +0000 (UTC)
commit 2d35c57718fb5dcad8f3ecfdecbbb614198c4f51
Author: Marc-André Lureau <marcandre lureau redhat com>
Date: Wed Jan 26 19:41:50 2022 +0400
glib/win32: implement fd passing with g_spawn_async_with_pipes_and_fds
Signed-off-by: Marc-André Lureau <marcandre lureau redhat com>
glib/gspawn-win32-helper.c | 73 +++++++++++++++++++++++++++++++++++++++++++++-
glib/gspawn-win32.c | 39 +++++++++++++++++++------
glib/gspawn.c | 2 ++
3 files changed, 104 insertions(+), 10 deletions(-)
---
diff --git a/glib/gspawn-win32-helper.c b/glib/gspawn-win32-helper.c
index 1e0628558..58fd719fa 100644
--- a/glib/gspawn-win32-helper.c
+++ b/glib/gspawn-win32-helper.c
@@ -203,12 +203,14 @@ int
main (int ignored_argc, char **ignored_argv)
#endif
{
+ GHashTable *fds; /* (element-type int int) */
int child_err_report_fd = -1;
int helper_sync_fd = -1;
int saved_stderr_fd = -1;
int i;
int fd;
int mode;
+ int maxfd = 2;
gintptr handle;
int saved_errno;
gintptr no_error = CHILD_NO_ERROR;
@@ -244,6 +246,7 @@ main (int ignored_argc, char **ignored_argv)
* which write error messages.
*/
child_err_report_fd = atoi (argv[ARG_CHILD_ERR_REPORT]);
+ maxfd = MAX (child_err_report_fd, maxfd);
/* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
* argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
@@ -259,6 +262,7 @@ main (int ignored_argc, char **ignored_argv)
* from another process works only if that other process exists.
*/
helper_sync_fd = atoi (argv[ARG_HELPER_SYNC]);
+ maxfd = MAX (helper_sync_fd, maxfd);
/* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
* should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
@@ -294,6 +298,8 @@ main (int ignored_argc, char **ignored_argv)
saved_stderr_fd = reopen_noninherited (dup (2), _O_WRONLY);
if (saved_stderr_fd == -1)
write_err_and_exit (child_err_report_fd, CHILD_DUP_FAILED);
+
+ maxfd = MAX (saved_stderr_fd, maxfd);
if (argv[ARG_STDERR][0] == '-')
; /* Nothing */
else if (argv[ARG_STDERR][0] == 'z')
@@ -316,12 +322,76 @@ main (int ignored_argc, char **ignored_argv)
else if (_wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0)
write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
+ fds = g_hash_table_new (NULL, NULL);
+ if (argv[ARG_FDS][0] != '-')
+ {
+ gchar **fdsv = g_strsplit (argv[ARG_FDS], ",", -1);
+ gsize i;
+
+ for (i = 0; fdsv[i]; i++)
+ {
+ char *endptr = NULL;
+ int sourcefd, targetfd;
+ gint64 val;
+
+ val = g_ascii_strtoll (fdsv[i], &endptr, 10);
+ g_assert (val <= G_MAXINT32);
+ sourcefd = val;
+ g_assert (endptr != fdsv[i]);
+ g_assert (*endptr == ':');
+ val = g_ascii_strtoll (endptr + 1, &endptr, 10);
+ targetfd = val;
+ g_assert (val <= G_MAXINT32);
+ g_assert (*endptr == '\0');
+
+ maxfd = MAX (maxfd, sourcefd);
+ maxfd = MAX (maxfd, targetfd);
+
+ g_hash_table_insert (fds, GINT_TO_POINTER (targetfd), GINT_TO_POINTER (sourcefd));
+ }
+
+ g_strfreev (fdsv);
+ }
+
+ maxfd++;
+ child_err_report_fd = checked_dup2 (child_err_report_fd, maxfd, child_err_report_fd, TRUE);
+ maxfd++;
+ helper_sync_fd = checked_dup2 (helper_sync_fd, maxfd, child_err_report_fd, TRUE);
+ maxfd++;
+ saved_stderr_fd = checked_dup2 (saved_stderr_fd, maxfd, child_err_report_fd, TRUE);
+
+ {
+ GHashTableIter iter;
+ gpointer sourcefd, targetfd;
+
+ g_hash_table_iter_init (&iter, fds);
+ while (g_hash_table_iter_next (&iter, &targetfd, &sourcefd))
+ {
+ /* If we're doing remapping fd assignments, we need to handle
+ * the case where the user has specified e.g. 5 -> 4, 4 -> 6.
+ * We do this by duping all source fds, taking care to ensure the new
+ * fds are larger than any target fd to avoid introducing new conflicts.
+ */
+ maxfd++;
+ checked_dup2 (GPOINTER_TO_INT (sourcefd), maxfd, child_err_report_fd, TRUE);
+ g_hash_table_iter_replace (&iter, GINT_TO_POINTER (maxfd));
+ }
+
+ g_hash_table_iter_init (&iter, fds);
+ while (g_hash_table_iter_next (&iter, &targetfd, &sourcefd))
+ checked_dup2 (GPOINTER_TO_INT (sourcefd), GPOINTER_TO_INT (targetfd), child_err_report_fd, TRUE);
+ }
+
+ g_hash_table_add (fds, GINT_TO_POINTER (child_err_report_fd));
+ g_hash_table_add (fds, GINT_TO_POINTER (helper_sync_fd));
+ g_hash_table_add (fds, GINT_TO_POINTER (saved_stderr_fd));
+
/* argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
* upwards should be closed
*/
if (argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
for (i = 3; i < 1000; i++) /* FIXME real limit? */
- if (i != child_err_report_fd && i != helper_sync_fd && i != saved_stderr_fd)
+ if (!g_hash_table_contains (fds, GINT_TO_POINTER (i)))
if (_get_osfhandle (i) != -1)
close (i);
@@ -379,6 +449,7 @@ main (int ignored_argc, char **ignored_argv)
LocalFree (wargv);
g_strfreev (argv);
+ g_hash_table_unref (fds);
return 0;
}
diff --git a/glib/gspawn-win32.c b/glib/gspawn-win32.c
index 1c06e7c6a..02eddccf1 100644
--- a/glib/gspawn-win32.c
+++ b/glib/gspawn-win32.c
@@ -102,6 +102,7 @@ enum {
ARG_CLOSE_DESCRIPTORS,
ARG_USE_PATH,
ARG_WAIT,
+ ARG_FDS,
ARG_PROGRAM,
ARG_COUNT = ARG_PROGRAM
};
@@ -575,6 +576,9 @@ fork_exec (gint *exit_status,
gint stdin_fd,
gint stdout_fd,
gint stderr_fd,
+ const gint *source_fds,
+ const gint *target_fds,
+ gsize n_fds,
gint *err_report,
GError **error)
{
@@ -635,7 +639,8 @@ fork_exec (gint *exit_status,
!(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
!(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
(working_directory == NULL || !*working_directory) &&
- (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
+ (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN) &&
+ n_fds == 0)
{
/* We can do without the helper process */
gboolean retval =
@@ -754,6 +759,21 @@ fork_exec (gint *exit_status,
else
new_argv[ARG_WAIT] = "w";
+ if (n_fds == 0)
+ new_argv[ARG_FDS] = g_strdup ("-");
+ else
+ {
+ GString *fds = g_string_new ("");
+ gsize n;
+
+ for (n = 0; n < n_fds; n++)
+ g_string_append_printf (fds, "%d:%d,", source_fds[n], target_fds[n]);
+
+ /* remove the trailing , */
+ g_string_truncate (fds, fds->len - 1);
+ new_argv[ARG_FDS] = g_string_free (fds, FALSE);
+ }
+
for (i = 0; i <= argc; i++)
new_argv[ARG_PROGRAM + i] = protected_argv[i];
@@ -780,6 +800,7 @@ fork_exec (gint *exit_status,
g_strfreev (protected_argv);
g_free (new_argv[0]);
g_free (new_argv[ARG_WORKING_DIRECTORY]);
+ g_free (new_argv[ARG_FDS]);
g_free (new_argv);
g_free (helper_process);
@@ -795,6 +816,7 @@ fork_exec (gint *exit_status,
g_strfreev (protected_argv);
g_free (new_argv[0]);
g_free (new_argv[ARG_WORKING_DIRECTORY]);
+ g_free (new_argv[ARG_FDS]);
g_free (new_argv);
g_free (helper_process);
g_strfreev ((gchar **) wargv);
@@ -826,6 +848,7 @@ fork_exec (gint *exit_status,
g_free (new_argv[0]);
g_free (new_argv[ARG_WORKING_DIRECTORY]);
+ g_free (new_argv[ARG_FDS]);
g_free (new_argv);
/* Check if gspawn-win32-helper couldn't be run */
@@ -997,6 +1020,7 @@ g_spawn_sync (const gchar *working_directory,
-1,
-1,
-1,
+ NULL, NULL, 0,
&reportpipe,
error))
return FALSE;
@@ -1221,6 +1245,7 @@ g_spawn_async_with_pipes (const gchar *working_directory,
-1,
-1,
-1,
+ NULL, NULL, 0,
NULL,
error);
}
@@ -1262,6 +1287,7 @@ g_spawn_async_with_fds (const gchar *working_directory,
stdin_fd,
stdout_fd,
stderr_fd,
+ NULL, NULL, 0,
NULL,
error);
@@ -1299,14 +1325,6 @@ g_spawn_async_with_pipes_and_fds (const gchar *working_directory,
g_return_val_if_fail (stdout_pipe_out == NULL || stdout_fd < 0, FALSE);
g_return_val_if_fail (stderr_pipe_out == NULL || stderr_fd < 0, FALSE);
- /* source_fds/target_fds isn’t supported on Windows at the moment. */
- if (n_fds != 0)
- {
- g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_INVAL,
- "FD redirection is not supported on Windows at the moment");
- return FALSE;
- }
-
return fork_exec (NULL,
(flags & G_SPAWN_DO_NOT_REAP_CHILD),
working_directory,
@@ -1322,6 +1340,9 @@ g_spawn_async_with_pipes_and_fds (const gchar *working_directory,
stdin_fd,
stdout_fd,
stderr_fd,
+ source_fds,
+ target_fds,
+ n_fds,
NULL,
error);
}
diff --git a/glib/gspawn.c b/glib/gspawn.c
index 7a2618489..60da07198 100644
--- a/glib/gspawn.c
+++ b/glib/gspawn.c
@@ -772,6 +772,8 @@ g_spawn_async_with_pipes (const gchar *working_directory,
* any target FDs which equal @stdin_fd, @stdout_fd or @stderr_fd will overwrite
* them in the spawned process.
*
+ * @source_fds is supported on Windows since 2.72.
+ *
* %G_SPAWN_FILE_AND_ARGV_ZERO means that the first element of @argv is
* the file to execute, while the remaining elements are the actual
* argument vector to pass to the file. Normally g_spawn_async_with_pipes()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]