[libgsystem] GSSubprocess: New API to allocate a separate pipe between parent and child



commit 7caa16d62721edb3800481b874613c4f989058f3
Author: Colin Walters <walters verbum org>
Date:   Sat Jan 26 16:57:54 2013 -0500

    GSSubprocess: New API to allocate a separate pipe between parent and child
    
    This is useful when reading structured data from the child.

 gsystem-subprocess-context-private.h |    2 +
 gsystem-subprocess-context.c         |  114 ++++++++++++++++++++++++++++++++++
 gsystem-subprocess-context.h         |   12 ++++
 gsystem-subprocess.c                 |   41 +++++++++---
 4 files changed, 159 insertions(+), 10 deletions(-)
---
diff --git a/gsystem-subprocess-context-private.h b/gsystem-subprocess-context-private.h
index 18cb0ca..b51ddb4 100644
--- a/gsystem-subprocess-context-private.h
+++ b/gsystem-subprocess-context-private.h
@@ -53,6 +53,8 @@ struct _GSSubprocessContext
   gint stderr_fd;
   gchar *stderr_path;
 
+  GArray *pipefds;
+
   GSpawnChildSetupFunc child_setup_func;
   gpointer child_setup_data;
 };
diff --git a/gsystem-subprocess-context.c b/gsystem-subprocess-context.c
index 6d894b3..96de25b 100644
--- a/gsystem-subprocess-context.c
+++ b/gsystem-subprocess-context.c
@@ -22,6 +22,13 @@
 
 #include "libgsystem.h"
 
+#ifdef G_OS_UNIX
+#include <gio/gunixoutputstream.h>
+#include <gio/gfiledescriptorbased.h>
+#include <gio/gunixinputstream.h>
+#include <glib-unix.h>
+#endif
+
 /**
  * SECTION:gssubprocesscontext
  * @title: GSSubprocess Context
@@ -153,6 +160,7 @@ gs_subprocess_context_init (GSSubprocessContext  *self)
   self->stderr_fd = -1;
   self->stdout_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT;
   self->stderr_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT;
+  self->pipefds = g_array_new (FALSE, FALSE, sizeof (int));
 }
 
 static void
@@ -168,6 +176,8 @@ gs_subprocess_context_finalize (GObject *object)
   g_free (self->stdout_path);
   g_free (self->stderr_path);
 
+  g_array_unref (self->pipefds);
+
   if (G_OBJECT_CLASS (gs_subprocess_context_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (gs_subprocess_context_parent_class)->finalize (object);
 }
@@ -241,6 +251,30 @@ gs_subprocess_context_class_init (GSSubprocessContextClass *class)
   g_object_class_install_properties (gobject_class, N_PROPS, gs_subprocess_context_pspecs);
 }
 
+/**
+ * gs_subprocess_context_argv_append:
+ * @self:
+ * @arg: An argument
+ *
+ * Append an argument to the child's argument vector.
+ */
+void
+gs_subprocess_context_argv_append (GSSubprocessContext  *self,
+                                   gchar                *arg)
+{
+  GPtrArray *new_argv = g_ptr_array_new ();
+  gchar **iter;
+
+  for (iter = self->argv; *iter; iter++)
+    g_ptr_array_add (new_argv, *iter);
+  g_ptr_array_add (new_argv, arg);
+  g_ptr_array_add (new_argv, NULL);
+
+  /* Don't free elements */
+  g_free (self->argv);
+  self->argv = (char**)g_ptr_array_free (new_argv, FALSE);
+}
+
 /* Environment */
 
 /**
@@ -377,4 +411,84 @@ gs_subprocess_context_set_child_setup (GSSubprocessContext           *self,
   self->child_setup_func = child_setup;
   self->child_setup_data = user_data;
 }
+
+static gboolean
+open_pipe_internal (GSSubprocessContext         *self,
+                    gboolean                     for_read,
+                    void                       **out_stream,
+                    gint                        *out_fdno,
+                    GError                     **error)
+{
+  int pipefds[2];
+
+  g_return_val_if_fail (out_stream != NULL, FALSE);
+  g_return_val_if_fail (out_fdno != NULL, FALSE);
+
+  if (!g_unix_open_pipe (pipefds, FD_CLOEXEC, error))
+    return FALSE;
+
+  if (for_read)
+    {
+      *out_stream = g_unix_input_stream_new (pipefds[0], TRUE);
+      *out_fdno = pipefds[1];
+    }
+  else
+    {
+      *out_stream = g_unix_output_stream_new (pipefds[1], TRUE);
+      *out_fdno = pipefds[0];
+    }
+  g_array_append_val (self->pipefds, *out_fdno);
+
+  return TRUE;
+}
+
+/**
+ * gs_subprocess_context_open_pipe_read:
+ * @self:
+ * @out_stream: (out) (transfer full): A newly referenced output stream
+ * @out_fdno: File descriptor number for the subprocess side of the pipe
+ *
+ * This allows you to open a pipe between the parent and child
+ * processes, independent of the standard streams.  For this function,
+ * the pipe is set up so that the parent can read, and the child can
+ * write.  For the opposite version, see
+ * gs_subprocess_context_open_pipe_write().
+ *
+ * The returned @out_fdno is the file descriptor number that the child
+ * will see; you need to communicate this number via a separate
+ * channel, such as the argument list.  For example, if you're using
+ * this pipe to send a password, provide
+ * <literal>--password-fd=&lt;fdno string&gt;</literal>.
+ *
+ * Returns: %TRUE on success, %FALSE on error (and @error will be set)
+ */
+gboolean
+gs_subprocess_context_open_pipe_read (GSSubprocessContext         *self,
+                                      GInputStream               **out_stream,
+                                      gint                        *out_fdno,
+                                      GError                     **error)
+{
+  return open_pipe_internal (self, TRUE, (void**)out_stream, out_fdno, error);
+}
+
+/**
+ * gs_subprocess_context_open_pipe_write:
+ * @self:
+ * @out_stream: (out) (transfer full): A newly referenced stream
+ * @out_fdno: File descriptor number for the subprocess side of the pipe
+ *
+ * Like gs_subprocess_context_open_pipe_read(), but returns a writable
+ * channel from which the child process can read.
+ *
+ * Returns: %TRUE on success, %FALSE on error (and @error will be set)
+ */
+gboolean
+gs_subprocess_context_open_pipe_write (GSSubprocessContext         *self,
+                                       GOutputStream              **out_stream,
+                                       gint                        *out_fdno,
+                                       GError                     **error)
+{
+  return open_pipe_internal (self, FALSE, (void**)out_stream, out_fdno, error);
+}
+
 #endif
diff --git a/gsystem-subprocess-context.h b/gsystem-subprocess-context.h
index 1f57e03..c35d491 100644
--- a/gsystem-subprocess-context.h
+++ b/gsystem-subprocess-context.h
@@ -63,6 +63,9 @@ GSSubprocessContext * gs_subprocess_context_new_argv0 (const gchar   *argv0,
                                                      gchar        **argv);
 #endif
 
+void             gs_subprocess_context_argv_append (GSSubprocessContext  *self,
+                                                    gchar                *arg);
+
 /* Environment */
 
 void             gs_subprocess_context_set_environment (GSSubprocessContext           *self,
@@ -99,6 +102,15 @@ void             gs_subprocess_context_set_stderr_file_path (GSSubprocessContext
 							    const gchar                  *path);
 void             gs_subprocess_context_set_stderr_fd        (GSSubprocessContext           *self,
 							    gint                          fd);
+
+gboolean         gs_subprocess_context_open_pipe_read       (GSSubprocessContext         *self,
+                                                             GInputStream               **out_stream,
+                                                             gint                        *out_fdno,
+                                                             GError                     **error);
+gboolean         gs_subprocess_context_open_pipe_write      (GSSubprocessContext         *self,
+                                                             GOutputStream              **out_stream,
+                                                             gint                        *out_fdno,
+                                                             GError                     **error);
 #endif
 
 /* Child setup, only available on UNIX */
diff --git a/gsystem-subprocess.c b/gsystem-subprocess.c
index 383e921..27c9ad8 100644
--- a/gsystem-subprocess.c
+++ b/gsystem-subprocess.c
@@ -279,9 +279,10 @@ unix_open_file (const char  *filename,
 
 typedef struct
 {
-  gint             fds[3];
-  GSpawnChildSetupFunc child_setup_func;
-  gpointer             child_setup_data;
+  gint                   fds[3];
+  GArray                *pipefds;
+  GSpawnChildSetupFunc   child_setup_func;
+  gpointer               child_setup_data;
 } ChildData;
 
 static void
@@ -289,6 +290,7 @@ child_setup (gpointer user_data)
 {
   ChildData *child_data = user_data;
   gint i;
+  gint result;
 
   /* We're on the child side now.  "Rename" the file descriptors in
    * child_data.fds[] to stdin/stdout/stderr.
@@ -298,14 +300,31 @@ child_setup (gpointer user_data)
    * have been created O_CLOEXEC.
    */
   for (i = 0; i < 3; i++)
-    if (child_data->fds[i] != -1 && child_data->fds[i] != i)
-      {
-        gint result;
+    {
+      if (child_data->fds[i] != -1 && child_data->fds[i] != i)
+        {
+          do
+            result = dup2 (child_data->fds[i], i);
+          while (G_UNLIKELY (result == -1 && errno == EINTR));
+        }
+    }
 
-        do
-          result = dup2 (child_data->fds[i], i);
-        while (result == -1 && errno == EINTR);
-      }
+  /* Unset the CLOEXEC flag on each of our pipes */
+  for (i = 0; i < child_data->pipefds->len; i++)
+    {
+      int fd = g_array_index (child_data->pipefds, int, i);
+      int flags;
+
+      do
+        flags = fcntl (fd, F_GETFL);
+      while (G_UNLIKELY (flags == -1 && errno == EINTR));
+
+      flags &= ~FD_CLOEXEC;
+      
+      do
+        result = fcntl (fd, F_SETFD, flags);
+      while (G_UNLIKELY (result == -1 && errno == EINTR));
+    }
 
   if (child_data->child_setup_func)
     child_data->child_setup_func (child_data->child_setup_data);
@@ -401,6 +420,8 @@ initable_init (GInitable     *initable,
   else
     g_assert_not_reached ();
 
+  child_data.pipefds = self->context->pipefds;
+
   if (self->context->keep_descriptors)
     spawn_flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]