Replace dbus-python with QtDBus
dbus-python seems to be not thread safe. After "881ee76 Merge qclipd into qubes-manager" this occasionally triggers SEGV. QtDBus seems to be somehow less robust: - doesn't automatically convert methods arguments to proper types based on introspection data - property access isn't working so must call org.freedesktop.DBus.Properties methods manually But besides those problems seems to work stable.
This commit is contained in:
		
							parent
							
								
									11bfea1118
								
							
						
					
					
						commit
						e0a15c12d4
					
				| @ -25,11 +25,11 @@ import os | |||||||
| import signal | import signal | ||||||
| import fcntl | import fcntl | ||||||
| import errno | import errno | ||||||
| import dbus |  | ||||||
| import dbus.service |  | ||||||
| from dbus.mainloop.qt import DBusQtMainLoop |  | ||||||
| from PyQt4.QtCore import * | from PyQt4.QtCore import * | ||||||
| from PyQt4.QtGui import * | from PyQt4.QtGui import * | ||||||
|  | from PyQt4.QtDBus import QDBus,QDBusVariant | ||||||
|  | from PyQt4.QtDBus import QDBusConnection | ||||||
|  | from PyQt4.QtDBus import QDBusInterface,QDBusAbstractAdaptor | ||||||
| 
 | 
 | ||||||
| from qubes.qubes import QubesVmCollection | from qubes.qubes import QubesVmCollection | ||||||
| from qubes.qubes import QubesException | from qubes.qubes import QubesException | ||||||
| @ -857,6 +857,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): | |||||||
|         self.last_measure_time = time.time() |         self.last_measure_time = time.time() | ||||||
|         QTimer.singleShot (self.update_interval, self.update_table) |         QTimer.singleShot (self.update_interval, self.update_table) | ||||||
| 
 | 
 | ||||||
|  |         QubesDbusNotifyServerAdaptor(self) | ||||||
|  | 
 | ||||||
|     def load_manager_settings(self): |     def load_manager_settings(self): | ||||||
|         # visible columns |         # visible columns | ||||||
|         self.manager_settings.beginGroup("columns") |         self.manager_settings.beginGroup("columns") | ||||||
| @ -974,6 +976,9 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): | |||||||
|         for vm in vms_list: |         for vm in vms_list: | ||||||
|             vm.last_power_state = vm.get_power_state() |             vm.last_power_state = vm.get_power_state() | ||||||
|             vm.last_running = vm.last_power_state in ["Running", "Transient"] |             vm.last_running = vm.last_power_state in ["Running", "Transient"] | ||||||
|  |             vm.rec_available = session_bus.interface().isServiceRegistered('org.QubesOS.Audio.%s' % vm.name).value() | ||||||
|  |             if vm.rec_available: | ||||||
|  |                 self.vm_rec[vm.name] = self.get_audio_rec_allowed(vm.name) | ||||||
|             if vm.last_running: |             if vm.last_running: | ||||||
|                 running_count += 1 |                 running_count += 1 | ||||||
|             if vm.internal: |             if vm.internal: | ||||||
| @ -1046,6 +1051,10 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): | |||||||
|                     prev_running = vm.last_running |                     prev_running = vm.last_running | ||||||
|                     vm.last_power_state = state |                     vm.last_power_state = state | ||||||
|                     vm.last_running = (state in ["Running", "Transient"]) |                     vm.last_running = (state in ["Running", "Transient"]) | ||||||
|  |                     vm.rec_available = session_bus.interface().isServiceRegistered( | ||||||
|  |                             'org.QubesOS.Audio.%s' % vm.name).value() | ||||||
|  |                     if vm.rec_available: | ||||||
|  |                         self.vm_rec[vm.name] = self.get_audio_rec_allowed(vm.name) | ||||||
|                     if not prev_running and vm.last_running: |                     if not prev_running and vm.last_running: | ||||||
|                         self.running_vms_count += 1 |                         self.running_vms_count += 1 | ||||||
|                         some_vms_have_changed_power_state = True |                         some_vms_have_changed_power_state = True | ||||||
| @ -1136,16 +1145,22 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): | |||||||
|             trayIcon.showMessage (str, msecs=5000) |             trayIcon.showMessage (str, msecs=5000) | ||||||
|         return res |         return res | ||||||
| 
 | 
 | ||||||
|  |     @pyqtSlot(bool, str) | ||||||
|     def recAllowedChanged(self, state, vmname): |     def recAllowedChanged(self, state, vmname): | ||||||
|         self.vm_rec[vmname] = bool(state) |         self.vm_rec[str(vmname)] = bool(state) | ||||||
| 
 | 
 | ||||||
|     def register_dbus_watches(self): |     def register_dbus_watches(self): | ||||||
|         global session_bus |         global session_bus | ||||||
| 
 | 
 | ||||||
|         if not session_bus: |         if not session_bus: | ||||||
|             session_bus = dbus.SessionBus() |             session_bus = QDBusConnection.sessionBus() | ||||||
| 
 | 
 | ||||||
|         session_bus.add_signal_receiver(self.recAllowedChanged, signal_name="RecAllowedChanged", dbus_interface="org.QubesOS.Audio") |         if not session_bus.connect(QString(), # service | ||||||
|  |                 QString(), # path | ||||||
|  |                 QString("org.QubesOS.Audio"), # interface | ||||||
|  |                 QString("RecAllowedChanged"), # name | ||||||
|  |                 self.recAllowedChanged): # slot | ||||||
|  |             print session_bus.lastError().message() | ||||||
| 
 | 
 | ||||||
|     def sortIndicatorChanged(self, column, order): |     def sortIndicatorChanged(self, column, order): | ||||||
|         self.sort_by_column = [name for name in self.columns_indices.keys() if self.columns_indices[name] == column][0] |         self.sort_by_column = [name for name in self.columns_indices.keys() if self.columns_indices[name] == column][0] | ||||||
| @ -1171,8 +1186,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): | |||||||
|             self.action_appmenus.setEnabled(not vm.is_netvm()) |             self.action_appmenus.setEnabled(not vm.is_netvm()) | ||||||
|             self.action_editfwrules.setEnabled(vm.is_networked() and not (vm.is_netvm() and not vm.is_proxyvm())) |             self.action_editfwrules.setEnabled(vm.is_networked() and not (vm.is_netvm() and not vm.is_proxyvm())) | ||||||
|             self.action_updatevm.setEnabled(vm.is_updateable() or vm.qid == 0) |             self.action_updatevm.setEnabled(vm.is_updateable() or vm.qid == 0) | ||||||
|             self.action_toggle_audio_input.setEnabled(vm.last_running and vm.qid != 0 and \ |             self.action_toggle_audio_input.setEnabled(vm.rec_available) | ||||||
|                     session_bus.name_has_owner('org.QubesOS.Audio.%s' % vm.name)) |  | ||||||
|             self.action_run_command_in_vm.setEnabled(not vm.last_power_state == "Paused" and vm.qid != 0) |             self.action_run_command_in_vm.setEnabled(not vm.last_power_state == "Paused" and vm.qid != 0) | ||||||
|             self.action_set_keyboard_layout.setEnabled(vm.qid != 0 and vm.last_running) |             self.action_set_keyboard_layout.setEnabled(vm.qid != 0 and vm.last_running) | ||||||
|         else: |         else: | ||||||
| @ -1491,13 +1505,23 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): | |||||||
|         settings_window = VMSettingsWindow(vm, app, self.qvm_collection, "applications") |         settings_window = VMSettingsWindow(vm, app, self.qvm_collection, "applications") | ||||||
|         settings_window.exec_() |         settings_window.exec_() | ||||||
| 
 | 
 | ||||||
|  |     def get_audio_rec_allowed(self, vmname): | ||||||
|  |         properties = QDBusInterface('org.QubesOS.Audio.%s' % vmname, | ||||||
|  |                                 '/org/qubesos/audio', 'org.freedesktop.DBus.Properties', session_bus) | ||||||
|  | 
 | ||||||
|  |         current_audio = properties.call('Get', 'org.QubesOS.Audio', 'RecAllowed') | ||||||
|  |         if current_audio.type() == current_audio.ReplyMessage: | ||||||
|  |             value = current_audio.arguments()[0].toPyObject().toBool() | ||||||
|  |             return bool(value) | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|     @pyqtSlot(name='on_action_toggle_audio_input_triggered') |     @pyqtSlot(name='on_action_toggle_audio_input_triggered') | ||||||
|     def action_toggle_audio_input_triggered(self): |     def action_toggle_audio_input_triggered(self): | ||||||
|         vm = self.get_selected_vm() |         vm = self.get_selected_vm() | ||||||
|         audio = session_bus.get_object('org.QubesOS.Audio.%s' % vm.name, |         properties = QDBusInterface('org.QubesOS.Audio.%s' % vm.name, | ||||||
|                                       '/org/qubesos/audio') |                                 '/org/qubesos/audio', 'org.freedesktop.DBus.Properties', session_bus) | ||||||
|         current_audio = bool(audio.Get('org.QubesOS.Audio', 'RecAllowed')) |         properties.call('Set', 'org.QubesOS.Audio', 'RecAllowed', | ||||||
|         audio.Set('org.QubesOS.Audio', 'RecAllowed', dbus.Boolean(not current_audio, variant_level=1)) |                 QDBusVariant(not self.get_audio_rec_allowed(vm.name))) | ||||||
|         # icon will be updated based on dbus signal |         # icon will be updated based on dbus signal | ||||||
| 
 | 
 | ||||||
|     @pyqtSlot(name='on_action_updatevm_triggered') |     @pyqtSlot(name='on_action_updatevm_triggered') | ||||||
| @ -1966,8 +1990,9 @@ class QubesTrayIcon(QSystemTrayIcon): | |||||||
| 
 | 
 | ||||||
|         self.connect (self, SIGNAL("activated (QSystemTrayIcon::ActivationReason)"), self.icon_clicked) |         self.connect (self, SIGNAL("activated (QSystemTrayIcon::ActivationReason)"), self.icon_clicked) | ||||||
| 
 | 
 | ||||||
|         self.tray_object = dbus.SessionBus().get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications", follow_name_owner_changes=True) |         self.tray_notifier = QDBusInterface("org.freedesktop.Notifications", | ||||||
|         self.tray_notifier = dbus.Interface(self.tray_object, "org.freedesktop.Notifications" ) |                 "/org/freedesktop/Notifications", | ||||||
|  |                 "org.freedesktop.Notifications", session_bus) | ||||||
| 
 | 
 | ||||||
|     def icon_clicked(self, reason): |     def icon_clicked(self, reason): | ||||||
|         if reason == QSystemTrayIcon.Context: |         if reason == QSystemTrayIcon.Context: | ||||||
| @ -1984,7 +2009,15 @@ class QubesTrayIcon(QSystemTrayIcon): | |||||||
|                 target.addAction(action) |                 target.addAction(action) | ||||||
| 
 | 
 | ||||||
|     def showMessage(self, message, msecs): |     def showMessage(self, message, msecs): | ||||||
|         self.tray_notifier.Notify("Qubes", 0, "/usr/share/qubes/icons/qubes.png", "Qubes VM Manager", message, [], [], msecs) |         # QtDBus bindings doesn't use introspection to get proper method | ||||||
|  |         # parameters types, so must cast explicitly | ||||||
|  |         v_replace_id = QVariant(0) | ||||||
|  |         v_replace_id.convert(QVariant.UInt) | ||||||
|  |         v_actions = QVariant([]) | ||||||
|  |         v_actions.convert(QVariant.StringList) | ||||||
|  |         self.tray_notifier.call("Notify", "Qubes", v_replace_id, | ||||||
|  |                 "/usr/share/qubes/icons/qubes.png", "Qubes VM Manager", | ||||||
|  |                 message, v_actions, QVariant.fromMap({}), msecs) | ||||||
| 
 | 
 | ||||||
|     def createAction(self, text, slot=None, shortcut=None, icon=None, |     def createAction(self, text, slot=None, shortcut=None, icon=None, | ||||||
|                      tip=None, checkable=False, signal="triggered()"): |                      tip=None, checkable=False, signal="triggered()"): | ||||||
| @ -2002,44 +2035,39 @@ class QubesTrayIcon(QSystemTrayIcon): | |||||||
|             action.setCheckable(True) |             action.setCheckable(True) | ||||||
|         return action |         return action | ||||||
| 
 | 
 | ||||||
| class QubesDbusNotify(dbus.service.Object): | class QubesDbusNotifyServerAdaptor(QDBusAbstractAdaptor): | ||||||
|     def __init__(self, bus, manager_window): |     """ This provides the DBus adaptor to the outside world""" | ||||||
|         # export to system bus (instead of session) to make possible |  | ||||||
|         # communication from outside of session (eg. from qmemman) |  | ||||||
|         dbus.service.Object.__init__(self, bus, dbus_object_path) |  | ||||||
|         self.manager_window = manager_window |  | ||||||
| 
 | 
 | ||||||
|     @dbus.service.method(dbus_interface=dbus_interface, |     Q_CLASSINFO("D-Bus Interface", dbus_interface) | ||||||
|             in_signature='ss', out_signature='') | 
 | ||||||
|  |     @pyqtSlot(str, str) | ||||||
|     def notify_error(self, vmname, message): |     def notify_error(self, vmname, message): | ||||||
|         vm = self.manager_window.qvm_collection.get_vm_by_name(vmname) |         vm = self.parent().qvm_collection.get_vm_by_name(vmname) | ||||||
|         if vm: |         if vm: | ||||||
|             self.manager_window.set_error(vm.qid, message) |             self.parent().set_error(vm.qid, message) | ||||||
|         else: |         else: | ||||||
|             # ignore VM-not-found error |             # ignore VM-not-found error | ||||||
|             pass |             pass | ||||||
|     @dbus.service.method(dbus_interface=dbus_interface, | 
 | ||||||
|             in_signature='ss', out_signature='') |     @pyqtSlot(str, str) | ||||||
|     def clear_error_exact(self, vmname, message): |     def clear_error_exact(self, vmname, message): | ||||||
|         vm = self.manager_window.qvm_collection.get_vm_by_name(vmname) |         vm = self.parent().qvm_collection.get_vm_by_name(vmname) | ||||||
|         if vm: |         if vm: | ||||||
|             self.manager_window.clear_error_exact(vm.qid, message) |             self.parent().clear_error_exact(vm.qid, message) | ||||||
|         else: |         else: | ||||||
|             # ignore VM-not-found error |             # ignore VM-not-found error | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
|     @dbus.service.method(dbus_interface=dbus_interface, |     @pyqtSlot(str) | ||||||
|             in_signature='s', out_signature='') |  | ||||||
|     def clear_error(self, vmname): |     def clear_error(self, vmname): | ||||||
|         vm = self.manager_window.qvm_collection.get_vm_by_name(vmname) |         vm = self.parent().qvm_collection.get_vm_by_name(vmname) | ||||||
|         if vm: |         if vm: | ||||||
|             self.manager_window.clear_error(vm.qid, message) |             self.parent().clear_error(vm.qid) | ||||||
|         else: |         else: | ||||||
|             # ignore VM-not-found error |             # ignore VM-not-found error | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
|     @dbus.service.method(dbus_interface=dbus_interface, |     @pyqtSlot() | ||||||
|             in_signature='', out_signature='') |  | ||||||
|     def show_manager(self): |     def show_manager(self): | ||||||
|         bring_manager_to_front() |         bring_manager_to_front() | ||||||
| 
 | 
 | ||||||
| @ -2087,15 +2115,11 @@ def bring_manager_to_front(): | |||||||
| def show_running_manager_via_dbus(): | def show_running_manager_via_dbus(): | ||||||
|     global system_bus |     global system_bus | ||||||
|     if system_bus is None: |     if system_bus is None: | ||||||
|         system_bus = dbus.SystemBus() |         system_bus = QDBusConnection.systemBus() | ||||||
| 
 | 
 | ||||||
|     try: |     qubes_manager = QDBusInterface('org.qubesos.QubesManager', | ||||||
|         qubes_manager = system_bus.get_object('org.qubesos.QubesManager', |             '/org/qubesos/QubesManager', 'org.qubesos.QubesManager', system_bus) | ||||||
|                 '/org/qubesos/QubesManager') |     qubes_manager.call('show_manager') | ||||||
|         qubes_manager.show_manager(dbus_interface='org.qubesos.QubesManager') |  | ||||||
|     except dbus.DBusException: |  | ||||||
|         # ignore the case when no qubes-manager is running |  | ||||||
|         pass |  | ||||||
| 
 | 
 | ||||||
| def exit_app(): | def exit_app(): | ||||||
|     notifier.stop() |     notifier.stop() | ||||||
| @ -2167,9 +2191,6 @@ def main(): | |||||||
| 
 | 
 | ||||||
|     sys.excepthook = handle_exception |     sys.excepthook = handle_exception | ||||||
| 
 | 
 | ||||||
|     # setup dbus connection |  | ||||||
|     DBusQtMainLoop(set_as_default=True) |  | ||||||
| 
 |  | ||||||
|     global manager_window |     global manager_window | ||||||
|     manager_window = VmManagerWindow() |     manager_window = VmManagerWindow() | ||||||
|     wm = WatchManager() |     wm = WatchManager() | ||||||
| @ -2181,12 +2202,12 @@ def main(): | |||||||
|     wm.add_watch(qubes_clipboard_info_file, EventsCodes.OP_FLAGS.get('IN_CLOSE_WRITE')) |     wm.add_watch(qubes_clipboard_info_file, EventsCodes.OP_FLAGS.get('IN_CLOSE_WRITE')) | ||||||
| 
 | 
 | ||||||
|     global system_bus |     global system_bus | ||||||
|     system_bus = dbus.SystemBus() |     system_bus = QDBusConnection.systemBus() | ||||||
|     name = dbus.service.BusName('org.qubesos.QubesManager', system_bus) |     system_bus.registerService('org.qubesos.QubesManager') | ||||||
|     dbus_notifier = QubesDbusNotify(system_bus, manager_window) |     system_bus.registerObject(dbus_object_path, manager_window) | ||||||
| 
 | 
 | ||||||
|     global session_bus |     global session_bus | ||||||
|     session_bus = dbus.SessionBus() |     session_bus = QDBusConnection.sessionBus() | ||||||
| 
 | 
 | ||||||
|     global trayIcon |     global trayIcon | ||||||
|     trayIcon = QubesTrayIcon(QIcon(":/qubes.png")) |     trayIcon = QubesTrayIcon(QIcon(":/qubes.png")) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski
						Marek Marczykowski