From ea1e0496b0329147b932d5a1486f5a81b4121651 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 12 May 2010 23:12:14 -0400 Subject: [PATCH] GDBus: add 'monitor' verb to gdbus(1) This uncovered a bug in name watching if the name wasn't activatable. Also provoked the need for on_connection variants of g_bus_watch_name (added g_bus_watch_proxy's variant as well). --- docs/reference/gio/gdbus.xml | 43 ++++++ docs/reference/gio/gio-sections.txt | 2 + gio/gdbus-tool.c | 253 +++++++++++++++++++++++++++++++++++- gio/gdbusnamewatching.c | 69 +++++++++- gio/gdbusnamewatching.h | 23 ++-- gio/gdbusproxywatching.c | 86 ++++++++++++ gio/gdbusproxywatching.h | 35 +++-- gio/gio.symbols | 2 + 8 files changed, 489 insertions(+), 24 deletions(-) diff --git a/docs/reference/gio/gdbus.xml b/docs/reference/gio/gdbus.xml index 4bd0eb7..a83511a 100644 --- a/docs/reference/gio/gdbus.xml +++ b/docs/reference/gio/gdbus.xml @@ -25,6 +25,19 @@ gdbus + monitor + + --system + --session + --address address + + --dest bus_name + + --object-path /path/to/object + + + + gdbus call --system @@ -61,6 +74,13 @@ + + + Monitors one or all objects owned by the owned of + bus_name. + + + Invokes a method on a remote object. Each argument to pass to the @@ -189,6 +209,29 @@ $ gdbus call --session \ 5000 (uint32 12,) + + Monitoring all objects on a service: + + +$ gdbus monitor --system --dest org.freedesktop.ConsoleKit +Monitoring signals from all objects owned by org.freedesktop.ConsoleKit +The name org.freedesktop.ConsoleKit is owned by :1.15 +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (false,) +/org/freedesktop/ConsoleKit/Seat1: org.freedesktop.ConsoleKit.Seat.ActiveSessionChanged ('',) +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (true,) +/org/freedesktop/ConsoleKit/Seat1: org.freedesktop.ConsoleKit.Seat.ActiveSessionChanged ('/org/freedesktop/ConsoleKit/Session2',) + + + Monitoring a single object on a service: + + +$ gdbus monitor --system --dest org.freedesktop.ConsoleKit --object-path /org/freedesktop/ConsoleKit/Session2 +Monitoring signals on object /org/freedesktop/ConsoleKit/Session2 owned by org.freedesktop.ConsoleKit +The name org.freedesktop.ConsoleKit is owned by :1.15 +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (false,) +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (true,) + + diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 1085674..3047f8e 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2446,6 +2446,7 @@ GBusNameAppearedCallback GBusNameVanishedCallback GBusNameWatcherFlags g_bus_watch_name +g_bus_watch_name_on_connection g_bus_unwatch_name @@ -2454,6 +2455,7 @@ g_bus_unwatch_name GBusProxyAppearedCallback GBusProxyVanishedCallback g_bus_watch_proxy +g_bus_watch_proxy_on_connection g_bus_unwatch_proxy diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index 96a66ca..95d8128 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -92,6 +92,7 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout) s = g_strdup_printf (_("Commands:\n" " help Shows this information\n" " introspect Introspect a remote object\n" + " monitor Monitor a remote object\n" " call Invoke a method on a remote object\n" "\n" "Use \"%s COMMAND --help\" to get help on each command.\n"), @@ -1380,6 +1381,246 @@ handle_introspect (gint *argc, /* ---------------------------------------------------------------------------------------------------- */ +static gchar *opt_monitor_dest = NULL; +static gchar *opt_monitor_object_path = NULL; + +static guint monitor_filter_id = 0; + +static void +monitor_signal_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + gchar *s; + s = g_variant_print (parameters, TRUE); + g_print ("%s: %s.%s %s\n", + object_path, + interface_name, + signal_name, + s); + g_free (s); +} + +static void +monitor_on_name_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + g_print ("The name %s is owned by %s\n", name, name_owner); + g_assert (monitor_filter_id == 0); + monitor_filter_id = g_dbus_connection_signal_subscribe (connection, + name_owner, + NULL, /* any interface */ + NULL, /* any member */ + opt_monitor_object_path, + NULL, /* arg0 */ + monitor_signal_cb, + NULL, /* user_data */ + NULL); /* user_data destroy notify */ +} + +static void +monitor_on_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_print ("The name %s does not have an owner\n", name); + + if (monitor_filter_id != 0) + { + g_dbus_connection_signal_unsubscribe (connection, monitor_filter_id); + monitor_filter_id = 0; + } +} + +static const GOptionEntry monitor_entries[] = +{ + { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_monitor_dest, N_("Destination name to monitor"), NULL}, + { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_monitor_object_path, N_("Object path to monitor"), NULL}, + { NULL } +}; + +static gboolean +handle_monitor (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; + GVariant *result; + GDBusNodeInfo *node; + gboolean complete_names; + gboolean complete_paths; + GMainLoop *loop; + + ret = FALSE; + c = NULL; + node = NULL; + result = NULL; + + modify_argv0_for_command (argc, argv, "monitor"); + + o = g_option_context_new (NULL); + if (request_completion) + g_option_context_set_ignore_unknown_options (o, TRUE); + g_option_context_set_help_enabled (o, FALSE); + g_option_context_set_summary (o, _("Monitor a remote object.")); + g_option_context_add_main_entries (o, monitor_entries, NULL /* GETTEXT_PACKAGE*/); + g_option_context_add_group (o, connection_get_group ()); + + complete_names = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0) + { + complete_names = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + complete_paths = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0) + { + complete_paths = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + 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; + } + + if (g_dbus_connection_get_unique_name (c) != NULL) + { + if (complete_names) + { + print_names (c, FALSE); + goto out; + } + /* this only makes sense on message bus connections */ + if (opt_monitor_dest == NULL) + { + if (request_completion) + g_print ("--dest \n"); + else + g_printerr (_("Error: Destination is not specified\n")); + goto out; + } + if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0) + { + print_names (c, g_str_has_prefix (opt_monitor_dest, ":")); + goto out; + } + } + if (complete_paths) + { + print_paths (c, opt_monitor_dest, "/"); + goto out; + } + if (opt_monitor_object_path == NULL) + { + if (request_completion) + { + g_print ("--object-path \n"); + goto out; + } + /* it's fine to not have an object path */ + } + if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0) + { + gchar *p; + s = g_strdup (opt_monitor_object_path); + p = strrchr (s, '/'); + if (p != NULL) + { + if (p == s) + p++; + *p = '\0'; + } + print_paths (c, opt_monitor_dest, s); + g_free (s); + goto out; + } + if (!request_completion && (opt_monitor_object_path != NULL && !g_variant_is_object_path (opt_monitor_object_path))) + { + g_printerr (_("Error: %s is not a valid object path\n"), opt_monitor_object_path); + goto out; + } + + /* All done with completion now */ + if (request_completion) + goto out; + + if (opt_monitor_object_path != NULL) + g_print ("Monitoring signals on object %s owned by %s\n", opt_monitor_object_path, opt_monitor_dest); + else + g_print ("Monitoring signals from all objects owned by %s\n", opt_monitor_dest); + + loop = g_main_loop_new (NULL, FALSE); + g_bus_watch_name_on_connection (c, + opt_monitor_dest, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + monitor_on_name_appeared, + monitor_on_name_vanished, + NULL, + NULL); + + g_main_loop_run (loop); + g_main_loop_unref (loop); + + ret = TRUE; + + out: + if (node != NULL) + g_dbus_node_info_unref (node); + if (result != NULL) + g_variant_unref (result); + if (c != NULL) + g_object_unref (c); + g_option_context_free (o); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + static gchar * pick_word_at (const gchar *s, gint cursor, @@ -1476,6 +1717,16 @@ main (gint argc, gchar *argv[]) ret = 0; goto out; } + else if (g_strcmp0 (command, "monitor") == 0) + { + if (handle_monitor (&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; @@ -1545,7 +1796,7 @@ main (gint argc, gchar *argv[]) { if (request_completion) { - g_print ("help \ncall \nintrospect \n"); + g_print ("help \ncall \nintrospect \nmonitor \n"); ret = 0; goto out; } diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index 3d96ee0..903e444 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -417,10 +417,11 @@ start_service_by_name_cb (GObject *source_object, * org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Epiphany2 * was not provided by any .service files * - * so just report vanished. + * This doesn't mean that the name doesn't have an owner, just + * that it's not provided by a .service file. So proceed to + * invoke GetNameOwner(). */ - call_vanished_handler (client, FALSE); - client->initialized = TRUE; + invoke_get_name_owner (client); } if (result != NULL) @@ -587,6 +588,68 @@ g_bus_watch_name (GBusType bus_type, } /** + * g_bus_watch_name_on_connection: + * @connection: A #GDBusConnection that is not closed. + * @name: The name (well-known or unique) to watch. + * @flags: Flags from the #GBusNameWatcherFlags enumeration. + * @name_appeared_handler: Handler to invoke when @name is known to exist or %NULL. + * @name_vanished_handler: Handler to invoke when @name is known to not exist or %NULL. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Like g_bus_watch_name() but takes a #GDBusConnection instead of a + * #GBusType. + * + * Returns: An identifier (never 0) that an be used with + * g_bus_unwatch_name() to stop watching the name. + * + * Since: 2.26 + */ +guint g_bus_watch_name_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (g_dbus_is_name (name), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->ref_count = 1; + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->flags = flags; + client->name_appeared_handler = name_appeared_handler; + client->name_vanished_handler = name_vanished_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + client->connection = g_object_ref (connection); + G_UNLOCK (lock); + + has_connection (client); + + return client->id; +} + +/** * g_bus_unwatch_name: * @watcher_id: An identifier obtained from g_bus_watch_name() * diff --git a/gio/gdbusnamewatching.h b/gio/gdbusnamewatching.h index 1bbf8e0..a424f43 100644 --- a/gio/gdbusnamewatching.h +++ b/gio/gdbusnamewatching.h @@ -58,14 +58,21 @@ typedef void (*GBusNameVanishedCallback) (GDBusConnection *connection, gpointer user_data); -guint g_bus_watch_name (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - GBusNameAppearedCallback name_appeared_handler, - GBusNameVanishedCallback name_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func); -void g_bus_unwatch_name (guint watcher_id); +guint g_bus_watch_name (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +guint g_bus_watch_name_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +void g_bus_unwatch_name (guint watcher_id); G_END_DECLS diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 7e90039..282254d 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -25,6 +25,7 @@ #include #include "gdbusutils.h" +#include "gdbusconnection.h" #include "gdbusnamewatching.h" #include "gdbusproxywatching.h" #include "gdbuserror.h" @@ -363,6 +364,91 @@ g_bus_watch_proxy (GBusType bus_type, } /** + * g_bus_watch_proxy_on_connection: + * @connection: A #GDBusConnection that is not closed. + * @name: The name (well-known or unique) to watch. + * @flags: Flags from the #GBusNameWatcherFlags enumeration. + * @object_path: The object path of the remote object to watch. + * @interface_name: The D-Bus interface name for the proxy. + * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type. + * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy. + * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the + * requested proxy is available. + * @proxy_vanished_handler: Handler to invoke when @name is known to not exist + * and the previously created proxy is no longer available. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Like g_bus_watch_proxy() but takes a #GDBusConnection instead of a + * #GBusType. + * + * Returns: An identifier (never 0) that can be used with + * g_bus_unwatch_proxy() to stop watching the remote object. + * + * Since: 2.26 + */ +guint +g_bus_watch_proxy_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (g_dbus_is_name (name), 0); + g_return_val_if_fail (g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0); + g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->proxy_appeared_handler = proxy_appeared_handler; + client->proxy_vanished_handler = proxy_vanished_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + client->name_watcher_id = g_bus_watch_name_on_connection (connection, + name, + flags, + on_name_appeared, + on_name_vanished, + client, + NULL); + + client->object_path = g_strdup (object_path); + client->interface_name = g_strdup (interface_name); + client->interface_type = interface_type; + client->proxy_flags = proxy_flags; + client->initial_construction = TRUE; + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + G_UNLOCK (lock); + + return client->id; +} + + +/** * g_bus_unwatch_proxy: * @watcher_id: An identifier obtained from g_bus_watch_proxy() * diff --git a/gio/gdbusproxywatching.h b/gio/gdbusproxywatching.h index 2dddaff..37c88dd 100644 --- a/gio/gdbusproxywatching.h +++ b/gio/gdbusproxywatching.h @@ -63,18 +63,29 @@ typedef void (*GBusProxyVanishedCallback) (GDBusConnection *connection, const gchar *name, gpointer user_data); -guint g_bus_watch_proxy (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GBusProxyAppearedCallback proxy_appeared_handler, - GBusProxyVanishedCallback proxy_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func); -void g_bus_unwatch_proxy (guint watcher_id); +guint g_bus_watch_proxy (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +guint g_bus_watch_proxy_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +void g_bus_unwatch_proxy (guint watcher_id); G_END_DECLS diff --git a/gio/gio.symbols b/gio/gio.symbols index 7f1302e..0d7c106 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1652,6 +1652,7 @@ g_bus_unown_name #if IN_HEADER(__G_DBUS_NAME_WATCHING_H__) #if IN_FILE(__G_DBUS_NAME_WATCHING_C__) g_bus_watch_name +g_bus_watch_name_on_connection g_bus_unwatch_name #endif #endif @@ -1683,6 +1684,7 @@ g_dbus_proxy_call_sync #if IN_HEADER(__G_DBUS_PROXY_WATCHING_H__) #if IN_FILE(__G_DBUS_PROXY_WATCHING_C__) g_bus_watch_proxy +g_bus_watch_proxy_on_connection g_bus_unwatch_proxy #endif #endif -- 2.7.4