From 75e54cd5ef009f52717edfe93dd4d46f94b2fea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 20 Mar 2019 04:44:30 +0100 Subject: [PATCH] Implement D-Bus Activation of desktop files manually This part of GIO library isn't suitable for short-lived processes (the call is done asynchronously and may not reach the application before qubes-desktop-run process is terminated). To fix this, implement dbus activation manually, synchronously. While at it, implement waiting for application to terminate (useful in DispVM), by waiting for its dbus name to disappear. dbus-python API isn't particularly nice, but don't switch to completely different library as a stable update. Fixes QubesOS/qubes-issues#2449 --- qubesagent/xdg.py | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/qubesagent/xdg.py b/qubesagent/xdg.py index 8b57411..3b9f08d 100755 --- a/qubesagent/xdg.py +++ b/qubesagent/xdg.py @@ -1,3 +1,5 @@ +import functools + from gi.repository import Gio # pylint: disable=import-error from gi.repository import GLib # pylint: disable=import-error import sys @@ -6,22 +8,51 @@ import os def pid_callback(launcher, pid, pid_list): pid_list.append(pid) +def dbus_name_change(loop, name, old_owner, new_owner): + if not new_owner: + loop.quit() + def launch(desktop, *files, **kwargs): wait = kwargs.pop('wait', True) launcher = Gio.DesktopAppInfo.new_from_filename(desktop) try: import dbus + from dbus.mainloop.glib import DBusGMainLoop if hasattr(launcher, 'get_boolean'): activatable = launcher.get_boolean('DBusActivatable') if activatable: + loop = GLib.MainLoop() + DBusGMainLoop(set_as_default=True) bus = dbus.SessionBus() service_id = launcher.get_id() # cut the .desktop suffix - service_id = service_id[:-8] + service_id = service_id[:-len('.desktop')] + # see D-Bus Activation Desktop entry specification + object_path = '/' + service_id.replace('.', '/').\ + replace('-', '_') try: - bus.start_service_by_name(service_id) - except dbus.DBusException: + proxy = bus.get_object(service_id, object_path) + match = bus.add_signal_receiver( + functools.partial(dbus_name_change, loop), + 'NameOwnerChanged', + dbus.BUS_DAEMON_IFACE, + dbus.BUS_DAEMON_NAME, + dbus.BUS_DAEMON_PATH) + if files: + proxy.Open(files, {}, + dbus_interface='org.freedesktop.Application') + else: + proxy.Activate({}, + dbus_interface='org.freedesktop.Application') + except dbus.DBusException as e: + print(e) + # fallback to non-dbus version pass + else: + if wait: + loop.run() + match.remove() + return except ImportError: pass if wait: