blob: ea351598e29bebd1ae07aefb0510b1a7d7bf1847 [file] [log] [blame]
From 911f1b44f21108dd8520e3ba68a48814fc8402b0 Mon Sep 17 00:00:00 2001
From: Philip Withnall <philip.withnall@collabora.co.uk>
Date: Tue, 10 Mar 2015 15:01:13 +0000
Subject: [PATCH] gdbus-tool: Add a command to wait for a well-known name on
the bus
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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.
There is a 5 minute timeout to ensure the process doesnt block forever.
https://bugzilla.gnome.org/show_bug.cgi?id=745971
---
docs/reference/gio/gdbus.xml | 53 +++++++++
gio/gdbus-tool.c | 256 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 308 insertions(+), 1 deletion(-)
diff --git a/docs/reference/gio/gdbus.xml b/docs/reference/gio/gdbus.xml
index 81682d9..12093d5 100644
--- a/docs/reference/gio/gdbus.xml
+++ b/docs/reference/gio/gdbus.xml
@@ -93,6 +93,17 @@
</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>
+ <arg choice="plain"><replaceable>bus_name</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>gdbus</command>
<arg choice="plain">help</arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -148,6 +159,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 +357,39 @@ $ 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 5 minutes. Set <option>--timeout</option> to 0 to disable the
+ timeout:
+</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 f24dd71..e14448b 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);
@@ -1891,6 +1892,249 @@ handle_monitor (gint *argc,
/* ---------------------------------------------------------------------------------------------------- */
+static gboolean opt_wait_activate_set = FALSE;
+static gchar *opt_wait_activate_name = NULL;
+static gboolean wait_hit_timeout = FALSE;
+static gint64 opt_wait_timeout = 5 * 60; /* 5 minutes */
+
+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 is 5 minutes"), "SECS" },
+ { NULL }
+};
+
+static void
+wait_name_appeared_cb (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ GMainLoop *loop = user_data;
+
+ g_main_loop_quit (loop);
+ wait_hit_timeout = FALSE;
+}
+
+static gboolean
+wait_timeout_cb (gpointer user_data)
+{
+ GMainLoop *loop = user_data;
+
+ g_main_loop_quit (loop);
+ wait_hit_timeout = TRUE;
+
+ /* 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;
+ GMainLoop *loop;
+ guint watch_id, timer_id = 0, activate_watch_id;
+ const gchar *activate_service, *wait_service;
+
+ 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;
+ }
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ /* 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,
+ g_main_loop_ref (loop),
+ (GDestroyNotify) g_main_loop_unref);
+
+ /* Safety timeout. */
+ if (opt_wait_timeout > 0)
+ {
+ timer_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ opt_wait_timeout,
+ wait_timeout_cb,
+ g_main_loop_ref (loop),
+ (GDestroyNotify) g_main_loop_unref);
+ }
+
+ g_main_loop_run (loop);
+
+ g_main_loop_unref (loop);
+ 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_hit_timeout;
+
+ 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,
@@ -2023,6 +2267,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;
@@ -2092,7 +2346,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;
}
--
1.9.3