From 94651afd8ab92d9c2310b9216f645b36ddca2fd7 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 25 Jul 2012 14:34:27 +0200 Subject: [PATCH] TpConnectionManager: Make possible to replace a running CM If a CM is started with --replace argument, this cause the running CM to disconnect all connections and leave. https://bugs.freedesktop.org/show_bug.cgi?id=52482 --- docs/reference/telepathy-glib-sections.txt | 2 + telepathy-glib/base-connection-manager.c | 122 ++++++++++++++++++++++++---- telepathy-glib/base-connection-manager.h | 1 + telepathy-glib/dbus-daemon.c | 48 ++++++++--- telepathy-glib/dbus-daemon.h | 3 + telepathy-glib/run.c | 19 ++++- 6 files changed, 167 insertions(+), 28 deletions(-) diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index a41a3be..f6e20b0 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -116,6 +116,7 @@ TpBaseConnectionManagerNewConnFunc TpBaseConnectionManagerGetInterfacesFunc tp_base_connection_manager_get_dbus_daemon tp_base_connection_manager_register +tp_base_connection_manager_replace tp_base_connection_manager_add_protocol TP_BASE_CONNECTION_MANAGER @@ -2496,6 +2497,7 @@ tp_dbus_daemon_list_names tp_dbus_daemon_list_activatable_names tp_dbus_daemon_release_name tp_dbus_daemon_request_name +tp_dbus_daemon_request_name_with_flags tp_dbus_daemon_register_object tp_dbus_daemon_unregister_object tp_dbus_daemon_init_known_interfaces diff --git a/telepathy-glib/base-connection-manager.c b/telepathy-glib/base-connection-manager.c index 662db99..497207d 100644 --- a/telepathy-glib/base-connection-manager.c +++ b/telepathy-glib/base-connection-manager.c @@ -319,24 +319,63 @@ enum enum { NO_MORE_CONNECTIONS, + REPLACED, N_SIGNALS }; static guint signals[N_SIGNALS] = {0}; static void +watch_name_owner_cb (TpDBusDaemon *bus_daemon, + const gchar *name, + const gchar *new_owner, + gpointer user_data) +{ + TpBaseConnectionManager *self = user_data; + + DEBUG ("'%s' owner changed, new owner is %s (ours is %s)", name, new_owner, + tp_dbus_daemon_get_unique_name (bus_daemon)); + + if (tp_strdiff (new_owner, tp_dbus_daemon_get_unique_name (bus_daemon))) + { + GHashTableIter iter; + gpointer key; + + DEBUG ("We lost our bus name, shutdown all connections and leave"); + + g_hash_table_iter_init (&iter, self->priv->connections); + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + tp_base_connection_change_status (key, + TP_CONNECTION_STATUS_DISCONNECTED, + TP_CONNECTION_STATUS_REASON_NAME_IN_USE); + } + + g_signal_emit (self, signals[REPLACED], 0); + } +} + +static void tp_base_connection_manager_dispose (GObject *object) { TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (object); TpBaseConnectionManagerPrivate *priv = self->priv; GObjectFinalizeFunc dispose = - G_OBJECT_CLASS (tp_base_connection_manager_parent_class)->dispose; + G_OBJECT_CLASS (tp_base_connection_manager_parent_class)->dispose; + TpBaseConnectionManagerClass *cls = + TP_BASE_CONNECTION_MANAGER_GET_CLASS (self); + gchar *name; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; + name = g_strdup_printf (TP_CM_BUS_NAME_BASE "%s", cls->cm_dbus_name); + tp_dbus_daemon_cancel_name_owner_watch (self->priv->dbus_daemon, name, + watch_name_owner_cb, self); + g_free (name); + if (priv->dbus_daemon != NULL) { g_object_unref (priv->dbus_daemon); @@ -593,6 +632,21 @@ tp_base_connection_manager_class_init (TpBaseConnectionManagerClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); + /** + * TpBaseConnectionManager::replaced: + * + * Emitted when the Connection Manager lost its DBus name. + * tp_run_connection_manager() uses this to detect when to shut down the + * connection manager. + */ + signals[REPLACED] = + g_signal_new ("replaced", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + tp_dbus_properties_mixin_class_init (object_class, 0); tp_dbus_properties_mixin_implement_interface (object_class, TP_IFACE_QUARK_CONNECTION_MANAGER, @@ -1079,20 +1133,9 @@ ERROR: g_error_free (error); } -/** - * tp_base_connection_manager_register: - * @self: The connection manager implementation - * - * Register the connection manager with an appropriate object path as - * determined from its @cm_dbus_name, and register the appropriate well-known - * bus name. - * - * Returns: %TRUE on success, %FALSE (having emitted a warning to stderr) - * on failure - */ - -gboolean -tp_base_connection_manager_register (TpBaseConnectionManager *self) +static gboolean +_tp_base_connection_manager_register_internal (TpBaseConnectionManager *self, + gboolean replace) { GError *error = NULL; TpBaseConnectionManagerClass *cls; @@ -1100,6 +1143,7 @@ tp_base_connection_manager_register (TpBaseConnectionManager *self) guint i; GHashTableIter iter; gpointer name, protocol; + guint flags; g_assert (TP_IS_BASE_CONNECTION_MANAGER (self)); cls = TP_BASE_CONNECTION_MANAGER_GET_CLASS (self); @@ -1124,10 +1168,17 @@ tp_base_connection_manager_register (TpBaseConnectionManager *self) string = g_string_new (TP_CM_BUS_NAME_BASE); g_string_append (string, cls->cm_dbus_name); - if (!tp_dbus_daemon_request_name (self->priv->dbus_daemon, string->str, - TRUE, &error)) + flags = DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_ALLOW_REPLACEMENT; + if (replace) + flags |= DBUS_NAME_FLAG_REPLACE_EXISTING; + + if (!tp_dbus_daemon_request_name_with_flags (self->priv->dbus_daemon, + string->str, flags, TRUE, &error)) goto except; + tp_dbus_daemon_watch_name_owner (self->priv->dbus_daemon, string->str, + watch_name_owner_cb, self, NULL); + g_string_assign (string, TP_CM_OBJECT_PATH_BASE); g_string_append (string, cls->cm_dbus_name); tp_dbus_daemon_register_object (self->priv->dbus_daemon, string->str, self); @@ -1178,6 +1229,43 @@ except: return FALSE; } +/** + * tp_base_connection_manager_register: + * @self: The connection manager implementation + * + * Register the connection manager with an appropriate object path as + * determined from its @cm_dbus_name, and register the appropriate well-known + * bus name. + * + * Returns: %TRUE on success, %FALSE (having emitted a warning to stderr) + * on failure + */ +gboolean +tp_base_connection_manager_register (TpBaseConnectionManager *self) +{ + return _tp_base_connection_manager_register_internal (self, FALSE); +} + +/** + * tp_base_connection_manager_replace: + * @self: The connection manager implementation + * + * Same as tp_base_connection_manager_register() but replacing existing + * Connection Manager if one as been registered in another process. + * + * Note that replacing a running Connection Manager cause it to disconnect all + * its connections and emit #TpBaseConnectionManager::replaced signal. That + * signal is catched by tp_run_connection_manager() to leave mainloop. + * + * Returns: %TRUE on success, %FALSE (having emitted a warning to stderr) + * on failure + */ +gboolean +tp_base_connection_manager_replace (TpBaseConnectionManager *self) +{ + return _tp_base_connection_manager_register_internal (self, TRUE); +} + static void service_iface_init (gpointer g_iface, gpointer iface_data) { diff --git a/telepathy-glib/base-connection-manager.h b/telepathy-glib/base-connection-manager.h index fe15b70..527cd7a 100644 --- a/telepathy-glib/base-connection-manager.h +++ b/telepathy-glib/base-connection-manager.h @@ -97,6 +97,7 @@ struct _TpBaseConnectionManager { GType tp_base_connection_manager_get_type (void); gboolean tp_base_connection_manager_register (TpBaseConnectionManager *self); +gboolean tp_base_connection_manager_replace (TpBaseConnectionManager *self); TpDBusDaemon *tp_base_connection_manager_get_dbus_daemon ( diff --git a/telepathy-glib/dbus-daemon.c b/telepathy-glib/dbus-daemon.c index f98236f..d770ade 100644 --- a/telepathy-glib/dbus-daemon.c +++ b/telepathy-glib/dbus-daemon.c @@ -742,16 +742,17 @@ _tp_dbus_daemon_get_name_owner (TpDBusDaemon *self, } /** - * tp_dbus_daemon_request_name: + * tp_dbus_daemon_request_name_with_flags: * @self: a TpDBusDaemon * @well_known_name: a well-known name to acquire + * @flags: flags to be passed to dbus_bus_request_name() (e.g. + * DBUS_NAME_FLAG_REPLACE_EXISTING) * @idempotent: whether to consider it to be a success if this process * already owns the name * @error: used to raise an error if %FALSE is returned * - * Claim the given well-known name without queueing, allowing replacement - * or replacing an existing name-owner. This makes a synchronous call to the - * bus daemon. + * Claim the given well-known name. Similar to tp_dbus_daemon_request_name() but + * more flexible as takes flags to use. * * Returns: %TRUE if @well_known_name was claimed, or %FALSE and sets @error if * an error occurred. @@ -759,10 +760,11 @@ _tp_dbus_daemon_get_name_owner (TpDBusDaemon *self, * Since: 0.7.30 */ gboolean -tp_dbus_daemon_request_name (TpDBusDaemon *self, - const gchar *well_known_name, - gboolean idempotent, - GError **error) +tp_dbus_daemon_request_name_with_flags (TpDBusDaemon *self, + const gchar *well_known_name, + guint flags, + gboolean idempotent, + GError **error) { TpProxy *as_proxy = (TpProxy *) self; DBusGConnection *gconn; @@ -790,8 +792,7 @@ tp_dbus_daemon_request_name (TpDBusDaemon *self, dbc = dbus_g_connection_get_connection (gconn); dbus_error_init (&dbus_error); - result = dbus_bus_request_name (dbc, well_known_name, - DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error); + result = dbus_bus_request_name (dbc, well_known_name, flags, &dbus_error); switch (result) { @@ -832,6 +833,33 @@ tp_dbus_daemon_request_name (TpDBusDaemon *self, } /** + * tp_dbus_daemon_request_name: + * @self: a TpDBusDaemon + * @well_known_name: a well-known name to acquire + * @idempotent: whether to consider it to be a success if this process + * already owns the name + * @error: used to raise an error if %FALSE is returned + * + * Claim the given well-known name without queueing, allowing replacement + * or replacing an existing name-owner. This makes a synchronous call to the + * bus daemon. + * + * Returns: %TRUE if @well_known_name was claimed, or %FALSE and sets @error if + * an error occurred. + * + * Since: 0.7.30 + */ +gboolean +tp_dbus_daemon_request_name (TpDBusDaemon *self, + const gchar *well_known_name, + gboolean idempotent, + GError **error) +{ + return tp_dbus_daemon_request_name_with_flags (self, well_known_name, + DBUS_NAME_FLAG_DO_NOT_QUEUE, idempotent, error); +} + +/** * tp_dbus_daemon_release_name: * @self: a TpDBusDaemon * @well_known_name: a well-known name owned by this process to release diff --git a/telepathy-glib/dbus-daemon.h b/telepathy-glib/dbus-daemon.h index 0ec7d9e..cc3595b 100644 --- a/telepathy-glib/dbus-daemon.h +++ b/telepathy-glib/dbus-daemon.h @@ -72,6 +72,9 @@ gboolean tp_dbus_daemon_cancel_name_owner_watch (TpDBusDaemon *self, gboolean tp_dbus_daemon_request_name (TpDBusDaemon *self, const gchar *well_known_name, gboolean idempotent, GError **error); +gboolean tp_dbus_daemon_request_name_with_flags (TpDBusDaemon *self, + const gchar *well_known_name, guint flags, gboolean idempotent, + GError **error); gboolean tp_dbus_daemon_release_name (TpDBusDaemon *self, const gchar *well_known_name, GError **error); diff --git a/telepathy-glib/run.c b/telepathy-glib/run.c index 7ae6e82..ce6bc1a 100644 --- a/telepathy-glib/run.c +++ b/telepathy-glib/run.c @@ -116,6 +116,13 @@ no_more_connections (TpBaseConnectionManager *conn) timeout_id = g_timeout_add (DIE_TIME, kill_connection_manager, NULL); } +static void +replaced (TpBaseConnectionManager *conn) +{ + g_debug ("Replaced, quit mainloop"); + quit_loop (); +} + #ifdef ENABLE_BACKTRACE static void print_backtrace (void) @@ -215,6 +222,12 @@ tp_run_connection_manager (const char *prog_name, TpDBusDaemon *bus_daemon = NULL; GError *error = NULL; int ret = 1; + int i; + gboolean replace = FALSE; + + for (i = 0; i < argc; i++) + if (!tp_strdiff (argv[i], "--replace")) + replace = TRUE; add_signal_handlers (); @@ -261,6 +274,9 @@ tp_run_connection_manager (const char *prog_name, g_signal_connect (manager, "no-more-connections", (GCallback) no_more_connections, NULL); + g_signal_connect (manager, "replaced", + (GCallback) replaced, NULL); + /* It appears that dbus-glib registers a filter that wrongly returns * DBUS_HANDLER_RESULT_HANDLED for signals, so for *our* filter to have any * effect, we need to install it before calling @@ -270,7 +286,8 @@ tp_run_connection_manager (const char *prog_name, dbus_connection_add_filter (connection, dbus_filter_function, NULL, NULL); dbus_connection_set_exit_on_disconnect (connection, FALSE); - if (!tp_base_connection_manager_register (manager)) + if ((!replace && !tp_base_connection_manager_register (manager)) || + (replace && !tp_base_connection_manager_replace (manager))) { g_object_unref (manager); manager = NULL; -- 1.7.10.4