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
This commit is contained in:
Marek Marczykowski-Górecki 2019-03-20 04:44:30 +01:00
parent 3d65517886
commit 75e54cd5ef
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724

View File

@ -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: