[glib/wip/le-gsubprocess: 767/767] gsubprocess: Support passing arbitrary fds



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]