Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions qui/devices/actionable_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,20 +361,20 @@ async def widget_action(self, *_args):
)


#### Start sys-usb
#### Start sys-usb (or other custom USBVMs)


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

async def widget_action(self, *_args):
self.sysusb.vm_object.start()
self.usbvm.vm_object.start()


#### Configuration-related actions
Expand Down
13 changes: 13 additions & 0 deletions qui/devices/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ def is_attachable(self) -> bool:
"""
return self.vm_class != "AdminVM" and self._vm.is_running()

@property
def is_usbvm(self) -> bool:
"""
Does the VM have a PCI USB controller attached to it?
We do not need to cache this since it is checked only at Qui Devices
initialization as well a PCI assignment/un-assignment
"""
for dev in self._vm.devices["pci"].get_assigned_devices():
for interface in dev.device.interfaces:
if interface.category == DeviceCategory.PCI_USB:
return True
return False

@property
def vm_object(self):
"""
Expand Down
52 changes: 40 additions & 12 deletions qui/devices/device_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ def __init__(self, app_name, qapp, dispatcher):
self.vms: Set[backend.VM] = set()
self.dispvm_templates: Set[backend.VM] = set()
self.parent_ports_to_hide = []
self.sysusb: backend.VM | None = None
self.active_usbvms: Set[backend.VM] = set()
self.dormant_usbvms: Set[backend.VM] = set()
self.dev_update_queue: Set = set()
self.vm_update_queue: Set = set()

Expand Down Expand Up @@ -144,6 +145,10 @@ def __init__(self, app_name, qapp, dispatcher):
"device-unassign:" + devclass, self.device_unassigned
)

self.dispatcher.add_handler("device-assign:pci", self.pci_action)
self.dispatcher.add_handler("device-unassign:pci", self.pci_action)
self.dispatcher.add_handler("domain-pre-delete", self.remove_domain_item)

self.dispatcher.add_handler("domain-shutdown", self.vm_shutdown)
self.dispatcher.add_handler("domain-start-failed", self.vm_shutdown)
self.dispatcher.add_handler("domain-start", self.vm_start)
Expand Down Expand Up @@ -242,9 +247,12 @@ def initialize_vm_data(self):
self.vms.add(wrapped_vm)
if wrapped_vm.is_dispvm_template:
self.dispvm_templates.add(wrapped_vm)
if vm.name == "sys-usb":
self.sysusb = wrapped_vm
self.sysusb.is_running = vm.is_running()
# Keep track of USBVMs
if wrapped_vm.is_usbvm:
if vm.is_running():
self.active_usbvms.add(wrapped_vm)
else:
self.dormant_usbvms.add(wrapped_vm)
except qubesadmin.exc.QubesException:
# we don't have access to VM state
pass
Expand Down Expand Up @@ -360,6 +368,24 @@ def device_unassigned(self, vm, _event, device, **_kwargs):
# assigned. Cheers!
return

def pci_action(self, vm, _event, **_kwargs):
# We assume PCI controllers could be assigned only when qube is shutdown
wrapped_vm = backend.VM(vm)
if wrapped_vm.is_usbvm:
if wrapped_vm not in self.dormant_usbvms:
self.dormant_usbvms.add(wrapped_vm)
elif wrapped_vm in self.dormant_usbvms:
self.dormant_usbvms.discard(wrapped_vm)

def remove_domain_item(self, _submitter, _event, vm, **_kwargs):
# In a perfect world, core should trigger `device-unassign:pci` event
# for PCI devices attached to an HVM before actually removing it and
# this method should not be necessary. But we are not certain :/
for wrapped_vm in self.dormant_usbvms:
if wrapped_vm.name == vm:
self.dormant_usbvms.discard(wrapped_vm)
break

def update_single_feature(self, _vm, _event, feature, value=None, oldvalue=None):
if not value:
new = set()
Expand Down Expand Up @@ -533,8 +559,9 @@ def vm_start(self, vm, _event, **_kwargs):
internal, attachable = False, False
if attachable and not internal:
self.vms.add(wrapped_vm)
if wrapped_vm == self.sysusb:
self.sysusb.is_running = True
if wrapped_vm in self.dormant_usbvms:
self.dormant_usbvms.discard(wrapped_vm)
self.active_usbvms.add(wrapped_vm)

for devclass in DEV_TYPES:
try:
Expand All @@ -548,8 +575,9 @@ def vm_start(self, vm, _event, **_kwargs):

def vm_shutdown(self, vm, _event, **_kwargs):
wrapped_vm = backend.VM(vm)
if wrapped_vm == self.sysusb:
self.sysusb.is_running = False
if wrapped_vm in self.active_usbvms:
self.active_usbvms.discard(wrapped_vm)
self.dormant_usbvms.add(wrapped_vm)

self.vms.discard(wrapped_vm)
self.dispvm_templates.discard(wrapped_vm)
Expand Down Expand Up @@ -633,13 +661,13 @@ def show_menu(self, _unused, _event):

menu_items.append(device_item)

if not self.sysusb.is_running:
sysusb_item = actionable_widgets.generate_wrapper_widget(
for wrapped_vm in self.dormant_usbvms:
usbvm_item = actionable_widgets.generate_wrapper_widget(
Gtk.MenuItem,
"activate",
actionable_widgets.StartSysUsb(self.sysusb, theme),
actionable_widgets.StartUSBVM(wrapped_vm, theme),
)
menu_items.append(sysusb_item)
menu_items.append(usbvm_item)

for item in menu_items:
tray_menu.add(item)
Expand Down