diff --git a/qubesusbproxy/core3ext.py b/qubesusbproxy/core3ext.py index f74bf93..dd02505 100644 --- a/qubesusbproxy/core3ext.py +++ b/qubesusbproxy/core3ext.py @@ -421,47 +421,66 @@ def on_qdb_change(self, vm, event, path): current_devices = dict((dev.ident, dev.frontend_domain) for dev in self.on_device_list_usb(vm, None)) + # send events about devices detached/attached outside by themselves + # (like device pulled out or manual qubes.USB qrexec call) # compare cached devices and current devices, collect: - # - newly appeared devices - # - devices disconnected from a vm - # - devices connected to a vm - new_devices = set() - connected_devices = dict() - disconnected_devices = dict() - devices_cache_for_vm = self.devices_cache[vm.name] - for dev_id, connected_to in current_devices.items(): - if dev_id not in devices_cache_for_vm: - new_devices.add(dev_id) - device = USBDevice(vm, dev_id) - vm.fire_event('device-added:usb', device=device) - elif devices_cache_for_vm[dev_id] != current_devices[dev_id]: - if devices_cache_for_vm[dev_id] is not None: - disconnected_devices[dev_id] = devices_cache_for_vm[dev_id] - if current_devices[dev_id] is not None: - connected_devices[dev_id] = current_devices[dev_id] - for dev_id, connected_to in devices_cache_for_vm.items(): + # - newly appeared devices (ident) + # - devices attached from a vm to frontend vm (ident: frontend_vm) + # - devices detached from frontend vm (ident: frontend_vm) + # - disappeared devices, e.g. plugged out (ident) + added = set() + attached = dict() + detached = dict() + removed = set() + cache = self.devices_cache[vm.name] + for dev_id, front_vm in current_devices.items(): + if dev_id not in cache: + added.add(dev_id) + if front_vm is not None: + attached[dev_id] = front_vm + elif cache[dev_id] != front_vm: + cached_front = cache[dev_id] + if front_vm is None: + detached[dev_id] = cached_front + elif cached_front is None: + attached[dev_id] = front_vm + else: + # front changed from one to another, so we signal it as: + # detach from first one and attach to the second one. + detached[dev_id] = cached_front + attached[dev_id] = front_vm + + for dev_id, cached_front in cache.items(): if dev_id not in current_devices: - device = USBDevice(vm, dev_id) - vm.fire_event('device-removed:usb', device=device) + removed.add(dev_id) + if cached_front is not None: + detached[dev_id] = cached_front + + for dev_id, front_vm in detached.items(): + dev = USBDevice(vm, dev_id) + asyncio.ensure_future(front_vm.fire_event_async( + 'device-detach:usb', device=dev)) + for dev_id in removed: + device = USBDevice(vm, dev_id) + vm.fire_event('device-removed:usb', device=device) + for dev_id in added: + device = USBDevice(vm, dev_id) + vm.fire_event('device-added:usb', device=device) + for dev_ident, front_vm in attached.items(): + dev = USBDevice(vm, dev_ident) + asyncio.ensure_future(front_vm.fire_event_async( + 'device-attach:usb', device=dev, options={})) self.devices_cache[vm.name] = current_devices - # send events about devices detached/attached outside by themselves - # (like device pulled out or manual qubes.USB qrexec call) - for dev_ident, front_vm in disconnected_devices.items(): - dev_id = USBDevice(vm, dev_ident) - asyncio.ensure_future(front_vm.fire_event_async('device-detach:usb', - device=dev_id)) - for dev_ident, front_vm in connected_devices.items(): - dev_id = USBDevice(vm, dev_ident) - asyncio.ensure_future(front_vm.fire_event_async('device-attach:usb', - device=dev_id, - options={})) + for front_vm in vm.app.domains: if not front_vm.is_running(): continue for assignment in front_vm.devices['usb'].get_assigned_devices(): - if assignment.backend_domain == vm and \ - assignment.ident in new_devices: + if (assignment.backend_domain == vm + and assignment.ident in added + and assignment.ident not in attached + ): asyncio.ensure_future(self._attach_and_notify( front_vm, assignment.device, assignment.options)) @@ -610,8 +629,8 @@ async def on_device_detach_usb(self, vm, event, device): async def on_domain_start(self, vm, _event, **_kwargs): # pylint: disable=unused-argument for assignment in vm.devices['usb'].get_assigned_devices(): - device = assignment.device - await self.on_device_attach_usb(vm, '', device, options={}) + asyncio.ensure_future(self._attach_and_notify( + vm, assignment.device, assignment.options)) @qubes.ext.handler('domain-shutdown') async def on_domain_shutdown(self, vm, _event, **_kwargs):