diff --git a/docs/reference/gio/gdbus.xml b/docs/reference/gio/gdbus.xml
index 4bd0eb746..a83511a81 100644
--- a/docs/reference/gio/gdbus.xml
+++ b/docs/reference/gio/gdbus.xml
@@ -23,6 +23,19 @@
--dest bus_name
--object-path /path/to/object
+
+ gdbus
+ monitor
+
+ --system
+ --session
+ --address address
+
+ --dest bus_name
+
+ --object-path /path/to/object
+
+
gdbus
call
@@ -60,6 +73,13 @@
org.freedesktop.DBus.Introspectable interface.
+
+
+
+ Monitors one or all objects owned by the owned of
+ bus_name.
+
+
@@ -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 108567410..3047f8ede 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 96a66ca80..95d81287a 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 3d96ee0a9..903e44413 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)
@@ -586,6 +587,68 @@ g_bus_watch_name (GBusType bus_type,
return client->id;
}
+/**
+ * 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 1bbf8e0cd..a424f4362 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 7e900393c..282254d46 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"
@@ -362,6 +363,91 @@ g_bus_watch_proxy (GBusType bus_type,
return client->id;
}
+/**
+ * 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 2dddaffbf..37c88dd35 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 7f1302e3e..0d7c106ad 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