[glib/wip/le-gsubprocess: 767/767] gsubprocess: Support passing arbitrary fds
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/le-gsubprocess: 767/767] gsubprocess: Support passing arbitrary fds
- Date: Sun, 13 Oct 2013 19:39:57 +0000 (UTC)
commit 9e8d88e5febbfde2af3ffc80aeebae5250d7a845
Author: Colin Walters <walters verbum org>
Date: Sun Oct 13 20:38:37 2013 +0100
gsubprocess: Support passing arbitrary fds
gio/gsubprocess.c | 40 ++++++++++++++++++++++++++++++++-
gio/gsubprocesslauncher-private.h | 2 +
gio/gsubprocesslauncher.c | 30 ++++++++++++++++++++++++
gio/gsubprocesslauncher.h | 5 ++++
gio/tests/gsubprocess-testprog.c | 20 ++++++++++++++++
gio/tests/gsubprocess.c | 45 +++++++++++++++++++++++++++++++++++++
6 files changed, 141 insertions(+), 1 deletions(-)
---
diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c
index 56ae921..e419007 100644
--- a/gio/gsubprocess.c
+++ b/gio/gsubprocess.c
@@ -178,6 +178,8 @@ typedef struct
gint fds[3];
GSpawnChildSetupFunc child_setup_func;
gpointer child_setup_data;
+ gint *fd_assignments;
+ guint n_fd_assignments;
} ChildData;
static void
@@ -203,6 +205,16 @@ child_setup (gpointer user_data)
while (result == -1 && errno == EINTR);
}
+ for (i = 0; i < child_data->n_fd_assignments; i += 2)
+ {
+ gint result;
+
+ do
+ result = dup2 (child_data->fd_assignments[i],
+ child_data->fd_assignments[i+1]);
+ while (result == -1 && errno == EINTR);
+ }
+
if (child_data->child_setup_func)
child_data->child_setup_func (child_data->child_setup_data);
}
@@ -319,7 +331,7 @@ initable_init (GInitable *initable,
GError **error)
{
GSubprocess *self = G_SUBPROCESS (initable);
- ChildData child_data = { { -1, -1, -1 } };
+ ChildData child_data = { { -1, -1, -1 }, 0 };
gint *pipe_ptrs[3] = { NULL, NULL, NULL };
gint pipe_fds[3] = { -1, -1, -1 };
gint close_fds[3] = { -1, -1, -1 };
@@ -398,6 +410,30 @@ initable_init (GInitable *initable,
}
#endif
+#ifdef G_OS_UNIX
+ if (self->launcher &&
+ g_hash_table_size (self->launcher->fd_assignments) > 0)
+ {
+ GHashTableIter hiter;
+ gpointer hkey, hvalue;
+ guint i = 0;
+
+ child_data.n_fd_assignments = g_hash_table_size (self->launcher->fd_assignments);
+ child_data.fd_assignments = g_new (int, child_data.n_fd_assignments * 2);
+
+ g_hash_table_iter_init (&hiter, self->launcher->fd_assignments);
+ while (g_hash_table_iter_next (&hiter, &hkey, &hvalue))
+ {
+ gint parent_fd = GPOINTER_TO_INT (hkey);
+ gint child_fd = GPOINTER_TO_INT (hvalue);
+
+ child_data.fd_assignments[i++] = parent_fd;
+ child_data.fd_assignments[i++] = child_fd;
+ }
+ g_assert (i == (child_data.n_fd_assignments * 2));
+ }
+#endif
+
/* argv0 has no '/' in it? We better do a PATH lookup. */
if (strchr (self->argv[0], G_DIR_SEPARATOR) == NULL)
{
@@ -456,6 +492,8 @@ out:
/* we don't need this past init... */
self->launcher = NULL;
+ g_free (child_data.fd_assignments);
+
for (i = 0; i < 3; i++)
if (close_fds[i] != -1)
close (close_fds[i]);
diff --git a/gio/gsubprocesslauncher-private.h b/gio/gsubprocesslauncher-private.h
index 55c608e..88793e0 100644
--- a/gio/gsubprocesslauncher-private.h
+++ b/gio/gsubprocesslauncher-private.h
@@ -44,6 +44,8 @@ struct _GSubprocessLauncher
gint stderr_fd;
gchar *stderr_path;
+ GHashTable *fd_assignments;
+
GSpawnChildSetupFunc child_setup_func;
gpointer child_setup_user_data;
GDestroyNotify child_setup_destroy_notify;
diff --git a/gio/gsubprocesslauncher.c b/gio/gsubprocesslauncher.c
index af12578..19af679 100644
--- a/gio/gsubprocesslauncher.c
+++ b/gio/gsubprocesslauncher.c
@@ -147,6 +147,8 @@ g_subprocess_launcher_finalize (GObject *object)
if (self->stderr_fd != -1)
close (self->stderr_fd);
+
+ g_clear_pointer (&self->fd_assignments, g_hash_table_unref);
#endif
if (self->child_setup_destroy_notify)
@@ -163,6 +165,9 @@ g_subprocess_launcher_init (GSubprocessLauncher *self)
self->stdin_fd = -1;
self->stdout_fd = -1;
self->stderr_fd = -1;
+#ifdef G_OS_UNIX
+ self->fd_assignments = g_hash_table_new (NULL, NULL);
+#endif
}
static void
@@ -563,6 +568,31 @@ g_subprocess_launcher_take_stderr_fd (GSubprocessLauncher *self,
}
/**
+ * g_subprocess_launcher_pass_fd:
+ * @self: a #GSubprocessLauncher
+ * @source_fd: File descriptor in parent process
+ * @target_fd: Target descriptor for child process
+ *
+ * Pass an arbitrary file descriptor from parent process to
+ * the child. By default, all file descriptors from the parent
+ * will be closed. This function allows you to create (for example)
+ * a custom pipe() or socketpair() before launching the process, and
+ * choose the target descriptor in the child.
+ *
+ * An example use case is GNUPG, which has a command line argument
+ * --passphrase-fd providing a file descriptor number where it expects
+ * the passphrase to be written.
+ */
+void
+g_subprocess_launcher_pass_fd (GSubprocessLauncher *self,
+ gint source_fd,
+ gint target_fd)
+{
+ g_hash_table_replace (self->fd_assignments, GINT_TO_POINTER (source_fd),
+ GINT_TO_POINTER (target_fd));
+}
+
+/**
* g_subprocess_launcher_set_child_setup:
* @self: a #GSubprocessLauncher
* @child_setup: a #GSpawnChildSetupFunc to use as the child setup function
diff --git a/gio/gsubprocesslauncher.h b/gio/gsubprocesslauncher.h
index 2ba878c..9a11b3c 100644
--- a/gio/gsubprocesslauncher.h
+++ b/gio/gsubprocesslauncher.h
@@ -100,6 +100,11 @@ GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_take_stderr_fd (GSubprocessLauncher *self,
gint fd);
+GLIB_AVAILABLE_IN_2_40
+void g_subprocess_launcher_pass_fd (GSubprocessLauncher *self,
+ gint source_fd,
+ gint target_fd);
+
/* Child setup, only available on UNIX */
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_child_setup (GSubprocessLauncher *self,
diff --git a/gio/tests/gsubprocess-testprog.c b/gio/tests/gsubprocess-testprog.c
index 3d8e5cc..94d7ea9 100644
--- a/gio/tests/gsubprocess-testprog.c
+++ b/gio/tests/gsubprocess-testprog.c
@@ -121,6 +121,24 @@ sleep_forever_mode (int argc,
return 0;
}
+static int
+write_to_fd_42 (int argc, char **argv)
+{
+ FILE *f = fdopen (42, "w");
+ const char buf[] = "hello world\n";
+ size_t bytes_written;
+
+ g_assert (f != NULL);
+
+ bytes_written = fwrite (buf, 1, sizeof (buf), f);
+ g_assert (bytes_written == sizeof (buf));
+
+ if (fclose (f) == -1)
+ g_assert_not_reached ();
+
+ return 0;
+}
+
int
main (int argc, char **argv)
{
@@ -162,6 +180,8 @@ main (int argc, char **argv)
return cat_mode (argc, argv);
else if (strcmp (mode, "sleep-forever") == 0)
return sleep_forever_mode (argc, argv);
+ else if (strcmp (mode, "write-to-fd-42") == 0)
+ return write_to_fd_42 (argc, argv);
else
{
g_printerr ("Unknown MODE %s\n", argv[1]);
diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
index b881fcc..a917a99 100644
--- a/gio/tests/gsubprocess.c
+++ b/gio/tests/gsubprocess.c
@@ -3,6 +3,8 @@
#ifdef G_OS_UNIX
#include <sys/wait.h>
+#include <glib-unix.h>
+#include <gio/gunixinputstream.h>
#include <gio/gfiledescriptorbased.h>
#endif
@@ -758,6 +760,48 @@ test_child_setup (void)
(void) g_file_delete (tmpfile, NULL, NULL);
}
+
+static void
+test_pass_fd (void)
+{
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ GInputStream *child_42_input;
+ GDataInputStream *child_42_datainput;
+ GSubprocessLauncher *launcher;
+ GSubprocess *proc;
+ GPtrArray *args;
+ int pipefds[2];
+ char *buf;
+ gsize len;
+
+ args = get_test_subprocess_args ("write-to-fd-42", NULL);
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+
+ g_unix_open_pipe (pipefds, FD_CLOEXEC, error);
+ g_assert_no_error (local_error);
+ g_subprocess_launcher_pass_fd (launcher, pipefds[1], 42);
+ proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
+ g_ptr_array_free (args, TRUE);
+ g_assert_no_error (local_error);
+
+ (void) close (pipefds[1]);
+
+ child_42_input = g_unix_input_stream_new (pipefds[0], TRUE);
+ child_42_datainput = g_data_input_stream_new (child_42_input);
+
+ buf = g_data_input_stream_read_line_utf8 (child_42_datainput, &len, NULL, error);
+ g_assert_no_error (local_error);
+
+ g_assert_cmpstr (buf, ==, "hello world");
+
+ g_free (buf);
+ g_object_unref (child_42_datainput);
+ g_object_unref (child_42_input);
+ g_object_unref (launcher);
+ g_object_unref (proc);
+}
+
#endif
int
@@ -785,6 +829,7 @@ main (int argc, char **argv)
g_test_add_func ("/gsubprocess/stdout-file", test_stdout_file);
g_test_add_func ("/gsubprocess/stdout-fd", test_stdout_fd);
g_test_add_func ("/gsubprocess/child-setup", test_child_setup);
+ g_test_add_func ("/gsubprocess/pass-fd", test_pass_fd);
#endif
return g_test_run ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]