diff --git a/Makefile b/Makefile
index cab057c..05993cd 100644
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,7 @@ res:
pyuic4 -o qubesmanager/ui_restoredlg.py restoredlg.ui
pyuic4 -o qubesmanager/ui_backupdlg.py backupdlg.ui
pyuic4 -o qubesmanager/ui_globalsettingsdlg.py globalsettingsdlg.ui
+ pyuic4 -o qubesmanager/ui_logdlg.py logdlg.ui
update-repo-current:
ln -f $(RPMS_DIR)/x86_64/qubes-manager-*$(VERSION)*.rpm ../yum/current-release/current/dom0/rpm/
diff --git a/icons/run-command.png b/icons/run-command.png
new file mode 100644
index 0000000..9dcc87a
Binary files /dev/null and b/icons/run-command.png differ
diff --git a/logdlg.ui b/logdlg.ui
new file mode 100644
index 0000000..4662d1e
--- /dev/null
+++ b/logdlg.ui
@@ -0,0 +1,122 @@
+
+
+ LogDialog
+
+
+
+ 0
+ 0
+ 750
+ 450
+
+
+
+ Dialog
+
+
+ -
+
+
+ Copy Dom0 clipboard to Qubes clipboard
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::TabFocus
+
+
+ Copy to Qubes clipboard
+
+
+
+ :/copy.png:/copy.png
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ rejected()
+ LogDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+ buttonBox
+ accepted()
+ LogDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+
diff --git a/mainwindow.ui b/mainwindow.ui
index 2f716e0..7f17ac8 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -239,7 +239,6 @@
-
@@ -727,16 +726,16 @@
Size on Disk
-
+
- :/copy.png:/copy.png
+ :/run-command.png:/run-command.png
- Copy clipboard to Qubes clipboard
+ Run command in VM
- Copy Dom0 clipboard to Qubes clipboard
+ Run command in the specified VM
diff --git a/newappvmdlg.ui b/newappvmdlg.ui
index 4ba6904..c8a86be 100644
--- a/newappvmdlg.ui
+++ b/newappvmdlg.ui
@@ -1,229 +1,116 @@
- NewAppVMDlg
-
+ NewVMDlg
+
0
0
- 507
- 209
+ 500
+ 200
- Create New AppVM
+ Create New VM
:/qubes.png:/qubes.png
-
- -
-
-
- 0
+
+
-
+
+
-
+
+
+ true
+
+
+
+ -
+
+
+ -
+
+
+ Use this template:
+
+
+
+ -
+
+
+ ProxyVM
+
+
+
+ -
+
+
+ NetVM
+
+
+
+ -
+
+
+ my-new-vm
+
+
+
+ -
+
+
+ AppVM
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+ HVM
+
+
+
+ -
+
+
+ Name & label:
+
+
+
+
+
+ -
+
+
+ Allow networking
+
+
+ true
-
-
- Basic
-
-
-
-
-
-
- myappvm
-
-
-
- -
-
-
- true
-
-
-
- -
-
-
- Name & label:
-
-
-
- -
-
-
- -
-
-
- Use this template:
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- Allow networking
-
-
- true
-
-
-
-
-
-
-
- Advanced
-
-
- -
-
-
- Disk storage
-
-
-
-
-
-
- false
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- 10000
-
-
- 2
-
-
-
- -
-
-
- false
-
-
- Allow to grow
-
-
-
- -
-
-
- GB
-
-
-
- -
-
-
- Private storage max. size
-
-
-
- -
-
-
- false
-
-
- Include in backups
-
-
- true
-
-
-
-
-
-
- -
-
-
- Memory/CPU
-
-
-
-
-
-
- false
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- 10000
-
-
- 100
-
-
- 400
-
-
-
- -
-
-
- MB
-
-
-
- -
-
-
- false
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- 1
-
-
-
- -
-
-
- VCPUs
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
- -
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
Qt::Horizontal
@@ -240,7 +127,7 @@
buttonBox
accepted()
- NewAppVMDlg
+ NewVMDlg
accept()
@@ -256,7 +143,7 @@
buttonBox
rejected()
- NewAppVMDlg
+ NewVMDlg
reject()
diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py
new file mode 100644
index 0000000..7ec8b2b
--- /dev/null
+++ b/qubesmanager/create_new_vm.py
@@ -0,0 +1,194 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2012 Agnieszka Kostrzewa
+# Copyright (C) 2012 Marek Marczykowski
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#
+
+import sys
+import os
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesVmLabels
+from qubes.qubes import QubesException
+
+import qubesmanager.resources_rc
+
+from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
+
+import subprocess
+import time
+import threading
+
+from ui_newappvmdlg import *
+from thread_monitor import *
+
+
+class NewVmDlg (QDialog, Ui_NewVMDlg):
+ def __init__(self, app, qvm_collection, trayIcon, parent = None):
+ super (NewVmDlg, self).__init__(parent)
+ self.setupUi(self)
+
+ self.app = app
+ self.trayIcon = trayIcon
+ self.qvm_collection = qvm_collection
+
+ # Theoretically we should be locking for writing here and unlock
+ # only after the VM creation finished. But the code would be more messy...
+ # Instead we lock for writing in the actual worker thread
+
+ try:
+ from qubes.qubes import QubesHVM
+ except ImportError:
+ pass
+ else:
+ self.hvm_radio.setEnabled(True)
+
+ self.qvm_collection.lock_db_for_reading()
+ self.qvm_collection.load()
+ self.qvm_collection.unlock_db()
+
+ self.label_list = QubesVmLabels.values()
+ self.label_list.sort(key=lambda l: l.index)
+ for (i, label) in enumerate(self.label_list):
+ self.vmlabel.insertItem(i, label.name)
+ self.vmlabel.setItemIcon (i, QIcon(label.icon_path))
+
+ self.template_vm_list = [vm for vm in self.qvm_collection.values() if not vm.internal and vm.is_template()]
+
+ default_index = 0
+ for (i, vm) in enumerate(self.template_vm_list):
+ if vm is self.qvm_collection.get_default_template():
+ default_index = i
+ self.template_name.insertItem(i, vm.name + " (default)")
+ else:
+ self.template_name.insertItem(i, vm.name)
+ self.template_name.setCurrentIndex(default_index)
+
+ self.vmname.selectAll()
+ self.vmname.setFocus()
+
+ def on_appvm_radio_toggled(self, checked):
+ if checked:
+ self.template_name.setEnabled(True)
+ self.allow_networking.setEnabled(True)
+ def on_netvm_radio_toggled(self, checked):
+ if checked:
+ self.template_name.setEnabled(True)
+ self.allow_networking.setEnabled(False)
+ def on_proxyvm_radio_toggled(self, checked):
+ if checked:
+ self.template_name.setEnabled(True)
+ self.allow_networking.setEnabled(True)
+ def on_hvm_radio_toggled(self, checked):
+ if checked:
+ self.template_name.setEnabled(False)
+ self.allow_networking.setEnabled(True)
+
+
+ def reject(self):
+ self.done(0)
+
+ def accept(self):
+ vmname = str(self.vmname.text())
+ if self.qvm_collection.get_vm_by_name(vmname) is not None:
+ QMessageBox.warning (None, "Incorrect AppVM Name!", "A VM with the name {0} already exists in the system!".format(vmname))
+ return
+
+ label = self.label_list[self.vmlabel.currentIndex()]
+
+ template_vm = None
+ if self.template_name.isEnabled():
+ template_vm = self.template_vm_list[self.template_name.currentIndex()]
+
+ allow_networking = None
+ if self.allow_networking.isEnabled():
+ allow_networking = self.allow_networking.isChecked()
+
+ if self.appvm_radio.isChecked():
+ createvm_method = self.qvm_collection.add_new_appvm
+ vmtype = "AppVM"
+ elif self.netvm_radio.isChecked():
+ createvm_method = self.qvm_collection.add_new_netvm
+ vmtype = "NetVM"
+ elif self.proxyvm_radio.isChecked():
+ createvm_method = self.qvm_collection.add_new_proxyvm
+ vmtype = "ProxyVM"
+ else: #hvm_radio.isChecked()
+ createvm_method = self.qvm_collection.add_new_hvm
+ vmtype = "HVM"
+
+
+ thread_monitor = ThreadMonitor()
+ thread = threading.Thread (target=self.do_create_vm, args=(createvm_method, vmname, label, template_vm, allow_networking, thread_monitor))
+ thread.daemon = True
+ thread.start()
+
+ progress = QProgressDialog ("Creating new {0} {1}...".format(vmtype, vmname), "", 0, 0)
+ progress.setCancelButton(None)
+ progress.setModal(True)
+ progress.show()
+
+ while not thread_monitor.is_finished():
+ self.app.processEvents()
+ time.sleep (0.1)
+
+ progress.hide()
+
+ if thread_monitor.success:
+ self.trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' has been created.".format(vmname), msecs=3000)
+ else:
+ QMessageBox.warning (None, "Error creating AppVM!", "ERROR: {0}".format(thread_monitor.error_msg))
+
+ self.done(0)
+
+
+
+ def do_create_vm (self, createvm_method, vmname, label, template_vm, allow_networking, thread_monitor):
+ vm = None
+ try:
+ self.qvm_collection.lock_db_for_writing()
+ self.qvm_collection.load()
+
+ if template_vm is not None:
+ vm = createvm_method(vmname, template_vm, label = label)
+ vm.create_on_disk(verbose=False, source_template = template_vm)
+ else:
+ vm = createvm_method(vmname, label = label)
+ vm.create_on_disk(verbose=False)
+
+ if allow_networking is not None:
+ firewall = vm.get_firewall_conf()
+ firewall["allow"] = allow_networking
+ firewall["allowDns"] = allow_networking
+ vm.write_firewall_conf(firewall)
+ self.qvm_collection.save()
+
+ except Exception as ex:
+ thread_monitor.set_error_msg (str(ex))
+ if vm:
+ vm.remove_from_disk()
+ finally:
+ self.qvm_collection.unlock_db()
+
+ thread_monitor.set_finished()
+
+
diff --git a/qubesmanager/log_dialog.py b/qubesmanager/log_dialog.py
new file mode 100644
index 0000000..aed86d2
--- /dev/null
+++ b/qubesmanager/log_dialog.py
@@ -0,0 +1,88 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2012 Agnieszka Kostrzewa
+# Copyright (C) 2012 Marek Marczykowski
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#
+
+import sys
+import os
+import fcntl
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from qubes.qubes import QubesException
+
+import qubesmanager.resources_rc
+
+from ui_logdlg import *
+
+
+class LogDialog(Ui_LogDialog, QDialog):
+
+ def __init__(self, app, log_path, parent=None):
+ super(LogDialog, self).__init__(parent)
+
+ self.app = app
+ self.log_path = log_path
+
+ self.setupUi(self)
+ self.setWindowTitle(log_path)
+
+ self.connect(self.copy_to_qubes_clipboard, SIGNAL("clicked()"), self.copy_to_qubes_clipboard_triggered)
+
+ self.__init_log_text__()
+
+ def __init_log_text__(self):
+ log = open(self.log_path)
+ self.displayed_text = log.read()
+ log.close()
+ self.log_text.setText(self.displayed_text)
+
+
+ def copy_to_qubes_clipboard_triggered(self):
+ copy_text_to_qubes_clipboard(self.displayed_text)
+
+########################COPY TEXT TO QUBES CLIPBOARD
+
+def copy_text_to_qubes_clipboard(text):
+ #inter-appviewer lock
+
+ try:
+ fd = os.open("/var/run/qubes/appviewer.lock", os.O_RDWR|os.O_CREAT, 0600);
+ fcntl.flock(fd, fcntl.LOCK_EX);
+ except IOError:
+ QMessageBox.warning (None, "Warning!", "Error while accessing Qubes clipboard!")
+ return
+
+ qubes_clipboard = open("/var/run/qubes/qubes_clipboard.bin", 'w')
+ qubes_clipboard.write(text)
+ qubes_clipboard.close()
+
+ qubes_clip_source = open("/var/run/qubes/qubes_clipboard.bin.source", 'w')
+ qubes_clip_source.write("dom0")
+ qubes_clip_source.close()
+
+ try:
+ fcntl.flock(fd, fcntl.LOCK_UN)
+ os.close(fd)
+ except IOError:
+ QMessageBox.warning (None, "Warning!", "Error while writing to Qubes clipboard!")
+ return
+
diff --git a/qubesmanager/main.py b/qubesmanager/main.py
index bc29047..3977b2b 100755
--- a/qubesmanager/main.py
+++ b/qubesmanager/main.py
@@ -23,6 +23,7 @@
import sys
import os
import fcntl
+import errno
import dbus
from PyQt4.QtCore import *
from PyQt4.QtGui import *
@@ -40,10 +41,12 @@ from qubes import qubesutils
import qubesmanager.resources_rc
import ui_newappvmdlg
from ui_mainwindow import *
+from create_new_vm import NewVmDlg
from settings import VMSettingsWindow
from restore import RestoreVMsWindow
from backup import BackupVMsWindow
from global_settings import GlobalSettingsWindow
+from log_dialog import LogDialog
from thread_monitor import *
from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
@@ -51,8 +54,8 @@ from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, Pro
import subprocess
import time
from datetime import datetime,timedelta
+from qubes.qubes import updates_stat_file
-updates_stat_file = 'last_update.stat'
qubes_guid_path = '/usr/bin/qubes_guid'
update_suggestion_interval = 14 # 14 days
@@ -458,21 +461,38 @@ class VmUpdateInfoWidget(QWidget):
self.previous_outdated = outdated
- if vm.is_updateable():
+ if vm.qid == 0:
update_recommended = self.previous_update_recommended
- stat_file = vm.dir_path + '/' + updates_stat_file
- if not os.path.exists(stat_file) or \
- time.time() - os.path.getmtime(stat_file) > \
- update_suggestion_interval * 24 * 3600:
- update_recommended = True
- else:
+ #slot for dom0 special treatment
+ #
+ #
+ elif vm.is_updateable():
+ update_recommended = self.previous_update_recommended
+ stat_file_path = vm.dir_path + '/' + updates_stat_file
+ if not os.path.exists(stat_file_path):
update_recommended = False
- if not self.show_text and self.previous_update_recommended != False:
- self.update_status_widget(None)
+ else:
+ if (not hasattr(vm, "updates_stat_file_read_time")) or vm.updates_stat_file_read_time <= os.path.getmtime(stat_file_path):
+ stat_file = open(stat_file_path, "r")
+ updates = stat_file.read().strip()
+ stat_file.close()
+ if updates.isdigit():
+ updates = int(updates)
+ else:
+ updates = 0
+
+ if updates == 0:
+ update_recommended = False
+ else:
+ update_recommended = True
+ vm.updates_stat_file_read_time = time.time()
+
if update_recommended and not self.previous_update_recommended:
self.update_status_widget("update")
-
+ elif self.previous_update_recommended and not update_recommended:
+ self.update_status_widget(None)
+
self.previous_update_recommended = update_recommended
@@ -482,7 +502,7 @@ class VmUpdateInfoWidget(QWidget):
if state == "update":
label_text = "Check updates"
icon_path = ":/update-recommended.png"
- tooltip_text = "Update recommended."
+ tooltip_text = "Updates pending!"
elif state == "outdated":
label_text = "VM outdated"
icon_path = ":/outdated.png"
@@ -588,10 +608,8 @@ class VmRowInTable(object):
if update_size_on_disk == True:
self.size_widget.update()
-class NewAppVmDlg (QDialog, ui_newappvmdlg.Ui_NewAppVMDlg):
- def __init__(self, parent = None):
- super (NewAppVmDlg, self).__init__(parent)
- self.setupUi(self)
+
+
vm_shutdown_timeout = 15000 # in msec
@@ -717,6 +735,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
self.context_menu.addAction(self.action_appmenus)
self.context_menu.addAction(self.action_editfwrules)
self.context_menu.addAction(self.action_updatevm)
+ self.context_menu.addAction(self.action_run_command_in_vm)
self.context_menu.addAction(self.action_set_keyboard_layout)
self.context_menu.addMenu(self.logs_menu)
self.context_menu.addMenu(self.blk_menu)
@@ -1022,6 +1041,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
self.action_appmenus.setEnabled(not vm.is_netvm())
self.action_editfwrules.setEnabled(vm.is_networked() and not (vm.is_netvm() and not vm.is_proxyvm()))
self.action_updatevm.setEnabled(vm.is_updateable() or vm.qid == 0)
+ self.action_run_command_in_vm.setEnabled(vm.qid != 0)
self.action_set_keyboard_layout.setEnabled(vm.qid != 0 and vm.last_running)
else:
self.action_settings.setEnabled(False)
@@ -1033,6 +1053,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
self.action_appmenus.setEnabled(False)
self.action_editfwrules.setEnabled(False)
self.action_updatevm.setEnabled(False)
+ self.action_run_command_in_vm.setEnabled(False)
self.action_set_keyboard_layout.setEnabled(False)
@@ -1045,92 +1066,10 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
@pyqtSlot(name='on_action_createvm_triggered')
def action_createvm_triggered(self):
- dialog = NewAppVmDlg()
-
- # Theoretically we should be locking for writing here and unlock
- # only after the VM creation finished. But the code would be more messy...
- # Instead we lock for writing in the actual worker thread
-
- self.qvm_collection.lock_db_for_reading()
- self.qvm_collection.load()
- self.qvm_collection.unlock_db()
-
- label_list = QubesVmLabels.values()
- label_list.sort(key=lambda l: l.index)
- for (i, label) in enumerate(label_list):
- 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 not vm.internal and vm.is_template()]
-
- default_index = 0
- for (i, vm) in enumerate(template_vm_list):
- if vm is self.qvm_collection.get_default_template():
- default_index = i
- dialog.template_name.insertItem(i, vm.name + " (default)")
- else:
- dialog.template_name.insertItem(i, vm.name)
- dialog.template_name.setCurrentIndex(default_index)
-
- dialog.vmname.selectAll()
- dialog.vmname.setFocus()
-
- if dialog.exec_():
- vmname = str(dialog.vmname.text())
- if self.qvm_collection.get_vm_by_name(vmname) is not None:
- QMessageBox.warning (None, "Incorrect AppVM Name!", "A VM with the name {0} already exists in the system!".format(vmname))
- return
-
- label = label_list[dialog.vmlabel.currentIndex()]
- template_vm = template_vm_list[dialog.template_name.currentIndex()]
-
- allow_networking = dialog.allow_networking.isChecked()
-
- thread_monitor = ThreadMonitor()
- thread = threading.Thread (target=self.do_create_appvm, args=(vmname, label, template_vm, allow_networking, thread_monitor))
- thread.daemon = True
- thread.start()
-
- progress = QProgressDialog ("Creating new AppVM {0}...".format(vmname), "", 0, 0)
- progress.setCancelButton(None)
- progress.setModal(True)
- progress.show()
-
- while not thread_monitor.is_finished():
- app.processEvents()
- time.sleep (0.1)
-
- progress.hide()
-
- if thread_monitor.success:
- trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' has been created.".format(vmname), msecs=3000)
- else:
- QMessageBox.warning (None, "Error creating AppVM!", "ERROR: {0}".format(thread_monitor.error_msg))
-
-
- def do_create_appvm (self, vmname, label, template_vm, allow_networking, thread_monitor):
- vm = None
- try:
- self.qvm_collection.lock_db_for_writing()
- self.qvm_collection.load()
-
- vm = self.qvm_collection.add_new_appvm(vmname, template_vm, label = label)
- vm.create_on_disk(verbose=False)
- firewall = vm.get_firewall_conf()
- firewall["allow"] = allow_networking
- firewall["allowDns"] = allow_networking
- vm.write_firewall_conf(firewall)
- self.qvm_collection.save()
- except Exception as ex:
- thread_monitor.set_error_msg (str(ex))
- if vm:
- vm.remove_from_disk()
- finally:
- self.qvm_collection.unlock_db()
-
- thread_monitor.set_finished()
-
+ dialog = NewVmDlg(app, self.qvm_collection, trayIcon)
+ dialog.exec_()
+
def get_selected_vm(self):
#vm selection relies on the VmInfo widget's value used for sorting by VM name
row_index = self.table.currentRow()
@@ -1383,6 +1322,47 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
thread_monitor.set_finished()
+ @pyqtSlot(name='on_action_run_command_in_vm_triggered')
+ def action_run_command_in_vm_triggered(self):
+ vm = self.get_selected_vm()
+
+ thread_monitor = ThreadMonitor()
+ thread = threading.Thread (target=self.do_run_command_in_vm, args=(vm, thread_monitor))
+ thread.daemon = True
+ thread.start()
+
+ while not thread_monitor.is_finished():
+ app.processEvents()
+ time.sleep (0.2)
+
+ if not thread_monitor.success:
+ QMessageBox.warning (None, "Error while running command", "Exception while running command:
{0}".format(thread_monitor.error_msg))
+
+
+ def do_run_command_in_vm(self, vm, thread_monitor):
+ cmd = ['kdialog', '--title', 'Qubes command entry', '--inputbox', 'Run command in '+vm.name+':']
+ kdialog = subprocess.Popen(cmd, stdout = subprocess.PIPE)
+ command_to_run = kdialog.stdout.read()
+ command_to_run = "'"+command_to_run.strip()+"'"
+ if command_to_run != "":
+ command_to_run = "qvm-run -a "+ vm.name + " " + command_to_run
+ try:
+ subprocess.check_call(command_to_run, shell=True)
+ except OSError as ex:
+ if ex.errno == errno.EINTR:
+ pass
+ else:
+ thread_monitor.set_error_msg(str(ex))
+ thread_monitor.set_finished()
+ except Exception as ex:
+ thread_monitor.set_error_msg(str(ex))
+ thread_monitor.set_finished()
+ thread_monitor.set_finished()
+
+
+
+
+
@pyqtSlot(name='on_action_set_keyboard_layout_triggered')
def action_set_keyboard_layout_triggered(self):
vm = self.get_selected_vm()
@@ -1494,33 +1474,6 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
def action_about_qubes_triggered(self):
QMessageBox.about(self, "About...", "Qubes OS
Release 1.0")
- @pyqtSlot(name='on_action_copy_clipboard_triggered')
- def action_copy_clipboard_triggered(self):
- clipboard = app.clipboard().text()
-
- #inter-appviewer lock
- try:
- fd = os.open("/var/run/qubes/appviewer.lock", os.O_RDWR|os.O_CREAT, 0600);
- fcntl.flock(fd, fcntl.LOCK_EX);
- except IOError:
- QMessageBox.warning (None, "Warning!", "Error while accessing Qubes clipboard!")
- return
-
- qubes_clipboard = open("/var/run/qubes/qubes_clipboard.bin", 'w')
- qubes_clipboard.write(clipboard)
- qubes_clipboard.close()
-
- qubes_clip_source = open("/var/run/qubes/qubes_clipboard.bin.source", 'w')
- qubes_clip_source.write("dom0")
- qubes_clip_source.close()
-
- try:
- fcntl.flock(fd, fcntl.LOCK_UN)
- os.close(fd)
- except IOError:
- QMessageBox.warning (None, "Warning!", "Error while writing to Qubes clipboard!")
- return
-
def createPopupMenu(self):
menu = QMenu()
@@ -1603,9 +1556,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
@pyqtSlot('QAction *')
def show_log(self, action):
log = str(action.data().toString())
-
- cmd = ['kdialog', '--textbox', log, '700', '450', '--title', log]
- subprocess.Popen(cmd)
+ log_dialog = LogDialog(app, log)
+ log_dialog.exec_()
@pyqtSlot('QAction *')
diff --git a/resources.qrc b/resources.qrc
index 0ca3594..e418cf4 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -12,6 +12,7 @@
icons/show-all-running.png
icons/mount.png
icons/log.png
+ icons/run-command.png
icons/kbd-layout.png
icons/copy.png
icons/pencil.png
diff --git a/rpm_spec/qmgr.spec b/rpm_spec/qmgr.spec
index aac95da..e14fd7a 100644
--- a/rpm_spec/qmgr.spec
+++ b/rpm_spec/qmgr.spec
@@ -44,6 +44,7 @@ cp qubesmanager/global_settings.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubes
cp qubesmanager/multiselectwidget.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
cp qubesmanager/restore.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
cp qubesmanager/settings.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
+cp qubesmanager/log_dialog.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
cp qubesmanager/thread_monitor.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
cp qubesmanager/resources_rc.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
cp qubesmanager/__init__.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
@@ -56,6 +57,7 @@ cp qubesmanager/ui_newappvmdlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesm
cp qubesmanager/ui_newfwruledlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
cp qubesmanager/ui_restoredlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
cp qubesmanager/ui_settingsdlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager
+cp qubesmanager/ui_logdlg.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
@@ -139,6 +141,9 @@ rm -rf $RPM_BUILD_ROOT
%{python_sitearch}/qubesmanager/ui_settingsdlg.py
%{python_sitearch}/qubesmanager/ui_settingsdlg.pyc
%{python_sitearch}/qubesmanager/ui_settingsdlg.pyo
+%{python_sitearch}/qubesmanager/ui_logdlg.py
+%{python_sitearch}/qubesmanager/ui_logdlg.pyc
+%{python_sitearch}/qubesmanager/ui_logdlg.pyo
/usr/share/applications/qubes-manager.desktop