diff --git a/.gitignore b/.gitignore index 0d20b64..7fbea01 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ *.pyc +*.pyo +*~ +rpm/ diff --git a/qubesmanager/.gitignore b/qubesmanager/.gitignore index f580b22..50937f1 100644 --- a/qubesmanager/.gitignore +++ b/qubesmanager/.gitignore @@ -1,2 +1,4 @@ qrc_resources.py ui_newappvmdlg.py +ui_editfwrulesdlg.py +ui_newfwruledlg.py diff --git a/qubesmanager/firewall.py b/qubesmanager/firewall.py index 47e4296..59af881 100644 --- a/qubesmanager/firewall.py +++ b/qubesmanager/firewall.py @@ -260,7 +260,7 @@ class QubesFirewallRulesModel(QAbstractItemModel): qvm_collection.unlock_db() for vm in qvm_collection.values(): - if vm.is_fwvm(): + if vm.is_proxyvm(): vm.write_iptables_xenstore_entry() def index(self, row, column, parent=QModelIndex()): diff --git a/qubesmanager/main.py b/qubesmanager/main.py index cdf5b40..bcc651b 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -44,6 +44,8 @@ import subprocess import time import threading +qubes_guid_path = '/usr/bin/qubes_guid' + class QubesConfigFileWatcher(ProcessEvent): def __init__ (self, update_func): self.update_func = update_func @@ -73,7 +75,7 @@ class VmStatusIcon(QLabel): icon = QIcon (":/dom0.png") elif vm.is_appvm(): icon = QIcon (vm.label.icon_path) - elif vm.is_templete(): + elif vm.is_template(): icon = QIcon (":/templatevm.png") elif vm.is_netvm(): icon = QIcon (":/netvm.png") @@ -105,7 +107,7 @@ class VmInfoWidget (QWidget): if vm.is_appvm() or vm.is_disposablevm(): label_tmpl = QLabel ("" + vm.template_vm.name + "") - elif vm.is_templete(): + elif vm.is_template(): label_tmpl = QLabel ("TemplateVM") elif vm.qid == 0: label_tmpl = QLabel ("AdminVM") @@ -371,8 +373,8 @@ class VmManagerWindow(QMainWindow): self.action_shutdownvm = self.createAction ("Shutdown VM", slot=self.shutdown_vm, icon="shutdownvm", tip="Shutdown a running VM") - self.action_updatevm = self.createAction ("Update VM", slot=None, - icon="updateable", tip="Update VM (only for 'updateable' VMs, e.g. templates)") + self.action_updatevm = self.createAction ("Commit VM changes", slot=self.update_vm, + icon="updateable", tip="Commit changes to template (only for 'updateable' template VMs); VM must be stopped") self.action_showallvms = self.createAction ("Show/Hide Inactive VMs", slot=None, checkable=True, icon="showallvms", tip="Show/Hide Inactive VMs") @@ -491,7 +493,7 @@ class VmManagerWindow(QMainWindow): # Now, the templates... for tvm in vms_list: - if tvm.is_templete(): + if tvm.is_template(): vms_to_display.append (tvm) label_list = QubesVmLabels.values() @@ -552,9 +554,10 @@ class VmManagerWindow(QMainWindow): # Update available actions: self.action_removevm.setEnabled(not vm.installed_by_rpm and not vm.is_running()) - #self.action_resumevm.setEnabled(not vm.is_running()) - #self.action_pausevm.setEnabled(vm.is_running() and vm.qid != 0) + self.action_resumevm.setEnabled(not vm.is_running()) + self.action_pausevm.setEnabled(vm.is_running() and vm.qid != 0) self.action_shutdownvm.setEnabled(vm.is_running() and vm.qid != 0) + self.action_updatevm.setEnabled(vm.is_updateable() and not vm.is_running()) self.action_editfwrules.setEnabled(vm.is_networked() and (vm.is_appvm() or vm.is_disposablevm())) def get_minimum_table_width(self): @@ -587,7 +590,7 @@ class VmManagerWindow(QMainWindow): dialog.vmlabel.insertItem(i, label.name) dialog.vmlabel.setItemIcon (i, QIcon(label.icon_path)) - template_vm_list = [vm for vm in self.qvm_collection.values() if vm.is_templete()] + template_vm_list = [vm for vm in self.qvm_collection.values() if vm.is_template()] default_index = 0 for (i, vm) in enumerate(template_vm_list): @@ -633,6 +636,7 @@ class VmManagerWindow(QMainWindow): def do_create_appvm (self, vmname, label, template_vm, thread_monitor): + vm = None try: self.qvm_collection.lock_db_for_writing() self.qvm_collection.load() @@ -643,7 +647,8 @@ class VmManagerWindow(QMainWindow): self.qvm_collection.save() except Exception as ex: thread_monitor.set_error_msg (str(ex)) - vm.remove_from_disk() + if vm: + vm.remove_from_disk() finally: self.qvm_collection.unlock_db() @@ -664,8 +669,8 @@ class VmManagerWindow(QMainWindow): self.qvm_collection.lock_db_for_reading() self.qvm_collection.load() self.qvm_collection.unlock_db() - - if vm.is_templete(): + + if vm.is_template(): dependent_vms = self.qvm_collection.get_vms_based_on(vm.qid) if len(dependent_vms) > 0: QMessageBox.warning (None, "Warning!", @@ -710,7 +715,7 @@ class VmManagerWindow(QMainWindow): self.qvm_collection.load() #TODO: the following two conditions should really be checked by qvm_collection.pop() overload... - if vm.is_templete() and qvm_collection.default_template_qid == vm.qid: + if vm.is_template() and qvm_collection.default_template_qid == vm.qid: qvm_collection.default_template_qid = None if vm.is_netvm() and qvm_collection.default_netvm_qid == vm.qid: qvm_collection.default_netvm_qid = None @@ -727,10 +732,29 @@ class VmManagerWindow(QMainWindow): thread_monitor.set_finished() def resume_vm(self): - pass + vm = self.get_selected_vm() + assert not vm.is_running() + try: + vm.verify_files() + xid = vm.start() + except (IOError, OSError, QubesException) as err: + QMessageBox.warning (None, "Error starting VM!", "ERROR: {0}".format(err)) + return + + retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)]) + if (retcode != 0): + QMessageBox.warning (None, "Error starting VM!", "ERROR: Cannot start qubes_guid!") + return + def pause_vm(self): - pass + vm = self.get_selected_vm() + assert vm.is_running() + try: + subprocess.check_call (["/usr/sbin/xm", "pause", vm.name]) + except Exception as ex: + QMessageBox.warning (None, "Error pausing VM!", "ERROR: {0}".format(ex)) + return def shutdown_vm(self): vm = self.get_selected_vm() @@ -754,6 +778,25 @@ class VmManagerWindow(QMainWindow): self.shutdown_monitor[vm.qid] = VmShutdownMonitor (vm) QTimer.singleShot (vm_shutdown_timeout, self.shutdown_monitor[vm.qid].check_if_vm_has_shutdown) + def update_vm(self): + vm = self.get_selected_vm() + assert not vm.is_running() + + reply = QMessageBox.question(None, "VM Update Confirmation", + "Are you sure you want to commit template '{0}' changes?
" + "AppVMs will see the changes after restart.".format(vm.name), + QMessageBox.Yes | QMessageBox.Cancel) + + app.processEvents() + + if reply == QMessageBox.Yes: + try: + vm.commit_changes(); + except Exception as ex: + QMessageBox.warning (None, "Error commiting changes!", "ERROR: {0}".format(ex)) + return + trayIcon.showMessage ("Qubes Manager", "Changes to template '{0}' commited.".format(vm.name), msecs=3000) + def showcpuload(self): self.__cpugraphs = self.action_showcpuload.isChecked() self.update_table_columns() @@ -775,7 +818,7 @@ class VmManagerWindow(QMainWindow): qvm_collection.unlock_db() for vm in qvm_collection.values(): - if vm.is_fwvm(): + if vm.is_proxyvm(): error_file = "/local/domain/{0}/qubes_iptables_error".format(vm.get_xid()) error = subprocess.Popen( @@ -824,7 +867,7 @@ class QubesTrayIcon(QSystemTrayIcon): # Handle the right click normally, i.e. display the context menu return else: - show_manager() + toggle_manager() def addActions(self, target, actions): for action in actions: @@ -854,6 +897,11 @@ class QubesTrayIcon(QSystemTrayIcon): def show_manager(): manager_window.show() +def toggle_manager(): + if manager_window.isVisible(): + manager_window.hide() + else: + manager_window.show() def exit_app(): notifier.stop() diff --git a/qubesmanager/ui_editfwrulesdlg.py b/qubesmanager/ui_editfwrulesdlg.py deleted file mode 100644 index 153abcb..0000000 --- a/qubesmanager/ui_editfwrulesdlg.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'editfwrulesdlg.ui' -# -# Created: Thu Mar 3 17:36:19 2011 -# by: PyQt4 UI code generator 4.7.3 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -class Ui_EditFwRulesDlg(object): - def setupUi(self, EditFwRulesDlg): - EditFwRulesDlg.setObjectName("EditFwRulesDlg") - EditFwRulesDlg.resize(500, 335) - self.verticalLayout_3 = QtGui.QVBoxLayout(EditFwRulesDlg) - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.policyAllowRadioButton = QtGui.QRadioButton(EditFwRulesDlg) - self.policyAllowRadioButton.setObjectName("policyAllowRadioButton") - self.verticalLayout_3.addWidget(self.policyAllowRadioButton) - self.policyDenyRadioButton = QtGui.QRadioButton(EditFwRulesDlg) - self.policyDenyRadioButton.setObjectName("policyDenyRadioButton") - self.verticalLayout_3.addWidget(self.policyDenyRadioButton) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setSizeConstraint(QtGui.QLayout.SetMaximumSize) - self.horizontalLayout.setObjectName("horizontalLayout") - self.verticalLayout_2 = QtGui.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.rulesTreeView = QtGui.QTreeView(EditFwRulesDlg) - self.rulesTreeView.setRootIsDecorated(False) - self.rulesTreeView.setUniformRowHeights(False) - self.rulesTreeView.setItemsExpandable(False) - self.rulesTreeView.setAllColumnsShowFocus(True) - self.rulesTreeView.setExpandsOnDoubleClick(True) - self.rulesTreeView.setObjectName("rulesTreeView") - self.rulesTreeView.header().setDefaultSectionSize(40) - self.rulesTreeView.header().setStretchLastSection(False) - self.verticalLayout_2.addWidget(self.rulesTreeView) - self.dnsCheckBox = QtGui.QCheckBox(EditFwRulesDlg) - self.dnsCheckBox.setChecked(True) - self.dnsCheckBox.setObjectName("dnsCheckBox") - self.verticalLayout_2.addWidget(self.dnsCheckBox) - self.horizontalLayout.addLayout(self.verticalLayout_2) - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.newRuleButton = QtGui.QPushButton(EditFwRulesDlg) - self.newRuleButton.setObjectName("newRuleButton") - self.verticalLayout.addWidget(self.newRuleButton) - self.editRuleButton = QtGui.QPushButton(EditFwRulesDlg) - self.editRuleButton.setObjectName("editRuleButton") - self.verticalLayout.addWidget(self.editRuleButton) - self.deleteRuleButton = QtGui.QPushButton(EditFwRulesDlg) - self.deleteRuleButton.setObjectName("deleteRuleButton") - self.verticalLayout.addWidget(self.deleteRuleButton) - spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem) - self.horizontalLayout.addLayout(self.verticalLayout) - self.verticalLayout_3.addLayout(self.horizontalLayout) - self.buttonBox = QtGui.QDialogButtonBox(EditFwRulesDlg) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_3.addWidget(self.buttonBox) - - self.retranslateUi(EditFwRulesDlg) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), EditFwRulesDlg.reject) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), EditFwRulesDlg.accept) - QtCore.QMetaObject.connectSlotsByName(EditFwRulesDlg) - EditFwRulesDlg.setTabOrder(self.policyAllowRadioButton, self.policyDenyRadioButton) - EditFwRulesDlg.setTabOrder(self.policyDenyRadioButton, self.dnsCheckBox) - EditFwRulesDlg.setTabOrder(self.dnsCheckBox, self.rulesTreeView) - EditFwRulesDlg.setTabOrder(self.rulesTreeView, self.newRuleButton) - EditFwRulesDlg.setTabOrder(self.newRuleButton, self.editRuleButton) - EditFwRulesDlg.setTabOrder(self.editRuleButton, self.deleteRuleButton) - EditFwRulesDlg.setTabOrder(self.deleteRuleButton, self.buttonBox) - - def retranslateUi(self, EditFwRulesDlg): - EditFwRulesDlg.setWindowTitle(QtGui.QApplication.translate("EditFwRulesDlg", "VM Firewall", None, QtGui.QApplication.UnicodeUTF8)) - self.policyAllowRadioButton.setText(QtGui.QApplication.translate("EditFwRulesDlg", "Allow network access except...", None, QtGui.QApplication.UnicodeUTF8)) - self.policyDenyRadioButton.setText(QtGui.QApplication.translate("EditFwRulesDlg", "Deny network access except...", None, QtGui.QApplication.UnicodeUTF8)) - self.dnsCheckBox.setText(QtGui.QApplication.translate("EditFwRulesDlg", "Allow DNS queries", None, QtGui.QApplication.UnicodeUTF8)) - self.newRuleButton.setText(QtGui.QApplication.translate("EditFwRulesDlg", "&New", None, QtGui.QApplication.UnicodeUTF8)) - self.editRuleButton.setText(QtGui.QApplication.translate("EditFwRulesDlg", "&Edit", None, QtGui.QApplication.UnicodeUTF8)) - self.deleteRuleButton.setText(QtGui.QApplication.translate("EditFwRulesDlg", "&Delete", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/qubesmanager/ui_newfwruledlg.py b/qubesmanager/ui_newfwruledlg.py deleted file mode 100644 index e195026..0000000 --- a/qubesmanager/ui_newfwruledlg.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'newfwruledlg.ui' -# -# Created: Thu Mar 3 17:36:19 2011 -# by: PyQt4 UI code generator 4.7.3 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -class Ui_NewFwRuleDlg(object): - def setupUi(self, NewFwRuleDlg): - NewFwRuleDlg.setObjectName("NewFwRuleDlg") - NewFwRuleDlg.setWindowModality(QtCore.Qt.NonModal) - NewFwRuleDlg.resize(381, 121) - NewFwRuleDlg.setModal(True) - self.buttonBox = QtGui.QDialogButtonBox(NewFwRuleDlg) - self.buttonBox.setGeometry(QtCore.QRect(10, 80, 361, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.label_2 = QtGui.QLabel(NewFwRuleDlg) - self.label_2.setGeometry(QtCore.QRect(10, 14, 62, 17)) - self.label_2.setObjectName("label_2") - self.addressEdit = QtGui.QLineEdit(NewFwRuleDlg) - self.addressEdit.setGeometry(QtCore.QRect(70, 10, 301, 27)) - self.addressEdit.setObjectName("addressEdit") - self.label_4 = QtGui.QLabel(NewFwRuleDlg) - self.label_4.setGeometry(QtCore.QRect(10, 44, 61, 21)) - self.label_4.setObjectName("label_4") - self.serviceComboBox = QtGui.QComboBox(NewFwRuleDlg) - self.serviceComboBox.setGeometry(QtCore.QRect(70, 40, 301, 27)) - self.serviceComboBox.setEditable(True) - self.serviceComboBox.setObjectName("serviceComboBox") - - self.retranslateUi(NewFwRuleDlg) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), NewFwRuleDlg.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), NewFwRuleDlg.reject) - QtCore.QMetaObject.connectSlotsByName(NewFwRuleDlg) - NewFwRuleDlg.setTabOrder(self.addressEdit, self.serviceComboBox) - NewFwRuleDlg.setTabOrder(self.serviceComboBox, self.buttonBox) - - def retranslateUi(self, NewFwRuleDlg): - NewFwRuleDlg.setWindowTitle(QtGui.QApplication.translate("NewFwRuleDlg", "New Address", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("NewFwRuleDlg", "Address", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setText(QtGui.QApplication.translate("NewFwRuleDlg", "Service", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/rpm_spec/qmgr.spec b/rpm_spec/qmgr.spec index 34b22a7..603bc84 100644 --- a/rpm_spec/qmgr.spec +++ b/rpm_spec/qmgr.spec @@ -22,16 +22,21 @@ The Graphical Qubes VM Manager. %build make res +python -m compileall qubesmanager +python -O -m compileall qubesmanager %install mkdir -p $RPM_BUILD_ROOT/usr/bin/ cp qubes-manager $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager/ -cp qubesmanager/main.py $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager -cp qubesmanager/qrc_resources.py $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager -cp qubesmanager/__init__.py $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager -cp qubesmanager/ui_newappvmdlg.py $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/main.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/firewall.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/qrc_resources.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/__init__.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/ui_newappvmdlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/ui_newfwruledlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/ui_editfwrulesdlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager mkdir -p $RPM_BUILD_ROOT/usr/share/applications cp qubes-manager.desktop $RPM_BUILD_ROOT/usr/share/applications @@ -56,12 +61,21 @@ rm -rf $RPM_BUILD_ROOT %{python_sitearch}/qubesmanager/main.py %{python_sitearch}/qubesmanager/main.pyc %{python_sitearch}/qubesmanager/main.pyo +%{python_sitearch}/qubesmanager/firewall.py +%{python_sitearch}/qubesmanager/firewall.pyc +%{python_sitearch}/qubesmanager/firewall.pyo %{python_sitearch}/qubesmanager/qrc_resources.py %{python_sitearch}/qubesmanager/qrc_resources.pyc %{python_sitearch}/qubesmanager/qrc_resources.pyo %{python_sitearch}/qubesmanager/ui_newappvmdlg.py %{python_sitearch}/qubesmanager/ui_newappvmdlg.pyc %{python_sitearch}/qubesmanager/ui_newappvmdlg.pyo +%{python_sitearch}/qubesmanager/ui_newfwruledlg.py +%{python_sitearch}/qubesmanager/ui_newfwruledlg.pyc +%{python_sitearch}/qubesmanager/ui_newfwruledlg.pyo +%{python_sitearch}/qubesmanager/ui_editfwrulesdlg.py +%{python_sitearch}/qubesmanager/ui_editfwrulesdlg.pyc +%{python_sitearch}/qubesmanager/ui_editfwrulesdlg.pyo /usr/share/applications/qubes-manager.desktop