[gnome-builder] subprocess: allow generic FD mapping into IdeSubprocess and IdeRunner



commit fce41c63999aced80679a089edb8f6ab63083b08
Author: Christian Hergert <chergert redhat com>
Date:   Mon Feb 20 23:43:11 2017 -0800

    subprocess: allow generic FD mapping into IdeSubprocess and IdeRunner
    
    This provides the abstraction we need to be able to map an FD into a given
    FD within the target process. Previously we could only do this for
    stdin/stdout/stderr.
    
    Additionally, this includes the same plumbing for IdeRunner which is our
    more specific, higher-level abstraction for running programs/tests using
    subprocess launchers.

 libide/runner/ide-runner.c                         |  127 +++++++++++++++++++-
 libide/runner/ide-runner.h                         |    3 +
 .../subprocess/ide-breakout-subprocess-private.h   |   28 +++--
 libide/subprocess/ide-breakout-subprocess.c        |   61 ++++++++--
 libide/subprocess/ide-subprocess-launcher.c        |   58 +++++++++
 libide/subprocess/ide-subprocess-launcher.h        |    3 +
 6 files changed, 259 insertions(+), 21 deletions(-)
---
diff --git a/libide/runner/ide-runner.c b/libide/runner/ide-runner.c
index 67a7f3a..86c4c55 100644
--- a/libide/runner/ide-runner.c
+++ b/libide/runner/ide-runner.c
@@ -56,6 +56,12 @@ typedef struct
   GSList *posthook_queue;
 } IdeRunnerRunState;
 
+typedef struct
+{
+  gint source_fd;
+  gint dest_fd;
+} FdMapping;
+
 enum {
   PROP_0,
   PROP_ARGV,
@@ -204,13 +210,28 @@ ide_runner_real_run_async (IdeRunner           *self,
    */
   if (priv->tty_fd != -1)
     {
-      IDE_TRACE_MSG ("Setting TTY fd to %d\n", priv->tty_fd);
+      IDE_TRACE_MSG ("Setting TTY fd to %d", priv->tty_fd);
       ide_subprocess_launcher_take_stdin_fd (launcher, dup (priv->tty_fd));
       ide_subprocess_launcher_take_stdout_fd (launcher, dup (priv->tty_fd));
       ide_subprocess_launcher_take_stderr_fd (launcher, dup (priv->tty_fd));
     }
 
   /*
+   * Now map in any additionally requested FDs.
+   */
+  if (priv->fd_mapping != NULL)
+    {
+      g_autoptr(GArray) ar = g_steal_pointer (&priv->fd_mapping);
+
+      for (guint i = 0; i < ar->len; i++)
+        {
+          FdMapping *map = &g_array_index (ar, FdMapping, i);
+
+          ide_subprocess_launcher_take_fd (launcher, map->source_fd, map->dest_fd);
+        }
+    }
+
+  /*
    * We want the runners to run on the host so that we aren't captive to
    * our containing system (flatpak, jhbuild, etc).
    */
@@ -372,6 +393,22 @@ ide_runner_finalize (GObject *object)
   g_queue_clear (&priv->argv);
   g_clear_object (&priv->env);
 
+  if (priv->fd_mapping != NULL)
+    {
+      for (guint i = 0; i < priv->fd_mapping->len; i++)
+        {
+          FdMapping *map = &g_array_index (priv->fd_mapping, FdMapping, i);
+
+          if (map->source_fd != -1)
+            {
+              close (map->source_fd);
+              map->source_fd = -1;
+            }
+        }
+    }
+
+  g_clear_pointer (&priv->fd_mapping, g_array_unref);
+
   if (priv->tty_fd != -1)
     {
       close (priv->tty_fd);
@@ -954,3 +991,91 @@ ide_runner_set_tty (IdeRunner *self,
 
   IDE_EXIT;
 }
+
+static gint
+sort_fd_mapping (gconstpointer a,
+                 gconstpointer b)
+{
+  const FdMapping *map_a = a;
+  const FdMapping *map_b = b;
+
+  return map_a->dest_fd - map_b->dest_fd;
+}
+
+/**
+ * ide_runner_take_fd:
+ * @self: An #IdeRunner
+ * @source_fd: the fd to map, this will be closed by #IdeRunner
+ * @dest_fd: the target FD in the spawned process, or -1 for next available
+ *
+ * This will ensure that @source_fd is mapped into the new process as @dest_fd.
+ * If @dest_fd is -1, then the next fd will be used and that value will be
+ * returned. Note that this is not a valid fd in the calling process, only
+ * within the destination process.
+ *
+ * Returns: @dest_fd or the FD or the next available dest_fd.
+ */
+gint
+ide_runner_take_fd (IdeRunner *self,
+                    gint       source_fd,
+                    gint       dest_fd)
+{
+  IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+  FdMapping map = { -1, -1 };
+
+  g_return_val_if_fail (IDE_IS_RUNNER (self), -1);
+  g_return_val_if_fail (source_fd > -1, -1);
+
+  if (priv->fd_mapping == NULL)
+    priv->fd_mapping = g_array_new (FALSE, FALSE, sizeof (FdMapping));
+
+  /*
+   * Quick and dirty hack to take the next FD, won't deal with people mapping
+   * to 1024 well, but we can fix that when we come across it.
+   */
+  if (dest_fd < 0)
+    {
+      if (priv->fd_mapping->len == 0)
+        dest_fd = 3;
+      else
+        dest_fd = g_array_index (priv->fd_mapping, FdMapping, priv->fd_mapping->len - 1).dest_fd + 1;
+    }
+
+  map.source_fd = source_fd;
+  map.dest_fd = dest_fd;
+
+  g_array_append_val (priv->fd_mapping, map);
+  g_array_sort (priv->fd_mapping, sort_fd_mapping);
+
+  return dest_fd;
+}
+
+guint
+ide_runner_get_n_fd_mappings (IdeRunner *self)
+{
+  IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_RUNNER (self), 0);
+
+  return priv->fd_mapping ? priv->fd_mapping->len : 0;
+}
+
+gint
+ide_runner_get_nth_fd_maping (IdeRunner *self,
+                              guint      i,
+                              gint      *dest_fd)
+{
+  IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+  FdMapping *map;
+
+  g_return_val_if_fail (IDE_IS_RUNNER (self), -1);
+  g_return_val_if_fail (priv->fd_mapping != NULL, -1);
+  g_return_val_if_fail (i < priv->fd_mapping->len, -1);
+  g_return_val_if_fail (dest_fd != NULL, -1);
+
+  map = &g_array_index (priv->fd_mapping, FdMapping, i);
+
+  *dest_fd = map->dest_fd;
+
+  return map->source_fd;
+}
diff --git a/libide/runner/ide-runner.h b/libide/runner/ide-runner.h
index d28ff82..8fa3e1b 100644
--- a/libide/runner/ide-runner.h
+++ b/libide/runner/ide-runner.h
@@ -73,6 +73,9 @@ void            ide_runner_append_argv     (IdeRunner            *self,
 gchar         **ide_runner_get_argv        (IdeRunner            *self);
 void            ide_runner_set_argv        (IdeRunner            *self,
                                             const gchar * const  *argv);
+gint            ide_runner_take_fd         (IdeRunner            *self,
+                                            gint                  source_fd,
+                                            gint                  dest_fd);
 GInputStream   *ide_runner_get_stdin       (IdeRunner            *self);
 GOutputStream  *ide_runner_get_stdout      (IdeRunner            *self);
 GOutputStream  *ide_runner_get_stderr      (IdeRunner            *self);
diff --git a/libide/subprocess/ide-breakout-subprocess-private.h 
b/libide/subprocess/ide-breakout-subprocess-private.h
index 3ec8a4e..ef4c6af 100644
--- a/libide/subprocess/ide-breakout-subprocess-private.h
+++ b/libide/subprocess/ide-breakout-subprocess-private.h
@@ -23,16 +23,24 @@
 
 G_BEGIN_DECLS
 
-IdeSubprocess *_ide_breakout_subprocess_new (const gchar          *cwd,
-                                             const gchar * const  *argv,
-                                             const gchar * const  *env,
-                                             GSubprocessFlags      flags,
-                                             gboolean              clear_flags,
-                                             gint                  stdin_fd,
-                                             gint                  stdout_fd,
-                                             gint                  stderr_fd,
-                                             GCancellable         *cancellable,
-                                             GError              **error) G_GNUC_INTERNAL;
+typedef struct
+{
+  gint source_fd;
+  gint dest_fd;
+} IdeBreakoutFdMapping;
+
+IdeSubprocess *_ide_breakout_subprocess_new (const gchar                 *cwd,
+                                             const gchar * const         *argv,
+                                             const gchar * const         *env,
+                                             GSubprocessFlags             flags,
+                                             gboolean                     clear_flags,
+                                             gint                         stdin_fd,
+                                             gint                         stdout_fd,
+                                             gint                         stderr_fd,
+                                             const IdeBreakoutFdMapping  *fd_map,
+                                             guint                        fd_map_len,
+                                             GCancellable                *cancellable,
+                                             GError                     **error) G_GNUC_INTERNAL;
 
 G_END_DECLS
 
diff --git a/libide/subprocess/ide-breakout-subprocess.c b/libide/subprocess/ide-breakout-subprocess.c
index 655482a..72cc435 100644
--- a/libide/subprocess/ide-breakout-subprocess.c
+++ b/libide/subprocess/ide-breakout-subprocess.c
@@ -36,6 +36,7 @@
 
 #include "application/ide-application.h"
 #include "subprocess/ide-breakout-subprocess.h"
+#include "subprocess/ide-breakout-subprocess-private.h"
 #include "util/ide-glib.h"
 
 #ifndef FLATPAK_HOST_COMMAND_FLAGS_CLEAR_ENV
@@ -82,6 +83,9 @@ struct _IdeBreakoutSubprocess
   GInputStream *stdout_pipe;
   GInputStream *stderr_pipe;
 
+  IdeBreakoutFdMapping *fd_mapping;
+  guint fd_mapping_len;
+
   GMainContext *main_context;
 
   guint sigint_id;
@@ -1350,6 +1354,33 @@ ide_breakout_subprocess_initable_init (GInitable     *initable,
 
 
   /*
+   * Now add the rest of our FDs that we might need to map in for which
+   * the subprocess launcher tried to map.
+   */
+  for (guint i = 0; i < self->fd_mapping_len; i++)
+    {
+      const IdeBreakoutFdMapping *map = &self->fd_mapping[i];
+      g_autoptr(GError) fd_error = NULL;
+      gint dest_handle;
+
+      dest_handle = g_unix_fd_list_append (fd_list, map->source_fd, &fd_error);
+
+      if (dest_handle != -1)
+        g_variant_builder_add (fd_builder, "{uh}", map->dest_fd, dest_handle);
+      else
+        g_warning ("%s", fd_error->message);
+
+      close (map->source_fd);
+    }
+
+  /*
+   * We don't want to allow these FDs to be used again.
+   */
+  self->fd_mapping_len = 0;
+  g_clear_pointer (&self->fd_mapping, g_free);
+
+
+  /*
    * Build streams for our application to use.
    */
   maybe_create_output_stream (&self->stdin_pipe, &stdin_pair[1], !!(self->flags & 
G_SUBPROCESS_FLAGS_STDIN_PIPE));
@@ -1568,6 +1599,10 @@ ide_breakout_subprocess_finalize (GObject *object)
   if (self->stderr_fd != -1)
     close (self->stderr_fd);
 
+  for (guint i = 0; i < self->fd_mapping_len; i++)
+    close (self->fd_mapping[i].source_fd);
+  g_clear_pointer (&self->fd_mapping, g_free);
+
   G_OBJECT_CLASS (ide_breakout_subprocess_parent_class)->finalize (object);
 
   EGG_COUNTER_DEC (instances);
@@ -1697,16 +1732,18 @@ ide_breakout_subprocess_init (IdeBreakoutSubprocess *self)
 }
 
 IdeSubprocess *
-_ide_breakout_subprocess_new (const gchar          *cwd,
-                              const gchar * const  *argv,
-                              const gchar * const  *env,
-                              GSubprocessFlags      flags,
-                              gboolean              clear_env,
-                              gint                  stdin_fd,
-                              gint                  stdout_fd,
-                              gint                  stderr_fd,
-                              GCancellable         *cancellable,
-                              GError              **error)
+_ide_breakout_subprocess_new (const gchar                 *cwd,
+                              const gchar * const         *argv,
+                              const gchar * const         *env,
+                              GSubprocessFlags             flags,
+                              gboolean                     clear_env,
+                              gint                         stdin_fd,
+                              gint                         stdout_fd,
+                              gint                         stderr_fd,
+                              const IdeBreakoutFdMapping  *fd_mapping,
+                              guint                        fd_mapping_len,
+                              GCancellable                *cancellable,
+                              GError                     **error)
 {
   g_autoptr(IdeBreakoutSubprocess) ret = NULL;
 
@@ -1725,6 +1762,10 @@ _ide_breakout_subprocess_new (const gchar          *cwd,
   ret->stdout_fd = stdout_fd;
   ret->stderr_fd = stderr_fd;
 
+  ret->fd_mapping = g_new0 (IdeBreakoutFdMapping, fd_mapping_len);
+  ret->fd_mapping_len = fd_mapping_len;
+  memcpy (ret->fd_mapping, fd_mapping, sizeof(gint) * fd_mapping_len);
+
   if (!g_initable_init (G_INITABLE (ret), cancellable, error))
     return NULL;
 
diff --git a/libide/subprocess/ide-subprocess-launcher.c b/libide/subprocess/ide-subprocess-launcher.c
index 6a1dd3e..2a4f797 100644
--- a/libide/subprocess/ide-subprocess-launcher.c
+++ b/libide/subprocess/ide-subprocess-launcher.c
@@ -43,6 +43,7 @@ typedef struct
   GPtrArray        *argv;
   gchar            *cwd;
   gchar           **environ;
+  GArray           *fd_mapping;
 
   gint              stdin_fd;
   gint              stdout_fd;
@@ -52,6 +53,12 @@ typedef struct
   guint             clear_env : 1;
 } IdeSubprocessLauncherPrivate;
 
+typedef struct
+{
+  gint source_fd;
+  gint dest_fd;
+} FdMapping;
+
 G_DEFINE_TYPE_WITH_PRIVATE (IdeSubprocessLauncher, ide_subprocess_launcher, G_TYPE_OBJECT)
 
 enum {
@@ -175,6 +182,7 @@ ide_subprocess_launcher_spawn_host_worker (GTask        *task,
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
   g_autoptr(IdeSubprocess) process = NULL;
   g_autoptr(GError) error = NULL;
+  g_autoptr(GArray) fds = NULL;
 
   IDE_ENTRY;
 
@@ -191,6 +199,8 @@ ide_subprocess_launcher_spawn_host_worker (GTask        *task,
   }
 #endif
 
+  fds = g_steal_pointer (&priv->fd_mapping);
+
   process = _ide_breakout_subprocess_new (priv->cwd,
                                           (const gchar * const *)priv->argv->pdata,
                                           (const gchar * const *)priv->environ,
@@ -199,6 +209,8 @@ ide_subprocess_launcher_spawn_host_worker (GTask        *task,
                                           priv->stdin_fd,
                                           priv->stdout_fd,
                                           priv->stderr_fd,
+                                          fds ? (gpointer)fds->data : NULL,
+                                          fds ? fds->len : 0,
                                           cancellable,
                                           &error);
 
@@ -280,6 +292,18 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
       priv->stderr_fd = -1;
     }
 
+  if (priv->fd_mapping != NULL)
+    {
+      g_autoptr(GArray) ar = g_steal_pointer (&priv->fd_mapping);
+
+      for (guint i = 0; i < ar->len; i++)
+        {
+          const FdMapping *map = &g_array_index (ar, FdMapping, i);
+
+          g_subprocess_launcher_take_fd (launcher, map->source_fd, map->dest_fd);
+        }
+    }
+
   /*
    * GSubprocessLauncher starts by inheriting the current environment.
    * So if clear-env is set, we need to unset those environment variables.
@@ -362,6 +386,19 @@ ide_subprocess_launcher_finalize (GObject *object)
   IdeSubprocessLauncher *self = (IdeSubprocessLauncher *)object;
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
+  if (priv->fd_mapping != NULL)
+    {
+      for (guint i = 0; i < priv->fd_mapping->len; i++)
+        {
+          FdMapping *map = &g_array_index (priv->fd_mapping, FdMapping, i);
+
+          if (map->source_fd != -1)
+            close (map->source_fd);
+        }
+
+      g_clear_pointer (&priv->fd_mapping, g_array_unref);
+    }
+
   g_clear_pointer (&priv->argv, g_ptr_array_unref);
   g_clear_pointer (&priv->cwd, g_free);
   g_clear_pointer (&priv->environ, g_strfreev);
@@ -882,3 +919,24 @@ ide_subprocess_launcher_replace_argv (IdeSubprocessLauncher *self,
   g_ptr_array_index (priv->argv, index) = g_strdup (arg);
   g_free (old_arg);
 }
+
+void
+ide_subprocess_launcher_take_fd (IdeSubprocessLauncher *self,
+                                 gint                   source_fd,
+                                 gint                   dest_fd)
+{
+  IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
+  FdMapping map = {
+    .source_fd = source_fd,
+    .dest_fd = dest_fd
+  };
+
+  g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
+  g_return_if_fail (source_fd > -1);
+  g_return_if_fail (dest_fd > -1);
+
+  if (priv->fd_mapping == NULL)
+    priv->fd_mapping = g_array_new (FALSE, FALSE, sizeof (FdMapping));
+
+  g_array_append_val (priv->fd_mapping, map);
+}
diff --git a/libide/subprocess/ide-subprocess-launcher.h b/libide/subprocess/ide-subprocess-launcher.h
index 69b5f0e..ae0e437 100644
--- a/libide/subprocess/ide-subprocess-launcher.h
+++ b/libide/subprocess/ide-subprocess-launcher.h
@@ -86,6 +86,9 @@ gchar                 *ide_subprocess_launcher_pop_argv            (IdeSubproces
 IdeSubprocess         *ide_subprocess_launcher_spawn               (IdeSubprocessLauncher  *self,
                                                                     GCancellable           *cancellable,
                                                                     GError                **error);
+void                   ide_subprocess_launcher_take_fd             (IdeSubprocessLauncher  *self,
+                                                                    gint                    source_fd,
+                                                                    gint                    dest_fd);
 void                   ide_subprocess_launcher_take_stdin_fd       (IdeSubprocessLauncher  *self,
                                                                     gint                    stdin_fd);
 void                   ide_subprocess_launcher_take_stdout_fd      (IdeSubprocessLauncher  *self,


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