[gnome-terminal] client: FD passing



commit f5fad240d85e8044055a50482452f9b4a53dc237
Author: Christian Persch <chpe gnome org>
Date:   Tue Mar 20 21:32:52 2012 +0100

    client: FD passing

 src/client.c               |  138 +++++++++++++++++++++++++++++++++++++++++++-
 src/org.gnome.Terminal.xml |    1 +
 src/terminal-controller.c  |    8 ++-
 src/terminal.c             |    1 +
 4 files changed, 144 insertions(+), 4 deletions(-)
---
diff --git a/src/client.c b/src/client.c
index 932f8a1..6fb7a90 100644
--- a/src/client.c
+++ b/src/client.c
@@ -34,6 +34,7 @@
 #include <glib.h>
 #include <glib/gprintf.h>
 #include <gio/gio.h>
+#include <gio/gunixfdlist.h>
 
 #include <gtk/gtk.h>
 
@@ -179,6 +180,10 @@ typedef struct
   char   *title;
   double  zoom;
 
+  /* Exec options */
+  GUnixFDList *fd_list;
+  GArray *fd_array;
+
   /* Processing options */
   gboolean wait;
 
@@ -242,6 +247,72 @@ option_bus_name_cb (const gchar *option_name,
   return TRUE;
 }
 
+typedef struct {
+  int index;
+  int fd;
+} PassFdElement;
+
+static gboolean
+option_fd_cb (const gchar *option_name,
+              const gchar *value,
+              gpointer     user_data,
+              GError     **error)
+{
+  OptionData *data = user_data;
+  int fd = -1;
+  PassFdElement e;
+
+  if (strcmp (option_name, "--fd") == 0) {
+    char *end = NULL;
+    errno = 0;
+    fd = g_ascii_strtoll (value, &end, 10);
+    if (errno != 0 || end == value) {
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                   "Invalid argument \"%s\" to --fd option", value);
+      return FALSE;
+    }
+
+  } else if (strcmp (option_name, "--stdin") == 0) {
+    fd = STDIN_FILENO;
+  } else if (strcmp (option_name, "--stdout") == 0) {
+    fd = STDOUT_FILENO;
+  } else if (strcmp (option_name, "--stderr") == 0) {
+    fd = STDERR_FILENO;
+  } else {
+    g_assert_not_reached ();
+  }
+
+  if (data->fd_list == NULL) {
+    data->fd_list = g_unix_fd_list_new ();
+    data->fd_array = g_array_new (FALSE, FALSE, sizeof (PassFdElement));
+  } else {
+    guint i, n;
+    n = data->fd_array->len;
+    for (i = 0; i < n; i++) {
+      e = g_array_index (data->fd_array, PassFdElement, i);
+      if (e.fd == fd) {
+        g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                     "Cannot pass FD %d twice", fd);
+        return FALSE;
+      }
+    }
+  }
+
+  e.fd = fd;
+  e.index = g_unix_fd_list_append (data->fd_list, fd, error);
+  if (e.index == -1)
+    return FALSE;
+
+  g_array_append_val (data->fd_array, e);
+
+  if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
+    quiet = TRUE;
+  if (fd == STDIN_FILENO)
+    data->wait = TRUE;
+
+  return TRUE;
+}
+
 static GOptionContext *
 get_goption_context (OptionData *data)
 {
@@ -282,6 +353,18 @@ get_goption_context (OptionData *data)
     { NULL, 0, 0, 0, NULL, NULL, NULL }
   };
 
+  const GOptionEntry exec_goptions[] = {
+    { "stdin", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_fd_cb,
+      N_("Forward stdin"), NULL },
+    { "stdout", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_fd_cb,
+      N_("Forward stdout"), NULL },
+    { "stderr", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_fd_cb,
+      N_("Forward stderr"), NULL },
+    { "fd", 0, 0, G_OPTION_ARG_CALLBACK, option_fd_cb,
+      N_("Forward file descriptor"), N_("FD") },
+    { NULL, 0, 0, 0, NULL, NULL, NULL }
+  };
+
   const GOptionEntry processing_goptions[] = {
     { "wait", 0, 0, G_OPTION_ARG_NONE, &data->wait,
       N_("Wait until the child exits"), NULL },
@@ -329,6 +412,15 @@ get_goption_context (OptionData *data)
   g_option_group_add_entries (group, terminal_goptions);
   g_option_context_add_group (context, group);
 
+  group = g_option_group_new ("exec-goptions",
+                              N_("Exec options:"),
+                              N_("Show exec options"),
+                              data,
+                              NULL);
+  g_option_group_set_translation_domain (group, GETTEXT_PACKAGE);
+  g_option_group_add_entries (group, exec_goptions);
+  g_option_context_add_group (context, group);
+
   group = g_option_group_new ("processing-goptions",
                               N_("Processing options:"),
                               N_("Show processing options"),
@@ -357,6 +449,13 @@ option_data_free (OptionData *data)
   g_free (data->working_directory);
   g_free (data->profile);
   g_free (data->title);
+
+  if (data->fd_list)
+    g_object_unref (data->fd_list);
+  if (data->fd_array)
+    g_array_free (data->fd_array, TRUE);
+
+  g_free (data);
 }
 
 static OptionData *
@@ -437,7 +536,8 @@ build_create_options_variant (OptionData *data)
  * Returns: a floating #GVariant
  */
 static GVariant *
-build_exec_options_variant (OptionData *data)
+build_exec_options_variant (OptionData *data,
+                            GUnixFDList **fd_list)
 {
   GVariantBuilder builder;
 
@@ -446,6 +546,31 @@ build_exec_options_variant (OptionData *data)
   terminal_client_append_exec_options (&builder,
                                        data->working_directory);
 
+  if (data->fd_array != NULL) {
+    int i, n_fds;
+
+    g_variant_builder_open (&builder, G_VARIANT_TYPE ("{sv}"));
+    g_variant_builder_add (&builder, "s", "fd-set");
+
+    g_variant_builder_open (&builder, G_VARIANT_TYPE ("v"));
+    g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(ih)"));
+    n_fds = (int) data->fd_array->len;
+    for (i = 0; i < n_fds; i++) {
+      PassFdElement e =  g_array_index (data->fd_array, PassFdElement, i);
+
+      g_variant_builder_add (&builder, "(ih)", e.fd, e.index);
+    }
+    g_variant_builder_close (&builder); /* a(ih) */
+    g_variant_builder_close (&builder); /* v */
+
+    g_variant_builder_close (&builder); /* {sv} */
+
+    *fd_list = data->fd_list;
+    data->fd_list = NULL;
+  } else {
+    *fd_list = NULL;
+  }
+
   return g_variant_builder_end (&builder);
 }
 
@@ -478,6 +603,8 @@ handle_open (int *argc,
   TerminalReceiver *receiver;
   GError *error = NULL;
   char *object_path;
+  GVariant *arguments;
+  GUnixFDList *fd_list;
 
   modify_argv0_for_command (argc, argv, "open");
 
@@ -537,17 +664,22 @@ handle_open (int *argc,
 
   g_free (object_path);
 
+  arguments = build_exec_options_variant (data, &fd_list);
   if (!terminal_receiver_call_exec_sync (receiver,
-                                         build_exec_options_variant (data),
+                                         arguments,
                                          g_variant_new_bytestring_array ((const char * const *) data->exec_argv, data->exec_argc),
+                                         fd_list,
+                                         NULL, /* outfdlist */
                                          NULL /* cancellable */,
                                          &error)) {
-    g_printerr ("Error: %s\n", error->message);
+    _printerr ("Error: %s\n", error->message);
     g_error_free (error);
+    g_clear_object (fd_list);
     g_object_unref (receiver);
     option_data_free (data);
     return FALSE;
   }
+  g_clear_object (fd_list);
 
   if (data->wait) {
     WaitData wait_data;
diff --git a/src/org.gnome.Terminal.xml b/src/org.gnome.Terminal.xml
index 06fe6ab..a6ada0e 100644
--- a/src/org.gnome.Terminal.xml
+++ b/src/org.gnome.Terminal.xml
@@ -29,6 +29,7 @@
   <interface name="org.gnome.Terminal.Terminal0">
     <annotation name="org.gtk.GDBus.C.Name" value="Receiver" />
     <method name="Exec">
+      <annotation name="org.gtk.GDBus.C.UnixFD" value="true" />
       <arg type="a{sv}" name="options" direction="in" />
       <arg type="aay" name="arguments" direction="in">
         <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true" />
diff --git a/src/terminal-controller.c b/src/terminal-controller.c
index db74871..b5b1635 100644
--- a/src/terminal-controller.c
+++ b/src/terminal-controller.c
@@ -82,6 +82,7 @@ terminal_controller_set_screen (TerminalController *controller,
 static gboolean 
 terminal_controller_exec (TerminalReceiver *receiver,
                           GDBusMethodInvocation *invocation,
+                          GUnixFDList *fd_list,
                           GVariant *options,
                           GVariant *arguments)
 {
@@ -90,6 +91,7 @@ terminal_controller_exec (TerminalReceiver *receiver,
   const char *working_directory;
   char **exec_argv, **envv;
   gsize exec_argc;
+  GVariantIter *fd_iter;
   GError *error;
 
   if (priv->screen == NULL) {
@@ -104,6 +106,8 @@ terminal_controller_exec (TerminalReceiver *receiver,
     working_directory = NULL;
   if (!g_variant_lookup (options, "environ", "^a&ay", &envv))
     envv = NULL;
+  if (!g_variant_lookup (options, "fd-set", "a(ih)", &fd_iter))
+    fd_iter = NULL;
 
   if (working_directory != NULL)
     _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
@@ -119,11 +123,13 @@ terminal_controller_exec (TerminalReceiver *receiver,
                              &error)) {
     g_dbus_method_invocation_take_error (invocation, error);
   } else {
-    terminal_receiver_complete_exec (receiver, invocation);
+    terminal_receiver_complete_exec (receiver, invocation, NULL /* outfdlist */);
   }
 
   g_free (exec_argv);
   g_free (envv);
+  if (fd_iter)
+    g_variant_iter_free (fd_iter);
 
 out:
 
diff --git a/src/terminal.c b/src/terminal.c
index 483de58..41e6003 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -213,6 +213,7 @@ handle_options (TerminalFactory *factory,
           if (!terminal_receiver_call_exec_sync (receiver,
                                                  g_variant_builder_end (&builder),
                                                  g_variant_new_bytestring_array ((const char * const *) argv, argc),
+                                                 NULL /* infdlist */, NULL /* outfdlist */,
                                                 NULL /* cancellable */,
                                                 &err)) {
             g_printerr ("Error: %s\n", err->message);



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