| 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 doesn’t 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 |