summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2014-04-09 16:37:39 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2014-04-10 20:21:00 +0100
commit05f0d4bd3d8ca9dfae377754d2dbd53cff9cf228 (patch)
tree9c64bc878046a543cd8def0f6253de6807450d8f
parentbe5e65fc6735a753ad7d347a08b075febf225f03 (diff)
tp_dbus_connection_try_register_object: export each GDBusInterfaceSkeleton from a GDBusObject
-rw-r--r--telepathy-glib/dbus.c189
1 files changed, 167 insertions, 22 deletions
diff --git a/telepathy-glib/dbus.c b/telepathy-glib/dbus.c
index 3120a8aea..100f5bec8 100644
--- a/telepathy-glib/dbus.c
+++ b/telepathy-glib/dbus.c
@@ -663,6 +663,8 @@ struct _Registration {
gchar *object_path;
/* (transfer full) */
GList *skeletons;
+ /* (transfer none), do not dereference */
+ gpointer object;
};
static GQuark
@@ -679,6 +681,56 @@ registration_quark (void)
}
static void
+tp_dbus_connection_registration_iface_added_cb (GDBusObject *object,
+ GDBusInterface *iface,
+ gpointer user_data)
+{
+ Registration *r = user_data;
+ GError *error = NULL;
+
+ if (!G_IS_DBUS_INTERFACE_SKELETON (iface))
+ {
+ DEBUG ("Not a GDBusInterfaceSkeleton: %p", iface);
+ return;
+ }
+
+ /* This can't happen if @object is a GDBusObjectSkeleton and has no
+ * TpSvc interfaces, since GDBusObjectSkeleton always removes the old
+ * interface before replacing it */
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
+ r->conn, r->object_path, &error))
+ {
+ WARNING ("Cannot export %s %p as %s at %s: %s",
+ G_OBJECT_TYPE_NAME (iface), iface,
+ g_dbus_interface_get_info (iface)->name,
+ r->object_path,
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ r->skeletons = g_list_prepend (r->skeletons, g_object_ref (iface));
+}
+
+static void
+tp_dbus_connection_registration_iface_removed_cb (GDBusObject *object,
+ GDBusInterface *iface,
+ gpointer user_data)
+{
+ Registration *r = user_data;
+ GList *iface_link;
+
+ iface_link = g_list_find (r->skeletons, iface);
+
+ if (iface_link != NULL)
+ {
+ g_assert (iface_link->data == (gpointer) iface);
+ r->skeletons = g_list_delete_link (r->skeletons, iface_link);
+ g_object_unref (iface);
+ }
+}
+
+static void
tp_dbus_connection_registration_free (gpointer p)
{
Registration *r = p;
@@ -689,7 +741,7 @@ tp_dbus_connection_registration_free (gpointer p)
for (iter = r->skeletons; iter != NULL; iter = iter->next)
{
DEBUG ("%p", iter->data);
- g_assert (TP_IS_SVC_INTERFACE_SKELETON (iter->data));
+ g_assert (G_IS_DBUS_INTERFACE_SKELETON (iter->data));
g_dbus_interface_skeleton_unexport (iter->data);
g_object_unref (iter->data);
}
@@ -697,6 +749,19 @@ tp_dbus_connection_registration_free (gpointer p)
g_list_free (r->skeletons);
g_free (r->object_path);
g_clear_object (&r->conn);
+
+ if (r->object != NULL)
+ {
+ /* If the object is not explicitly unexported, this won't run until
+ * g_object_dispose() after all signal handlers have already been
+ * disconnected, so we can't just disconnect by the gulong ID - but we
+ * can do this instead. */
+ g_signal_handlers_disconnect_by_func (r->object,
+ tp_dbus_connection_registration_iface_added_cb, r);
+ g_signal_handlers_disconnect_by_func (r->object,
+ tp_dbus_connection_registration_iface_removed_cb, r);
+ }
+
g_slice_free (Registration, r);
}
@@ -764,11 +829,15 @@ tp_dbus_connection_try_register_object (GDBusConnection *dbus_connection,
GError **error)
{
GDBusConnection *conn;
- GType *interfaces;
+ GType *interfaces = NULL;
guint n = 0;
guint i;
Registration *r;
gboolean ret = FALSE;
+ GHashTable *skeletons = NULL;
+ GHashTableIter hash_iter;
+ gpointer skeleton;
+ gpointer iface_name_p;
g_return_val_if_fail (G_IS_DBUS_CONNECTION (dbus_connection), FALSE);
g_return_val_if_fail (tp_dbus_check_valid_object_path (object_path, error),
@@ -778,6 +847,7 @@ tp_dbus_connection_try_register_object (GDBusConnection *dbus_connection,
conn = dbus_connection;
r = g_slice_new0 (Registration);
r->conn = g_object_ref (conn);
+ r->object = object;
r->object_path = g_strdup (object_path);
r->skeletons = NULL;
@@ -817,35 +887,111 @@ tp_dbus_connection_try_register_object (GDBusConnection *dbus_connection,
return FALSE;
}
- /* FIXME: if @object is a GDBusObject or GDBusObjectManagerServer,
- * export it that way instead? */
+ skeletons = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
+ g_object_unref);
+
+ /* If it's a GDBusObject, take its interfaces. */
+ if (G_IS_DBUS_OBJECT (object))
+ {
+ GList *object_interfaces;
+ GList *list_iter;
+
+ DEBUG ("Getting GDBusObject skeletons");
+
+ object_interfaces = g_dbus_object_get_interfaces (object);
- interfaces = g_type_interfaces (G_OBJECT_TYPE (object), &n);
+ for (list_iter = object_interfaces;
+ list_iter != NULL;
+ list_iter = list_iter->next)
+ {
+ GDBusInterface *iface = list_iter->data;
+ const gchar *iface_name = g_dbus_interface_get_info (iface)->name;
+
+ if (!G_IS_DBUS_INTERFACE_SKELETON (iface))
+ {
+ DEBUG ("- not a GDBusInterfaceSkeleton: %s %p",
+ G_OBJECT_TYPE_NAME (iface), iface);
+ continue;
+ }
+
+ if (g_hash_table_contains (skeletons, iface_name))
+ {
+ WARNING ("%s %p has more than one implementation of %s",
+ G_OBJECT_TYPE_NAME (object), object, iface_name);
+ /* use the second one added - that's consistent with
+ * GDBusObjectManagerServer */
+ }
+
+ DEBUG ("- %s skeleton: %s %p",
+ iface_name, G_OBJECT_TYPE_NAME (iface), iface);
- for (i = 0; i < n; i++)
+ g_hash_table_replace (skeletons, (gchar *) iface_name,
+ g_object_ref (iface));
+ }
+
+ g_list_free_full (object_interfaces, g_object_unref);
+
+ g_signal_connect (object, "interface-added",
+ G_CALLBACK (tp_dbus_connection_registration_iface_added_cb), r);
+ g_signal_connect (object, "interface-removed",
+ G_CALLBACK (tp_dbus_connection_registration_iface_removed_cb), r);
+ }
+ else
{
- GType iface = interfaces[i];
- const TpSvcInterfaceInfo *iinfo;
- TpSvcInterfaceSkeleton *skeleton;
- GError *inner_error = NULL;
+ DEBUG ("Getting TpSvc* skeletons");
- iinfo = tp_svc_interface_peek_dbus_interface_info (iface);
+ interfaces = g_type_interfaces (G_OBJECT_TYPE (object), &n);
- if (iinfo == NULL)
+ /* Get the TpSvc interfaces if any. These take precedence over whatever
+ * was in the GDBusObject, because in practice CMs rely on overriding
+ * base-classes' interfaces. */
+ for (i = 0; i < n; i++)
{
- DEBUG ("- %s is not a D-Bus interface", g_type_name (iface));
- continue;
+ GType iface = interfaces[i];
+ const TpSvcInterfaceInfo *iinfo;
+
+ iinfo = tp_svc_interface_peek_dbus_interface_info (iface);
+
+ if (iinfo == NULL)
+ {
+ DEBUG ("- %s is not a D-Bus interface", g_type_name (iface));
+ continue;
+ }
+
+ skeleton = _tp_svc_interface_skeleton_new (object, iface, iinfo);
+
+ DEBUG ("- %s skeleton %p (wrapping %s %p)",
+ iinfo->interface_info->name, skeleton, g_type_name (iface),
+ object);
+
+ if (g_hash_table_contains (skeletons, iinfo->interface_info->name))
+ {
+ DEBUG (" (overriding existing implementation of %s)",
+ iinfo->interface_info->name);
+ }
+
+ g_hash_table_replace (skeletons,
+ (gchar *) iinfo->interface_info->name, skeleton);
}
- skeleton = _tp_svc_interface_skeleton_new (object, iface, iinfo);
+ g_free (interfaces);
+ }
+
+ DEBUG ("Exporting skeletons");
+
+ g_hash_table_iter_init (&hash_iter, skeletons);
+
+ while (g_hash_table_iter_next (&hash_iter, &iface_name_p, &skeleton))
+ {
+ const gchar *iface_name = iface_name_p;
+ GError *inner_error = NULL;
if (!g_dbus_interface_skeleton_export (
G_DBUS_INTERFACE_SKELETON (skeleton), conn, object_path,
&inner_error))
{
- DEBUG ("cannot export %s %p skeleton %p as '%s': %s #%d: %s",
- g_type_name (iface), object, skeleton,
- iinfo->interface_info->name,
+ DEBUG ("cannot export %p skeleton %p as '%s': %s #%d: %s",
+ object, skeleton, iface_name,
g_quark_to_string (inner_error->domain), inner_error->code,
inner_error->message);
g_object_unref (skeleton);
@@ -856,15 +1002,14 @@ tp_dbus_connection_try_register_object (GDBusConnection *dbus_connection,
goto finally;
}
- r->skeletons = g_list_prepend (r->skeletons, skeleton);
+ r->skeletons = g_list_prepend (r->skeletons, g_object_ref (skeleton));
- DEBUG ("- %s skeleton %p (wrapping %s %p)",
- iinfo->interface_info->name, skeleton, g_type_name (iface), object);
+ DEBUG ("- %s skeleton %p (wrapping %p)", iface_name, skeleton, object);
}
ret = TRUE;
finally:
- g_free (interfaces);
+ g_clear_pointer (&skeletons, g_hash_table_unref);
return ret;
}