[glib] gdbus-tool: Add a command to wait for a well-known name on the bus



commit 7890573f6ec21fa66258dc32bbcbacb17cb67ced
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Tue Mar 10 15:01:13 2015 +0000

    gdbus-tool: Add a command to wait for a well-known name on the bus
    
    This is effectively the mc-wait-for-name tool from
    telepathy-mission-control; moving it in to gdbus-tool will make it more
    widely useful without making people depend on telepathy-mission-control
    for no other reason. The code here is reimplemented from scratch to use
    GDBus.
    
    It blocks until the specified well-known name is owned by some process
    on the bus (which can be the session, system, or any other bus). By
    passing --activate, the same (or a different) name can be auto-started
    on the bus first.
    
    A timeout can be specified to ensure the process doesn’t block forever.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=745971

 docs/reference/gio/gdbus.xml |   55 +++++++++
 gio/gdbus-tool.c             |  249 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 303 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gio/gdbus.xml b/docs/reference/gio/gdbus.xml
index 81682d9..efcec33 100644
--- a/docs/reference/gio/gdbus.xml
+++ b/docs/reference/gio/gdbus.xml
@@ -93,6 +93,20 @@
   </cmdsynopsis>
   <cmdsynopsis>
     <command>gdbus</command>
+    <arg choice="plain">wait</arg>
+    <group>
+      <arg choice="plain">--system</arg>
+      <arg choice="plain">--session</arg>
+      <arg choice="plain">--address <replaceable>address</replaceable></arg>
+    </group>
+    <arg choice="plain">--activate <replaceable>bus_name</replaceable></arg>
+    <group>
+      <arg choice="plain">--timeout <replaceable>seconds</replaceable></arg>
+    </group>
+    <arg choice="plain"><replaceable>bus_name</replaceable></arg>
+  </cmdsynopsis>
+  <cmdsynopsis>
+    <command>gdbus</command>
     <arg choice="plain">help</arg>
   </cmdsynopsis>
 </refsynopsisdiv>
@@ -148,6 +162,15 @@
         </para></listitem>
       </varlistentry>
       <varlistentry>
+        <term><option>wait</option></term>
+        <listitem><para>
+          Waits until <replaceable>bus_name</replaceable> is owned by some
+          process on the bus. If the <option>--activate</option> is specified,
+          that bus name will be auto-started first. It may be the same as the
+          bus name being waited for, or different.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
         <term><option>help</option></term>
         <listitem><para>
           Prints help and exit.
@@ -337,6 +360,38 @@ $ gdbus emit --session --object-path /foo --signal org.bar.Foo "['foo', 'bar', '
 $ gdbus emit --session --object-path /bar --signal org.bar.Bar someString --dest :1.42
 </programlisting>
 
+<para>
+  Waiting for a well-known name to be owned on the bus; this will
+  <emphasis>not</emphasis> auto-start the service:
+</para>
+<programlisting>
+$ gdbus wait --session org.bar.SomeName
+</programlisting>
+
+<para>
+  Auto-starting then waiting for a well-known name to be owned on the bus:
+</para>
+<programlisting>
+$ gdbus wait --session --activate org.bar.SomeName
+</programlisting>
+
+<para>
+  Auto-starting a different service, then waiting for a well-known name to be
+  owned on the bus. This is useful in situations where
+  <replaceable>SomeName</replaceable> is not directly activatable:
+</para>
+<programlisting>
+$ gdbus wait --session --activate org.bar.PrerequisiteName org.bar.SomeName
+</programlisting>
+
+<para>
+  Waiting for a well-known name and giving up after 30 seconds. By default,
+  the timeout is disabled; or set <option>--timeout</option> to 0 to disable it:
+</para>
+<programlisting>
+$ gdbus wait --session --timeout 30 org.bar.SomeName
+</programlisting>
+
 </refsect1>
 
 <refsect1>
diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c
index 7503528..3e34b98 100644
--- a/gio/gdbus-tool.c
+++ b/gio/gdbus-tool.c
@@ -98,6 +98,7 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout)
                          "  monitor      Monitor a remote object\n"
                          "  call         Invoke a method on a remote object\n"
                          "  emit         Emit a signal\n"
+                         "  wait         Wait for a bus name to appear\n"
                          "\n"
                          "Use “%s COMMAND --help” to get help on each command.\n"),
                        program_name);
@@ -1949,6 +1950,242 @@ handle_monitor (gint        *argc,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gboolean opt_wait_activate_set = FALSE;
+static gchar *opt_wait_activate_name = NULL;
+static gint64 opt_wait_timeout = 0;  /* no timeout */
+
+typedef enum {
+  WAIT_STATE_RUNNING,  /* waiting to see the service */
+  WAIT_STATE_SUCCESS,  /* seen it successfully */
+  WAIT_STATE_TIMEOUT,  /* timed out before seeing it */
+} WaitState;
+
+static gboolean
+opt_wait_activate_cb (const gchar  *option_name,
+                      const gchar  *value,
+                      gpointer      data,
+                      GError      **error)
+{
+  /* @value may be NULL */
+  opt_wait_activate_set = TRUE;
+  opt_wait_activate_name = g_strdup (value);
+
+  return TRUE;
+}
+
+static const GOptionEntry wait_entries[] =
+{
+  { "activate", 'a', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+    opt_wait_activate_cb,
+    N_("Service to activate before waiting for the other one (well-known name)"),
+    "[NAME]" },
+  { "timeout", 't', 0, G_OPTION_ARG_INT64, &opt_wait_timeout,
+    N_("Timeout to wait for before exiting with an error (seconds); 0 for "
+       "no timeout (default)"), "SECS" },
+  { NULL }
+};
+
+static void
+wait_name_appeared_cb (GDBusConnection *connection,
+                       const gchar     *name,
+                       const gchar     *name_owner,
+                       gpointer         user_data)
+{
+  WaitState *wait_state = user_data;
+
+  *wait_state = WAIT_STATE_SUCCESS;
+}
+
+static gboolean
+wait_timeout_cb (gpointer user_data)
+{
+  WaitState *wait_state = user_data;
+
+  *wait_state = WAIT_STATE_TIMEOUT;
+
+  /* Removed in handle_wait(). */
+  return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+handle_wait (gint        *argc,
+             gchar      **argv[],
+             gboolean     request_completion,
+             const gchar *completion_cur,
+             const gchar *completion_prev)
+{
+  gint ret;
+  GOptionContext *o;
+  gchar *s;
+  GError *error;
+  GDBusConnection *c;
+  guint watch_id, timer_id = 0, activate_watch_id;
+  const gchar *activate_service, *wait_service;
+  WaitState wait_state = WAIT_STATE_RUNNING;
+
+  ret = FALSE;
+  c = NULL;
+
+  modify_argv0_for_command (argc, argv, "wait");
+
+  o = g_option_context_new (NULL);
+  g_option_context_set_help_enabled (o, FALSE);
+  g_option_context_set_summary (o, _("Wait for a bus name to appear."));
+  g_option_context_add_main_entries (o, wait_entries, GETTEXT_PACKAGE);
+  g_option_context_add_group (o, connection_get_group ());
+
+  if (!g_option_context_parse (o, argc, argv, NULL))
+    {
+      if (!request_completion)
+        {
+          s = g_option_context_get_help (o, FALSE, NULL);
+          g_printerr ("%s", s);
+          g_free (s);
+          goto out;
+        }
+    }
+
+  error = NULL;
+  c = connection_get_dbus_connection (&error);
+  if (c == NULL)
+    {
+      if (request_completion)
+        {
+          if (g_strcmp0 (completion_prev, "--address") == 0)
+            {
+              g_print ("unix:\n"
+                       "tcp:\n"
+                       "nonce-tcp:\n");
+            }
+          else
+            {
+              g_print ("--system \n--session \n--address \n");
+            }
+        }
+      else
+        {
+          g_printerr (_("Error connecting: %s\n"), error->message);
+          g_error_free (error);
+        }
+      goto out;
+    }
+
+  /* All done with completion now */
+  if (request_completion)
+    goto out;
+
+  /*
+   * Try and disentangle the command line arguments, with the aim of supporting:
+   *    gdbus wait --session --activate ActivateName WaitName
+   *    gdbus wait --session --activate ActivateAndWaitName
+   *    gdbus wait --activate --session ActivateAndWaitName
+   *    gdbus wait --session WaitName
+   */
+  if (*argc == 2 && opt_wait_activate_set && opt_wait_activate_name != NULL)
+    {
+      activate_service = opt_wait_activate_name;
+      wait_service = (*argv)[1];
+    }
+  else if (*argc == 2 &&
+           opt_wait_activate_set && opt_wait_activate_name == NULL)
+    {
+      activate_service = (*argv)[1];
+      wait_service = (*argv)[1];
+    }
+  else if (*argc == 2 && !opt_wait_activate_set)
+    {
+      activate_service = NULL;  /* disabled */
+      wait_service = (*argv)[1];
+    }
+  else if (*argc == 1 &&
+           opt_wait_activate_set && opt_wait_activate_name != NULL)
+    {
+      activate_service = opt_wait_activate_name;
+      wait_service = opt_wait_activate_name;
+    }
+  else if (*argc == 1 &&
+           opt_wait_activate_set && opt_wait_activate_name == NULL)
+    {
+      g_printerr (_("Error: A service to activate for must be specified.\n"));
+      goto out;
+    }
+  else if (*argc == 1 && !opt_wait_activate_set)
+    {
+      g_printerr (_("Error: A service to wait for must be specified.\n"));
+      goto out;
+    }
+  else /* if (*argc > 2) */
+    {
+      g_printerr (_("Error: Too many arguments.\n"));
+      goto out;
+    }
+
+  if (activate_service != NULL &&
+      (!g_dbus_is_name (activate_service) ||
+       g_dbus_is_unique_name (activate_service)))
+    {
+      g_printerr (_("Error: %s is not a valid well-known bus name.\n"),
+                  activate_service);
+      goto out;
+    }
+
+  if (!g_dbus_is_name (wait_service) || g_dbus_is_unique_name (wait_service))
+    {
+      g_printerr (_("Error: %s is not a valid well-known bus name.\n"),
+                  wait_service);
+      goto out;
+    }
+
+  /* All done with completion now */
+  if (request_completion)
+    goto out;
+
+  /* Start the prerequisite service if needed. */
+  if (activate_service != NULL)
+    {
+      activate_watch_id = g_bus_watch_name_on_connection (c, activate_service,
+                                                          G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+                                                          NULL, NULL,
+                                                          NULL, NULL);
+    }
+  else
+    {
+      activate_watch_id = 0;
+    }
+
+  /* Wait for the expected name to appear. */
+  watch_id = g_bus_watch_name_on_connection (c,
+                                             wait_service,
+                                             G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                             wait_name_appeared_cb,
+                                             NULL, &wait_state, NULL);
+
+  /* Safety timeout. */
+  if (opt_wait_timeout > 0)
+    timer_id = g_timeout_add (opt_wait_timeout, wait_timeout_cb, &wait_state);
+
+  while (wait_state == WAIT_STATE_RUNNING)
+    g_main_context_iteration (NULL, TRUE);
+
+  g_bus_unwatch_name (watch_id);
+  if (timer_id != 0)
+      g_source_remove (timer_id);
+  if (activate_watch_id != 0)
+      g_bus_unwatch_name (activate_watch_id);
+
+  ret = (wait_state == WAIT_STATE_SUCCESS);
+
+ out:
+  g_clear_object (&c);
+  g_option_context_free (o);
+  g_free (opt_wait_activate_name);
+  opt_wait_activate_name = NULL;
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static gchar *
 pick_word_at (const gchar  *s,
               gint          cursor,
@@ -2081,6 +2318,16 @@ main (gint argc, gchar *argv[])
         ret = 0;
       goto out;
     }
+  else if (g_strcmp0 (command, "wait") == 0)
+    {
+      if (handle_wait (&argc,
+                       &argv,
+                       request_completion,
+                       completion_cur,
+                       completion_prev))
+        ret = 0;
+      goto out;
+    }
   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
     {
       const gchar *completion_line;
@@ -2150,7 +2397,7 @@ main (gint argc, gchar *argv[])
     {
       if (request_completion)
         {
-          g_print ("help \nemit \ncall \nintrospect \nmonitor \n");
+          g_print ("help \nemit \ncall \nintrospect \nmonitor \nwait \n");
           ret = 0;
           goto out;
         }


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