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 @@ - @@ -296,6 +295,7 @@ + @@ -346,7 +346,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