diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 0335648..9be8354 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -20,18 +20,14 @@ # # -import traceback - import signal -import quamash - -from qubesadmin import Qubes, exc +from qubesadmin import exc from qubesadmin import utils as admin_utils -from qubesadmin import events from qubes.storage.file import get_disk_usage from PyQt5 import QtCore # pylint: disable=import-error from PyQt5 import QtWidgets # pylint: disable=import-error +from PyQt5 import QtGui from . import ui_backupdlg # pylint: disable=no-name-in-module from . import multiselectwidget @@ -393,57 +389,10 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard): self.select_dir_page.completeChanged.emit() -# Bases on the original code by: -# Copyright (c) 2002-2007 Pascal Varet - -def handle_exception(exc_type, exc_value, exc_traceback): - filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop() - filename = os.path.basename(filename) - error = "%s: %s" % (exc_type.__name__, exc_value) - - QtWidgets.QMessageBox.critical( - None, - "Houston, we have a problem...", - "Whoops. A critical error has occured. This is most likely a bug " - "in Qubes Global Settings application.

%s" % - error + "at line %d of file %s.

" - % (line, filename)) - - -def loop_shutdown(): - pending = asyncio.Task.all_tasks() - for task in pending: - with suppress(asyncio.CancelledError): - task.cancel() - - def main(): - qt_app = QtWidgets.QApplication(sys.argv) - qt_app.setOrganizationName("The Qubes Project") - qt_app.setOrganizationDomain("http://qubes-os.org") - qt_app.setApplicationName("Qubes Backup VMs") - qt_app.lastWindowClosed.connect(loop_shutdown) - - sys.excepthook = handle_exception - - qubes_app = Qubes() - - loop = quamash.QEventLoop(qt_app) - asyncio.set_event_loop(loop) - dispatcher = events.EventsDispatcher(qubes_app) - - backup_window = BackupVMsWindow(qt_app, qubes_app, dispatcher) - backup_window.show() - - try: - loop.run_until_complete( - asyncio.ensure_future(dispatcher.listen_for_events())) - except asyncio.CancelledError: - pass - except Exception: # pylint: disable=broad-except - loop_shutdown() - exc_type, exc_value, exc_traceback = sys.exc_info()[:3] - handle_exception(exc_type, exc_value, exc_traceback) + utils.run_asynchronous("Qubes Backup VMs", + QtGui.QIcon.fromTheme("qubes-manager"), + BackupVMsWindow) if __name__ == "__main__": diff --git a/qubesmanager/bootfromdevice.py b/qubesmanager/bootfromdevice.py index e106dbe..24f9370 100644 --- a/qubesmanager/bootfromdevice.py +++ b/qubesmanager/bootfromdevice.py @@ -17,7 +17,7 @@ # # -import sys +import functools import subprocess from . import utils from . import ui_bootfromdevice # pylint: disable=no-name-in-module @@ -28,7 +28,7 @@ from qubesadmin.tools import qvm_start class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, QtWidgets.QDialog): - def __init__(self, vm, qapp, parent=None): + def __init__(self, vm, qapp, qubesapp=None, parent=None): super(VMBootFromDeviceWindow, self).__init__(parent) self.vm = vm @@ -147,19 +147,8 @@ def main(args=None): args = parser.parse_args(args) vm = args.domains.pop() - qapp = QtWidgets.QApplication(sys.argv) - qapp.setOrganizationName('Invisible Things Lab') - qapp.setOrganizationDomain("https://www.qubes-os.org/") - qapp.setApplicationName("Boot Qube From Device") - -# if not utils.is_debug(): #FIXME -# sys.excepthook = handle_exception - - bootfromdevice_window = VMBootFromDeviceWindow(vm, qapp) - bootfromdevice_window.show() - - qapp.exec_() - qapp.exit() + utils.run_synchronous("Boot Qube From Device", + functools.partial(VMBootFromDeviceWindow, vm)) if __name__ == "__main__": diff --git a/qubesmanager/global_settings.py b/qubesmanager/global_settings.py index a1ec87f..1b2d027 100644 --- a/qubesmanager/global_settings.py +++ b/qubesmanager/global_settings.py @@ -20,14 +20,11 @@ # # -import sys import os import os.path -import traceback import subprocess from PyQt5 import QtWidgets # pylint: disable=import-error -from qubesadmin import Qubes from qubesadmin.utils import parse_size from . import ui_globalsettingsdlg # pylint: disable=no-name-in-module @@ -406,39 +403,9 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings, self.__apply_updates__() self.__apply_repos__() -# Bases on the original code by: -# Copyright (c) 2002-2007 Pascal Varet - -def handle_exception(exc_type, exc_value, exc_traceback): - filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop() - filename = os.path.basename(filename) - error = "%s: %s" % (exc_type.__name__, exc_value) - - QtWidgets.QMessageBox.critical( - None, - "Houston, we have a problem...", - "Whoops. A critical error has occured. This is most likely a bug " - "in Qubes Global Settings application.

%s" % - error + "at line %d of file %s.

" - % (line, filename)) - def main(): - qtapp = QtWidgets.QApplication(sys.argv) - qtapp.setOrganizationName("The Qubes Project") - qtapp.setOrganizationDomain("http://qubes-os.org") - qtapp.setApplicationName("Qubes Global Settings") - - sys.excepthook = handle_exception - - app = Qubes() - - global_window = GlobalSettingsWindow(qtapp, app) - - global_window.show() - - qtapp.exec_() - qtapp.exit() + utils.run_synchronous("Qubes Global Settings", GlobalSettingsWindow) if __name__ == "__main__": diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 4dd6b15..ccb1098 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -21,21 +21,13 @@ # with this program; if not, see . # # -import sys import os import os.path import subprocess from datetime import datetime, timedelta -import traceback -from contextlib import suppress -import quamash -import asyncio - -from qubesadmin import Qubes from qubesadmin import exc from qubesadmin import utils -from qubesadmin import events from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error @@ -1306,71 +1298,11 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow): log_dlg.exec_() -# Bases on the original code by: -# Copyright (c) 2002-2007 Pascal Varet - -def handle_exception(exc_type, exc_value, exc_traceback): - - filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop() - filename = os.path.basename(filename) - error = "%s: %s" % (exc_type.__name__, exc_value) - - strace = "" - stacktrace = traceback.extract_tb(exc_traceback) - while stacktrace: - (filename, line, func, txt) = stacktrace.pop() - strace += "----\n" - strace += "line: %s\n" % txt - strace += "func: %s\n" % func - strace += "line no.: %d\n" % line - strace += "file: %s\n" % filename - - msg_box = QtWidgets.QMessageBox() - msg_box.setDetailedText(strace) - msg_box.setIcon(QtWidgets.QMessageBox.Critical) - msg_box.setWindowTitle("Houston, we have a problem...") - msg_box.setText("Whoops. A critical error has occured. " - "This is most likely a bug in Qubes Manager.

" - "%s" % error + - "
at line %d
of file %s.

" - % (line, filename)) - - msg_box.exec_() - - -def loop_shutdown(): - pending = asyncio.Task.all_tasks() - for task in pending: - with suppress(asyncio.CancelledError): - task.cancel() - - def main(): - qt_app = QtWidgets.QApplication(sys.argv) - qt_app.setOrganizationName("The Qubes Project") - qt_app.setOrganizationDomain("http://qubes-os.org") - qt_app.setApplicationName("Qube Manager") - qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager")) - qt_app.lastWindowClosed.connect(loop_shutdown) - - qubes_app = Qubes() - - loop = quamash.QEventLoop(qt_app) - asyncio.set_event_loop(loop) - dispatcher = events.EventsDispatcher(qubes_app) - - manager_window = VmManagerWindow(qt_app, qubes_app, dispatcher) - manager_window.show() - - try: - loop.run_until_complete( - asyncio.ensure_future(dispatcher.listen_for_events())) - except asyncio.CancelledError: - pass - except Exception: # pylint: disable=broad-except - loop_shutdown() - exc_type, exc_value, exc_traceback = sys.exc_info()[:3] - handle_exception(exc_type, exc_value, exc_traceback) + manager_utils.run_asynchronous( + "Qube Manager", + QtGui.QIcon.fromTheme("qubes-manager"), + VmManagerWindow) if __name__ == "__main__": diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index a38f46f..48f7531 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -20,11 +20,9 @@ # # -import sys from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error import os import os.path -import traceback import logging import logging.handlers @@ -33,6 +31,7 @@ from qubes import backup from . import ui_restoredlg # pylint: disable=no-name-in-module from . import multiselectwidget from . import backup_utils +from . import utils from multiprocessing import Queue from queue import Empty @@ -292,39 +291,8 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtWidgets.QWizard): self.select_dir_page.completeChanged.emit() -# Bases on the original code by: -# Copyright (c) 2002-2007 Pascal Varet - -def handle_exception(exc_type, exc_value, exc_traceback): - - filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop() - filename = os.path.basename(filename) - error = "%s: %s" % (exc_type.__name__, exc_value) - - QtWidgets.QMessageBox.critical( - None, "Houston, we have a problem...", - "Whoops. A critical error has occured. This is most likely a bug " - "in Qubes Restore VMs application.

%s" % error + - "at line %d of file %s.

" % (line, filename)) - - def main(): - - qt_app = QtWidgets.QApplication(sys.argv) - qt_app.setOrganizationName("The Qubes Project") - qt_app.setOrganizationDomain("http://qubes-os.org") - qt_app.setApplicationName("Qubes Restore VMs") - - sys.excepthook = handle_exception - - qubes_app = Qubes() - - restore_window = RestoreVMsWindow(qt_app, qubes_app) - - restore_window.show() - - qt_app.exec_() - qt_app.exit() + utils.run_synchronous("Qubes Restore VMs", RestoreVMsWindow) if __name__ == "__main__": diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 256845f..7bcfd5e 100644 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -23,12 +23,10 @@ # import collections -import os.path -import os +import functools import re import subprocess import traceback -import sys from qubesadmin.tools import QubesArgumentParser from qubesadmin import devices from qubesadmin import utils as admin_utils @@ -128,11 +126,13 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog): ('services', 5), )) - def __init__(self, vm, qapp, init_page="basic", parent=None): + def __init__(self, vm, init_page="basic", qapp=None, qubesapp=None, + parent=None): super(VMSettingsWindow, self).__init__(parent) self.vm = vm self.qapp = qapp + self.qubesapp = qubesapp self.threads_list = [] self.progress = None self.thread_closes = False @@ -1218,38 +1218,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog): self.fw_model.remove_child(i) -# Bases on the original code by: -# Copyright (c) 2002-2007 Pascal Varet - -def handle_exception(exc_type, exc_value, exc_traceback): - - filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop() - filename = os.path.basename(filename) - error = "%s: %s" % (exc_type.__name__, exc_value) - - strace = "" - stacktrace = traceback.extract_tb(exc_traceback) - while stacktrace: - (filename, line, func, txt) = stacktrace.pop() - strace += "----\n" - strace += "line: %s\n" % txt - strace += "func: %s\n" % func - strace += "line no.: %d\n" % line - strace += "file: %s\n" % filename - - msg_box = QtWidgets.QMessageBox() - msg_box.setDetailedText(strace) - msg_box.setIcon(QtWidgets.QMessageBox.Critical) - msg_box.setWindowTitle("Houston, we have a problem...") - msg_box.setText("Whoops. A critical error has occured. " - "This is most likely a bug in Qubes Manager.

" - "%s" % error + - "
at line %d
of file %s.

" - % (line, filename)) - - msg_box.exec_() - - parser = QubesArgumentParser(vmname_nargs=1) parser.add_argument('--tab', metavar='TAB', @@ -1265,19 +1233,8 @@ def main(args=None): args = parser.parse_args(args) vm = args.domains.pop() - qapp = QtWidgets.QApplication(sys.argv) - qapp.setOrganizationName('Invisible Things Lab') - qapp.setOrganizationDomain("https://www.qubes-os.org/") - qapp.setApplicationName("Qube Settings") - - if not utils.is_debug(): - sys.excepthook = handle_exception - - settings_window = VMSettingsWindow(vm, qapp, args.tab) - settings_window.show() - - qapp.exec_() - qapp.exit() + utils.run_synchronous("Qube Settings", + functools.partial(VMSettingsWindow, vm, args.tab)) if __name__ == "__main__": diff --git a/qubesmanager/template_manager.py b/qubesmanager/template_manager.py index 31ff0a9..cc85837 100644 --- a/qubesmanager/template_manager.py +++ b/qubesmanager/template_manager.py @@ -20,21 +20,12 @@ # # -import sys -import os -import os.path -import traceback -import quamash -import asyncio -from contextlib import suppress - -from qubesadmin import Qubes from qubesadmin import exc -from qubesadmin import events from PyQt5 import QtWidgets, QtGui, QtCore # pylint: disable=import-error from . import ui_templatemanager # pylint: disable=no-name-in-module +from . import utils column_names = ['State', 'Qube', 'Current template', 'New template'] @@ -385,72 +376,11 @@ class VMRow: row, column_names.index('State')) self.checkbox = None -# Bases on the original code by: -# Copyright (c) 2002-2007 Pascal Varet - - -def handle_exception(exc_type, exc_value, exc_traceback): - - filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop() - filename = os.path.basename(filename) - error = "%s: %s" % (exc_type.__name__, exc_value) - - strace = "" - stacktrace = traceback.extract_tb(exc_traceback) - while stacktrace: - (filename, line, func, txt) = stacktrace.pop() - strace += "----\n" - strace += "line: %s\n" % txt - strace += "func: %s\n" % func - strace += "line no.: %d\n" % line - strace += "file: %s\n" % filename - - msg_box = QtWidgets.QMessageBox() - msg_box.setDetailedText(strace) - msg_box.setIcon(QtWidgets.QMessageBox.Critical) - msg_box.setWindowTitle("Houston, we have a problem...") - msg_box.setText("Whoops. A critical error has occured. " - "This is most likely a bug in Qubes Manager.

" - "%s" % error + - "
at line %d
of file %s.

" - % (line, filename)) - - msg_box.exec_() - - -def loop_shutdown(): - pending = asyncio.Task.all_tasks() - for task in pending: - with suppress(asyncio.CancelledError): - task.cancel() - def main(): - qt_app = QtWidgets.QApplication(sys.argv) - qt_app.setOrganizationName("The Qubes Project") - qt_app.setOrganizationDomain("http://qubes-os.org") - qt_app.setApplicationName("Qube Manager") - qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager")) - qt_app.lastWindowClosed.connect(loop_shutdown) - - qubes_app = Qubes() - - loop = quamash.QEventLoop(qt_app) - asyncio.set_event_loop(loop) - dispatcher = events.EventsDispatcher(qubes_app) - - manager_window = TemplateManagerWindow(qt_app, qubes_app, dispatcher) - manager_window.show() - - try: - loop.run_until_complete( - asyncio.ensure_future(dispatcher.listen_for_events())) - except asyncio.CancelledError: - pass - except Exception: # pylint: disable=broad-except - loop_shutdown() - exc_type, exc_value, exc_traceback = sys.exc_info()[:3] - handle_exception(exc_type, exc_value, exc_traceback) + utils.run_asynchronous("Template Manager", + QtGui.QIcon.fromTheme("qubes-manager"), + TemplateManagerWindow) if __name__ == "__main__": diff --git a/qubesmanager/utils.py b/qubesmanager/utils.py index 2edeb3b..5b12256 100644 --- a/qubesmanager/utils.py +++ b/qubesmanager/utils.py @@ -23,7 +23,14 @@ import itertools import os import re import qubesadmin +import traceback +import asyncio +from contextlib import suppress +import sys +import quamash +from qubesadmin import events +from PyQt5 import QtWidgets from PyQt5.QtGui import QIcon # pylint: disable=import-error @@ -215,3 +222,87 @@ def format_dependencies_list(dependencies): prop, holder.name) return list_text + + +def loop_shutdown(): + pending = asyncio.Task.all_tasks() + for task in pending: + with suppress(asyncio.CancelledError): + task.cancel() + + +# Bases on the original code by: +# Copyright (c) 2002-2007 Pascal Varet +def handle_exception(exc_type, exc_value, exc_traceback): + + filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop() + filename = os.path.basename(filename) + error = "%s: %s" % (exc_type.__name__, exc_value) + + strace = "" + stacktrace = traceback.extract_tb(exc_traceback) + while stacktrace: + (filename, line, func, txt) = stacktrace.pop() + strace += "----\n" + strace += "line: %s\n" % txt + strace += "func: %s\n" % func + strace += "line no.: %d\n" % line + strace += "file: %s\n" % filename + + msg_box = QtWidgets.QMessageBox() + msg_box.setDetailedText(strace) + msg_box.setIcon(QtWidgets.QMessageBox.Critical) + msg_box.setWindowTitle("Houston, we have a problem...") + msg_box.setText("Whoops. A critical error has occured. " + "This is most likely a bug in Qubes Manager.

" + "%s" % error + + "
at line %d
of file %s.

" + % (line, filename)) + + msg_box.exec_() + + +def run_asynchronous(app_name, icon, window_class): + qt_app = QtWidgets.QApplication(sys.argv) + qt_app.setOrganizationName("The Qubes Project") + qt_app.setOrganizationDomain("http://qubes-os.org") + qt_app.setApplicationName(app_name) + qt_app.setWindowIcon(icon) + qt_app.lastWindowClosed.connect(loop_shutdown) + + qubes_app = qubesadmin.Qubes() + + loop = quamash.QEventLoop(qt_app) + asyncio.set_event_loop(loop) + dispatcher = events.EventsDispatcher(qubes_app) + + window = window_class(qt_app, qubes_app, dispatcher) + window.show() + + try: + loop.run_until_complete( + asyncio.ensure_future(dispatcher.listen_for_events())) + except asyncio.CancelledError: + pass + except Exception: # pylint: disable=broad-except + loop_shutdown() + exc_type, exc_value, exc_traceback = sys.exc_info()[:3] + handle_exception(exc_type, exc_value, exc_traceback) + + +def run_synchronous(app_name, window_class): + qt_app = QtWidgets.QApplication(sys.argv) + qt_app.setOrganizationName("The Qubes Project") + qt_app.setOrganizationDomain("http://qubes-os.org") + qt_app.setApplicationName(app_name) + + sys.excepthook = handle_exception + + qubes_app = qubesadmin.Qubes() + + window = window_class(qt_app, qubes_app) + + window.show() + + qt_app.exec_() + qt_app.exit()