diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 651a996..8539208 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -49,6 +49,11 @@ class BackupThread(QtCore.QThread): try: if not self.vm.is_running(): self.vm.start() + except exc.QubesException: + # we may have insufficient exceptions to ensure the qube is running + pass + + try: self.vm.app.qubesd_call( 'dom0', 'admin.backup.Execute', backup_utils.get_profile_name(True)) @@ -103,8 +108,8 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard): qubes_app=self.qubes_app, filter_function=(lambda vm: vm.klass != 'TemplateVM' - and vm.is_running() - and not vm.features.get('internal', False)), + and utils.is_running(vm, False) + and not utils.get_feature(vm, 'internal', False)), allow_internal=True, ) self.appvm_combobox.setCurrentIndex( @@ -215,7 +220,7 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard): def __fill_vms_list__(self, selected=None): for vm in self.qubes_app.domains: - if vm.features.get('internal', False): + if utils.get_feature(vm, 'internal', False): continue item = BackupVMsWindow.VmListItem(vm) @@ -298,13 +303,17 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard): if self.currentPage() is self.confirm_page: self.save_settings(use_temp=True) - backup_summary = self.qubes_app.qubesd_call( - 'dom0', 'admin.backup.Info', - backup_utils.get_profile_name(True)) + try: + backup_summary = self.qubes_app.qubesd_call( + 'dom0', 'admin.backup.Info', + backup_utils.get_profile_name(True)).decode() + except exc.QubesDaemonCommunicationError: + backup_summary = "Failed to get backup summary: " \ + "insufficient permissions" self.textEdit.setReadOnly(True) self.textEdit.setFontFamily("Monospace") - self.textEdit.setText(backup_summary.decode()) + self.textEdit.setText(backup_summary) elif self.currentPage() is self.commit_page: diff --git a/qubesmanager/backup_utils.py b/qubesmanager/backup_utils.py index 31a842f..a754417 100644 --- a/qubesmanager/backup_utils.py +++ b/qubesmanager/backup_utils.py @@ -43,10 +43,10 @@ def fill_appvms_list(dialog): dialog.appvm_combobox.setCurrentIndex(0) # current selected is null "" for vm in dialog.qubes_app.domains: - if vm.features.get('internal', False) or vm.klass == 'TemplateVM': + if utils.get_feature(vm, 'internal', False) or vm.klass == 'TemplateVM': continue - if vm.is_running() and vm.qid != 0: + if utils.is_running(vm, False) and vm.qid != 0: dialog.appvm_combobox.addItem(vm.name) @@ -101,6 +101,11 @@ def select_path_button_clicked(dialog, select_file=False, read_only=False): dialog.tr("Unexpected characters in path!"), dialog.tr("Backup path can only contain the following " "special characters: /:.,_+=() -")) + except Exception as ex: + QtWidgets.QMessageBox.warning( + dialog, + dialog.tr("Failed to select path!"), + dialog.tr("Error {} occurred.".format(str(ex)))) except subprocess.CalledProcessError: if not read_only: diff --git a/qubesmanager/bootfromdevice.py b/qubesmanager/bootfromdevice.py index 03fa172..bd6344c 100644 --- a/qubesmanager/bootfromdevice.py +++ b/qubesmanager/bootfromdevice.py @@ -24,6 +24,7 @@ from . import ui_bootfromdevice # pylint: disable=no-name-in-module from PyQt5 import QtWidgets, QtGui # pylint: disable=import-error from qubesadmin import tools from qubesadmin.tools import qvm_start +from qubesadmin import exc class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, @@ -75,13 +76,20 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, self.done(0) def __warn_if_running__(self): - if self.vm.is_running(): + try: + if self.vm.is_running(): + QtWidgets.QMessageBox.warning( + self, + self.tr("Warning!"), + self.tr("Qube must be turned off before booting it from " + "device. Please turn off the qube.")) + except exc.QubesPropertyAccessError: QtWidgets.QMessageBox.warning( self, self.tr("Warning!"), - self.tr("Qube must be turned off before booting it from " - "device. Please turn off the qube.") - ) + self.tr("Insufficient permissions to determine if qube is " + "running. It must be turned off before booting it from " + "device.")) def __init_buttons__(self): self.fileVM.setEnabled(False) @@ -98,8 +106,14 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, allow_internal=True ) - device_choice = [(str(device), device) for domain in self.vm.app.domains - for device in domain.devices["block"]] + device_choice = [] + for domain in self.vm.app.domains: + try: + for device in domain.devices["block"]: + device_choice.append((str(device), device)) + except exc.QubesException: + # insufficient permissions + pass utils.initialize_widget( widget=self.blockDeviceComboBox, diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index f695774..d643973 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -105,22 +105,27 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg): choices=[(vm.name, vm) for vm in self.app.domains if not utils.is_internal(vm) and vm.klass == 'TemplateVM'], mark_existing_as_default=True, - default_value=self.app.default_template) + default_value=getattr(self.app, 'default_template', None)) utils.initialize_widget_with_default( widget=self.netvm, choices=[(vm.name, vm) for vm in self.app.domains - if not utils.is_internal(vm) and vm.provides_network], + if not utils.is_internal(vm) and + getattr(vm, 'provides_network', False)], add_none=True, add_qubes_default=True, - default_value=self.app.default_netvm) + default_value=getattr(self.app, 'default_netvm', None)) - utils.initialize_widget_with_default( - widget=self.storage_pool, - choices=[(str(pool), pool) for pool in self.app.pools.values()], - add_qubes_default=True, - mark_existing_as_default=True, - default_value=self.app.default_pool) + try: + utils.initialize_widget_with_default( + widget=self.storage_pool, + choices=[(str(pool), pool) for pool in self.app.pools.values()], + add_qubes_default=True, + mark_existing_as_default=True, + default_value=self.app.default_pool) + except qubesadmin.exc.QubesPropertyAccessError: + self.storage_pool.clear() + self.storage_pool.addItem("(default)", qubesadmin.DEFAULT) self.name.setValidator(QtGui.QRegExpValidator( QtCore.QRegExp("[a-zA-Z0-9_-]*", QtCore.Qt.CaseInsensitive), None)) @@ -133,8 +138,6 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg): self.tr('No template available!'), self.tr('Cannot create a qube when no template exists.')) - # Order of types is important and used elsewhere; if it's changed - # check for changes needed in self.type_change type_list = [ (self.tr("Qube based on a template (AppVM)"), 'AppVM'), (self.tr("Standalone qube copied from a template"), diff --git a/qubesmanager/template_manager.py b/qubesmanager/template_manager.py index 796b29f..7ce6e46 100644 --- a/qubesmanager/template_manager.py +++ b/qubesmanager/template_manager.py @@ -345,7 +345,7 @@ class VMRow: table_widget.setItem(row_no, columns.index('New template'), self.dummy_new_item) - self.vm_state_change(self.vm.is_running(), row_no) + self.vm_state_change(is_vm_running(self.vm), row_no) def vm_state_change(self, is_running, row=None): self.state_item.set_state(is_running) @@ -385,6 +385,13 @@ class VMRow: self.checkbox = None +def is_vm_running(vm): + try: + return vm.is_running() + except exc.QubesPropertyAccessError: + return False + + def main(): utils.run_asynchronous(TemplateManagerWindow) diff --git a/qubesmanager/utils.py b/qubesmanager/utils.py index 0a08553..3a14d9e 100644 --- a/qubesmanager/utils.py +++ b/qubesmanager/utils.py @@ -51,8 +51,20 @@ from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error def is_internal(vm): """checks if the VM is either an AdminVM or has the 'internal' features""" - return (vm.klass == 'AdminVM' - or vm.features.get('internal', False)) + try: + return (vm.klass == 'AdminVM' + or vm.features.get('internal', False)) + except exc.QubesDaemonCommunicationError: + return False + + +def is_running(vm, default_state): + """Checks if the VM is running, returns default_state if we have + insufficient permissions to deteremine that.""" + try: + return vm.is_running() + except exc.QubesPropertyAccessError: + return default_state def translate(string):