Skip to content

Commit cf0df31

Browse files
committed
Show "List USB Devices" option if sys-usb exists
Also show individual menu items if user has more than one usbvm resolves: QubesOS/qubes-issues#10131 resolves: QubesOS/qubes-issues#10202
1 parent 1789756 commit cf0df31

File tree

3 files changed

+63
-19
lines changed

3 files changed

+63
-19
lines changed

qui/devices/actionable_widgets.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -361,20 +361,20 @@ async def widget_action(self, *_args):
361361
)
362362

363363

364-
#### Start sys-usb
364+
#### Start sys-usb (or other custom USBVMs)
365365

366366

367-
class StartSysUsb(ActionableWidget, SimpleActionWidget):
368-
def __init__(self, sysusb: backend.VM, variant: str = "dark"):
367+
class StartUSBVM(ActionableWidget, SimpleActionWidget):
368+
def __init__(self, usbvm: backend.VM, variant: str = "dark"):
369369
super().__init__(
370-
icon_name=sysusb.icon_name,
371-
text="<b>List USB Devices " "(start sys-usb)</b>",
370+
icon_name=usbvm.icon_name,
371+
text="<b>List USB Devices " "(start {})</b>".format(usbvm.name),
372372
variant=variant,
373373
)
374-
self.sysusb = sysusb
374+
self.usbvm = usbvm
375375

376376
async def widget_action(self, *_args):
377-
self.sysusb.vm_object.start()
377+
self.usbvm.vm_object.start()
378378

379379

380380
#### Configuration-related actions

qui/devices/backend.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ def is_attachable(self) -> bool:
8787
"""
8888
return self.vm_class != "AdminVM" and self._vm.is_running()
8989

90+
@property
91+
def is_usbvm(self) -> bool:
92+
"""
93+
Does the VM have a PCI USB controller attached to it?
94+
We do not need to cache this since it is checked only at Qui Devices
95+
initialization as well a PCI assignment/un-assignment
96+
"""
97+
for dev in self._vm.devices["pci"].get_assigned_devices():
98+
for interface in dev.device.interfaces:
99+
if interface.category == DeviceCategory.PCI_USB:
100+
return True
101+
return False
102+
90103
@property
91104
def vm_object(self):
92105
"""

qui/devices/device_widget.py

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ def __init__(self, app_name, qapp, dispatcher):
112112
self.vms: Set[backend.VM] = set()
113113
self.dispvm_templates: Set[backend.VM] = set()
114114
self.parent_ports_to_hide = []
115-
self.sysusb: backend.VM | None = None
115+
self.active_usbvms: Set[backend.VM] = set()
116+
self.dormant_usbvms: Set[backend.VM] = set()
116117
self.dev_update_queue: Set = set()
117118
self.vm_update_queue: Set = set()
118119

@@ -144,6 +145,13 @@ def __init__(self, app_name, qapp, dispatcher):
144145
"device-unassign:" + devclass, self.device_unassigned
145146
)
146147

148+
self.dispatcher.add_handler("device-assign:pci", self.pci_action)
149+
self.dispatcher.add_handler("device-unassign:pci", self.pci_action)
150+
self.dispatcher.add_handler(
151+
"domain-pre-delete",
152+
self.remove_domain_item
153+
)
154+
147155
self.dispatcher.add_handler("domain-shutdown", self.vm_shutdown)
148156
self.dispatcher.add_handler("domain-start-failed", self.vm_shutdown)
149157
self.dispatcher.add_handler("domain-start", self.vm_start)
@@ -242,9 +250,12 @@ def initialize_vm_data(self):
242250
self.vms.add(wrapped_vm)
243251
if wrapped_vm.is_dispvm_template:
244252
self.dispvm_templates.add(wrapped_vm)
245-
if vm.name == "sys-usb":
246-
self.sysusb = wrapped_vm
247-
self.sysusb.is_running = vm.is_running()
253+
# Keep track of USBVMs
254+
if wrapped_vm.is_usbvm:
255+
if vm.is_running():
256+
self.active_usbvms.add(wrapped_vm)
257+
else:
258+
self.dormant_usbvms.add(wrapped_vm)
248259
except qubesadmin.exc.QubesException:
249260
# we don't have access to VM state
250261
pass
@@ -360,6 +371,24 @@ def device_unassigned(self, vm, _event, device, **_kwargs):
360371
# assigned. Cheers!
361372
return
362373

374+
def pci_action(self, vm, _event, **_kwargs):
375+
# We assume PCI controllers could be assigned only when qube is shutdown
376+
wrapped_vm = backend.VM(vm)
377+
if wrapped_vm.is_usbvm:
378+
if wrapped_vm not in self.dormant_usbvms:
379+
self.dormant_usbvms.add(wrapped_vm)
380+
elif wrapped_vm in self.dormant_usbvms:
381+
self.dormant_usbvms.discard(wrapped_vm)
382+
383+
def remove_domain_item(self, _submitter, _event, vm, **_kwargs):
384+
# In a perfect world, core should trigger `device-unassign:pci` event
385+
# for PCI devices attached to an HVM before actually removing it and
386+
# this method should not be necessary. But we are not certain :/
387+
for wrapped_vm in self.dormant_usbvms:
388+
if wrapped_vm.name == vm:
389+
self.dormant_usbvms.discard(wrapped_vm)
390+
break
391+
363392
def update_single_feature(self, _vm, _event, feature, value=None, oldvalue=None):
364393
if not value:
365394
new = set()
@@ -533,8 +562,9 @@ def vm_start(self, vm, _event, **_kwargs):
533562
internal, attachable = False, False
534563
if attachable and not internal:
535564
self.vms.add(wrapped_vm)
536-
if wrapped_vm == self.sysusb:
537-
self.sysusb.is_running = True
565+
if wrapped_vm in self.dormant_usbvms:
566+
self.dormant_usbvms.discard(wrapped_vm)
567+
self.active_usbvms.add(wrapped_vm)
538568

539569
for devclass in DEV_TYPES:
540570
try:
@@ -548,8 +578,9 @@ def vm_start(self, vm, _event, **_kwargs):
548578

549579
def vm_shutdown(self, vm, _event, **_kwargs):
550580
wrapped_vm = backend.VM(vm)
551-
if wrapped_vm == self.sysusb:
552-
self.sysusb.is_running = False
581+
if wrapped_vm in self.active_usbvms:
582+
self.active_usbvms.discard(wrapped_vm)
583+
self.dormant_usbvms.add(wrapped_vm)
553584

554585
self.vms.discard(wrapped_vm)
555586
self.dispvm_templates.discard(wrapped_vm)
@@ -633,13 +664,13 @@ def show_menu(self, _unused, _event):
633664

634665
menu_items.append(device_item)
635666

636-
if not self.sysusb.is_running:
637-
sysusb_item = actionable_widgets.generate_wrapper_widget(
667+
for wrapped_vm in self.dormant_usbvms:
668+
usbvm_item = actionable_widgets.generate_wrapper_widget(
638669
Gtk.MenuItem,
639670
"activate",
640-
actionable_widgets.StartSysUsb(self.sysusb, theme),
671+
actionable_widgets.StartUSBVM(wrapped_vm, theme),
641672
)
642-
menu_items.append(sysusb_item)
673+
menu_items.append(usbvm_item)
643674

644675
for item in menu_items:
645676
tray_menu.add(item)

0 commit comments

Comments
 (0)