diff --git a/icons/ban.svg b/icons/ban.svg
new file mode 100644
index 00000000..1296e51a
--- /dev/null
+++ b/icons/ban.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py
index 1ddb7424..93f8cf46 100644
--- a/qubesmanager/qube_manager.py
+++ b/qubesmanager/qube_manager.py
@@ -103,6 +103,7 @@ def __init__(self):
"Halted" : QIcon(":/blank")
}
self.outdatedIcons = {
+ "blocked" : QIcon(":/ban"),
"update" : QIcon(":/updateable"),
"outdated" : QIcon(":/outdated"),
"to-be-outdated" : QIcon(":/outdated"),
@@ -110,6 +111,8 @@ def __init__(self):
"skipped": QIcon(':/skipped')
}
self.outdatedTooltips = {
+ "blocked" : self.tr(
+ "The qube is prohibited from being started"),
"update" : self.tr("Updates available"),
"outdated" : self.tr(
"The qube must be restarted for recent changes in "
@@ -239,6 +242,12 @@ def update_power_state(self):
self.state['outdated'] = ""
try:
+ if self.vm.klass != "AdminVM" and manager_utils.get_feature(
+ self.vm, 'prohibit-start', False):
+ # Special case where being outdated, eol & skipped is irrelevant
+ self.state['outdated'] = 'blocked'
+ return
+
if manager_utils.is_running(self.vm, False):
if hasattr(self.vm, 'template') and \
manager_utils.is_running(self.vm.template, False):
@@ -879,6 +888,10 @@ def __init__(self, qt_app, qubes_app, dispatcher, _parent=None):
self.on_domain_updates_available)
dispatcher.add_handler('domain-feature-delete:skip-update',
self.on_domain_updates_available)
+ dispatcher.add_handler('domain-feature-set:prohibit-start',
+ self.on_domain_updates_available)
+ dispatcher.add_handler('domain-feature-delete:prohibit-start',
+ self.on_domain_updates_available)
self.installEventFilter(self)
@@ -1125,6 +1138,9 @@ def check_updates(self, info=None):
try:
if info.vm.klass in {'TemplateVM', 'StandaloneVM'}:
if manager_utils.get_feature(
+ info.vm, 'prohibit-start', False):
+ info.state['outdated'] = 'blocked'
+ elif manager_utils.get_feature(
info.vm, 'skip-update', False):
info.state['outdated'] = 'skipped'
elif manager_utils.get_feature(
@@ -1352,6 +1368,17 @@ def table_selection_changed(self):
if not vm.updateable and vm.klass != 'AdminVM':
self.action_updatevm.setEnabled(False)
+ if vm.vm.features.get('prohibit-start', False):
+ self.action_open_console.setEnabled(False)
+ self.action_resumevm.setEnabled(False)
+ self.action_startvm_tools_install.setEnabled(False)
+ self.action_pausevm.setEnabled(False)
+ self.action_restartvm.setEnabled(False)
+ self.action_killvm.setEnabled(False)
+ self.action_shutdownvm.setEnabled(False)
+ self.action_updatevm.setEnabled(False)
+ self.action_run_command_in_vm.setEnabled(False)
+
self.update_template_menu()
self.update_network_menu()
diff --git a/qubesmanager/tests/conftest.py b/qubesmanager/tests/conftest.py
index d303e7f8..e39f071b 100644
--- a/qubesmanager/tests/conftest.py
+++ b/qubesmanager/tests/conftest.py
@@ -27,6 +27,7 @@ def test_qubes_app():
test_qapp = MockQubesComplete()
test_qapp._qubes['sys-usb'].features[
'supported-feature.keyboard-layout'] = '1'
+ test_qapp._qubes['sys-usb'].features['prohibit-start'] = None
test_qapp.update_vm_calls()
return test_qapp
diff --git a/qubesmanager/tests/test_qube_manager.py b/qubesmanager/tests/test_qube_manager.py
index d9a93385..3a8f383f 100644
--- a/qubesmanager/tests/test_qube_manager.py
+++ b/qubesmanager/tests/test_qube_manager.py
@@ -1604,3 +1604,13 @@ def test_704_check_later(mock_timer, mock_question):
assert mock_question.call_count == 0
assert mock_timer.call_count == 1
+
+
+def test_705_prohibit_start_vms(qubes_manager):
+ qubes_manager.qubes_app._qubes['test-old'].features[ \
+ 'prohibit_start'] = \
+ 'Do not start - Qube is compromised by Gremlins!'
+ _select_vm(qubes_manager, 'test-old')
+ qubes_manager.qubes_app._qubes['test-standalone'].features[ \
+ 'prohibit_start'] = 'Ancient qube which breaks on update'
+ _select_vm(qubes_manager, 'test-standalone')
diff --git a/resources.qrc b/resources.qrc
index 9c4d87d2..524d6496 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -3,6 +3,7 @@
icons/add.svg
icons/apps.svg
icons/backup.svg
+ icons/ban.svg
icons/blank.svg
icons/checked.svg
icons/checkmark.svg