From b976580e439d0c07bdf140d2395dae6d78fa4718 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 9 Feb 2023 01:14:47 -0800 Subject: [PATCH] session: use GDBus object manager to track objects This changes things to use the object manager to discover the VM instance. We'll have to add support for other wrappers to use object managers so that they can be added to the devices list as they are seen. --- lib/meson.build | 1 + lib/mks-session.c | 149 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 120 insertions(+), 30 deletions(-) diff --git a/lib/meson.build b/lib/meson.build index 51a31a7..59b8d99 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -34,6 +34,7 @@ libmks_private_sources = [ interface_prefix: 'org.qemu.Display1.', namespace: 'MksQemu', sources: 'dbus-display1.xml', + object_manager: true, ) ] diff --git a/lib/mks-session.c b/lib/mks-session.c index 4347d69..69f65bf 100644 --- a/lib/mks-session.c +++ b/lib/mks-session.c @@ -91,10 +91,19 @@ struct _MksSession */ GListModel *devices_read_only; - /* @vm contains our "top-level" proxy object to the Qemu instance. This is - * where access to other devices begins. It is setup during either GInitable - * or GAsyncInitable initialization. + /* An object manager client is used to monitor for new objects exported by + * the Qemu instance. Those objects are then wrapped by MksDevice objects + * as necessary and exported to consumers via @devices. */ + GDBusObjectManager *object_manager; + + /* A GDBusObjectProxy to the `/org/qemu/Display1/VM` instance. Generally + * this used via the `org.qemu.Display1.VM` interface. We track the + * top-level object-manager instance so that we can detect easily when + * the VM has been removed from the peer. (Which could happen if it is + * running on a private D-Bus rather than a socketpair(). + */ + MksQemuObject *vm_object; MksQemuVM *vm; }; @@ -161,19 +170,96 @@ mks_session_vm_notify_cb (MksSession *self, } static void -mks_session_set_vm (MksSession *self, - MksQemuVM *vm) +mks_session_set_vm (MksSession *self, + MksQemuObject *vm_object, + MksQemuVM *vm) { g_assert (MKS_IS_SESSION (self)); + g_assert (MKS_QEMU_IS_OBJECT (vm_object)); g_assert (MKS_QEMU_IS_VM (vm)); + if (self->vm != NULL) + return; + + g_set_object (&self->vm_object, vm_object); + g_set_object (&self->vm, vm); + g_signal_connect_object (vm, "notify", G_CALLBACK (mks_session_vm_notify_cb), self, G_CONNECT_SWAPPED); - self->vm = g_object_ref (vm); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_UUID]); +} + +static void +mks_session_object_manager_object_added_cb (MksSession *self, + MksQemuObject *object, + GDBusObjectManager *manager) +{ + g_autolist(GDBusInterface) interfaces = NULL; + + g_assert (MKS_IS_SESSION (self)); + g_assert (MKS_QEMU_IS_OBJECT (object)); + g_assert (G_IS_DBUS_OBJECT_MANAGER (manager)); + + interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object)); + + for (const GList *iter = interfaces; iter; iter = iter->next) + { + GDBusInterface *iface = iter->data; + + if (MKS_QEMU_IS_VM (iface)) + mks_session_set_vm (self, object, MKS_QEMU_VM (iface)); + } +} + +static void +mks_session_object_manager_object_removed_cb (MksSession *self, + MksQemuObject *object, + GDBusObjectManager *manager) +{ + g_assert (MKS_IS_SESSION (self)); + g_assert (MKS_QEMU_IS_OBJECT (object)); + g_assert (G_IS_DBUS_OBJECT_MANAGER (manager)); + + if (object == self->vm_object) + { + g_clear_object (&self->vm); + g_clear_object (&self->vm_object); + g_list_store_remove_all (self->devices); + } +} + +static void +mks_session_set_object_manager (MksSession *self, + GDBusObjectManager *object_manager) +{ + g_assert (MKS_IS_SESSION (self)); + g_assert (!object_manager || MKS_QEMU_IS_OBJECT_MANAGER_CLIENT (object_manager)); + g_assert (self->object_manager == NULL); + + if (g_set_object (&self->object_manager, object_manager)) + { + g_autolist(GDBusObjectProxy) objects = NULL; + + g_signal_connect_object (object_manager, + "object-added", + G_CALLBACK (mks_session_object_manager_object_added_cb), + self, + G_CONNECT_SWAPPED); + g_signal_connect_object (object_manager, + "object-removed", + G_CALLBACK (mks_session_object_manager_object_removed_cb), + self, + G_CONNECT_SWAPPED); + + objects = g_dbus_object_manager_get_objects (object_manager); + for (const GList *iter = objects; iter; iter = iter->next) + mks_session_object_manager_object_added_cb (self, iter->data, object_manager); + } } static void @@ -184,6 +270,10 @@ mks_session_dispose (GObject *object) if (self->devices != NULL) g_list_store_remove_all (self->devices); + g_clear_object (&self->object_manager); + g_clear_object (&self->vm); + g_clear_object (&self->vm_object); + G_OBJECT_CLASS (mks_session_parent_class)->dispose (object); } @@ -192,7 +282,6 @@ mks_session_finalize (GObject *object) { MksSession *self = (MksSession *)object; - g_clear_object (&self->vm); g_clear_object (&self->devices); g_clear_object (&self->devices_read_only); g_clear_object (&self->connection); @@ -320,7 +409,8 @@ mks_session_initable_init (GInitable *initable, GError **error) { MksSession *self = (MksSession *)initable; - g_autoptr(MksQemuVM) vm = NULL; + g_autoptr(GDBusObjectManager) object_manager = NULL; + g_autolist(GDBusObjectProxy) objects = NULL; g_assert (MKS_IS_SESSION (self)); g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); @@ -334,17 +424,17 @@ mks_session_initable_init (GInitable *initable, return FALSE; } - vm = mks_qemu_vm_proxy_new_sync (self->connection, - G_DBUS_PROXY_FLAGS_NONE, - "org.qemu", - "/org/qemu/Display1/VM", - cancellable, - error); + object_manager = + mks_qemu_object_manager_client_new_sync (self->connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + "org.qemu", + "/org/qemu/Display1", + cancellable, + error); - if (vm != NULL) - mks_session_set_vm (self, vm); + mks_session_set_object_manager (self, object_manager); - return vm != NULL; + return object_manager != NULL; } static void @@ -352,7 +442,7 @@ mks_session_async_initable_vm_cb (GObject *object, GAsyncResult *result, gpointer user_data) { - g_autoptr(MksQemuVM) vm = NULL; + g_autoptr(GDBusObjectManager) object_manager = NULL; g_autoptr(GError) error = NULL; g_autoptr(GTask) task = user_data; MksSession *self; @@ -361,15 +451,14 @@ mks_session_async_initable_vm_cb (GObject *object, g_assert (G_IS_TASK (task)); self = g_task_get_source_object (task); - vm = mks_qemu_vm_proxy_new_finish (result, &error); + object_manager = mks_qemu_object_manager_client_new_finish (result, &error); g_assert (MKS_IS_SESSION (self)); - g_assert (!vm || MKS_QEMU_IS_VM (vm)); + g_assert (!object_manager || MKS_QEMU_IS_OBJECT_MANAGER_CLIENT (object_manager)); - if (vm != NULL) - mks_session_set_vm (self, vm); + mks_session_set_object_manager (self, object_manager); - if (error) + if (error != NULL) g_task_return_error (task, g_steal_pointer (&error)); else g_task_return_boolean (task, TRUE); @@ -398,13 +487,13 @@ mks_session_async_initable_init_async (GAsyncInitable *async_initable, G_IO_ERROR_NOT_CONNECTED, "Not connected"); else - mks_qemu_vm_proxy_new (self->connection, - G_DBUS_PROXY_FLAGS_NONE, - "org.qemu", - "/org/qemu/Display1/VM", - cancellable, - mks_session_async_initable_vm_cb, - g_steal_pointer (&task)); + mks_qemu_object_manager_client_new (self->connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + "org.qemu", + "/org/qemu/Display1/VM", + cancellable, + mks_session_async_initable_vm_cb, + g_steal_pointer (&task)); } static gboolean