Skip to content

Commit dddf91d

Browse files
committed
Add Debug Console to Qui Domains systray widget
fixes: QubesOS/qubes-issues#9788
1 parent aeb21cb commit dddf91d

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed

qui/tray/domains.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,34 @@ async def perform_action(self):
338338
)
339339

340340

341+
class RunDebugConsoleItem(VMActionMenuItem):
342+
"""Run Debug Console menu Item. When activated runs a qvm-console-dispvm."""
343+
344+
def __init__(self, vm, icon_cache):
345+
img = Gtk.Image.new_from_file(
346+
"/usr/share/icons/HighContrast/16x16/apps/logviewer.png"
347+
)
348+
super().__init__(
349+
vm,
350+
label=_("Debug Console"),
351+
img=img,
352+
)
353+
self.visible = False
354+
self.connect("show", self.on_show_event)
355+
356+
def on_show_event(self, widget):
357+
if self.visible:
358+
widget.show()
359+
else:
360+
widget.hide()
361+
362+
async def perform_action(self):
363+
# pylint: disable=consider-using-with
364+
await asyncio.create_subprocess_exec(
365+
"qvm-console-dispvm", self.vm.name, stderr=subprocess.PIPE
366+
)
367+
368+
341369
class OpenFileManagerItem(VMActionMenuItem):
342370
"""Attempts to open a file manager in the VM. If fails, displays an
343371
error message."""
@@ -392,15 +420,37 @@ def __init__(self, vm, app, icon_cache):
392420
self.add(
393421
RunTerminalItem(self.vm, icon_cache, as_root=app.terminal_as_root)
394422
)
423+
424+
# Debug console for developers, troubleshooting, headless qubes
425+
self.debug_console = RunDebugConsoleItem(self.vm, icon_cache)
426+
self.add(self.debug_console)
427+
395428
self.add(PreferencesItem(self.vm, icon_cache))
396429
self.add(PauseItem(self.vm, icon_cache))
397430
self.add(ShutdownItem(self.vm, icon_cache))
398431
if self.vm.klass != "DispVM" or not self.vm.auto_cleanup:
399432
self.add(RestartItem(self.vm, icon_cache))
400433

401434
self.set_reserve_toggle_size(False)
435+
self.debug_console_update()
402436
self.show_all()
403437

438+
def debug_console_update(self, *_args, **_kwargs):
439+
# Debug console is shown only if debug property is set, no GUIVM is set
440+
# ... or with `wizard-mode` feature per qube or per entire GUIVM.
441+
if (
442+
self.app.wizard_mode
443+
or getattr(self.vm, "debug")
444+
or not getattr(self.vm, "guivm")
445+
or not self.vm.features.check_with_template("gui", False)
446+
or self.vm.features.get("wizard-mode", False)
447+
):
448+
self.debug_console.visible = True
449+
self.debug_console.show()
450+
else:
451+
self.debug_console.visible = False
452+
self.debug_console.hide()
453+
404454

405455
class PausedMenu(Gtk.Menu):
406456
"""The sub-menu for a paused domain"""
@@ -696,6 +746,11 @@ def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
696746
self.set_application_id(app_name)
697747
self.register() # register Gtk Application
698748

749+
# to display debug console for all qubes
750+
self.wizard_mode = self.qapp.domains[self.qapp.local_name].features.get(
751+
"wizard-mode", False
752+
)
753+
699754
def register_events(self):
700755
self.dispatcher.add_handler("connection-established", self.refresh_all)
701756
self.dispatcher.add_handler("domain-pre-start", self.update_domain_item)
@@ -743,8 +798,34 @@ def register_events(self):
743798
self.dispatcher.add_handler("property-set:netvm", self.property_change)
744799
self.dispatcher.add_handler("property-set:label", self.property_change)
745800

801+
self.dispatcher.add_handler("property-set:debug", self.debug_change)
802+
self.dispatcher.add_handler("property-set:guivm", self.debug_change)
803+
self.dispatcher.add_handler("domain-feature-set:gui", self.debug_change)
804+
self.dispatcher.add_handler(
805+
"domain-feature-delete:gui", self.debug_change
806+
)
807+
self.dispatcher.add_handler(
808+
"domain-feature-set:wizard-mode", self.debug_change
809+
)
810+
self.dispatcher.add_handler(
811+
"domain-feature-delete:wizard-mode", self.debug_change
812+
)
813+
746814
self.stats_dispatcher.add_handler("vm-stats", self.update_stats)
747815

816+
def debug_change(self, vm, *_args, **_kwargs):
817+
if vm == self.qapp.local_name:
818+
self.wizard_mode = self.qapp.domains[
819+
self.qapp.local_name
820+
].features.get("wizard-mode", False)
821+
vms = self.menu_items
822+
else:
823+
vms = {vm}
824+
for menu in vms:
825+
submenu = self.menu_items[menu].get_submenu()
826+
if isinstance(submenu, StartedMenu):
827+
submenu.debug_console_update()
828+
748829
def show_menu(self, _unused, event):
749830
self.terminal_as_root = False
750831
self.tray_menu.popup_at_pointer(event) # None means current event
@@ -1096,6 +1177,21 @@ def _disconnect_signals(self, _event):
10961177
"property-set:label", self.property_change
10971178
)
10981179

1180+
self.dispatcher.remove_handler("property-set:debug", self.debug_change)
1181+
self.dispatcher.remove_handler("property-set:guivm", self.debug_change)
1182+
self.dispatcher.remove_handler(
1183+
"domain-feature-set:gui", self.debug_change
1184+
)
1185+
self.dispatcher.remove_handler(
1186+
"domain-feature-delete:gui", self.debug_change
1187+
)
1188+
self.dispatcher.remove_handler(
1189+
"domain-feature-set:wizard-mode", self.debug_change
1190+
)
1191+
self.dispatcher.remove_handler(
1192+
"domain-feature-delete:wizard-mode", self.debug_change
1193+
)
1194+
10991195
self.stats_dispatcher.remove_handler("vm-stats", self.update_stats)
11001196

11011197
@property

0 commit comments

Comments
 (0)