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