Skip to content

Commit 60d62ac

Browse files
committed
Merge remote-tracking branch 'origin/pr/51'
* origin/pr/51: core3ext: reattach assigned USB devices on resume from suspend Pull request description: This patch ensures USB devices are reattached on resume, which is necessary for S0ix since we don't detach the USB controller drivers and hence the udev rules aren't retriggered automatically. I've relocated the attachment logic into `_auto_attach_devices`, which is now shared between the domain-start and domain-resumed handlers. I've also added a new event, domain-resumed, in core-admin to support this since domain-unpaused isn't sufficient for our needs. That PR can be found here and is a dependency for this change: QubesOS/qubes-core-admin#725
2 parents 3569eeb + 402a8b9 commit 60d62ac

2 files changed

Lines changed: 41 additions & 27 deletions

File tree

qubesusbproxy/core3ext.py

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,37 @@ def __init__(self):
521521
"/etc/qubes-rpc/qubes.USB"
522522
)
523523
self.devices_cache = collections.defaultdict(dict)
524+
self.autoattach_locks = collections.defaultdict(asyncio.Lock)
525+
526+
async def _auto_attach_devices(self, vm):
527+
async with self.autoattach_locks[vm.uuid]:
528+
to_attach = {}
529+
assignments = get_assigned_devices(vm.devices["usb"])
530+
# the most specific assignments first
531+
for assignment in reversed(sorted(assignments)):
532+
for device in assignment.devices:
533+
if isinstance(device, qubes.device_protocol.UnknownDevice):
534+
continue
535+
if device.attachment:
536+
continue
537+
if not assignment.matches(device):
538+
print(
539+
"Unrecognized identity, skipping attachment of device "
540+
f"from the port {assignment}",
541+
file=sys.stderr,
542+
)
543+
continue
544+
# chose first assignment (the most specific) and ignore rest
545+
if device not in to_attach:
546+
# make it unique
547+
to_attach[device] = assignment.clone(device=device)
548+
in_progress = set()
549+
for assignment in to_attach.values():
550+
in_progress.add(
551+
asyncio.ensure_future(self.attach_and_notify(vm, assignment))
552+
)
553+
if in_progress:
554+
await asyncio.wait(in_progress)
524555

525556
@qubes.ext.handler("domain-init", "domain-load")
526557
def on_domain_init_load(self, vm, event):
@@ -744,41 +775,22 @@ async def on_device_assign_usb(self, vm, event, device, options):
744775
@qubes.ext.handler("domain-start")
745776
async def on_domain_start(self, vm, _event, **_kwargs):
746777
# pylint: disable=unused-argument
747-
to_attach = {}
748-
assignments = get_assigned_devices(vm.devices["usb"])
749-
# the most specific assignments first
750-
for assignment in reversed(sorted(assignments)):
751-
for device in assignment.devices:
752-
if isinstance(device, qubes.device_protocol.UnknownDevice):
753-
continue
754-
if device.attachment:
755-
continue
756-
if not assignment.matches(device):
757-
print(
758-
"Unrecognized identity, skipping attachment of device "
759-
f"from the port {assignment}",
760-
file=sys.stderr,
761-
)
762-
continue
763-
# chose first assignment (the most specific) and ignore rest
764-
if device not in to_attach:
765-
# make it unique
766-
to_attach[device] = assignment.clone(device=device)
767-
in_progress = set()
768-
for assignment in to_attach.values():
769-
in_progress.add(
770-
asyncio.ensure_future(self.attach_and_notify(vm, assignment))
771-
)
772-
if in_progress:
773-
await asyncio.wait(in_progress)
778+
await self._auto_attach_devices(vm)
774779

775780
@qubes.ext.handler("domain-shutdown")
776781
async def on_domain_shutdown(self, vm, _event, **_kwargs):
777782
# pylint: disable=unused-argument
778783
vm.fire_event("device-list-change:usb")
779784
utils.device_list_change(self, {}, vm, None, USBDevice)
785+
del self.autoattach_locks[vm.uuid]
786+
787+
@qubes.ext.handler("domain-resumed")
788+
async def on_domain_resumed(self, vm, _event, **_kwargs):
789+
# pylint: disable=unused-argument
790+
await self._auto_attach_devices(vm)
780791

781792
@qubes.ext.handler("qubes-close", system=True)
782793
def on_qubes_close(self, app, event):
783794
# pylint: disable=unused-argument
784795
self.devices_cache.clear()
796+
self.autoattach_locks.clear()

qubesusbproxy/tests.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2323
#
2424
import time
25+
import uuid
2526
import unittest
2627
from unittest import mock
2728
from unittest.mock import Mock, AsyncMock
@@ -620,6 +621,7 @@ class TestVM(qubes.tests.TestEmitter):
620621
def __init__(self, qdb, running=True, name="test-vm", **kwargs):
621622
super().__init__(**kwargs)
622623
self.name = name
624+
self.uuid = uuid.uuid4()
623625
self.klass = "AdminVM" if name == "dom0" else "AppVM"
624626
self.icon = "red"
625627
self.untrusted_qdb = TestQubesDB(qdb)

0 commit comments

Comments
 (0)