--- /dev/null
+TEMPLATE = app
+TARGET = Palacios
+QT += core \
+ gui \
+ xml
+CONFIG += debug_and_release
+DEFINES += QTONLY
+
+LIBS += -lvncclient -lgnutls -lX11
+
+
+HEADERS += palacios/vm_console_widget.h \
+ palacios/vm_creation_wizard.h \
+ palacios/newpalacios.h \
+ palacios/defs.h \
+ palacios/vnc_module/remoteview.h \
+ palacios/vnc_module/vncclientthread.h \
+ palacios/vnc_module/vncview.h
+SOURCES += palacios/vm_view.cpp \
+ palacios/vm_console_widget.cpp \
+ palacios/vm_mode_dialog.cpp \
+ palacios/vm_info_widget.cpp \
+ palacios/vm_creation_wizard.cpp \
+ palacios/main.cpp \
+ palacios/newpalacios.cpp \
+ palacios/vm_threads.cpp \
+ palacios/vnc_module/remoteview.cpp \
+ palacios/vnc_module/vncclientthread.cpp \
+ palacios/vnc_module/vncview.cpp
+FORMS +=
+RESOURCES += palacios_resources.qrc
--- /dev/null
+TEMPLATE = app
+TARGET = Palacios
+QT += core \
+ gui \
+ xml
+CONFIG += debug_and_release
+DEFINES += QTONLY
+
+INCLUDEPATH += . \
+ $$PWD/ext/include
+DEPENDPATH += . \
+ $$PWD/external_libs/include
+LIBS += -L$$PWD/ext/lib -lvncclient -lgnutls
+
+HEADERS += palacios/vm_console_widget.h \
+ palacios/vm_creation_wizard.h \
+ palacios/newpalacios.h \
+ palacios/defs.h \
+ palacios/vnc_module/remoteview.h \
+ palacios/vnc_module/vncclientthread.h \
+ palacios/vnc_module/vncview.h
+SOURCES += palacios/vm_view.cpp \
+ palacios/vm_console_widget.cpp \
+ palacios/vm_mode_dialog.cpp \
+ palacios/vm_info_widget.cpp \
+ palacios/vm_creation_wizard.cpp \
+ palacios/main.cpp \
+ palacios/newpalacios.cpp \
+ palacios/vm_threads.cpp \
+ palacios/vnc_module/remoteview.cpp \
+ palacios/vnc_module/vncclientthread.cpp \
+ palacios/vnc_module/vncview.cpp
+FORMS +=
+RESOURCES += palacios_resources.qrc
--- /dev/null
+This directory contains an implementation of a graphical user
+interface for Palacios VMs, analogous to the "server console" tool in
+VMware or VirtualBox, etc. The GUI builds directly on top of the
+Palacios command-line utilities.
+
+The GUI was initially implemented by Abhinav Kannan and is:
+
+(c) 2012 Abhinav Kannan
+(c) 2012 V3VEE Project
+
+As with other Linux interface components of Palacios, it is released
+under GPL. Palacios itself is released under a BSD license.
+
+
+PREREQUISITES
+=============
+
+To build the GUI, you will need to install various libraries and
+components. Specifically, you need:
+
+- QT4
+- QT4 development support and tools (qmake)
+- LibVNCServer
+- GNUTLS and GNUTLS development support
+
+
+The easiest way to do this is to become root and then use
+your package manager.
+
+For example, on a Red Hat or Fedora system:
+
+su - (become root)
+yum install qt4
+yum install qt4-devel
+yum install libvncsever
+yum install libvncserver-devel
+yum install gnutls
+yum install gnutls-devel
+exit (return to normal user)
+
+If you need to set up as a none-root user, please see the file
+report.pdf.
+
+
+BUILDING
+========
+
+1. Use qmake to build a Makefile for your machine:
+
+qmake Palacos.Pro
+
+2. make it
+
+make
+
+3. you should now have an executable file "Palacios"
+ this will become palacios/linux_usr/v3_x0gui
+ and will be wrapped by the script palacios/linux_usr/v3_gui
+
+If you would like to change things, please read report.pdf.
+The "Palacios.pro" file mentioned in the report is
+included here as "Palacios.pro.locallibs"
+
+RUNNING
+=======
+
+To run the GUI, the Palacios command-line utilities and xterm need to
+be on your path, and v3vee.ko kernel module must be inserted into
+the running kernel. For example:
+
+export PATH=$PATH:/home/foo/palacios/linxu_usr:/path/to/xterm
+insmod /home/foo/palacios/v3vee.ko
+Palacios &
+
+
--- /dev/null
+/*
+ * defs.h
+ *
+ * Created on: Oct 4, 2012
+ * Author: Abhinav Kannan
+ */
+
+#ifndef DEFS_H_
+#define DEFS_H_
+
+const char* STATUS_BAR_MSG_READY = "Ready";
+
+const char* TITLE_MAIN_WINDOW = "Palacios";
+const char* TITLE_DOCK_TELEMETRY = "Kernel messages";
+const char* TITLE_DOCK_VM_LIST = "List of VMs";
+
+const char* MENU_FILE = "File";
+const char* MENU_VIEW = "View";
+const char* MENU_VM = "VM";
+const char* MENU_HELP = "Help";
+
+const char* FILE_MENU_NEW_VM = "New VM";
+const char* NEW_VM_STATUS_TIP = "Create a new virtual machine";
+const char* FILE_MENU_EXIT = "Exit";
+const char* EXIT_STATUS_TIP = "Exit Palacios VMM";
+
+const char* VM_MENU_START = "Start VM";
+const char* VM_MENU_STOP = "Stop VM";
+const char* VM_MENU_PAUSE = "Pause VM";
+const char* VM_MENU_RESTART = "Restart VM";
+const char* VM_MENU_REMOVE = "Delete VM";
+const char* VM_MENU_ACTIVATE = "Activate VM";
+const char* VM_MENU_RELOAD = "Reload VMs";
+
+const char* VM_STOP_WARNING_MESSAGE = "You are about to stop a running virtual machine. Please stop all executing processes"
+ "within the virtual machine to insure safe termination of VM. Do you want to continue?";
+const char* VM_DELETE_WARNING_MESSAGE = "Are you sure you want to delete this VM?";
+const char* DELETE_RUNNING_VM_ERROR = "This VM is currently running! Please stop the VM before deleting";
+
+const char* HELP_MENU_ABOUT = "About Palacios";
+const char* ABOUT_PALACIOS =
+ "Palacios is a virtual machine monitor (VMM) "
+ "that is available for public use as a community resource. Palacios is highly configurable "
+ "and designed to be embeddable into different host operating systems, such as Linux and the "
+ "Kitten lightweight kernel. Palacios is a non-paravirtualized VMM that makes extensive use of "
+ "the virtualization extensions in modern Intel and AMD x86 processors. "
+ "Palacios is a compact codebase that has been designed to be easy to understand and readily "
+ "configurable for different environments. It is unique in being designed to be embeddable into "
+ "other OSes instead of being implemented in the context of a specific OS. Palacios is distributed under the BSD license."
+
+ "\nPalacios is part of the V3VEE Project";
+
+const char* XTERM_CMD = "/usr/bin/xterm";
+
+const char* FILE_VM_LIST = "virtual_machines_list.txt";
+
+const char* TAG_VM = "vm";
+
+const char* ERROR_TELEMETRY = "Telemetry information currently unavailable";
+
+const char* LABEL_ACTIVE_INVENTORY = "Active Inventory";
+const char* LABEL_ACTIVE_NOT_INVENTORY = "Not in inventory";
+const char* LABEL_INACTIVE_INVENTORY = "Inactive Inventory";
+
+const char* ERROR_SETUP_MODULE_INSTALL = "Kernel Module not installed";
+const char* ERROR_SETUP_MODULE_INSTALL_FAILED = "Kernel module not installed correctly";
+const char* ERROR_SETUP_MEMORY = "Memory not intialized";
+
+const char* ERROR_APP_CLOSE = "There are running VMs in the current session. Stop or pause the VMs before exiting";
+const char* ERROR_VM_RUNNING = "VM is already running";
+const char* ERROR_UPDATE_VM_STATE = "Error could not update VM state";
+const char* ERROR_RUN_ACTIVE_NOT_INVENTORY = "VM instance exists on the system but has not been added to the inventory. Activate VM to proceed";
+const char* ERROR_RUN_INACTIVE_INVENTORY = "VM instance has not been activated";
+const char* ERROR_NO_DEVFILE_FOR_LAUNCH = "Could not find /dev/v3-vm# file to launch VM";
+const char* ERROR_STOP_VM = "VM is not running";
+const char* ERROR_VM_NOT_INVENTORY = "Cannot stop VM. VM is either inactive or not available in inventory";
+const char* ERROR_LAUNCH_VM_DEVICE_NOT_FOUND = "Error launching VM! Device file not found";
+const char* ERROR_LAUNCH_VM_IOCTL = "VM Launch: IOCTL error! Check kernel logs for details";
+const char* ERROR_STOP_VM_PATH = "Error executing stop command. Check path variable";
+const char* ERROR_STOP_VM_IOCTL = "VM Stop: IOCTL error! Check kernel logs for details";
+const char* ERROR_STOP_VM_DEVICE_NOT_FOUND = "Could not stop VM! Device file not found";
+const char* ERROR_PAUSE_VM_IOCTL = "VM Pause: IOCTL error! Check kernel logs for details";
+const char* ERROR_PAUSE_VM_DEVICE_NOT_FOUND = "Error pausing VM! Could not open device file";
+const char* ERROR_RESTART_VM_IOCTL = "VM Restart: IOCTL error! Check kernel logs for details";
+const char* ERROR_RESTART_VM_DEVICE_NOT_FOUND = "Error restarting VM! Device file not found.";
+
+const char* ERROR_VM_CREATE_PATH = "VM creation failed: Check PATH variable";
+const char* ERROR_VM_CREATE_IOCTL = "VM creation failed: Check kernel logs for more details";
+const char* ERROR_VM_CREATE_DB = "VM creation failed: Could not save VM. Error in database";
+const char* ERROR_VM_CREATE_PROC = "VM creation failed: Unable to get dev file";
+const char* ERROR_VM_CREATE_FAILED = "VM creation failed: Could not create dev file for new VM. Check kernel logs for more details";
+const char* SUCCESS_VM_ADDED = "VM added to inventory. Activate to use";
+const char* SUCCESS_VM_CREATE = "VM created successfully!";
+
+const char* VM_TAB_TITLE = "VM Details";
+
+const char* ERROR_VM_LAUNCH = "VM Launch failed: Check kernel logs for details";
+
+const char* ERROR_VM_DELETE_PATH = "VM Deletion failed: Check PATH variable";
+const char* ERROR_VM_DELETE_IOCTL = "VM Deletion failed: Check kernel logs for more details";
+const char* ERROR_VM_DELETE_DB = "VM Deletion failed: Database error";
+const char* ERROR_VM_DELETE_INVALID_ARGUMENT = "VM Deletion failed: Could not find dev file!";
+const char* SUCCESS_VM_DELETE = "VM deleted successfully";
+#endif /* DEFS_H_ */
--- /dev/null
+#include "newpalacios.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ NewPalacios w;
+ /*int checkSetup = w.checkPalacios();
+
+ if (checkSetup == -1) {
+ a.exit();
+ return 0;
+ } else {
+ w.setMinimumSize(820, 640);
+ w.showMaximized();
+ return a.exec();
+ }*/
+
+ w.setMinimumSize(820, 640);
+ w.showMaximized();
+ //w.checkPalacios();
+ int eventLoop = a.exec();
+ return eventLoop;
+}
--- /dev/null
+#include <QMessageBox>
+#include <QDebug>
+#include <QtGui>
+#include <QtAlgorithms>
+
+#include "newpalacios.h"
+#include "defs.h"
+
+NewPalacios::NewPalacios(QWidget *parent) :
+ QMainWindow(parent) {
+
+ // Call UI setup methods
+ createWizard();
+ createCentralWidget();
+ createActions();
+ createMenus();
+ createToolBars();
+ createStatusBar();
+ createDockWindows();
+ readExistingVmsFile();
+
+ setWindowTitle(tr(TITLE_MAIN_WINDOW));
+
+ mExitAppFromMenu = false;
+}
+
+NewPalacios::~NewPalacios() {
+ // Cleanup actions
+ delete mExitApp;
+ delete mVmNew;
+ delete mVmStop;
+ delete mVmPause;
+ delete mVmStart;
+ delete mVmActivate;
+ delete mReloadVms;
+ delete mAboutApp;
+
+ // Clean up menu
+ delete mFileMenu;
+ delete mViewMenu;
+ delete mVmMenu;
+ delete mHelpMenu;
+
+ // Clean up toolbar
+ delete mVmToolBar;
+ delete mVmCtrlToolBar;
+
+ /*if (mLoadVmsThread != NULL)
+ delete mLoadVmsThread;
+ if (mAddVmThread != NULL)
+ delete mAddVmThread;
+ if (mDeleteVmThread != NULL)
+ delete mDeleteVmThread;*/
+
+ delete mVmTreeView;
+ delete mVmTelemetryView;
+ delete mVmWizard;
+ delete mVmInfoWidget;
+
+ // Delete the central widget at the end
+ delete mVmControlPanel;
+}
+
+void NewPalacios::createCentralWidget() {
+ // The VM View will be the central widget
+ // of this window. The List of VMs will
+ // be docked in the left corner
+ mVmControlPanel = new QTabWidget();
+ mVmControlPanel->setTabsClosable(true);
+ connect(mVmControlPanel, SIGNAL(tabCloseRequested(int)),
+ this, SLOT(vmTabClosed(int)));
+
+ mVmInfoWidget = new VmInfoWidget(mVmControlPanel);
+
+ // Set the widget to the window
+ this->setCentralWidget(mVmControlPanel);
+}
+
+void NewPalacios::createActions() {
+ mVmNew = new QAction(QIcon(":/images/images/new_vm.png"),
+ tr(FILE_MENU_NEW_VM), this);
+ connect(mVmNew, SIGNAL(triggered()), this, SLOT(createVmInstance()));
+
+ mExitApp = new QAction(QIcon(":/images/images/exit.png"),
+ tr(FILE_MENU_EXIT), this);
+ connect(mExitApp, SIGNAL(triggered()), this, SLOT(exitApplication()));
+
+ mVmStart = new QAction(QIcon(":/images/images/start_vm.png"),
+ tr(VM_MENU_START), this);
+ connect(mVmStart, SIGNAL(triggered()), this, SLOT(selectVmMode()));
+
+ mVmStop = new QAction(QIcon(":/images/images/stop_vm.png"),
+ tr(VM_MENU_STOP), this);
+ connect(mVmStop, SIGNAL(triggered()), this, SLOT(stopVm()));
+
+ mVmPause = new QAction(QIcon(":/images/images/pause_vm.png"),
+ tr(VM_MENU_PAUSE), this);
+ connect(mVmPause, SIGNAL(triggered()), this, SLOT(pauseVm()));
+
+ mVmActivate = new QAction(QIcon(":/images/images/activate_vm.png"),
+ tr(VM_MENU_ACTIVATE), this);
+ connect(mVmActivate, SIGNAL(triggered()), this, SLOT(activateVm()));
+
+ mAboutApp = new QAction(tr(HELP_MENU_ABOUT), this);
+ connect(mAboutApp, SIGNAL(triggered()), this, SLOT(aboutPalacios()));
+
+ mReloadVms = new QAction(QIcon(":/images/images/reload_vm.png"), tr(VM_MENU_RELOAD), this);
+ connect(mReloadVms, SIGNAL(triggered()), this, SLOT(reloadVms()));
+
+ connect(mVmWizard, SIGNAL(accepted()), this, SLOT(addNewVm()));
+}
+
+void NewPalacios::createMenus() {
+ mFileMenu = menuBar()->addMenu(tr(MENU_FILE));
+ mFileMenu->addAction(mVmNew);
+ mFileMenu->addSeparator();
+ mFileMenu->addAction(mExitApp);
+
+ mViewMenu = menuBar()->addMenu(tr(MENU_VIEW));
+
+ mVmMenu = menuBar()->addMenu(tr(MENU_VM));
+ mVmMenu->addAction(mVmStart);
+ mVmMenu->addAction(mVmStop);
+ mVmMenu->addAction(mVmPause);
+ mVmMenu->addAction(mVmActivate);
+ mVmMenu->addAction(mReloadVms);
+
+ mHelpMenu = menuBar()->addMenu(tr(MENU_HELP));
+ mHelpMenu->addAction(mAboutApp);
+}
+
+void NewPalacios::createToolBars() {
+ mVmToolBar = addToolBar(tr(MENU_FILE));
+ mVmToolBar->addAction(mVmNew);
+
+ mVmCtrlToolBar = addToolBar(tr(MENU_VM));
+ mVmCtrlToolBar->addAction(mVmStart);
+ mVmCtrlToolBar->addAction(mVmStop);
+ mVmCtrlToolBar->addAction(mVmPause);
+ mVmCtrlToolBar->addAction(mVmActivate);
+ mVmCtrlToolBar->addAction(mReloadVms);
+}
+
+void NewPalacios::createStatusBar() {
+ statusBar()->showMessage(tr(STATUS_BAR_MSG_READY));
+}
+
+void NewPalacios::createDockWindows() {
+ QDockWidget* dockVmList = new QDockWidget(tr(TITLE_DOCK_VM_LIST), this);
+
+ // Setup VM instance tree view
+ mVmTreeView = new QTreeWidget(dockVmList);
+ mVmTreeView->setColumnCount(1);
+ mVmTreeView->headerItem()->setHidden(true);
+ // Header for active VMs
+ QTreeWidgetItem* activeVms = new QTreeWidgetItem(mVmTreeView);
+ activeVms->setText(0, tr(LABEL_ACTIVE_INVENTORY));
+ mVmTreeView->addTopLevelItem(activeVms);
+
+ // Header for inactive VMs in the inventory
+ QTreeWidgetItem* inactiveVms = new QTreeWidgetItem(mVmTreeView);
+ inactiveVms->setText(0, tr(LABEL_INACTIVE_INVENTORY));
+ mVmTreeView->addTopLevelItem(inactiveVms);
+
+ // Header for active VMs not in inventory
+ QTreeWidgetItem* activeNotInventoryVms = new QTreeWidgetItem(mVmTreeView);
+ activeNotInventoryVms->setText(0, tr(LABEL_ACTIVE_NOT_INVENTORY));
+ mVmTreeView->addTopLevelItem(activeNotInventoryVms);
+
+ mVmTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(mVmTreeView, SIGNAL(customContextMenuRequested(const QPoint &)),
+ this, SLOT(vmContextMenu(const QPoint &)));
+ connect(mVmTreeView, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this,
+ SLOT(vmItemClickListener(QTreeWidgetItem*, int)));
+
+ dockVmList->setAllowedAreas(Qt::LeftDockWidgetArea);
+ dockVmList->setFeatures(QDockWidget::DockWidgetClosable);
+ dockVmList->setWidget(mVmTreeView);
+ addDockWidget(Qt::LeftDockWidgetArea, dockVmList);
+ mViewMenu->addAction(dockVmList->toggleViewAction());
+
+ // Telemetry dock
+ QDockWidget* dockTelemetry = new QDockWidget(tr(TITLE_DOCK_TELEMETRY), this);
+ mVmTelemetryView = new QTextEdit(dockTelemetry);
+ mVmTelemetryView->setReadOnly(true);
+ dockTelemetry->setAllowedAreas(Qt::BottomDockWidgetArea);
+ dockTelemetry->setFeatures(QDockWidget::NoDockWidgetFeatures);
+ dockTelemetry->setWidget(mVmTelemetryView);
+ addDockWidget(Qt::BottomDockWidgetArea, dockTelemetry);
+ connect(dockTelemetry, SIGNAL(visibilityChanged(bool)), this, SLOT(updateTelemetry(bool)));
+}
+
+void NewPalacios::updateTelemetry(bool visible) {
+ if (visible) {
+ mTelemProc = new QProcess();
+ mTelemProc->setProcessChannelMode(QProcess::MergedChannels);
+ QStringList args;
+ args << "-c" << "tail -f /var/log/messages";
+
+ // Slots used for debugging
+ //connect(mTelemProc, SIGNAL(started()), this, SLOT(processStarted()));
+ //connect(mTelemProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processExit(int, QProcess::ExitStatus)));
+ //connect(mTelemProc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
+
+ // Connect output of dmesg to text widget
+ connect(mTelemProc, SIGNAL(readyReadStandardOutput()), this, SLOT(updateTelemetryView()));
+
+ mTelemProc->start("sh", args);
+ if (!mTelemProc->waitForStarted()) {
+ if (mVmTelemetryView != NULL) {
+ mVmTelemetryView->setText(tr(ERROR_TELEMETRY));
+ }
+ }
+ } else {
+ /*if (mTelemProc != NULL) {
+ mTelemProc->close();
+ mTelemProc->terminate();
+ delete mTelemProc;
+ }*/
+ }
+}
+
+void NewPalacios::updateTelemetryView() {
+ if (mVmTelemetryView != NULL && mTelemProc != NULL) {
+ mVmTelemetryView->setText(mTelemProc->readAllStandardOutput());
+ }
+}
+
+void NewPalacios::createWizard() {
+ mVmWizard = new NewVmWizard();
+}
+
+// Listener for VM item clicks
+void NewPalacios::vmItemClickListener(QTreeWidgetItem* item, int col) {
+ if (item->text(0).compare(LABEL_ACTIVE_INVENTORY) == 0
+ || item->text(0).compare(LABEL_INACTIVE_INVENTORY) == 0
+ || item->text(0).compare(LABEL_ACTIVE_NOT_INVENTORY) == 0) {
+ // If user clicks on the main headers
+ // do nothing
+ isHeaderClicked = true;
+ return;
+ }
+
+ isHeaderClicked = false;
+
+ QString vmName = item->text(col);
+ mVmName = vmName;
+
+ // Locate the VM clicked in the list. This is inefficient because each time we need
+ // to search the list. If a better way is found. replace here
+ VmInfo* vm = NULL;
+ for (int i = 0; i < mVmList.size(); i++) {
+ if (vmName.compare(mVmList[i]->getVmName()) == 0) {
+ mVmPos = i;
+ vm = mVmList[i];
+ break;
+ }
+ }
+ // Load the details of the seleted VM
+ if (vm != NULL) {
+ mVmInfoWidget->updateInfoView(vm);
+ }
+}
+
+void NewPalacios::vmContextMenu(const QPoint &pos) {
+ //QPoint globalPos = mVmListView->mapToGlobal(pos);
+ QPoint globalPos = mVmTreeView->mapToGlobal(pos);
+
+ QTreeWidgetItem* item = mVmTreeView->itemAt(pos);
+ if (item->text(0).compare(LABEL_ACTIVE_INVENTORY) == 0
+ || item->text(0).compare(LABEL_INACTIVE_INVENTORY) == 0
+ || item->text(0).compare(LABEL_ACTIVE_NOT_INVENTORY) == 0) {
+ // Clicked on the headers
+ // do nothing
+ isHeaderClicked = true;
+ return;
+ }
+
+ isHeaderClicked = false;
+
+ // Update VM pos, the global index locator for VMs
+ QString vmName = item->text(0);
+ // Update global VM name
+ mVmName = vmName;
+ for (int i=0; i<mVmList.size(); i++) {
+ if (vmName.compare(mVmList[i]->getVmName()) == 0) {
+ mVmPos = i;
+ break;
+ }
+ }
+
+ QMenu *menu = new QMenu();
+ if (mVmList[mVmPos]->getCategory() == VmInfo::INACTIVE_INVENTORY
+ || mVmList[mVmPos]->getCategory() == VmInfo::ACTIVE_NOT_INVENTORY) {
+
+ menu->addAction(new QAction(tr(VM_MENU_ACTIVATE), this));
+ } else {
+ if (mVmList[mVmPos]->getState() == VmInfo::RUNNING) {
+ menu->addAction(new QAction(tr(VM_MENU_PAUSE), this));
+
+ } else if (mVmList[mVmPos]->getState() == VmInfo::PAUSED) {
+ menu->addAction(new QAction(tr(VM_MENU_RESTART), this));
+
+ } else if (mVmList[mVmPos]->getState() == VmInfo::STOPPED) {
+ menu->addAction(new QAction(tr(VM_MENU_START), this));
+
+ }
+
+ menu->addAction(new QAction(tr(VM_MENU_STOP), this));
+ }
+
+ menu->addAction(new QAction(tr(VM_MENU_REMOVE), this));
+
+ QAction* selectedAction = menu->exec(globalPos);
+ if (selectedAction == NULL) {
+ // User did not select any option
+ return;
+ }
+
+ QString actionItem = selectedAction->text();
+ if (actionItem.compare(tr(VM_MENU_START)) == 0
+ || actionItem.compare(tr(VM_MENU_RESTART)) == 0) {
+ // If VM was stopped or paused
+ selectVmMode();
+
+ } else if (actionItem.compare(tr(VM_MENU_PAUSE)) == 0) {
+ // Pause VM
+ pauseVm();
+
+ } else if (actionItem.compare(tr(VM_MENU_RESTART)) == 0) {
+ // Restart VM
+ restartVm();
+
+ } else if (actionItem.compare(tr(VM_MENU_STOP)) == 0) {
+ // Stop the VM if possible
+ stopVm();
+
+ } else if (actionItem.compare(tr(VM_MENU_REMOVE)) == 0) {
+ bool isVmRunning = false;
+ for (int i=0; i < mRunningVms.size(); i++) {
+ if (mVmName.compare(mRunningVms[i]->getVmName()) == 0) {
+ isVmRunning = true;
+ break;
+ }
+ }
+
+ QMessageBox vmDeleteWarningBox;
+
+ if (isVmRunning) {
+ vmDeleteWarningBox.setText(DELETE_RUNNING_VM_ERROR);
+ vmDeleteWarningBox.setIcon(QMessageBox::Critical);
+ } else {
+ vmDeleteWarningBox.setText(VM_DELETE_WARNING_MESSAGE);
+ vmDeleteWarningBox.setIcon(QMessageBox::Warning);
+ }
+
+ vmDeleteWarningBox.setStandardButtons(
+ QMessageBox::Ok | QMessageBox::Cancel);
+ vmDeleteWarningBox.setDefaultButton(QMessageBox::Cancel);
+ int retVal = vmDeleteWarningBox.exec();
+
+ switch (retVal) {
+ case QMessageBox::Ok:
+ if (!isVmRunning) {
+ deleteVm(mVmName);
+ }
+ break;
+ case QMessageBox::Cancel:
+ break;
+ }
+
+ } else if (actionItem.compare(tr(VM_MENU_ACTIVATE)) == 0) {
+ activateVm();
+ return;
+ }
+
+}
+
+void NewPalacios::aboutPalacios() {
+ QMessageBox::about(this, tr(HELP_MENU_ABOUT), tr(ABOUT_PALACIOS));
+}
+
+// This function checks for the necessary setup
+// required by palacios to run. The setup for palacios
+// includes:
+// 1. Check for v3vee.ko module
+// 2. Check if palacios has been allocated memory
+void NewPalacios::checkPalacios() {
+ int err = 0;
+ QMessageBox setupError;
+ setupError.setStandardButtons(QMessageBox::Ok);
+ setupError.setIcon(QMessageBox::Critical);
+
+ // v3vee.ko check
+ QProcess* v3 = new QProcess();
+ v3->setProcessChannelMode(QProcess::MergedChannels);
+ v3->start("lsmod");
+ v3->waitForFinished();
+ QByteArray reply = v3->readAllStandardOutput();
+
+ if (reply.isEmpty() || !reply.contains("v3vee")) {
+ setupError.setText(tr(ERROR_SETUP_MODULE_INSTALL));
+ err = -1;
+ }
+
+ QFile file("/proc/v3vee/v3-mem");
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ setupError.setText(tr(ERROR_SETUP_MODULE_INSTALL_FAILED));
+ err = -1;
+ }
+
+ QTextStream in(&file);
+
+ while(in.atEnd()) {
+ QString line = in.readLine();
+ if (line.contains("null")) {
+ setupError.setText(tr(ERROR_SETUP_MEMORY));
+ err = -1;
+ break;
+ }
+ }
+
+ if (err != 0) {
+ int ret = setupError.exec();
+
+ if (ret == QMessageBox::Ok) {
+ // Palacios not setup correctly, exit
+ this->close();
+ }
+ }
+}
+
+void NewPalacios::closeEvent(QCloseEvent* event) {
+ if (mExitAppFromMenu == true) {
+ event->accept();
+ return;
+ }
+
+ bool flag = false;
+
+ for (int i=0; i<mVmList.size(); i++) {
+ if (mVmList[i]->getState() == VmInfo::RUNNING) {
+ flag = true;
+ break;
+ }
+ }
+
+ if (!flag) {
+ if (mTelemProc != NULL) {
+ mTelemProc->close();
+ mTelemProc->terminate();
+ delete mTelemProc;
+ }
+ event->accept();
+ } else {
+
+ QMessageBox appCloseWarning;
+ appCloseWarning.setText(tr("There are still running VMs. It is suggested to close the running VMs tab before exiting. Click cancel to go back"));
+ appCloseWarning.setIcon(QMessageBox::Warning);
+ appCloseWarning.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+
+ int ret = appCloseWarning.exec();
+ switch (ret) {
+ case QMessageBox::Ok:
+ event->accept();
+ break;
+ case QMessageBox::Cancel:
+ event->ignore();
+ break;
+ }
+ }
+}
+
+void NewPalacios::exitApplication() {
+ QMessageBox appCloseWarning;
+ appCloseWarning.setText(tr("There are still running VMs. It is suggested to close the running VMs tab before exiting. Click cancel to go back"));
+ appCloseWarning.setIcon(QMessageBox::Warning);
+ appCloseWarning.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+
+ bool flag = false;
+
+ // Check if there are running VMs
+ for (int i=0; i<mVmList.size(); i++) {
+ if (mVmList[i]->getState() == VmInfo::RUNNING) {
+ flag = true;
+ break;
+ }
+ }
+
+ // If there are no running VMs
+ // exit
+ if (!flag) {
+ if (mTelemProc != NULL) {
+ mTelemProc->close();
+ mTelemProc->terminate();
+ delete mTelemProc;
+ }
+ mExitAppFromMenu = true;
+ this->close();
+ } else {
+ // Inform user about running VMs and ask for consent
+ // before closing application
+ int ret = appCloseWarning.exec();
+ switch (ret) {
+ case QMessageBox::Ok:
+ mExitAppFromMenu = true;
+ this->close();
+ break;
+ case QMessageBox::Cancel:
+ mExitAppFromMenu = false;
+ break;
+ }
+ }
+}
+
+// Convenience method for showing messages
+void NewPalacios::showMessage(QString msg, bool err, bool warning) {
+ QMessageBox msgBox;
+ msgBox.setText(msg);
+ if (err == true) {
+ msgBox.setIcon(QMessageBox::Critical);
+ } else if (warning == true) {
+ msgBox.setIcon(QMessageBox::Warning);
+ }
+
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.exec();
+}
+
+/*void NewPalacios::processStarted() {
+ //qDebug() << "Process started...";
+}
+
+void NewPalacios::processExit(int errorCode, QProcess::ExitStatus exitStatus) {
+ if (exitStatus == QProcess::CrashExit) {
+ //qDebug() << "The process crashed!!";
+ } else {
+ //qDebug() << "The process exited normally..";
+ }
+}
+
+void NewPalacios::processError(QProcess::ProcessError error) {
+ //qDebug() << "There was an error in the process execution...";
+
+ switch (error) {
+ case QProcess::FailedToStart:
+ //qDebug() << "Process failed to start...";
+ break;
+ case QProcess::Crashed:
+ //qDebug() << "Process crashed...";
+ break;
+ case QProcess::Timedout:
+ //qDebug() << "Process timed out...";
+ break;
+ case QProcess::WriteError:
+ //qDebug() << "Process had write error...";
+ break;
+ case QProcess::ReadError:
+ //qDebug() << "Process had read error...";
+ break;
+ case QProcess::UnknownError:
+ //qDebug() << "Process unknown error...";
+ break;
+
+ }
+}*/
+
+void NewPalacios::createVmInstance() {
+ if (mVmWizard != NULL) {
+ mVmWizard->restart();
+ mVmWizard->show();
+ }
+}
+
+void NewPalacios::selectVmMode() {
+ if (isHeaderClicked) {
+ return;
+ }
+
+ if (mVmList[mVmPos]->getCategory() != VmInfo::ACTIVE_INVENTORY) {
+ QMessageBox warning;
+ showMessage(tr("VM is not active!"), true);
+ return;
+ }
+
+
+ if (mVmList[mVmPos]->getState() == VmInfo::PAUSED) {
+ // If machine is paused, we just restart using the previous selected mode
+ restartVm();
+ } else {
+ // Machine is started fresh
+ mVmModeDialog = new VmModeDialog(this);
+ connect(mVmModeDialog, SIGNAL(setMode(int, QString)),
+ this, SLOT(getVmMode(int, QString)));
+ mVmModeDialog->show();
+ }
+}
+
+void NewPalacios::getVmMode(int mode, QString streamName) {
+ mVmMode = mode;
+ mStreamName = streamName;
+ // Start VM
+ startVm();
+}
+
+void NewPalacios::startVm() {
+ if (mVmList.isEmpty()) {
+ // There is no VM in the list
+ return;
+ }
+
+ // Check if we are trying to start an inactive VM
+ if (mVmList[mVmPos]->getCategory() == VmInfo::INACTIVE_INVENTORY) {
+ showMessage(tr(ERROR_RUN_INACTIVE_INVENTORY), true);
+ return;
+ }
+
+ // Check if we are trying to start a VM not in the inventory
+ if (mVmList[mVmPos]->getCategory() == VmInfo::ACTIVE_NOT_INVENTORY) {
+ showMessage(tr(ERROR_RUN_ACTIVE_NOT_INVENTORY), true);
+ return;
+ }
+
+ int pos = 0;
+ // Check if the VM is already running
+ for (int i=0; i < mRunningVms.size(); i++) {
+ if (mVmName.compare(mRunningVms[i]->getVmName()) == 0
+ && mVmMode != VmConsoleWidget::STREAM) {
+ // If we are running the VM in stream mode then we
+ // can have multiple streams open simultaneously
+ // For the other modes, only one instance can be running
+ showMessage(tr(ERROR_VM_RUNNING), true);
+ return;
+ }
+ }
+
+ QString v3_devfile = mVmList[mVmPos]->getVmDevFile();
+ if (v3_devfile == NULL) {
+ showMessage(tr(ERROR_NO_DEVFILE_FOR_LAUNCH), true);
+ return;
+ }
+
+ int vm_status = mVmList[mVmPos]->getState();
+
+ QProcess* v3LaunchProc = NULL;
+ QStringList args;
+ bool flag = false;
+ QByteArray message;
+
+ switch (vm_status) {
+ case VmInfo::STOPPED:
+ // If VM is stopped, launch it <v3_launch>
+ v3LaunchProc = new QProcess();
+ v3LaunchProc->setProcessChannelMode(QProcess::MergedChannels);
+
+ // Connect debug slots
+ //connect(v3LaunchProc, SIGNAL(started()), this, SLOT(processStarted()));
+ //connect(v3LaunchProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processExit(int, QProcess::ExitStatus)));
+ //connect(v3LaunchProc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
+
+ args << v3_devfile;
+ v3LaunchProc->start("v3_launch", args);
+
+ flag = v3LaunchProc->waitForFinished();
+ message = v3LaunchProc->readAllStandardOutput();
+
+ if (!flag) {
+ showMessage(tr(ERROR_VM_LAUNCH), true);
+ delete v3LaunchProc;
+ return;
+
+ } else if (message.contains("Error opening V3Vee VM device")) {
+ showMessage(tr(ERROR_LAUNCH_VM_DEVICE_NOT_FOUND), true);
+ delete v3LaunchProc;
+ return;
+
+ }
+
+ break;
+ /*case VmInfo::PAUSED:
+ // If VM is paused, call <v3_continue>
+ restartVm();
+ break;*/
+ case VmInfo::RUNNING:
+ // If VM is running, do nothing
+ // We will be just calling v3_cons_sc/v3_stream/vnc to
+ // connect to it
+ break;
+ }
+
+ QString name = mVmList[mVmPos]->getVmName();
+ // Create a new console instance and set the launch file
+ VmXConsoleParent* consoleParent = new VmXConsoleParent(name);
+
+ // Add it to list of running VMs
+ mRunningVms.append(consoleParent);
+ pos = mRunningVms.indexOf(consoleParent);
+
+ // Launch in new tab
+ // Disable updates in widgets to reduce screen flicker
+ // due to layouting in widgets
+ consoleParent->setUpdatesEnabled(false);
+ mVmControlPanel->setUpdatesEnabled(false);
+ if (mVmMode != VmConsoleWidget::STREAM) {
+ // If not in stream mode, header will be VM name
+ mVmControlPanel->insertTab(mVmControlPanel->count(), consoleParent, name);
+ } else {
+ // If stream mode, header will be stream name
+ mVmControlPanel->insertTab(mVmControlPanel->count(), consoleParent, mStreamName);
+ }
+ mVmControlPanel->setCurrentWidget(consoleParent);
+
+ // Start VM
+ consoleParent->showWidget(mVmMode, v3_devfile, mStreamName);
+ // Re-enable updates to widgets
+ mVmControlPanel->setUpdatesEnabled(true);
+ consoleParent->setUpdatesEnabled(true);
+
+ // Signal to tell the background xterm process to quit when console widget is closed
+ connect(mRunningVms[pos], SIGNAL(windowClosingWithId(QString)), this,
+ SLOT(consoleWindowClosed(QString)));
+ // Update VM state
+ updateVmState(VmInfo::RUNNING);
+
+ for (int i=0; i<mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->childCount(); i++) {
+ QTreeWidgetItem* child = mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->child(i);
+ if (mVmName.compare(child->text(0)) == 0) {
+ child->setIcon(0, QIcon(":/images/images/start_vm.png"));
+ break;
+ }
+ }
+}
+
+void NewPalacios::updateVmState(int mode) {
+ // TODO: Move this to a background thread
+ QString vm_name = mVmList[mVmPos]->getVmName();
+
+ QFile file("virtual_machines_list.txt");
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ showMessage(tr(ERROR_UPDATE_VM_STATE), true);
+ stopVm();
+ return;
+ }
+
+ QFile temp("temp.txt");
+ if (!temp.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ showMessage(tr(ERROR_UPDATE_VM_STATE), true);
+ stopVm();
+ return;
+ }
+
+ QTextStream in(&file);
+ QTextStream out(&temp);
+
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (line.compare("\n") == 0) {
+ continue;
+ }
+
+ QStringList vmDet = line.split(",");
+ QString nameStr = vmDet.at(0);
+
+ if (nameStr.compare(vm_name) == 0) {
+ QString configStr = vmDet.at(1);
+ QString devfileStr = vmDet.at(2);
+ QString stateStr = vmDet.at(3);
+ QString imageStr = vmDet.at(4);
+
+ out << nameStr << "," << configStr << "," << devfileStr << "," << QString::number(mode) <<"," << imageStr << endl;
+ } else {
+ out << line << endl;
+ }
+ }
+
+ out.flush();
+ file.remove("virtual_machines_list.txt");
+ temp.rename("virtual_machines_list.txt");
+
+ file.close();
+ temp.close();
+
+ // Update VM object
+ mVmList[mVmPos]->setState(mode);
+}
+
+void NewPalacios::stopVm() {
+ if (mVmList.isEmpty()
+ || mVmList[mVmPos]->getState() == VmInfo::STOPPED) {
+ // If the VM list is empty or the VM is already stopped
+ // or if the VM is passive
+ // return
+ showMessage(tr(ERROR_STOP_VM), false, true);
+ return;
+ }
+
+ if (mVmList[mVmPos]->getCategory() == VmInfo::INACTIVE_INVENTORY
+ || mVmList[mVmPos]->getCategory() == VmInfo::ACTIVE_NOT_INVENTORY) {
+ // If the VM is inactive or active not inventory
+ showMessage(tr(ERROR_VM_NOT_INVENTORY), true);
+ return;
+ }
+
+ QString name = NULL;
+ QMessageBox vmStopWarningBox;
+ vmStopWarningBox.setText(VM_STOP_WARNING_MESSAGE);
+ vmStopWarningBox.setIcon(QMessageBox::Warning);
+ vmStopWarningBox.setStandardButtons(
+ QMessageBox::Cancel | QMessageBox::Ok);
+ vmStopWarningBox.setDefaultButton(QMessageBox::Cancel);
+ int ret = vmStopWarningBox.exec();
+ int posInTabWidget = -1;
+ QStringList args;
+ QProcess* v3StopProc = NULL;
+
+ switch (ret) {
+ case QMessageBox::Ok:
+
+ v3StopProc = new QProcess();
+ v3StopProc->setProcessChannelMode(QProcess::MergedChannels);
+ args << mVmList[mVmPos]->getVmDevFile();
+ v3StopProc->start("v3_stop", args);
+ if (!v3StopProc->waitForFinished()) {
+ showMessage(tr(ERROR_STOP_VM_PATH), true);
+ delete v3StopProc;
+ return;
+
+ } else if (v3StopProc->readAllStandardOutput().contains("Error opening V3Vee VM device")) {
+ showMessage(tr(ERROR_STOP_VM_DEVICE_NOT_FOUND), true);
+ delete v3StopProc;
+ return;
+ }
+
+ name = mVmList[mVmPos]->getVmName();
+ for (int i=0; i<mRunningVms.length(); i++) {
+ if (name.compare(mRunningVms[i]->getVmName()) == 0) {
+ // Remove widget from tab if placed there
+ posInTabWidget = mVmControlPanel->indexOf(mRunningVms[i]);
+ if (posInTabWidget != -1) {
+ // Console is present in tab
+ mVmControlPanel->removeTab(posInTabWidget);
+ }
+ // Close the console
+ mRunningVms[i]->close();
+ // Remove it from list of running console windows
+ mRunningVms.removeAt(i);
+ break;
+ }
+ }
+
+ // Update VM state
+ updateVmState(VmInfo::STOPPED);
+
+ // Update icon
+ for (int i=0; i<mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->childCount(); i++) {
+ QTreeWidgetItem* child = mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->child(i);
+ if (mVmName.compare(child->text(0)) == 0) {
+ child->setIcon(0, QIcon(":/images/images/stop_vm.png"));
+ break;
+ }
+ }
+
+ break;
+ case QMessageBox::Cancel:
+ break;
+ }
+}
+
+void NewPalacios::pauseVm() {
+ if (mVmList.isEmpty()) {
+ return;
+ }
+
+ if (mVmList[mVmPos]->getCategory() == VmInfo::INACTIVE_INVENTORY
+ || mVmList[mVmPos]->getCategory() == VmInfo::ACTIVE_NOT_INVENTORY) {
+ showMessage(tr(ERROR_VM_NOT_INVENTORY), false, true);
+ return;
+ }
+
+ QString v3_devfile = mVmList[mVmPos]->getVmDevFile();
+ if (v3_devfile == NULL) {
+ showMessage(tr("Device file not found"), true);
+ return;
+ }
+
+ QProcess* v3Pauseproc = new QProcess();
+ v3Pauseproc->setProcessChannelMode(QProcess::MergedChannels);
+ QStringList args;
+ args << v3_devfile;
+ v3Pauseproc->start("v3_pause", args);
+ if (!v3Pauseproc->waitForFinished()) {
+ showMessage(v3Pauseproc->errorString(), true);
+ } else if (v3Pauseproc->readAllStandardOutput().contains("Error opening V3Vee VM device")) {
+ showMessage(tr(ERROR_PAUSE_VM_DEVICE_NOT_FOUND), true);
+ }
+
+ // Update VM state
+ updateVmState(VmInfo::PAUSED);
+
+ // Update icon
+ for (int i=0; i<mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->childCount(); i++) {
+ QTreeWidgetItem* child = mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->child(i);
+ if (mVmName.compare(child->text(0)) == 0) {
+ child->setIcon(0, QIcon(":/images/images/pause_vm.png"));
+ break;
+ }
+ }
+
+ delete v3Pauseproc;
+}
+
+void NewPalacios::restartVm() {
+ if (mVmList.isEmpty()) {
+ return;
+ }
+
+ if (mVmList[mVmPos]->getCategory() == VmInfo::INACTIVE_INVENTORY
+ || mVmList[mVmPos]->getCategory() == VmInfo::ACTIVE_NOT_INVENTORY) {
+ showMessage(tr(ERROR_VM_NOT_INVENTORY), false, true);
+ return;
+ }
+
+ QString v3_devfile = mVmList[mVmPos]->getVmDevFile();
+ if (v3_devfile == NULL) {
+ showMessage(tr("Device file not found"), true);
+ return;
+ }
+
+ QProcess* v3Continueproc = new QProcess();
+ v3Continueproc->setProcessChannelMode(QProcess::MergedChannels);
+ QStringList args;
+ args << v3_devfile;
+ v3Continueproc->start("v3_continue", args);
+
+ if (!v3Continueproc->waitForFinished()) {
+ showMessage(v3Continueproc->errorString(), true);
+ } else if (v3Continueproc->readAllStandardOutput().contains("Error opening V3Vee VM device")) {
+ showMessage(tr(ERROR_RESTART_VM_IOCTL), true);
+ }
+
+ // Update icon
+ for (int i=0; i<mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->childCount(); i++) {
+ QTreeWidgetItem* child = mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->child(i);
+ if (mVmName.compare(child->text(0)) == 0) {
+ child->setIcon(0, QIcon(":/images/images/start_vm.png"));
+ break;
+ }
+ }
+
+ delete v3Continueproc;
+}
+
+// Method to create a VM from inactive list
+int NewPalacios::createInactiveVm() {
+ QString vmImageFile = mVmList[mVmPos]->getImageFile();
+ QString vmName = mVmList[mVmPos]->getVmName();
+
+ // Create the VM instance
+ QProcess* v3CreateProc = new QProcess();
+ QStringList args;
+ args.clear();
+ args << vmImageFile << vmName;
+ v3CreateProc->start("v3_create", args);
+
+ if (v3CreateProc->waitForFinished()) {
+ if (v3CreateProc->readAllStandardOutput().contains("Error (-1)")) {
+ // v3_create has failed with IOCTL error
+ showMessage(tr(ERROR_VM_CREATE_IOCTL), true);
+ delete v3CreateProc;
+ return -1;
+ }
+ } else {
+ showMessage(tr(ERROR_VM_CREATE_PATH), true);
+ delete v3CreateProc;
+ return -1;
+ }
+
+ // Cleanup
+ delete v3CreateProc;
+
+ // Check the last line of /proc/v3vee/v3-guests
+ // to see the /dev/v3-vm# of the new VM
+ bool isCreated = false;
+ QProcess* proc = new QProcess();
+ proc->setProcessChannelMode(QProcess::MergedChannels);
+ proc->start("cat /proc/v3vee/v3-guests");
+
+ if (!proc->waitForFinished()) {
+ showMessage(tr(ERROR_VM_CREATE_PROC), true);
+ delete proc;
+ return -1;
+ }
+
+ //QByteArray temp = vmName.toAscii();
+ //const char* vmEntry = temp.data();
+
+ // Read standard output of process
+ QByteArray val = proc->readAllStandardOutput();
+ if (!val.isNull()) {
+ // The created VM can be defined anywhere in the /proc file
+ // we need to go over all entries till we find it
+ QList<QByteArray> procs = val.split('\n');
+ for (int i=0; i<procs.size(); i++) {
+ QList<QByteArray> temp = procs[i].split('\t');
+ QByteArray a = temp.at(0);
+ QString vmProcName(a);
+ if (vmName.compare(vmProcName) == 0) {
+ // We have found the VM, get dev file name
+ QByteArray b = temp.at(1);
+ QString vmDevFile(b);
+ vmDevFile.remove(QChar('\n'), Qt::CaseInsensitive);
+ mVmList[mVmPos]->setVmDevFile(vmDevFile);
+ // Make VM instance active
+ mVmList[mVmPos]->setCategory(VmInfo::ACTIVE_INVENTORY);
+ isCreated = true;
+ break;
+ }
+ }
+ }
+
+ if (!isCreated) {
+ // We did not find an entry in
+ // /proc file so there must have
+ // been an error in creation
+ showMessage(tr(ERROR_VM_CREATE_FAILED), true);
+ delete proc;
+ return -1;
+ }
+
+ if (proc != NULL)
+ delete proc;
+
+ return 0;
+}
+
+int NewPalacios::updateDb(int cat) {
+ QString vm_name = mVmList[mVmPos]->getVmName();
+
+ QFile file("virtual_machines_list.txt");
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ showMessage("Error opening DB! Please restart application", true);
+ return -1;
+ }
+
+ QFile temp("temp.txt");
+ if (!temp.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ showMessage("Error opening DB! Please restart application", true);
+ return -1;
+ }
+
+ QTextStream in(&file);
+ QTextStream out(&temp);
+
+
+ if (cat == VmInfo::INACTIVE_INVENTORY) {
+ // If we are updating an inactive entry
+ // search the file and update
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (line.compare("\n") == 0) {
+ continue;
+ }
+ QStringList vmDet = line.split(",");
+ QString nameStr = vmDet.at(0);
+
+ if (vm_name.compare(nameStr) != 0) {
+ // If the name does not match, then copy to new place
+ out << line << endl;
+ } else {
+ // Update DB state
+ VmInfo* v = mVmList[mVmPos];
+ QString updateLine = v->getVmName()
+ + "," + v->getVmConfigFile()
+ + "," + v->getVmDevFile()
+ + "," + QString::number(VmInfo::STOPPED)
+ + "," + v->getImageFile();
+ out << updateLine << endl;
+ }
+ }
+ } else if (cat == VmInfo::ACTIVE_NOT_INVENTORY) {
+
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (line.compare("\n") == 0) {
+ continue;
+ }
+
+ // Copy all the contents into new file
+ out << line << endl;
+ }
+
+ // Copy the new VM info
+ VmInfo* vv = mVmList[mVmPos];
+ QString updateLine = vv->getVmName()
+ + "," + vv->getVmConfigFile()
+ + "," + vv->getVmDevFile()
+ + "," + QString::number(VmInfo::STOPPED)
+ + "," + vv->getImageFile();
+ out << updateLine << endl;
+ }
+
+ out.flush();
+ file.remove("virtual_machines_list.txt");
+ temp.rename("virtual_machines_list.txt");
+
+ file.close();
+ temp.close();
+
+ return 0;
+}
+
+void NewPalacios::activateVm() {
+ // 1. Check VM category
+ // 1a. If VM is active but not in inventory, upgrade category
+ // 1b. If VM is inactive, create VM and upgrade category
+ QTreeWidgetItem* item = NULL;
+
+ // Remove VM from inactive list
+ // The list could be from either the inactive inventory or
+ // active not inventory
+ int category = mVmList[mVmPos]->getCategory();
+
+ if (category == VmInfo::ACTIVE_NOT_INVENTORY) {
+ // The VM exists in the /proc file and
+ // is already created. We will only update
+ // VM category.
+
+ // Ask user for Config file and Image file
+ QString configFile = QFileDialog::getOpenFileName(this, tr("Select config file"), ".",
+ "XML file (*.xml)");
+ if (configFile == NULL) {
+ // User pressed cancel
+ return;
+ }
+ QString imageFile = QFileDialog::getOpenFileName(this, tr("Select image file"), ".",
+ "Image file (*.img *.bZ)");
+ if (imageFile == NULL) {
+ // User pressed cancel
+ return;
+ }
+
+ mVmList[mVmPos]->setVmConfigFile(configFile);
+ mVmList[mVmPos]->setImageFile(imageFile);
+
+ // Remove from active not inventory list
+ for (int i=0; i<mVmTreeView->topLevelItem(VmInfo::ACTIVE_NOT_INVENTORY)->childCount(); i++) {
+ item = mVmTreeView->topLevelItem(VmInfo::ACTIVE_NOT_INVENTORY)->child(i);
+ if (item->text(0).compare(mVmList[mVmPos]->getVmName()) == 0) {
+ item = mVmTreeView->topLevelItem(VmInfo::ACTIVE_NOT_INVENTORY)->takeChild(i);
+ break;
+ }
+ }
+
+ // Add it to active list
+ QTreeWidgetItem* subParent = mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY);
+ subParent->addChild(item);
+
+ // Update Category
+ mVmList[mVmPos]->setCategory(VmInfo::ACTIVE_INVENTORY);
+
+ // Update DB with newly created entry
+ updateDb(VmInfo::ACTIVE_NOT_INVENTORY);
+ return;
+
+ } else if (category == VmInfo::INACTIVE_INVENTORY) {
+ // The VM is in the inventory but inactive.
+ // This means that we need to create it.
+ int ret = createInactiveVm();
+
+ if (ret != 0) {
+ // VM was not created due to some problem
+ // TODO: Decide if you want to delete the reference
+ // from the text file or keep it there for a while and
+ // try again
+ return;
+ }
+
+ // Once VM is created, we need to first remove it from the
+ // inactive list and add it to active list
+ for (int i=0; i<mVmTreeView->topLevelItem(VmInfo::INACTIVE_INVENTORY)->childCount(); i++) {
+ item = mVmTreeView->topLevelItem(VmInfo::INACTIVE_INVENTORY)->child(i);
+ if (item->text(0).compare(mVmList[mVmPos]->getVmName()) == 0) {
+ item = mVmTreeView->topLevelItem(VmInfo::INACTIVE_INVENTORY)->takeChild(i);
+ break;
+ }
+ }
+
+ // Add it to active list
+ QTreeWidgetItem* subParent = mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY);
+ subParent->addChild(item);
+
+ // Update DB with newly created entry
+ updateDb(VmInfo::INACTIVE_INVENTORY);
+ }
+}
+
+void NewPalacios::reloadVms() {
+ // Clear all previous entries
+ for (int i=0; i<mVmTreeView->topLevelItemCount(); i++) {
+ qDeleteAll(mVmTreeView->topLevelItem(i)->takeChildren());
+ }
+
+ // Call load VM thread
+ readExistingVmsFile();
+}
+
+// This handler is used in serial mode when user clicks
+// the close button of the floating window
+void NewPalacios::consoleWindowClosed(QString name) {
+ // Remove the console from the list of
+ // running consoles
+ for (int i=0; i < mRunningVms.length(); i++) {
+ if (name.compare(mRunningVms[i]->getVmName()) == 0) {
+ mRunningVms.removeAt(i);
+ break;
+ }
+ }
+}
+
+// This handler is used in v3_cons_sc mode when the user clicks
+// the close button on the tab widget
+void NewPalacios::vmTabClosed(int index) {
+ if (index == 0) {
+ // As of now do not remove the info tab
+ return;
+ }
+
+ // Get the name of the VM tab which we need to close
+ // Need to cast the return value of TabWidget->widget as it returns an object
+ // of the base class. static_cast is used because the conversion is from base
+ // class object to derived class object
+ VmXConsoleParent* widget = static_cast<VmXConsoleParent*>(mVmControlPanel->widget(index));
+ QString name = widget->getVmName();
+ for (int i=0; i<mRunningVms.length(); i++) {
+ if (name.compare(mRunningVms[i]->getVmName()) == 0) {
+ // Remove the tab
+ mVmControlPanel->removeTab(index);
+ // Remove the console from running vm instances
+ mRunningVms.removeAt(i);
+ // Send close event to widget
+ widget->close();
+ break;
+ }
+ }
+}
+
+/* This function reads from the vm list file */
+void NewPalacios::readExistingVmsFile() {
+ mThread = new QThread();
+ mLoadVmsThread = new LoadVmsThread();
+ mLoadVmsThread->moveToThread(mThread);
+ connect(mThread, SIGNAL(started()), mLoadVmsThread, SLOT(loadVms()));
+ connect(mLoadVmsThread, SIGNAL(finished()), mThread, SLOT(quit()));
+ // Action which listens to completion of initial VM loading
+ connect(mLoadVmsThread, SIGNAL(finished()), this,
+ SLOT(finishLoadingVmsFromFile()));
+ mThread->start();
+}
+
+// Update UI with VMs loaded from file
+void NewPalacios::finishLoadingVmsFromFile() {
+ if (mLoadVmsThread->getStatus() == LoadVmsThread::STATUS_OK) {
+ mVmList.clear();
+ mVmPos = 0;
+
+ int category = -1;
+
+ // Load VMs into memory
+ mVmList.append(mLoadVmsThread->getVmList());
+
+ QTreeWidgetItem* item = NULL;
+ for (int i = 0; i < mVmList.size(); i++) {
+ if (mVmList[i]->getCategory() == VmInfo::ACTIVE_INVENTORY) {
+ item = new QTreeWidgetItem();
+ item->setText(0, mVmList[i]->getVmName());
+
+ category = mVmList[i]->getState();
+ switch (category) {
+ case VmInfo::STOPPED:
+ item->setIcon(0, QIcon(":/images/images/stop_vm.png"));
+ break;
+ case VmInfo::PAUSED:
+ item->setIcon(0, QIcon(":/images/images/pause_vm.png"));
+ break;
+ case VmInfo::RUNNING:
+ item->setIcon(0, QIcon(":/images/images/start_vm.png"));
+ break;
+ }
+
+ mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY)->addChild(item);
+
+ } else if (mVmList[i]->getCategory() == VmInfo::INACTIVE_INVENTORY) {
+ item = new QTreeWidgetItem();
+ item->setText(0, mVmList[i]->getVmName());
+ mVmTreeView->topLevelItem(VmInfo::INACTIVE_INVENTORY)->addChild(item);
+
+ } else if (mVmList[i]->getCategory() == VmInfo::ACTIVE_NOT_INVENTORY) {
+ item = new QTreeWidgetItem();
+ item->setText(0, mVmList[i]->getVmName());
+ mVmTreeView->topLevelItem(VmInfo::ACTIVE_NOT_INVENTORY)->addChild(item);
+ }
+ }
+
+ mVmControlPanel->insertTab(0, mVmInfoWidget, tr(VM_TAB_TITLE));
+
+ if (mThread != NULL)
+ delete mThread;
+
+ }
+}
+
+/* This method is called after completion of the VM wizard
+ * It updates the backend vm file with new vm */
+void NewPalacios::addNewVm() {
+ QString name = mVmWizard->field("guestName").toString();
+ QString file = mVmWizard->field("configLoc").toString();
+ QFileInfo f(mVmWizard->field("imageLoc").toString());
+ QString img = f.fileName();
+
+ mThread = new QThread();
+ mAddVmThread = new AddVmThread(name, file, img);
+ mAddVmThread->moveToThread(mThread);
+ connect(mThread, SIGNAL(started()), mAddVmThread, SLOT(addVm()));
+ connect(mAddVmThread, SIGNAL(finished()), mThread, SLOT(quit()));
+ // Call method on window to update the vm list
+ connect(mAddVmThread, SIGNAL(finished()), this, SLOT(updateVmList()));
+ mThread->start();
+}
+
+/* Update UI with new VM added to list */
+void NewPalacios::updateVmList() {
+ if(mAddVmThread != NULL && mAddVmThread->getStatus()
+ == AddVmThread::ERROR_V3CREATE_DB) {
+ showMessage(tr(ERROR_VM_CREATE_DB), true);
+
+ } else if (mAddVmThread != NULL &&
+ mAddVmThread->getStatus() == AddVmThread::STATUS_OK) {
+ // Newly created VM is inactive but in inventory
+ showMessage(tr(SUCCESS_VM_ADDED), false);
+ mVmList.append(mAddVmThread->getNewVm());
+ QTreeWidgetItem* item = new QTreeWidgetItem();
+ item->setText(0, mAddVmThread->getName());
+
+ QTreeWidgetItem* subParent = mVmTreeView->topLevelItem(VmInfo::INACTIVE_INVENTORY);
+ subParent->addChild(item);
+ }
+
+ if (mThread != NULL)
+ delete mThread;
+}
+
+/* Handler to delete VM instance from list and backend file */
+void NewPalacios::deleteVm(QString item) {
+ int category = -1;
+ QString devfile;
+
+ for (int i = 0; i < mVmList.size(); i++) {
+ if (item.compare(mVmList[i]->getVmName()) == 0) {
+ category = mVmList[i]->getCategory();
+ devfile = mVmList[i]->getVmDevFile();
+ break;
+ }
+ }
+
+ mThread = new QThread();
+ mDeleteVmThread = new DeleteVmThread(category, item, devfile);
+ mDeleteVmThread->moveToThread(mThread);
+ connect(mThread, SIGNAL(started()), mDeleteVmThread, SLOT(deleteVm()));
+ connect(mDeleteVmThread, SIGNAL(finished()), mThread, SLOT(quit()));
+ connect(mDeleteVmThread, SIGNAL(finished()), this, SLOT(handleVmDeletion()));
+ mThread->start();
+}
+
+void NewPalacios::handleVmDeletion() {
+ if (mDeleteVmThread->getStatus()
+ == DeleteVmThread::ERROR_V3FREE_PATH) {
+ showMessage(tr(ERROR_VM_DELETE_PATH), true);
+
+ } else if (mDeleteVmThread->getStatus()
+ == DeleteVmThread::ERROR_V3FREE_IOCTL) {
+ showMessage(tr(ERROR_VM_DELETE_IOCTL), true);
+
+ } else if (mDeleteVmThread->getStatus()
+ == DeleteVmThread::ERROR_V3FREE_DB) {
+ showMessage(tr(ERROR_VM_DELETE_DB), true);
+
+ } else if (mDeleteVmThread->getStatus()
+ == DeleteVmThread::ERROR_V3FREE_INVALID_ARGUMENT) {
+ showMessage(tr(ERROR_VM_DELETE_INVALID_ARGUMENT), true);
+
+ } else if (mDeleteVmThread->getStatus()
+ == DeleteVmThread::STATUS_OK) {
+ // Delete VM from list
+ for (int i = 0; i < mVmList.size(); i++) {
+ if (mVmName.compare(mVmList[i]->getVmName()) == 0) {
+ // Deletion is a little tricky
+ // 1. Find the VM to be deleted and get its name
+ // 2. Remove VM from list
+ // 3. Determine the category
+ // 4. Search the appropriate category for the VM
+ // 5. Remove from UI widget and delete
+ int cat = mVmList[i]->getCategory();
+ mVmList.removeAt(i);
+
+ if (cat == VmInfo::ACTIVE_INVENTORY) {
+ QTreeWidgetItem* activeItems = mVmTreeView->topLevelItem(VmInfo::ACTIVE_INVENTORY);
+ for (int j=0; j<activeItems->childCount(); j++) {
+ QString n = activeItems->child(j)->text(0);
+ if (mVmName.compare(n) == 0) {
+ QTreeWidgetItem* achild = activeItems->takeChild(j);
+ delete achild;
+ break;
+ }
+
+ }
+ } else if (cat == VmInfo::INACTIVE_INVENTORY) {
+ QTreeWidgetItem* inactiveItems = mVmTreeView->topLevelItem(VmInfo::INACTIVE_INVENTORY);
+ for (int j=0; j<inactiveItems->childCount(); j++) {
+ QString in = inactiveItems->child(j)->text(0);
+ if (mVmName.compare(in) == 0) {
+ QTreeWidgetItem* ichild = inactiveItems->takeChild(j);
+ delete ichild;
+ break;
+ }
+ }
+ } else if (cat == VmInfo::ACTIVE_NOT_INVENTORY) {
+ QTreeWidgetItem* activeNotInvItems = mVmTreeView->topLevelItem(VmInfo::ACTIVE_NOT_INVENTORY);
+ for (int j=0; j<activeNotInvItems->childCount(); j++) {
+ QString an = activeNotInvItems->child(j)->text(0);
+ if (mVmName.compare(an) == 0) {
+ QTreeWidgetItem* aichild = activeNotInvItems->takeChild(j);
+ delete aichild;
+ break;
+ }
+ }
+
+ }
+
+ // Clear VM from Info widget view
+ mVmInfoWidget->deleteVm();
+ break;
+ }
+ }
+
+ showMessage(tr(SUCCESS_VM_DELETE), false);
+ }
+
+ if (mThread != NULL)
+ delete mThread;
+}
--- /dev/null
+#ifndef NEWPALACIOS_H
+#define NEWPALACIOS_H
+
+#include <QtGui>
+#include <QApplication>
+#include <QMainWindow>
+#include <QWidget>
+#include <QListWidget>
+#include <QTableWidget>
+#include <QVBoxLayout>
+#include <QTextEdit>
+#include <QFile>
+#include <QTextStream>
+#include <QList>
+#include <QAction>
+#include <QMenu>
+#include <QMenuBar>
+#include <QToolBar>
+#include <QStatusBar>
+#include <QProcess>
+#include <QObject>
+#include <QDomDocument>
+#include <QDomNode>
+#include <QDomNodeList>
+#include <QDomElement>
+#include <QDomText>
+#include <QDomAttr>
+#include <QDomNamedNodeMap>
+
+#include "vm_creation_wizard.h"
+#include "vm_console_widget.h"
+
+class VmInfo;
+class VmInfoWidget;
+class VmXConsoleParent;
+class VmVncWidget;
+class LoadVmsThread;
+class AddVmThread;
+class DeleteVmThread;
+class VmModeDialog;
+
+class NewPalacios: public QMainWindow {
+Q_OBJECT
+
+public:
+ NewPalacios(QWidget *parent = 0);
+ ~NewPalacios();
+
+protected:
+ void closeEvent(QCloseEvent*);
+
+private slots:
+ void createVmInstance();
+ void aboutPalacios();
+ void exitApplication();
+ void startVm();
+ void stopVm();
+ void pauseVm();
+ void restartVm();
+ void activateVm();
+ void reloadVms();
+ // Item click listener for tree items
+ void vmItemClickListener(QTreeWidgetItem*, int);
+ void finishLoadingVmsFromFile();
+ void addNewVm();
+ void updateVmList();
+ void deleteVm(QString);
+ void handleVmDeletion();
+ // Context menu for VMs
+ void vmContextMenu(const QPoint &p);
+ void consoleWindowClosed(QString);
+ void vmTabClosed(int);
+ // This slot will be triggered when play/menu start
+ // button is pressed. This will launch a dialog which
+ // will allow the user to select operating mode
+ void selectVmMode();
+ // This slot will be triggered from the mode selection
+ // dialog when the user has finished selection.
+ void getVmMode(int, QString);
+ void updateTelemetry(bool);
+ void updateTelemetryView();
+
+private slots:
+ // These are used for debugging QProcess commands
+ //void processStarted();
+ //void processExit(int, QProcess::ExitStatus);
+ //void processError(QProcess::ProcessError);
+
+signals:
+ void vmLoadingComplete();
+
+public:
+ void readExistingVmsFile();
+ // Check for Palacios setup
+ void checkPalacios();
+
+private:
+ // Functions to setup different components of the window
+ void createCentralWidget();
+ void createActions();
+ void createMenus();
+ void createToolBars();
+ void createStatusBar();
+ void createDockWindows();
+ void createWizard();
+ void createEventHandler();
+ // Show error message boxes
+ void showMessage(QString err, bool error, bool warning=false);
+ // Update state of VM
+ void updateVmState(int);
+ // Create inactive VM
+ int createInactiveVm();
+ // Update DB
+ int updateDb(int);
+
+private:
+ // Main view of the application.
+ QTabWidget* mVmControlPanel;
+ VmInfoWidget* mVmInfoWidget;
+ // This is used to represent the classes of VM
+ QTreeWidget* mVmTreeView;
+ QTextEdit* mVmTelemetryView;
+
+ // Progress dialog to inform user about background thread processing
+ //QProgressDialog* mProgress;
+
+ // Runs Palacios in terminal mode
+ QList<VmXConsoleParent*> mRunningVms;
+
+ bool mExitAppFromMenu;
+ // Used to identify VMs inside list
+ int mVmPos;
+ QString mVmName;
+
+ // Action variables used to handle events such as menu clicks, button clicks etc.
+ QAction* mExitApp;
+ QAction* mVmNew;
+ QAction* mVmStop;
+ QAction* mVmPause;
+ QAction* mVmStart;
+ QAction* mVmActivate;
+ QAction* mReloadVms;
+ QAction* mAboutApp;
+
+ // Menu variables
+ QMenu* mFileMenu;
+ QMenu* mViewMenu;
+ QMenu* mVmMenu;
+ QMenu* mHelpMenu;
+
+ // Toolbar variables
+ QToolBar* mVmToolBar;
+ QToolBar* mVmCtrlToolBar;
+
+ // This dialog will be used to give option
+ // to user to select from three modes of
+ // operation
+ VmModeDialog* mVmModeDialog;
+ int mVmMode;
+ QString mStreamName;
+ bool isHeaderClicked;
+
+ // Process to read kernel logs
+ QProcess* mLogProc;
+
+ // Wizard to help in vm creation
+ NewVmWizard* mVmWizard;
+
+ // List of created VMs. Each VM object contains information parsed from
+ // the config files provided as part of VM creation
+ QList<VmInfo*> mVmList;
+ //QList<VM*> mVmList;
+
+ /* We save the information about VMs in a text file. We store the name of the VM instance
+ * and the path of the configuration file used to create the VM. Every time we create/add/delete
+ * a VM instance, this file is edited. This is simplest way as of now to store this information.
+ * If in future we need more information to be stored we could use a database and Qt's
+ * model-view system */
+
+ QThread* mThread;
+
+ // Thread to load existing VM intances
+ LoadVmsThread* mLoadVmsThread;
+ // Thread to add a new VM instance
+ AddVmThread* mAddVmThread;
+ // Thread to delete a VM instance
+ DeleteVmThread* mDeleteVmThread;
+
+ // Telemetry process
+ QProcess* mTelemProc;
+};
+
+class VmModeDialog : public QWidget {
+Q_OBJECT
+public:
+ VmModeDialog(QWidget* parent = 0);
+
+private slots:
+ void selectMode(bool);
+ void okButton();
+ void cancelButton();
+
+signals:
+ // This signal will be caught in the main window
+ // and the mode will be set accordingly
+ void setMode(int, QString);
+
+private:
+ void setupDialog();
+
+private:
+ bool isV3Cons;
+ bool isV3Stream;
+ bool isV3Vnc;
+ int mode;
+ QGroupBox* v3_modes;
+ QRadioButton* v3_stream;
+ QRadioButton* v3_cons;
+ QRadioButton* v3_vnc;
+ QWidget* v3_stream_info;
+ QLineEdit* v3_stream_name;
+};
+
+// Class to hold information about a VM instance
+class VmInfo {
+public:
+ VmInfo() {
+ }
+
+ ~VmInfo() {
+ }
+
+ // Tells us about the state of the VM
+ enum {
+ STOPPED, PAUSED, RUNNING
+ };
+
+ // Tells about the category of VM
+ enum {
+ ACTIVE_INVENTORY, INACTIVE_INVENTORY, ACTIVE_NOT_INVENTORY
+ };
+
+ // Return state of VM
+ int getState() {
+ return mVmState;
+ }
+
+ // Ser state of VM
+ void setState(int state) {
+ this->mVmState = state;
+ }
+
+ void setCategory(int cat) {
+ this->mVmCategory = cat;
+ }
+
+ int getCategory() {
+ return mVmCategory;
+ }
+
+ void setImageFile(QString img) {
+ this->mVmImageFile = img;
+ }
+
+ QString getImageFile() {
+ return mVmImageFile;
+ }
+
+ QString getVmName() {
+ return mVmName;
+ }
+
+ void setVmName(QString name) {
+ this->mVmName = name;
+ }
+
+ QString getVmDevFile() {
+ return mVmDevFile;
+ }
+
+ void setVmDevFile(QString name) {
+ this->mVmDevFile = name;
+ }
+
+ QString getVmConfigFile() {
+ return this->mVmConfigFile;
+ }
+
+ void setVmConfigFile(QString file) {
+ this->mVmConfigFile = file;
+ }
+
+private:
+ int mVmState;
+ int mVmCategory;
+ QString mVmName;
+ QString mVmDevFile;
+ QString mVmImageFile;
+ QString mVmConfigFile;
+};
+
+class VmInfoWidget: public QWidget {
+Q_OBJECT
+public:
+ VmInfoWidget(QWidget* parent = 0);
+ ~VmInfoWidget();
+
+private:
+ void setInfoView();
+ void setupUi();
+
+public:
+ void parseElement(const QDomElement&, QTreeWidgetItem*);
+ void parseAttribute(const QDomElement&, QTreeWidgetItem*);
+ void parseText(const QDomElement&, QTreeWidgetItem*);
+ void updateInfoView(VmInfo* vm);
+ void deleteVm();
+
+private:
+ QTreeWidget* mVmInfoView;
+};
+
+class VmXConsoleParent: public QWidget {
+Q_OBJECT
+public:
+ VmXConsoleParent(QString name, QWidget* parent = 0);
+ void showWidget(int mode, QString devfile, QString streamName);
+ void showTelemetryInfo();
+ bool isRunning();
+ QString getVmName();
+
+signals:
+ void windowClosingWithId(QString name);
+ void windowClosing();
+
+protected:
+ void closeEvent(QCloseEvent* event);
+
+private:
+ bool mIsConsoleRunning;
+ QString mVmName;
+ VmConsoleWidget* mConsole;
+};
+
+class LoadVmsThread: public QObject {
+Q_OBJECT
+public:
+ static const int STATUS_OK = 0;
+ static const int ERROR_FILE_CANNOT_BE_OPENED = -1;
+
+ int getStatus();
+ QList<VmInfo*> getVmList();
+
+signals:
+ void finished();
+
+public slots:
+ void loadVms();
+private:
+ int status;
+ QList<VmInfo*> list;
+};
+
+class AddVmThread: public QObject {
+Q_OBJECT
+public:
+ static const int STATUS_OK = 0;
+ static const int ERROR_V3CREATE_PATH = -1;
+ static const int ERROR_V3CREATE_IOCTL = -2;
+ static const int ERROR_V3CREATE_PROC = -3;
+ static const int ERROR_V3CREATE_DEV = -4;
+ static const int ERROR_V3CREATE_DB = -5;
+
+ AddVmThread(QString name, QString conf, QString img);
+ int getStatus();
+ QString getName();
+ VmInfo* getNewVm();
+
+signals:
+ void finished();
+
+public slots:
+ void addVm();
+
+private:
+ int status;
+ QString vmName;
+ QString vmDevFile;
+ QString vmConfigFile;
+ QString vmImageFile;
+ VmInfo* vm;
+};
+
+class DeleteVmThread: public QObject {
+Q_OBJECT
+public:
+ static const int STATUS_OK = 0;
+ static const int ERROR_V3FREE_PATH = -1;
+ static const int ERROR_V3FREE_IOCTL = -2;
+ static const int ERROR_V3FREE_DB = -3;
+ static const int ERROR_V3FREE_INVALID_ARGUMENT = -4;
+
+ DeleteVmThread(int, QString, QString);
+ int getStatus();
+
+signals:
+ void finished();
+
+public slots:
+ void deleteVm();
+
+private slots:
+ // These are used for debugging QProcess commands
+ //void processStarted();
+ //void processExit(int, QProcess::ExitStatus);
+ //void processError(QProcess::ProcessError);
+
+private:
+ int status;
+ int vmCategory;
+ QString vmToDelete;
+ QString vmDevfile;
+};
+
+#endif // NEWPALACIOS_H
--- /dev/null
+/*
+ * vm_console_widget.cpp
+ *
+ * Created on: Oct 4, 2012
+ * Author: abhinav
+ */
+
+#include "vm_console_widget.h"
+
+#include <QDebug>
+#include <QMessageBox>
+#include <QVBoxLayout>
+#include <X11/Xlib.h>
+
+VmConsoleWidget::VmConsoleWidget(QWidget* parent) :
+ QWidget(parent), mCols(100), mRows(25), mTermProcess(0) {
+ mTermProcess = NULL;
+ mVncServer = NULL;
+}
+
+VmConsoleWidget::~VmConsoleWidget() {
+
+}
+
+bool VmConsoleWidget::tryTerminate() {
+ if (mTermProcess == NULL) {
+ return true;
+ }
+
+ if (mTermProcess != NULL && mTermProcess->state() == QProcess::Running) {
+ mTermProcess->terminate();
+ bool xwindow_closed = mTermProcess->waitForFinished();
+ delete mTermProcess;
+ return xwindow_closed;
+
+ } else if (mVncView != NULL) {
+ mVncServer->terminate();
+ bool vncserver_exited = mVncServer->waitForFinished();
+ delete mVncView;
+ delete mVncServer;
+ return vncserver_exited;
+ }
+
+ return true;
+}
+
+void VmConsoleWidget::mainWindowClosing() {
+ close();
+}
+
+void VmConsoleWidget::closeEvent(QCloseEvent* e) {
+ if (!tryTerminate())
+ qDebug() << "Warning: could not terminate process...there could be a leak";
+ else
+ qDebug() << "Process terminated";
+
+ e->accept();
+}
+
+void VmConsoleWidget::resizeEvent(QResizeEvent* re) {
+ QWidget::resizeEvent(re);
+
+ if (mTermProcess == NULL)
+ return;
+
+ // Search for xterm window and update its size
+ Display *dsp = XOpenDisplay(NULL);
+ Window wnd = winId();
+
+ bool childFound = false;
+ while (!childFound && mTermProcess->state() == QProcess::Running) {
+ Window root, parent, *children;
+ uint numwin;
+ XQueryTree(dsp, wnd, &root, &parent, &children, &numwin);
+ childFound = (children != NULL);
+
+ if (childFound) {
+ XResizeWindow(dsp, *children, width(), height());
+ XFree(children);
+ }
+ }
+
+ XCloseDisplay(dsp);
+}
+
+bool VmConsoleWidget::isRunning() {
+ if (mTermProcess == NULL) {
+ return false;
+ }
+
+ // FIXME: This function is currently very unreliable
+ // The QProcess->state() function should return the correct
+ // state of the process but it is currently not working as expected
+ // Debugging with GDB also does not reveal the problem
+ // Maybe the way this method is being called is incorrect
+ return (mTermProcess->state() == QProcess::Running) ? true : false;
+}
+
+bool VmConsoleWidget::start(int mode, QString devfile, QString streamName) {
+ qDebug() << "VmConsoleWidget : start() [" << streamName << "]";
+
+ // Start VM in correct mode
+ if (mode == VmConsoleWidget::STREAM) {
+ mVmMode = "v3_stream";
+ } else if (mode == VmConsoleWidget::CONSOLE) {
+ mVmMode = "v3_cons_sc";
+ } else {
+ mVmMode = "v3_vncclient";
+ }
+
+ qDebug() << "Vm Mode: " << mVmMode;
+ qDebug() << "Vm dev file: " << devfile;
+
+ QStringList args;
+
+ if (streamName.compare("") != 0) {
+ // This is v3_stream mode
+ args << "-sb" << "-geometry" << QString("%1x%2").arg(mCols).arg(mRows) << "-j"
+ << "-into" << QString::number(winId()) << "-e" << mVmMode << devfile << streamName;
+
+ // Start the xterm process
+ qDebug() << "Starting terminal with arguments '" << args.join(" ");
+ mTermProcess = new QProcess();
+
+ // Connect signals and slots
+ connect(mTermProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this,
+ SLOT(termProcessExited(int, QProcess::ExitStatus)));
+
+ connect(mTermProcess, SIGNAL(error(QProcess::ProcessError)), this,
+ SLOT(errorMessage(QProcess::ProcessError)));
+
+ mTermProcess->start("/usr/bin/xterm", args);
+
+ } else {
+ // This is non-stream mode
+
+ if (mVmMode.compare("v3_cons_sc") == 0) {
+ // Console mode
+ args << "-sb" << "-geometry" << QString("%1x%2").arg(mCols).arg(mRows) << "-j"
+ << "-into" << QString::number(winId()) << "-e" << mVmMode << devfile;
+
+
+ qDebug() << "Starting terminal with arguments '" << args.join(" ");
+ mTermProcess = new QProcess();
+
+ // Connect signals and slots
+ connect(mTermProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this,
+ SLOT(termProcessExited(int, QProcess::ExitStatus)));
+
+ connect(mTermProcess, SIGNAL(error(QProcess::ProcessError)), this,
+ SLOT(errorMessage(QProcess::ProcessError)));
+
+ mTermProcess->start("/usr/bin/xterm", args);
+
+ } else {
+ // VNC mode
+ mVncServer = new QProcess();
+ mVncServer->setProcessChannelMode(QProcess::MergedChannels);
+
+ connect(mVncServer, SIGNAL(finished(int, QProcess::ExitStatus)), this,
+ SLOT(termProcessExited(int, QProcess::ExitStatus)));
+
+ connect(mVncServer, SIGNAL(error(QProcess::ProcessError)), this,
+ SLOT(errorMessage(QProcess::ProcessError)));
+
+ QStringList args;
+ args << "--port=5951" << "--password=test123" << devfile;
+ qDebug() << "arguments: " << args.join(" ");
+ mVncServer->start("v3_vncserver", args);
+
+ if (!mVncServer->waitForFinished()) {
+ qDebug() << "Error with VNC server";
+ QMessageBox msg;
+ msg.setText("Error with VNC server!");
+ msg.setStandardButtons(QMessageBox::Ok);
+ msg.setIcon(QMessageBox::Critical);
+ msg.exec();
+ return false;
+ }
+
+ //args << "-sb" << "-geometry" << QString("%1x%2").arg(mCols).arg(mRows) << "-j"
+ // << "-into" << QString::number(winId()) << "-e" << "vncviewer localhost:5951";
+
+ QVBoxLayout* vncBox = new QVBoxLayout();
+ mVncView = new VncView(this, QUrl("vnc://localhost:5951"), KConfigGroup());
+ //mVncView = new VncView(this, QUrl("vnc://localhost:5901"), KConfigGroup());
+ vncBox->addWidget(mVncView);
+ this->setLayout(vncBox);
+
+ mVncView->show();
+ mVncView->start();
+ }
+ }
+
+ // Flag to indicate success and failure
+ bool status = false;
+
+ if (mVmMode.compare("v3_vncclient") == 0) {
+ // This is VNC mode
+ status = true;
+
+ } else {
+ int success;
+
+ // This is v3_stream or v3_cons_sc mode
+ if (mTermProcess != NULL && mTermProcess->waitForStarted()) {
+ success = 0;
+ qDebug() << "Process started";
+ } else {
+ success = -1;
+ qDebug() << "Process did not start";
+ }
+
+ if (success == 0) {
+ /* Wait for the xterm window to be opened and resize it
+ * to our own widget size.
+ */
+ Display *dsp = XOpenDisplay(NULL);
+ Window wnd = winId();
+
+ bool childFound = false;
+ while (!childFound && mTermProcess->state() == QProcess::Running) {
+ Window root, parent, *children;
+ uint numwin;
+ XQueryTree(dsp, wnd, &root, &parent, &children, &numwin);
+ childFound = (children != NULL);
+
+ if (childFound) {
+ XResizeWindow(dsp, *children, width(), height());
+ }
+ }
+
+ XCloseDisplay(dsp);
+
+ if (!childFound)
+ success = -2;
+ }
+
+ if (success < 0) {
+ qDebug() << (success == -1 ? "Starting the process failed"
+ : "Process started, but exited before opening a terminal");
+
+ if (success < -1)
+ tryTerminate();
+ }
+
+ status = (success == 0);
+ }
+
+ return status;
+}
+
+void VmConsoleWidget::termProcessExited(int a, QProcess::ExitStatus b) {
+ qDebug() << "Term Process Exited: " << a << " : " << b;
+
+ if (mTermProcess != NULL) {
+ delete mTermProcess;
+ }
+
+ if (mVncServer != NULL) {
+ delete mVncServer;
+ }
+
+ mTermProcess = NULL;
+ mVncServer = NULL;
+
+ emit exited();
+}
+
+void VmConsoleWidget::errorMessage(QProcess::ProcessError err) {
+ qDebug() << "Process error: " << err;
+}
--- /dev/null
+/*
+ * vm_console_widget.h
+ *
+ * Created on: Oct 5, 2012
+ * Author: abhinav
+ */
+
+#ifndef VM_CONSOLE_WIDGET_H_
+#define VM_CONSOLE_WIDGET_H_
+
+#include "vnc_module/vncview.h"
+
+#include <QProcess>
+#include <QWidget>
+#include <QCloseEvent>
+
+class VmConsoleWidget: public QWidget {
+Q_OBJECT
+
+public:
+ // Different operation modes of VM
+ enum {
+ STREAM, CONSOLE, VNC
+ };
+
+ VmConsoleWidget(QWidget* parent = 0);
+ virtual ~VmConsoleWidget();
+ bool isRunning();
+
+public slots:
+ bool start(int mode, QString devfile, QString streamName);
+ bool tryTerminate();
+ void mainWindowClosing();
+
+protected slots:
+ void termProcessExited(int, QProcess::ExitStatus);
+ void errorMessage(QProcess::ProcessError);
+
+signals:
+ void exited();
+
+protected:
+ void closeEvent(QCloseEvent *);
+ void resizeEvent(QResizeEvent *);
+
+private:
+ int mCols;
+ int mRows;
+ QString mVmMode;
+ // Process for xterm
+ QProcess* mTermProcess;
+ // VNC
+ QProcess* mVncServer;
+ VncView* mVncView;
+
+};
+
+
+#endif /* VM_CONSOLE_WIDGET_H_ */
--- /dev/null
+/*
+ * newvmwizard.cpp
+ *
+ * Created on: Sep 20, 2012
+ * Author: abhinav
+ */
+
+#include <QMessageBox>
+#include "vm_creation_wizard.h"
+
+NewVmWizard::NewVmWizard(QWidget* parent) :
+ QWizard(parent) {
+
+ setPage(Page_Intro, new IntroPage);
+ setPage(Page_Image_File, new GuestImagePage);
+ setPage(Page_Final, new FinalPage);
+ setStartId(Page_Intro);
+
+ setOptions(QWizard::NoBackButtonOnLastPage);
+ setWindowTitle(tr("New Virtual Machine"));
+}
+
+NewVmWizard::~NewVmWizard() {
+}
+
+IntroPage::IntroPage(QWidget* parent) :
+ QWizardPage(parent) {
+ setTitle(tr("Introduction"));
+ setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/images/palacios.png"));
+
+ topLabel = new QLabel(
+ tr("This wizard will help you to create a new virtual machine"));
+ topLabel->setWordWrap(true);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(topLabel);
+ setLayout(layout);
+}
+
+IntroPage::~IntroPage() {
+ delete topLabel;
+}
+
+int IntroPage::nextId() const {
+ return NewVmWizard::Page_Image_File;
+}
+
+GuestImagePage::GuestImagePage(QWidget* parent) :
+ QWizardPage(parent) {
+ setTitle(tr("VM Creation"));
+ setSubTitle(
+ tr(
+ "Please fill all the fields. Make sure all file paths are correct"));
+
+ guestNameLabel = new QLabel(tr("Enter a name for the guest"));
+ guestName = new QLineEdit();
+ guestNameLabel->setBuddy(guestName);
+
+ configLocLabel = new QLabel(tr("Enter path of config file"));
+ configLoc = new QLineEdit();
+ configLocLabel->setBuddy(configLoc);
+
+ imageLocLabel = new QLabel(tr("Enter path of image file"));
+ imageLoc = new QLineEdit();
+ imageLocLabel->setBuddy(imageLoc);
+
+ browseConfig = new QPushButton(tr("Browse"));
+ browseImage = new QPushButton(tr("Browse"));
+
+ registerField("guestName*", guestName);
+ registerField("configLoc*", configLoc);
+ registerField("imageLoc*", imageLoc);
+ //registerField("memory*", memory);
+
+ connect(browseConfig, SIGNAL(clicked()), this, SLOT(locateConfigFile()));
+ connect(browseImage, SIGNAL(clicked()), this, SLOT(locateImageFile()));
+
+ QGridLayout* layout = new QGridLayout();
+ layout->addWidget(guestNameLabel, 0, 0);
+ layout->addWidget(guestName, 0, 1);
+ layout->addWidget(configLocLabel, 1, 0);
+ layout->addWidget(configLoc, 1, 1);
+ layout->addWidget(browseConfig, 1, 2);
+ layout->addWidget(imageLocLabel, 2, 0);
+ layout->addWidget(imageLoc, 2, 1);
+ layout->addWidget(browseImage, 2, 2);
+
+ setLayout(layout);
+}
+
+GuestImagePage::~GuestImagePage() {
+ delete guestNameLabel;
+ delete guestName;
+ delete configLocLabel;
+ delete configLoc;
+ delete imageLocLabel;
+ delete imageLoc;
+ delete browseConfig;
+ delete browseImage;
+}
+
+void GuestImagePage::locateConfigFile() {
+ QString configFile = QFileDialog::getOpenFileName(this, tr("Open file"), ".",
+ "XML file (*.xml)");
+ configLoc->setText(configFile);
+}
+
+void GuestImagePage::locateImageFile() {
+ QString imageFile = QFileDialog::getOpenFileName(this, tr("Open file"), ".",
+ "Image file (*.img *.bZ)");
+ imageLoc->setText(imageFile);
+}
+
+int GuestImagePage::nextId() const {
+ return NewVmWizard::Page_Final;
+}
+
+bool GuestImagePage::validatePage() {
+ v3Proc = new QProcess();
+ v3Proc->setProcessChannelMode(QProcess::MergedChannels);
+ v3Proc->start("cat /proc/v3vee/v3-guests");
+ v3Proc->waitForFinished();
+ qDebug() << "Reply from proc to check for new VM creation: " << v3Proc->readAllStandardOutput();
+ QString name = field("guestName").toString();
+ QByteArray temp = name.toLocal8Bit();
+ const char* vmEntry = temp.data();
+ bool exists = false;
+
+ QByteArray val = v3Proc->readAllStandardOutput();
+ if (!val.isNull()) {
+ QList<QByteArray> procLine = val.split('\n');
+ if (procLine.size() > 0) {
+ for (int i=0; i<procLine.size(); i++) {
+ if (procLine[i].contains(vmEntry)) {
+ exists = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (exists) {
+ QMessageBox warning;
+ warning.setText("A Virtual Machine with this name already exists! Please use another name");
+ warning.setIcon(QMessageBox::Critical);
+ warning.setStandardButtons(
+ QMessageBox::Ok);
+ warning.exec();
+ return false;
+ }
+
+ delete v3Proc;
+ return true;
+}
+
+FinalPage::FinalPage(QWidget* parent) :
+ QWizardPage(parent) {
+ this->setCommitPage(true);
+ this->setFinalPage(true);
+ setTitle(tr("Status"));
+ setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/images/palacios.png"));
+
+ finalLabel = new QLabel("VM Creation Successful");
+
+ QVBoxLayout* layout = new QVBoxLayout();
+ layout->addWidget(finalLabel);
+
+ setLayout(layout);
+}
+
+FinalPage::~FinalPage() {
+ delete finalLabel;
+}
+
+int FinalPage::nextId() const {
+ return -1;
+}
--- /dev/null
+/*
+ * newvmwizard.h
+ *
+ * Created on: Sep 20, 2012
+ * Author: abhinav
+ */
+
+#ifndef NEWVMWIZARD_H_
+#define NEWVMWIZARD_H_
+
+#include <QtGui>
+#include <QWizard>
+
+class IntroPage;
+class GuestImagePage;
+class FinalPage;
+
+class NewVmWizard: public QWizard {
+Q_OBJECT
+public:
+ enum {
+ Page_Intro, Page_Image_File, Page_Final
+ };
+ NewVmWizard(QWidget* parent = 0);
+ ~NewVmWizard();
+
+signals:
+ void finishedWizard(QString guestName);
+
+private:
+ IntroPage* mIntroPage;
+ GuestImagePage* mImagePage;
+ FinalPage* mFinalPage;
+};
+
+class IntroPage: public QWizardPage {
+Q_OBJECT
+
+public:
+ IntroPage(QWidget* parent = 0);
+ ~IntroPage();
+ int nextId() const;
+
+private:
+ QLabel* topLabel;
+};
+
+class GuestImagePage: public QWizardPage {
+Q_OBJECT
+
+public:
+ GuestImagePage(QWidget* parent = 0);
+ ~GuestImagePage();
+ bool validatePage();
+ int nextId() const;
+
+private slots:
+ void locateConfigFile();
+ void locateImageFile();
+
+private:
+ QLabel* guestNameLabel;
+ QLabel* configLocLabel;
+ QLabel* imageLocLabel;
+
+ QLineEdit* guestName;
+ QLineEdit* configLoc;
+ QLineEdit* imageLoc;
+
+ QPushButton* browseConfig;
+ QPushButton* browseImage;
+
+ QProcess* v3Proc;
+};
+
+class FinalPage: public QWizardPage {
+Q_OBJECT
+
+public:
+ FinalPage(QWidget* parent = 0);
+ ~FinalPage();
+ int nextId() const;
+
+private:
+ QLabel* finalLabel;
+};
+
+#endif /* NEWVMWIZARD_H_ */
--- /dev/null
+#include "newpalacios.h"
+
+VmInfoWidget::VmInfoWidget(QWidget* parent) :
+ QWidget(parent) {
+ setInfoView();
+ setupUi();
+}
+
+VmInfoWidget::~VmInfoWidget() {
+ delete mVmInfoView;
+}
+
+void VmInfoWidget::setInfoView() {
+ // Create the main tree widget
+ mVmInfoView = new QTreeWidget();
+ mVmInfoView->setColumnCount(2);
+ QStringList headerLabels;
+ headerLabels << "Property" << "Value";
+ mVmInfoView->setHeaderLabels(headerLabels);
+}
+
+void VmInfoWidget::setupUi() {
+ QVBoxLayout* vmDetailsLayout = new QVBoxLayout(this);
+ vmDetailsLayout->addWidget(mVmInfoView);
+ setLayout(vmDetailsLayout);
+}
+
+void VmInfoWidget::parseAttribute(const QDomElement &element,
+ QTreeWidgetItem* parent) {
+ QDomNamedNodeMap atts = element.attributes();
+
+ for (unsigned int i=0; i<atts.length(); i++) {
+ QTreeWidgetItem* item = new QTreeWidgetItem(parent);
+
+ item->setText(0, "[" + atts.item(i).nodeName() +"]");
+ item->setText(1, "[" + atts.item(i).nodeValue() +"]");
+ }
+}
+
+void VmInfoWidget::parseText(const QDomElement &element,
+ QTreeWidgetItem* parent) {
+ QString value = element.text();
+ parent->setText(1, value);
+}
+
+void VmInfoWidget::parseElement(const QDomElement &element,
+ QTreeWidgetItem* parent) {
+
+ QTreeWidgetItem* item = new QTreeWidgetItem(parent);
+
+ if (element.tagName().compare("memory") == 0) {
+ // Memory tag
+ item->setText(0, "memory");
+ parseAttribute(element, item);
+ parseText(element, item);
+
+ } else if (element.tagName().compare("telemetry") == 0) {
+ // Telemetry tag
+ item->setText(0, "telemetry");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("extensions") == 0) {
+ // Extensions tag
+ item->setText(0, "extensions");
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("extension") == 0) {
+ // Specific Extension
+ item->setText(0, "extension");
+ parseAttribute(element, item);
+ parseText(element, item);
+
+ } else if (element.tagName().compare("paging") == 0) {
+ // Paging tag
+ item->setText(0, "paging");
+ parseAttribute(element, item);
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("strategy") == 0) {
+ item->setText(0, "strategy");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("large_pages") == 0) {
+ item->setText(0, "large_pages");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("schedule_hz") == 0) {
+ // Schedule Hz
+ item->setText(0, "schedule_hz");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("cores") == 0) {
+ // Cores
+ item->setText(0, "cores");
+ parseAttribute(element, item);
+
+ } else if (element.tagName().compare("core") == 0) {
+ item->setText(0, "core");
+
+ } else if (element.tagName().compare("memmap") == 0) {
+ item->setText(0, "memmap");
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("region") == 0) {
+ item->setText(0, "region");
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("start") == 0) {
+ item->setText(0, "start");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("end") == 0) {
+ item->setText(0, "end");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("host_addr") == 0) {
+ item->setText(0, "host_addr");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("files") == 0) {
+ item->setText(0, "files");
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("file") == 0) {
+ item->setText(0, "file");
+ parseAttribute(element, item);
+
+ } else if (element.tagName().compare("devices") == 0) {
+ // Devices
+ item->setText(0, "devices");
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("device") == 0) {
+ // Device
+ item->setText(0, "device");
+ parseAttribute(element, item);
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("storage") == 0) {
+ item->setText(0, "storage");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("apic") == 0) {
+ item->setText(0, "apic");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("bus") == 0) {
+ // Bus tag, is seen in many device tags
+
+ } else if (element.tagName().compare("vendor_id") == 0) {
+ item->setText(0, "vendor_id");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("device_id") == 0) {
+ item->setText(0, "device_id");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("irq") == 0) {
+ item->setText(0, "irq");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("controller") == 0) {
+ // Controller tag, is also seen in some device files
+ item->setText(0, "controller");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("file") == 0) {
+ item->setText(0, "file");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("frontend") == 0) {
+ item->setText(0, "frontend");
+ parseAttribute(element, item);
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("model") == 0) {
+ item->setText(0, "model");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("type") == 0) {
+ item->setText(0, "type");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("bus_num") == 0) {
+ item->setText(0, "bus_num");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("drive_num") == 0) {
+ item->setText(0, "drive_num");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("path") == 0) {
+ item->setText(0, "path");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("ip") == 0) {
+ item->setText(0, "ip");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("port") == 0) {
+ item->setText(0, "port");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("tag") == 0) {
+ item->setText(0, "tag");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("size") == 0) {
+ item->setText(0, "size");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("mac") == 0) {
+ item->setText(0, "mac");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("name") == 0) {
+ item->setText(0, "name");
+ parseText(element, item);
+
+ } else if (element.tagName().compare("ports") == 0) {
+ item->setText(0, "ports");
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (!child.isElement()) {
+ child = child.nextSibling();
+ continue;
+ }
+
+ parseElement(child.toElement(), item);
+ child = child.nextSibling();
+ }
+
+ } else if (element.tagName().compare("mode") == 0) {
+ item->setText(0, "mode");
+ parseText(element, item);
+
+ }
+}
+
+void VmInfoWidget::updateInfoView(VmInfo* vm) {
+ // Open config file
+ QFile file(vm->getVmConfigFile());
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qDebug() << "File cannot be opened!";
+ return;
+ }
+
+ // Setup DOM parser
+ QDomDocument doc("vm_config");
+ if (!doc.setContent(&file)) {
+ qDebug() << "Error setting content";
+ return;
+ }
+
+ file.close();
+
+ // Root element
+ QTreeWidgetItem* rootItem = new QTreeWidgetItem();
+ QDomElement root = doc.documentElement();
+
+ rootItem->setText(0, root.tagName());
+ parseAttribute(root, rootItem);
+ mVmInfoView->takeTopLevelItem(0);
+ mVmInfoView->addTopLevelItem(rootItem);
+
+ // Elements
+ QDomNode n = root.firstChild();
+ while (!n.isNull()) {
+
+ if (n.isElement()) {
+ parseElement(n.toElement(), rootItem);
+ }
+
+ n = n.nextSibling();
+ }
+
+ mVmInfoView->expandAll();
+}
+
+void VmInfoWidget::deleteVm() {
+ QTreeWidgetItem* item = mVmInfoView->takeTopLevelItem(0);
+ delete item;
+}
--- /dev/null
+#include <QDebug>
+
+#include "newpalacios.h"
+
+VmModeDialog::VmModeDialog(QWidget* parent) {
+ setupDialog();
+}
+
+void VmModeDialog::setupDialog() {
+ isV3Cons = false;
+ isV3Stream = false;
+ isV3Vnc = false;
+
+ // Group box to hold radio buttons
+ v3_modes = new QGroupBox(tr("Select VM mode"));
+ // Widget to give information about stream name
+ // for v3_stream
+ v3_stream_info = new QWidget(v3_modes);
+
+ v3_stream = new QRadioButton(tr("Stream Mode"));
+ v3_cons = new QRadioButton(tr("Console Mode"));
+ v3_vnc = new QRadioButton(tr("VNC mode"));
+
+ // Setup stream info widget
+ QLabel* v3_stream_label = new QLabel(tr("Stream name"));
+ v3_stream_name = new QLineEdit();
+
+ QGridLayout* grid = new QGridLayout();
+ grid->addWidget(v3_stream_label, 0, 0);
+ grid->addWidget(v3_stream_name, 0, 1);
+ v3_stream_info->setLayout(grid);
+ v3_stream_info->setVisible(false);
+
+ // Setup group box
+ QVBoxLayout* box = new QVBoxLayout();
+ box->addWidget(v3_cons);
+ box->addWidget(v3_stream);
+ box->addWidget(v3_stream_info);
+ box->addWidget(v3_vnc);
+ v3_modes->setLayout(box);
+
+ // Setup main layout for dialog
+ QVBoxLayout* mainLayout = new QVBoxLayout();
+ mainLayout->addWidget(v3_modes);
+ QHBoxLayout* actionLayout = new QHBoxLayout();
+ QPushButton* ok = new QPushButton(tr("OK"));
+ QPushButton* cancel = new QPushButton(tr("Cancel"));
+ actionLayout->addWidget(ok);
+ actionLayout->addWidget(cancel);
+ mainLayout->addLayout(actionLayout);
+
+ setLayout(mainLayout);
+ resize(300, 200);
+ move(200, 200);
+ setWindowTitle("Select VM mode");
+
+ // Connect signals and slots
+ connect(v3_cons, SIGNAL(toggled(bool)), this, SLOT(selectMode(bool)));
+ connect(v3_stream, SIGNAL(toggled(bool)), this, SLOT(selectMode(bool)));
+ connect(v3_vnc, SIGNAL(toggled(bool)), this, SLOT(selectMode(bool)));
+ connect(ok, SIGNAL(clicked()), this, SLOT(okButton()));
+ connect(cancel, SIGNAL(clicked()), this, SLOT(cancelButton()));
+}
+
+void VmModeDialog::okButton() {
+ if (isV3Cons == false
+ && isV3Stream == false
+ && isV3Vnc == false) {
+
+ // Do not emit anything
+ close();
+ return;
+ }
+
+ QString name = "";
+
+ if (isV3Stream) {
+ name = v3_stream_name->text();
+ }
+
+ emit setMode(mode, name);
+ close();
+}
+
+void VmModeDialog::cancelButton() {
+ close();
+}
+
+void VmModeDialog::selectMode(bool checked) {
+ if (checked == true) {
+ if (v3_cons->isChecked()) {
+ // If console is checked, then set
+ // mode to v3_cons
+ mode = VmConsoleWidget::CONSOLE;
+ isV3Cons = true;
+ isV3Stream = false;
+ isV3Vnc = false;
+ v3_stream_info->setVisible(false);
+
+ } else if (v3_stream->isChecked()) {
+ mode = VmConsoleWidget::STREAM;
+ isV3Cons = false;
+ isV3Stream = true;
+ isV3Vnc = false;
+ v3_stream_info->setVisible(true);
+
+ } else if (v3_vnc->isChecked()) {
+ mode = VmConsoleWidget::VNC;
+ isV3Cons = false;
+ isV3Stream = false;
+ isV3Vnc = true;
+ v3_stream_info->setVisible(false);
+
+ } else {
+ isV3Cons = false;
+ isV3Stream = false;
+ isV3Vnc = false;
+ }
+ }
+}
--- /dev/null
+#include <QDebug>
+
+#include "newpalacios.h"
+
+class ProcVm {
+public:
+ QString name;
+ QString devFile;
+};
+
+class DbVm {
+public:
+ QString name;
+ QString config;
+ QString image;
+ int state;
+ QString dev;
+};
+
+int LoadVmsThread::getStatus() {
+ return status;
+}
+
+QList<VmInfo*> LoadVmsThread::getVmList() {
+ return list;
+}
+
+void LoadVmsThread::loadVms() {
+ bool doesProcExist = false;
+ bool isDbPresent = false;
+
+ // Check if /proc file exists
+ QFileInfo procInfo("/proc/v3vee/v3-guests");
+ if (procInfo.exists()) {
+ // Proc file exists
+ doesProcExist = true;
+ }
+
+ // Read proc file
+ //QStringList procVms;
+ QList<ProcVm> procVms;
+
+ if (doesProcExist) {
+ QProcess* p = new QProcess();
+ p->setProcessChannelMode(QProcess::MergedChannels);
+ QStringList args;
+ args << "-c" << "cat /proc/v3vee/v3-guests";
+ p->start("sh", args);
+ if (!p->waitForFinished()) {
+ // Since we are unable to open the proc file
+ // for whatever some reason, the only way
+ // to identify this state would be to mark /proc
+ // as not existing
+ doesProcExist = false;
+
+ } else {
+ QByteArray procFile = p->readAllStandardOutput();
+ if (!procFile.isNull()) {
+ if (procFile.isEmpty()) {
+ doesProcExist = false;
+
+ } else {
+ QList<QByteArray> procContents = procFile.split('\n');
+
+ for (int i=0; i<procContents.size(); i++) {
+ QByteArray b = procContents.at(i);
+ QString procentry(b);
+ QStringList vals = procentry.split('\t');
+
+ if (vals.size() == 2) {
+ ProcVm procVm;
+ QString val = vals.at(0);
+ val.remove(QChar('\n'), Qt::CaseInsensitive);
+ procVm.name = val;
+
+ val = vals.at(1);
+ val.remove(QChar('\n'), Qt::CaseInsensitive);
+ procVm.devFile = val;
+ procVms.append(procVm);
+ }
+ }
+ }
+ }
+ }
+
+ // Delete proc process
+ delete p;
+ }
+
+ // Check if proc is empty
+ if (doesProcExist && procVms.size() == 0) {
+ // There was nothing in the proc file
+ doesProcExist = true;
+ }
+
+ // Check if DB exists
+ QFileInfo dbFile("virtual_machines_list.txt");
+ if (dbFile.exists()) {
+ isDbPresent = true;
+ }
+
+ QFile file("virtual_machines_list.txt");
+ // Open DB file
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ // The DB file is missing
+ isDbPresent = false;
+ }
+
+ // Check if there is any source to load the VM
+ if (!doesProcExist && !isDbPresent) {
+ // If proc file does not exist or is empty
+ // and the DB does not exist then there are
+ // no VMs to load
+ // return with STATUS_OK
+ status = STATUS_OK;
+ emit finished();
+ return;
+ }
+
+ //QStringList dbVms;
+ QList<DbVm> dbVms;
+
+ if (isDbPresent) {
+ // Read the DB file
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (line.compare("\n") == 0) {
+ continue;
+ }
+
+ QStringList vmDet = line.split(",");
+ DbVm dbVm;
+ dbVm.name = vmDet.at(0);
+ dbVm.config = vmDet.at(1);
+ dbVm.dev = vmDet.at(2);
+ dbVm.state = vmDet.at(3).toInt();
+ dbVm.image = vmDet.at(4);
+
+ dbVms.append(dbVm);
+ }
+ }
+
+ if (!doesProcExist && isDbPresent) {
+ // If /proc does not exist but DB does
+ // then all VMs are inactive
+ for (int i=0; i<dbVms.size(); i++) {
+ VmInfo* vmDb = new VmInfo();
+ vmDb->setVmName(dbVms[i].name);
+ vmDb->setVmConfigFile(dbVms[i].config);
+ vmDb->setVmDevFile(dbVms[i].dev);
+ vmDb->setState(dbVms[i].state);
+ vmDb->setImageFile(dbVms[i].image);
+ vmDb->setCategory(VmInfo::INACTIVE_INVENTORY);
+ // Add VM to loading list
+ list.append(vmDb);
+ }
+
+ } else if (doesProcExist && !isDbPresent) {
+ // If /proc exists but DB does not
+ for (int i=0; i<procVms.size(); i++) {
+ VmInfo* vmProc = new VmInfo();
+ vmProc->setVmName(procVms[i].name);
+ vmProc->setVmConfigFile("");
+ vmProc->setVmDevFile(procVms[i].devFile);
+ vmProc->setState(VmInfo::STOPPED);
+ vmProc->setImageFile("");
+ vmProc->setCategory(VmInfo::ACTIVE_NOT_INVENTORY);
+ // Add VM to loading list
+ list.append(vmProc);
+ }
+
+ } else if (doesProcExist && isDbPresent) {
+ // Both files exist
+ // Compare entries in /proc and text file, mark non-matching entries in /proc as active not inventory
+ QList<int> procIndices;
+ QList<int> dbIndices;
+
+ int found = -1;
+
+ for (int i=0; i<procVms.size(); i++) {
+ for (int j=0; j<dbVms.size(); j++) {
+ if ((procVms[i].devFile.compare(dbVms[j].dev) == 0)
+ && (procVms[i].name.compare(dbVms[j].name) == 0)) {
+ found = j;
+ procIndices.append(i);
+ dbIndices.append(j);
+ break;
+ }
+ }
+
+ if (found >= 0) {
+ VmInfo* vm = new VmInfo();
+ vm->setVmName(dbVms[found].name);
+ vm->setVmConfigFile(dbVms[found].config);
+ vm->setVmDevFile(dbVms[found].dev);
+ vm->setState(dbVms[found].state);
+ vm->setImageFile(dbVms[found].image);
+
+ vm->setCategory(VmInfo::ACTIVE_INVENTORY);
+ // Add VM to loading list
+ list.append(vm);
+ found = -1;
+ }
+ }
+
+ // Once we have compared /proc with the DB and marked matching indices
+ // we mark the rest of the entries in /proc as active not inventory
+ for (int j=0; j<procVms.size(); j++) {
+ if (!procIndices.contains(j)) {
+ VmInfo* vmProcOnly = new VmInfo();
+ vmProcOnly->setVmName(procVms[j].name);
+ vmProcOnly->setVmConfigFile("");
+ vmProcOnly->setVmDevFile(procVms[j].devFile);
+ vmProcOnly->setState(VmInfo::STOPPED);
+ vmProcOnly->setImageFile("");
+ vmProcOnly->setCategory(VmInfo::ACTIVE_NOT_INVENTORY);
+ list.append(vmProcOnly);
+ }
+ }
+
+ // Once we have compared /proc with the DB and marked matching indices
+ // we mark the rest of the entries in dbVms as inactive
+ for (int j=0; j<dbVms.size(); j++) {
+ if (!dbIndices.contains(j)) {
+ VmInfo* vmDbOnly = new VmInfo();
+ vmDbOnly->setVmName(dbVms[j].name);
+ vmDbOnly->setVmConfigFile(dbVms[j].config);
+ vmDbOnly->setVmDevFile("");
+ vmDbOnly->setState(dbVms[j].state);
+ vmDbOnly->setImageFile(dbVms[j].image);
+ vmDbOnly->setCategory(VmInfo::INACTIVE_INVENTORY);
+ list.append(vmDbOnly);
+ }
+ }
+ }
+
+ status = STATUS_OK;
+ file.close();
+
+ emit finished();
+}
+
+AddVmThread::AddVmThread(QString name, QString conf, QString img) {
+ this->status = STATUS_OK;
+ this->vmName = name;
+ this->vmConfigFile = conf;
+ this->vmImageFile = img;
+}
+
+int AddVmThread::getStatus() {
+ return status;
+}
+
+VmInfo* AddVmThread::getNewVm() {
+ return vm;
+}
+
+QString AddVmThread::getName() {
+ return vmName;
+}
+
+void AddVmThread::addVm() {
+ QFile file("virtual_machines_list.txt");
+ if (!file.open(QIODevice::Append | QIODevice::Text)) {
+ status = ERROR_V3CREATE_DB;
+ emit finished();
+ return;
+ }
+
+ // Add VM to the inventory (database). The VM still needs to be
+ // activated to run
+ // Create new VM instance
+ // The dev file for this VM will be set when it is activated
+ vm = new VmInfo();
+ vm->setVmName(vmName);
+ vm->setVmConfigFile(vmConfigFile);
+ vm->setImageFile(vmImageFile);
+ vm->setCategory(VmInfo::INACTIVE_INVENTORY);
+
+ QTextStream out(&file);
+
+ out << vmName;
+ out << ",";
+ out << vmConfigFile;
+ out << ",";
+ out << vmDevFile;
+ out << ",";
+ out << QString::number(VmInfo::STOPPED);
+ out << ",";
+ out << vmImageFile;
+ out << endl;
+
+ out.flush();
+ file.close();
+
+ status = STATUS_OK;
+
+ emit finished();
+}
+
+DeleteVmThread::DeleteVmThread(int category, QString name, QString devfile) {
+ this->vmCategory = category;
+ this->vmToDelete = name;
+ this->status = -2;
+ this->vmDevfile = devfile;
+}
+
+int DeleteVmThread::getStatus() {
+ return status;
+}
+
+/*void DeleteVmThread::processStarted() {
+ //qDebug() << "V3 Free process started";
+}
+
+void DeleteVmThread::processExit(int errorCode, QProcess::ExitStatus exitStatus) {
+ if (exitStatus == QProcess::CrashExit) {
+ //qDebug() << "v3_free process crashed!!";
+ } else {
+ //qDebug() << "v3_free process exited normally..";
+ }
+}
+
+void DeleteVmThread::processError(QProcess::ProcessError error) {
+ //qDebug() << "There was an error in the v3_free process execution...";
+
+ switch (error) {
+ case QProcess::FailedToStart:
+ //qDebug() << "Process failed to start...";
+ break;
+ case QProcess::Crashed:
+ //qDebug() << "Process crashed...";
+ break;
+ case QProcess::Timedout:
+ //qDebug() << "Process timed out...";
+ break;
+ case QProcess::WriteError:
+ //qDebug() << "Process had write error...";
+ break;
+ case QProcess::ReadError:
+ //qDebug() << "Process had read error...";
+ break;
+ case QProcess::UnknownError:
+ //qDebug() << "Process unknown error...";
+ break;
+
+ }
+}*/
+
+void DeleteVmThread::deleteVm() {
+
+ // Check for the category of VM being deleted
+ // If Active or Active not inventory then only
+ // invoke v3_free since we have an instance of the
+ // VM in proc. If it is Inactive and we are deleting
+ // then we need to just remove it from the DB
+
+ // Create variables
+ // C++ complains if we create variables inside switch statement
+ QProcess* v3Freeproc;
+ QStringList args;
+ QByteArray procOutput;
+
+ switch (vmCategory) {
+ case VmInfo::ACTIVE_INVENTORY:
+ case VmInfo::ACTIVE_NOT_INVENTORY:
+ if (vmDevfile == NULL) {
+ // Cannot delete VM without dev file
+ status = ERROR_V3FREE_INVALID_ARGUMENT;
+ emit finished();
+ return;
+ }
+
+ v3Freeproc = new QProcess();
+ v3Freeproc->setProcessChannelMode(QProcess::MergedChannels);
+
+ // Connect debug slots
+ connect(v3Freeproc, SIGNAL(started()), this, SLOT(processStarted()));
+ connect(v3Freeproc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processExit(int, QProcess::ExitStatus)));
+ connect(v3Freeproc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
+
+ args << vmDevfile;
+ v3Freeproc->start("v3_free", args);
+
+ if (!v3Freeproc->waitForFinished()) {
+ // Reinsert data into database since v3_free was not successful
+ status = ERROR_V3FREE_PATH;
+ delete v3Freeproc;
+ emit finished();
+ return;
+ } else {
+ procOutput = v3Freeproc->readAllStandardOutput();
+
+ if (procOutput.contains("IOCTL error")) {
+ status = ERROR_V3FREE_IOCTL;
+ delete v3Freeproc;
+ emit finished();
+ return;
+ }
+ }
+
+ // V3_free success
+ if (v3Freeproc != NULL) {
+ delete v3Freeproc;
+ }
+
+ break;
+ case VmInfo::INACTIVE_INVENTORY:
+ default:
+ break;
+ }
+
+ QFile file("virtual_machines_list.txt");
+ QFile tempFile("temp.txt");
+
+ bool oldFile = file.open(QIODevice::ReadOnly | QIODevice::Text);
+ bool newFile = tempFile.open(QIODevice::WriteOnly | QIODevice::Text);
+
+ if (!oldFile || !newFile) {
+ status = ERROR_V3FREE_DB;
+ emit finished();
+ return;
+ }
+
+ QTextStream in(&file);
+ QTextStream out(&tempFile);
+
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ QStringList vm = line.split(",");
+ if (vm.at(0).compare(vmToDelete) != 0) {
+ out << line << endl;
+ }
+ }
+
+ out.flush();
+ file.remove("virtual_machines_list.txt");
+ tempFile.rename("virtual_machines_list.txt");
+
+ file.close();
+ tempFile.close();
+ status = STATUS_OK;
+
+ emit finished();
+}
--- /dev/null
+#include <QDebug>
+#include <QWidget>
+#include <QStringList>
+#include <QCloseEvent>
+#include <QResizeEvent>
+
+#include "newpalacios.h"
+
+VmXConsoleParent::VmXConsoleParent(QString name, QWidget* parent)
+ : QWidget(parent) {
+
+ mVmName = name;
+
+ QVBoxLayout* l = new QVBoxLayout;
+ mConsole = new VmConsoleWidget(this);
+ l->addWidget(mConsole);
+ l->setStretchFactor(mConsole, 1000);
+
+ this->setLayout(l);
+ this->setWindowTitle(name);
+
+ connect(this, SIGNAL(windowClosing()), mConsole,
+ SLOT(mainWindowClosing()));
+}
+
+void VmXConsoleParent::showWidget(int mode, QString devfile, QString streamName) {
+ mConsole->start(mode, devfile, streamName);
+
+ // Workaround for QProcess error
+ mIsConsoleRunning = true;
+}
+
+bool VmXConsoleParent::isRunning() {
+ // FIXME: For some reason QProcess does not seem to return the correct state
+ // Qprocess->state() should give the correct state of the running process
+ // but currently there is a problem with geting the right state.
+ // For the time being a boolean flag is used to represent the state (running/not running)
+
+ //return mConsole->isRunning();
+ return mIsConsoleRunning;
+}
+
+QString VmXConsoleParent::getVmName() {
+ return mVmName;
+}
+
+void VmXConsoleParent::closeEvent(QCloseEvent* event) {
+ // Workaround for QProcess error
+ /*if (!tryTerminate())
+ qDebug() << "Warning: could not terminate process...there could be a leak";
+ else
+ qDebug() << "Process terminated";*/
+
+ mIsConsoleRunning = false;
+ emit windowClosingWithId(mVmName);
+ emit windowClosing();
+ event->accept();
+}
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2002-2003 Tim Jansen <tim@tjansen.de>
+** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
+**
+** This file is part of KDE.
+**
+** 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; see the file COPYING. If not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+** Boston, MA 02110-1301, USA.
+**
+****************************************************************************/
+
+#include "remoteview.h"
+
+#ifndef QTONLY
+ #include <KDebug>
+ #include <KStandardDirs>
+#endif
+
+#include <QBitmap>
+
+RemoteView::RemoteView(QWidget *parent)
+ : QWidget(parent),
+ m_status(Disconnected),
+ m_host(QString()),
+ m_port(0),
+ m_viewOnly(false),
+ m_grabAllKeys(false),
+ m_scale(false),
+ m_keyboardIsGrabbed(false),
+#ifndef QTONLY
+ m_wallet(0),
+#endif
+ m_dotCursorState(CursorOff)
+{
+}
+
+RemoteView::~RemoteView()
+{
+#ifndef QTONLY
+ delete m_wallet;
+#endif
+}
+
+RemoteView::RemoteStatus RemoteView::status()
+{
+ return m_status;
+}
+
+void RemoteView::setStatus(RemoteView::RemoteStatus s)
+{
+ if (m_status == s)
+ return;
+
+ if (((1+ m_status) != s) && (s != Disconnected)) {
+ // follow state transition rules
+
+ if (s == Disconnecting) {
+ if (m_status == Disconnected)
+ return;
+ } else {
+ Q_ASSERT(((int) s) >= 0);
+ if (m_status > s) {
+ m_status = Disconnected;
+ emit statusChanged(Disconnected);
+ }
+ // smooth state transition
+ RemoteStatus origState = m_status;
+ for (int i = origState; i < s; ++i) {
+ m_status = (RemoteStatus) i;
+ emit statusChanged((RemoteStatus) i);
+ }
+ }
+ }
+ m_status = s;
+ emit statusChanged(m_status);
+}
+
+bool RemoteView::supportsScaling() const
+{
+ return false;
+}
+
+bool RemoteView::supportsLocalCursor() const
+{
+ return false;
+}
+
+QString RemoteView::host()
+{
+ return m_host;
+}
+
+QSize RemoteView::framebufferSize()
+{
+ return QSize(0, 0);
+}
+
+void RemoteView::startQuitting()
+{
+}
+
+bool RemoteView::isQuitting()
+{
+ return false;
+}
+
+int RemoteView::port()
+{
+ return m_port;
+}
+
+void RemoteView::updateConfiguration()
+{
+}
+
+void RemoteView::keyEvent(QKeyEvent *)
+{
+}
+
+bool RemoteView::viewOnly()
+{
+ return m_viewOnly;
+}
+
+void RemoteView::setViewOnly(bool viewOnly)
+{
+ m_viewOnly = viewOnly;
+}
+
+bool RemoteView::grabAllKeys()
+{
+ return m_grabAllKeys;
+}
+
+void RemoteView::setGrabAllKeys(bool grabAllKeys)
+{
+ m_grabAllKeys = grabAllKeys;
+
+ if (grabAllKeys) {
+ m_keyboardIsGrabbed = true;
+ grabKeyboard();
+ } else if (m_keyboardIsGrabbed) {
+ releaseKeyboard();
+ }
+}
+
+QPixmap RemoteView::takeScreenshot()
+{
+ return QPixmap::grabWidget(this);
+}
+
+void RemoteView::showDotCursor(DotCursorState state)
+{
+ m_dotCursorState = state;
+}
+
+RemoteView::DotCursorState RemoteView::dotCursorState() const
+{
+ return m_dotCursorState;
+}
+
+bool RemoteView::scaling() const
+{
+ return m_scale;
+}
+
+void RemoteView::enableScaling(bool scale)
+{
+ m_scale = scale;
+}
+
+void RemoteView::switchFullscreen(bool)
+{
+}
+
+void RemoteView::scaleResize(int, int)
+{
+}
+
+KUrl RemoteView::url()
+{
+ return m_url;
+}
+
+#ifndef QTONLY
+QString RemoteView::readWalletPassword(bool fromUserNameOnly)
+{
+ const QString KRDCFOLDER = "KRDC";
+
+ window()->setDisabled(true); // WORKAROUND: disable inputs so users cannot close the current tab (see #181230)
+ m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), window()->winId());
+ window()->setDisabled(false);
+
+ if (m_wallet) {
+ bool walletOK = m_wallet->hasFolder(KRDCFOLDER);
+ if (!walletOK) {
+ walletOK = m_wallet->createFolder(KRDCFOLDER);
+ kDebug(5010) << "Wallet folder created";
+ }
+ if (walletOK) {
+ kDebug(5010) << "Wallet OK";
+ m_wallet->setFolder(KRDCFOLDER);
+ QString password;
+
+ QString key;
+ if (fromUserNameOnly)
+ key = m_url.userName();
+ else
+ key = m_url.prettyUrl(KUrl::RemoveTrailingSlash);
+
+ if (m_wallet->hasEntry(key) &&
+ !m_wallet->readPassword(key, password)) {
+ kDebug(5010) << "Password read OK";
+
+ return password;
+ }
+ }
+ }
+ return QString();
+}
+
+void RemoteView::saveWalletPassword(const QString &password, bool fromUserNameOnly)
+{
+ QString key;
+ if (fromUserNameOnly)
+ key = m_url.userName();
+ else
+ key = m_url.prettyUrl(KUrl::RemoveTrailingSlash);
+
+ if (m_wallet && m_wallet->isOpen()) {
+ kDebug(5010) << "Write wallet password";
+ m_wallet->writePassword(key, password);
+ }
+}
+#endif
+
+QCursor RemoteView::localDotCursor() const
+{
+#ifdef QTONLY
+ return QCursor(); //TODO
+#else
+ QBitmap cursorBitmap(KGlobal::dirs()->findResource("appdata",
+ "pics/pointcursor.png"));
+ QBitmap cursorMask(KGlobal::dirs()->findResource("appdata",
+ "pics/pointcursormask.png"));
+ return QCursor(cursorBitmap, cursorMask);
+#endif
+}
+
+void RemoteView::focusInEvent(QFocusEvent *event)
+{
+ if (m_grabAllKeys) {
+ m_keyboardIsGrabbed = true;
+ grabKeyboard();
+ }
+
+ QWidget::focusInEvent(event);
+}
+
+void RemoteView::focusOutEvent(QFocusEvent *event)
+{
+ if (m_grabAllKeys || m_keyboardIsGrabbed) {
+ m_keyboardIsGrabbed = false;
+ releaseKeyboard();
+ }
+
+ QWidget::focusOutEvent(event);
+}
+
+#include "moc_remoteview.cpp"
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2002-2003 Tim Jansen <tim@tjansen.de>
+** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
+**
+** This file is part of KDE.
+**
+** 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; see the file COPYING. If not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+** Boston, MA 02110-1301, USA.
+**
+****************************************************************************/
+
+#ifndef REMOTEVIEW_H
+#define REMOTEVIEW_H
+
+#ifdef QTONLY
+ #include <QUrl>
+ #define KUrl QUrl
+ #define KRDCCORE_EXPORT
+#else
+ #include <KUrl>
+ #include <KWallet/Wallet>
+ #include <krdc_export.h>
+#endif
+
+#include <QWidget>
+
+class HostPreferences;
+
+/**
+ * Generic widget that displays a remote framebuffer.
+ * Implement this if you want to add another backend.
+ *
+ * Things to take care of:
+ * @li The RemoteView is responsible for its size. In
+ * non-scaling mode, set the fixed size of the widget
+ * to the remote resolution. In scaling mode, set the
+ * maximum size to the remote size and minimum size to the
+ * smallest resolution that your scaler can handle.
+ * @li if you override mouseMoveEvent()
+ * you must ignore the QEvent, because the KRDC widget will
+ * need it for stuff like toolbar auto-hide and bump
+ * scrolling. If you use x11Event(), make sure that
+ * MotionNotify events will be forwarded.
+ *
+ */
+class KRDCCORE_EXPORT RemoteView : public QWidget
+{
+ Q_OBJECT
+
+public:
+
+ Q_ENUMS(Quality)
+
+ enum Quality {
+ Unknown,
+ High,
+ Medium,
+ Low
+ };
+
+ /**
+ * Describes the state of a local cursor, if there is such a concept in the backend.
+ * With local cursors, there are two cursors: the cursor on the local machine (client),
+ * and the cursor on the remote machine (server). Because there is usually some lag,
+ * some backends show both cursors simultanously. In the VNC backend the local cursor
+ * is a dot and the remote cursor is the 'real' cursor, usually an arrow.
+ */
+
+ Q_ENUMS(DotCursorState)
+
+ enum DotCursorState {
+ CursorOn, ///< Always show local cursor (and the remote one).
+ CursorOff, ///< Never show local cursor, only the remote one.
+ /// Try to measure the lag and enable the local cursor if the latency is too high.
+ CursorAuto
+ };
+
+ /**
+ * State of the connection. The state of the connection is returned
+ * by @ref RemoteView::status().
+ *
+ * Not every state transition is allowed. You are only allowed to transition
+ * a state to the following state, with three exceptions:
+ * @li You can move from every state directly to Disconnected
+ * @li You can move from every state except Disconnected to
+ * Disconnecting
+ * @li You can move from Disconnected to Connecting
+ *
+ * @ref RemoteView::setStatus() will follow this rules for you.
+ * (If you add/remove a state here, you must adapt it)
+ */
+
+ Q_ENUMS(RemoteStatus)
+
+ enum RemoteStatus {
+ Connecting = 0,
+ Authenticating = 1,
+ Preparing = 2,
+ Connected = 3,
+ Disconnecting = -1,
+ Disconnected = -2
+ };
+
+ Q_ENUMS(ErrorCode)
+
+ enum ErrorCode {
+ Nonne = 0,
+ Internal,
+ Connection,
+ Protocol,
+ IO,
+ Name,
+ NoServer,
+ ServerBlocked,
+ Authentication
+ };
+
+ virtual ~RemoteView();
+
+ /**
+ * Checks whether the backend supports scaling. The
+ * default implementation returns false.
+ * @return true if scaling is supported
+ * @see scaling()
+ */
+ virtual bool supportsScaling() const;
+
+ /**
+ * Checks whether the widget is in scale mode. The
+ * default implementation always returns false.
+ * @return true if scaling is activated. Must always be
+ * false if @ref supportsScaling() returns false
+ * @see supportsScaling()
+ */
+ virtual bool scaling() const;
+
+ /**
+ * Checks whether the backend supports the concept of local cursors. The
+ * default implementation returns false.
+ * @return true if local cursors are supported/known
+ * @see DotCursorState
+ * @see showDotCursor()
+ * @see dotCursorState()
+ */
+ virtual bool supportsLocalCursor() const;
+
+ /**
+ * Sets the state of the dot cursor, if supported by the backend.
+ * The default implementation does nothing.
+ * @param state the new state (CursorOn, CursorOff or
+ * CursorAuto)
+ * @see dotCursorState()
+ * @see supportsLocalCursor()
+ */
+ virtual void showDotCursor(DotCursorState state);
+
+ /**
+ * Returns the state of the local cursor. The default implementation returns
+ * always CursorOff.
+ * @return true if local cursors are supported/known
+ * @see showDotCursor()
+ * @see supportsLocalCursor()
+ */
+ virtual DotCursorState dotCursorState() const;
+
+ /**
+ * Checks whether the view is in view-only mode. This means
+ * that all input is ignored.
+ */
+ virtual bool viewOnly();
+
+ /**
+ * Checks whether grabbing all possible keys is enabled.
+ */
+ virtual bool grabAllKeys();
+
+ /**
+ * Returns the resolution of the remote framebuffer.
+ * It should return a null @ref QSize when the size
+ * is not known.
+ * The backend must also emit a @ref framebufferSizeChanged()
+ * when the size of the framebuffer becomes available
+ * for the first time or the size changed.
+ * @return the remote framebuffer size, a null QSize
+ * if unknown
+ */
+ virtual QSize framebufferSize();
+
+ /**
+ * Initiate the disconnection. This doesn't need to happen
+ * immediately. The call must not block.
+ * @see isQuitting()
+ */
+ virtual void startQuitting();
+
+ /**
+ * Checks whether the view is currently quitting.
+ * @return true if it is quitting
+ * @see startQuitting()
+ * @see setStatus()
+ */
+ virtual bool isQuitting();
+
+ /**
+ * @return the host the view is connected to
+ */
+ virtual QString host();
+
+ /**
+ * @return the port the view is connected to
+ */
+ virtual int port();
+
+ /**
+ * Initialize the view (for example by showing configuration
+ * dialogs to the user) and start connecting. Should not block
+ * without running the event loop (so displaying a dialog is ok).
+ * When the view starts connecting the application must call
+ * @ref setStatus() with the status Connecting.
+ * @return true if successful (so far), false
+ * otherwise
+ * @see connected()
+ * @see disconnected()
+ * @see disconnectedError()
+ * @see statusChanged()
+ */
+ virtual bool start() = 0;
+
+ /**
+ * Called when the configuration is changed.
+ * The default implementation does nothing.
+ */
+ virtual void updateConfiguration();
+
+ /**
+ * @return screenshot of the view
+ */
+ virtual QPixmap takeScreenshot();
+
+#ifndef QTONLY
+ /**
+ * Returns the current host preferences of this view.
+ */
+ virtual HostPreferences* hostPreferences() = 0;
+#endif
+
+ /**
+ * Returns the current status of the connection.
+ * @return the status of the connection
+ * @see setStatus()
+ */
+ RemoteStatus status();
+
+ /**
+ * @return the current url
+ */
+ KUrl url();
+
+public slots:
+ /**
+ * Called to enable or disable scaling.
+ * Ignored if @ref supportsScaling() is false.
+ * The default implementation does nothing.
+ * @param s true to enable, false to disable.
+ * @see supportsScaling()
+ * @see scaling()
+ */
+ virtual void enableScaling(bool scale);
+
+ /**
+ * Enables/disables the view-only mode.
+ * Ignored if @ref supportsScaling() is false.
+ * The default implementation does nothing.
+ * @param viewOnly true to enable, false to disable.
+ * @see supportsScaling()
+ * @see viewOnly()
+ */
+ virtual void setViewOnly(bool viewOnly);
+
+ /**
+ * Enables/disables grabbing all possible keys.
+ * @param grabAllKeys true to enable, false to disable.
+ * Default is false.
+ * @see grabAllKeys()
+ */
+ virtual void setGrabAllKeys(bool grabAllKeys);
+
+ /**
+ * Called to let the backend know it when
+ * we switch from/to fullscreen.
+ * @param on true when switching to fullscreen,
+ * false when switching from fullscreen.
+ */
+ virtual void switchFullscreen(bool on);
+
+ /**
+ * Sends a QKeyEvent to the remote server.
+ * @param event the key to send
+ */
+ virtual void keyEvent(QKeyEvent *event);
+
+ /**
+ * Called when the visible place changed so remote
+ * view can resize itself.
+ */
+ virtual void scaleResize(int w, int h);
+
+signals:
+ /**
+ * Emitted when the size of the remote screen changes. Also
+ * called when the size is known for the first time.
+ * @param x the width of the screen
+ * @param y the height of the screen
+ */
+ void framebufferSizeChanged(int w, int h);
+
+ /**
+ * Emitted when the view connected successfully.
+ */
+ void connected();
+
+ /**
+ * Emitted when the view disconnected without error.
+ */
+ void disconnected();
+
+ /**
+ * Emitted when the view disconnected with error.
+ */
+ void disconnectedError();
+
+ /**
+ * Emitted when the view has a specific error.
+ */
+ void errorMessage(const QString &title, const QString &message);
+
+ /**
+ * Emitted when the status of the view changed.
+ * @param s the new status
+ */
+ void statusChanged(RemoteView::RemoteStatus s);
+
+ /**
+ * Emitted when the password dialog is shown or hidden.
+ * @param b true when the dialog is shown, false when it has been hidden
+ */
+ void showingPasswordDialog(bool b);
+
+ /**
+ * Emitted when the mouse on the remote side has been moved.
+ * @param x the new x coordinate
+ * @param y the new y coordinate
+ * @param buttonMask the mask of mouse buttons (bit 0 for first mouse
+ * button, 1 for second button etc)a
+ */
+ void mouseStateChanged(int x, int y, int buttonMask);
+
+protected:
+ RemoteView(QWidget *parent = 0);
+
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+
+ /**
+ * The status of the remote view.
+ */
+ RemoteStatus m_status;
+
+ /**
+ * Set the status of the connection.
+ * Emits a statusChanged() signal.
+ * Note that the states need to be set in a certain order,
+ * see @ref Status. setStatus() will try to do this
+ * transition automatically, so if you are in Connecting
+ * and call setStatus(Preparing), setStatus() will
+ * emit a Authenticating and then Preparing.
+ * If you transition backwards, it will emit a
+ * Disconnected before doing the transition.
+ * @param s the new status
+ */
+ virtual void setStatus(RemoteStatus s);
+
+ QCursor localDotCursor() const;
+
+ QString m_host;
+ int m_port;
+ bool m_viewOnly;
+ bool m_grabAllKeys;
+ bool m_scale;
+ bool m_keyboardIsGrabbed;
+ KUrl m_url;
+
+#ifndef QTONLY
+ QString readWalletPassword(bool fromUserNameOnly = false);
+ void saveWalletPassword(const QString &password, bool fromUserNameOnly = false);
+ KWallet::Wallet *m_wallet;
+#endif
+
+ DotCursorState m_dotCursorState;
+};
+
+#endif
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
+**
+** This file is part of KDE.
+**
+** 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; see the file COPYING. If not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+** Boston, MA 02110-1301, USA.
+**
+****************************************************************************/
+
+#include "vncclientthread.h"
+
+#include <QMutexLocker>
+#include <QTimer>
+
+//for detecting intel AMT KVM vnc server
+static const QString INTEL_AMT_KVM_STRING= "Intel(r) AMT KVM";
+static QString outputErrorMessageString;
+
+QVector<QRgb> VncClientThread::m_colorTable;
+
+void VncClientThread::setClientColorDepth(rfbClient* cl, VncClientThread::ColorDepth cd)
+{
+ switch(cd) {
+ case bpp8:
+ if (m_colorTable.isEmpty()) {
+ m_colorTable.resize(256);
+ int r,g,b;
+ for (int i = 0; i < 256; ++i) {
+ //pick out the red (3 bits), green (3 bits) and blue (2 bits) bits and make them maximum significant in 8bits
+ //this gives a colortable for 8bit true colors
+ r= (i & 0x07) << 5;
+ g= (i & 0x38) << 2;
+ b= i & 0xc0;
+ m_colorTable[i] = qRgb(r, g, b);
+ }
+ }
+ cl->format.depth = 8;
+ cl->format.bitsPerPixel = 8;
+ cl->format.redShift = 0;
+ cl->format.greenShift = 3;
+ cl->format.blueShift = 6;
+ cl->format.redMax = 7;
+ cl->format.greenMax = 7;
+ cl->format.blueMax = 3;
+ break;
+ case bpp16:
+ cl->format.depth = 16;
+ cl->format.bitsPerPixel = 16;
+ cl->format.redShift = 11;
+ cl->format.greenShift = 5;
+ cl->format.blueShift = 0;
+ cl->format.redMax = 0x1f;
+ cl->format.greenMax = 0x3f;
+ cl->format.blueMax = 0x1f;
+ break;
+ case bpp32:
+ default:
+ cl->format.depth = 24;
+ cl->format.bitsPerPixel = 32;
+ cl->format.redShift = 16;
+ cl->format.greenShift = 8;
+ cl->format.blueShift = 0;
+ cl->format.redMax = 0xff;
+ cl->format.greenMax = 0xff;
+ cl->format.blueMax = 0xff;
+ }
+}
+
+rfbBool VncClientThread::newclient(rfbClient *cl)
+{
+ VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+ Q_ASSERT(t);
+
+ //8bit color hack for Intel(r) AMT KVM "classic vnc" = vnc server built in in Intel Vpro chipsets.
+ if (INTEL_AMT_KVM_STRING == cl->desktopName) {
+ kDebug(5011) << "Intel(R) AMT KVM: switching to 8 bit color depth (workaround, recent libvncserver needed)";
+ t->setColorDepth(bpp8);
+ }
+ setClientColorDepth(cl, t->colorDepth());
+
+ const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel;
+ const int size = width * height * (depth / 8);
+ if (t->frameBuffer)
+ delete [] t->frameBuffer; // do not leak if we get a new framebuffer size
+ t->frameBuffer = new uint8_t[size];
+ cl->frameBuffer = t->frameBuffer;
+ memset(cl->frameBuffer, '\0', size);
+
+ switch (t->quality()) {
+ case RemoteView::High:
+ cl->appData.encodingsString = "copyrect zlib hextile raw";
+ cl->appData.compressLevel = 0;
+ cl->appData.qualityLevel = 9;
+ break;
+ case RemoteView::Medium:
+ cl->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
+ cl->appData.compressLevel = 5;
+ cl->appData.qualityLevel = 7;
+ break;
+ case RemoteView::Low:
+ case RemoteView::Unknown:
+ default:
+ cl->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
+ cl->appData.compressLevel = 9;
+ cl->appData.qualityLevel = 1;
+ }
+
+ SetFormatAndEncodings(cl);
+ kDebug(5011) << "Client created";
+ return true;
+}
+
+void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
+{
+// kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
+ VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+ Q_ASSERT(t);
+
+ const int width = cl->width, height = cl->height;
+ QImage img;
+ switch(t->colorDepth()) {
+ case bpp8:
+ img = QImage(cl->frameBuffer, width, height, QImage::Format_Indexed8);
+ img.setColorTable(m_colorTable);
+ break;
+ case bpp16:
+ img = QImage(cl->frameBuffer, width, height, QImage::Format_RGB16);
+ break;
+ case bpp32:
+ img = QImage(cl->frameBuffer, width, height, QImage::Format_RGB32);
+ break;
+ }
+
+ if (img.isNull()) {
+ kDebug(5011) << "image not loaded";
+ }
+
+ t->setImage(img);
+
+ t->emitUpdated(x, y, w, h);
+}
+
+void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
+{
+ const QString cutText = QString::fromUtf8(text, textlen);
+ kDebug(5011) << cutText;
+
+ if (!cutText.isEmpty()) {
+ VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+ Q_ASSERT(t);
+
+ t->emitGotCut(cutText);
+ }
+}
+
+char *VncClientThread::passwdHandler(rfbClient *cl)
+{
+ kDebug(5011) << "password request" << kBacktrace();
+
+ VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+ Q_ASSERT(t);
+
+ t->passwordRequest();
+ t->m_passwordError = true;
+
+ return strdup(t->password().toLocal8Bit());
+}
+
+void VncClientThread::outputHandler(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ QString message;
+ message.vsprintf(format, args);
+
+ va_end(args);
+
+ message = message.trimmed();
+
+ kDebug(5011) << message;
+
+ if ((message.contains("Couldn't convert ")) ||
+ (message.contains("Unable to connect to VNC server")))
+ outputErrorMessageString = i18n("Server not found.");
+
+ if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
+ (message.contains("VNC connection failed: Too many authentication failures")))
+ outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
+
+ if (message.contains("VNC connection failed: Authentication failed"))
+ outputErrorMessageString = i18n("VNC authentication failed.");
+
+ if (message.contains("VNC server closed connection"))
+ outputErrorMessageString = i18n("VNC server closed connection.");
+
+ // internal messages, not displayed to user
+ if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
+ outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY";
+}
+
+VncClientThread::VncClientThread(QObject *parent)
+ : QThread(parent)
+ , frameBuffer(0)
+ , cl(0)
+ , m_stopped(false)
+{
+ QMutexLocker locker(&mutex);
+
+ QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
+ outputErrorMessagesCheckTimer->setInterval(500);
+ connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
+ outputErrorMessagesCheckTimer->start();
+}
+
+VncClientThread::~VncClientThread()
+{
+ if(isRunning()) {
+ stop();
+ terminate();
+ const bool quitSuccess = wait(1000);
+ kDebug(5011) << "Attempting to stop in deconstructor, will crash if this fails:" << quitSuccess;
+ }
+
+ if (cl) {
+ // Disconnect from vnc server & cleanup allocated resources
+ rfbClientCleanup(cl);
+ }
+
+ delete [] frameBuffer;
+}
+
+void VncClientThread::checkOutputErrorMessage()
+{
+ if (!outputErrorMessageString.isEmpty()) {
+ kDebug(5011) << outputErrorMessageString;
+ QString errorMessage = outputErrorMessageString;
+ outputErrorMessageString.clear();
+ // show authentication failure error only after the 3rd unsuccessful try
+ if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
+ outputErrorMessage(errorMessage);
+ }
+}
+
+void VncClientThread::setHost(const QString &host)
+{
+ QMutexLocker locker(&mutex);
+ m_host = host;
+}
+
+void VncClientThread::setPort(int port)
+{
+ QMutexLocker locker(&mutex);
+ m_port = port;
+}
+
+void VncClientThread::setQuality(RemoteView::Quality quality)
+{
+ m_quality = quality;
+ //set color depth dependent on quality
+ switch(quality) {
+ case RemoteView::Low:
+ setColorDepth(bpp8);
+ break;
+ case RemoteView::High:
+ setColorDepth(bpp32);
+ break;
+ case RemoteView::Medium:
+ default:
+ setColorDepth(bpp16);
+ }
+}
+
+void VncClientThread::setColorDepth(ColorDepth colorDepth)
+{
+ m_colorDepth= colorDepth;
+}
+
+RemoteView::Quality VncClientThread::quality() const
+{
+ return m_quality;
+}
+
+VncClientThread::ColorDepth VncClientThread::colorDepth() const
+{
+ return m_colorDepth;
+}
+
+void VncClientThread::setImage(const QImage &img)
+{
+ QMutexLocker locker(&mutex);
+ m_image = img;
+}
+
+const QImage VncClientThread::image(int x, int y, int w, int h)
+{
+ QMutexLocker locker(&mutex);
+
+ if (w == 0) // full image requested
+ return m_image;
+ else
+ return m_image.copy(x, y, w, h);
+}
+
+void VncClientThread::emitUpdated(int x, int y, int w, int h)
+{
+ emit imageUpdated(x, y, w, h);
+}
+
+void VncClientThread::emitGotCut(const QString &text)
+{
+ emit gotCut(text);
+}
+
+void VncClientThread::stop()
+{
+ QMutexLocker locker(&mutex);
+ m_stopped = true;
+}
+
+void VncClientThread::run()
+{
+ QMutexLocker locker(&mutex);
+
+ while (!m_stopped) { // try to connect as long as the server allows
+ locker.relock();
+ m_passwordError = false;
+ locker.unlock();
+
+ rfbClientLog = outputHandler;
+ rfbClientErr = outputHandler;
+ //24bit color dept in 32 bits per pixel = default. Will change colordepth and bpp later if needed
+ cl = rfbGetClient(8, 3, 4);
+ setClientColorDepth(cl, this->colorDepth());
+ cl->MallocFrameBuffer = newclient;
+ cl->canHandleNewFBSize = true;
+ cl->GetPassword = passwdHandler;
+ cl->GotFrameBufferUpdate = updatefb;
+ cl->GotXCutText = cuttext;
+ rfbClientSetClientData(cl, 0, this);
+
+ locker.relock();
+ cl->serverHost = strdup(m_host.toUtf8().constData());
+
+ if (m_port < 0 || !m_port) // port is invalid or empty...
+ m_port = 5900; // fallback: try an often used VNC port
+
+ if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
+ m_port += 5900;
+ cl->serverPort = m_port;
+ locker.unlock();
+
+ kDebug(5011) << "--------------------- trying init ---------------------";
+
+ if (rfbInitClient(cl, 0, 0))
+ break;
+ else
+ cl = 0;
+
+ locker.relock();
+ if (m_passwordError)
+ continue;
+
+ return;
+ }
+
+ locker.relock();
+ kDebug(5011) << "--------------------- Starting main VNC event loop ---------------------";
+ while (!m_stopped) {
+ locker.unlock();
+ const int i = WaitForMessage(cl, 500);
+ if (i < 0) {
+ break;
+ }
+ if (i) {
+ if (!HandleRFBServerMessage(cl)) {
+ break;
+ }
+ }
+
+ locker.relock();
+ while (!m_eventQueue.isEmpty()) {
+ ClientEvent* clientEvent = m_eventQueue.dequeue();
+ locker.unlock();
+ clientEvent->fire(cl);
+ delete clientEvent;
+ locker.relock();
+ }
+ }
+
+ m_stopped = true;
+}
+
+ClientEvent::~ClientEvent()
+{
+}
+
+void PointerClientEvent::fire(rfbClient* cl)
+{
+ SendPointerEvent(cl, m_x, m_y, m_buttonMask);
+}
+
+void KeyClientEvent::fire(rfbClient* cl)
+{
+ SendKeyEvent(cl, m_key, m_pressed);
+}
+
+void ClientCutEvent::fire(rfbClient* cl)
+{
+ SendClientCutText(cl, text.toUtf8().data(), text.size());
+}
+
+void VncClientThread::mouseEvent(int x, int y, int buttonMask)
+{
+ QMutexLocker lock(&mutex);
+ if (m_stopped)
+ return;
+
+ m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
+}
+
+void VncClientThread::keyEvent(int key, bool pressed)
+{
+ QMutexLocker lock(&mutex);
+ if (m_stopped)
+ return;
+
+ m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
+}
+
+void VncClientThread::clientCut(const QString &text)
+{
+ QMutexLocker lock(&mutex);
+ if (m_stopped)
+ return;
+
+ m_eventQueue.enqueue(new ClientCutEvent(text));
+}
+
+#include "moc_vncclientthread.cpp"
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
+**
+** This file is part of KDE.
+**
+** 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; see the file COPYING. If not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+** Boston, MA 02110-1301, USA.
+**
+****************************************************************************/
+
+#ifndef VNCCLIENTTHREAD_H
+#define VNCCLIENTTHREAD_H
+
+#ifdef QTONLY
+ #include <QDebug>
+ #define kDebug(n) qDebug()
+ #define kBacktrace() ""
+ #define i18n tr
+#else
+ #include <KDebug>
+ #include <KLocale>
+#endif
+
+#include "remoteview.h"
+
+#include <QQueue>
+#include <QThread>
+#include <QImage>
+#include <QMutex>
+
+extern "C" {
+#include <rfb/rfbclient.h>
+}
+
+class ClientEvent
+{
+public:
+ virtual ~ClientEvent();
+
+ virtual void fire(rfbClient*) = 0;
+};
+
+class KeyClientEvent : public ClientEvent
+{
+public:
+ KeyClientEvent(int key, int pressed)
+ : m_key(key), m_pressed(pressed) {}
+
+ void fire(rfbClient*);
+
+private:
+ int m_key;
+ int m_pressed;
+};
+
+class PointerClientEvent : public ClientEvent
+{
+public:
+ PointerClientEvent(int x, int y, int buttonMask)
+ : m_x(x), m_y(y), m_buttonMask(buttonMask) {}
+
+ void fire(rfbClient*);
+
+private:
+ int m_x;
+ int m_y;
+ int m_buttonMask;
+};
+
+class ClientCutEvent : public ClientEvent
+{
+public:
+ ClientCutEvent(const QString &text)
+ : text(text) {}
+
+ void fire(rfbClient*);
+
+private:
+ QString text;
+};
+
+class VncClientThread: public QThread
+{
+ Q_OBJECT
+
+public:
+ Q_ENUMS(ColorDepth)
+
+ enum ColorDepth {
+ bpp32,
+ bpp16,
+ bpp8
+ };
+
+ explicit VncClientThread(QObject *parent = 0);
+ ~VncClientThread();
+ const QImage image(int x = 0, int y = 0, int w = 0, int h = 0);
+ void setImage(const QImage &img);
+ void emitUpdated(int x, int y, int w, int h);
+ void emitGotCut(const QString &text);
+ void stop();
+ void setHost(const QString &host);
+ void setPort(int port);
+ void setQuality(RemoteView::Quality quality);
+ void setPassword(const QString &password) {
+ m_password = password;
+ }
+ const QString password() const {
+ return m_password;
+ }
+
+ RemoteView::Quality quality() const;
+ ColorDepth colorDepth() const;
+ uint8_t *frameBuffer;
+
+signals:
+ void imageUpdated(int x, int y, int w, int h);
+ void gotCut(const QString &text);
+ void passwordRequest();
+ void outputErrorMessage(const QString &message);
+
+public slots:
+ void mouseEvent(int x, int y, int buttonMask);
+ void keyEvent(int key, bool pressed);
+ void clientCut(const QString &text);
+
+protected:
+ void run();
+
+private:
+ static void setClientColorDepth(rfbClient *cl, ColorDepth cd);
+ void setColorDepth(ColorDepth colorDepth);
+ //these static methods are callback functions for libvncclient
+ static rfbBool newclient(rfbClient *cl);
+ static void updatefb(rfbClient *cl, int x, int y, int w, int h);
+ static void cuttext(rfbClient *cl, const char *text, int textlen);
+ static char* passwdHandler(rfbClient *cl);
+ static void outputHandler(const char *format, ...);
+
+ QImage m_image;
+ rfbClient *cl;
+ QString m_host;
+ QString m_password;
+ int m_port;
+ QMutex mutex;
+ RemoteView::Quality m_quality;
+ ColorDepth m_colorDepth;
+ QQueue<ClientEvent* > m_eventQueue;
+ //color table for 8bit indexed colors
+ static QVector<QRgb> m_colorTable;
+
+ volatile bool m_stopped;
+ volatile bool m_passwordError;
+
+private slots:
+ void checkOutputErrorMessage();
+};
+
+#endif
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2007-2012 Urs Wolfer <uwolfer @ kde.org>
+**
+** This file is part of KDE.
+**
+** 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; see the file COPYING. If not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+** Boston, MA 02110-1301, USA.
+**
+****************************************************************************/
+
+#include "vncview.h"
+
+#ifdef QTONLY
+ #include <QMessageBox>
+ #include <QInputDialog>
+ #define KMessageBox QMessageBox
+ /*#define error(parent, message, caption) \
+ critical(parent, caption, message)*/
+#else
+ #include "settings.h"
+ #include <KActionCollection>
+ #include <KMainWindow>
+ #include <KMessageBox>
+ #include <KPasswordDialog>
+ #include <KXMLGUIClient>
+#endif
+
+#include <QApplication>
+#include <QImage>
+#include <QPainter>
+#include <QMouseEvent>
+
+// Definition of key modifier mask constants
+#define KMOD_Alt_R 0x01
+#define KMOD_Alt_L 0x02
+#define KMOD_Meta_L 0x04
+#define KMOD_Control_L 0x08
+#define KMOD_Shift_L 0x10
+
+VncView::VncView(QWidget *parent, const KUrl &url, KConfigGroup configGroup)
+ : RemoteView(parent),
+ m_initDone(false),
+ m_buttonMask(0),
+ m_repaint(false),
+ m_quitFlag(false),
+ m_firstPasswordTry(true),
+ m_dontSendClipboard(false),
+ m_horizontalFactor(1.0),
+ m_verticalFactor(1.0),
+ m_forceLocalCursor(false)
+{
+ m_url = url;
+ m_host = url.host();
+ m_port = url.port();
+
+ connect(&vncThread, SIGNAL(imageUpdated(int,int,int,int)), this, SLOT(updateImage(int,int,int,int)), Qt::BlockingQueuedConnection);
+ connect(&vncThread, SIGNAL(gotCut(QString)), this, SLOT(setCut(QString)), Qt::BlockingQueuedConnection);
+ connect(&vncThread, SIGNAL(passwordRequest()), this, SLOT(requestPassword()), Qt::BlockingQueuedConnection);
+ connect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));
+
+ m_clipboard = QApplication::clipboard();
+ connect(m_clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
+
+#ifndef QTONLY
+ m_hostPreferences = new VncHostPreferences(configGroup, this);
+#else
+ Q_UNUSED(configGroup);
+#endif
+}
+
+VncView::~VncView()
+{
+ startQuitting();
+}
+
+bool VncView::eventFilter(QObject *obj, QEvent *event)
+{
+ if (m_viewOnly) {
+ if (event->type() == QEvent::KeyPress ||
+ event->type() == QEvent::KeyRelease ||
+ event->type() == QEvent::MouseButtonDblClick ||
+ event->type() == QEvent::MouseButtonPress ||
+ event->type() == QEvent::MouseButtonRelease ||
+ event->type() == QEvent::Wheel ||
+ event->type() == QEvent::MouseMove)
+ return true;
+ }
+
+ return RemoteView::eventFilter(obj, event);
+}
+
+QSize VncView::framebufferSize()
+{
+ return m_frame.size();
+}
+
+QSize VncView::sizeHint() const
+{
+ return size();
+}
+
+QSize VncView::minimumSizeHint() const
+{
+ return size();
+}
+
+void VncView::scaleResize(int w, int h)
+{
+ RemoteView::scaleResize(w, h);
+
+ kDebug(5011) << w << h;
+ if (m_scale) {
+ m_verticalFactor = (qreal) h / m_frame.height();
+ m_horizontalFactor = (qreal) w / m_frame.width();
+
+#ifndef QTONLY
+ if (Settings::keepAspectRatio()) {
+ m_verticalFactor = m_horizontalFactor = qMin(m_verticalFactor, m_horizontalFactor);
+ }
+#else
+ m_verticalFactor = m_horizontalFactor = qMin(m_verticalFactor, m_horizontalFactor);
+#endif
+
+ const qreal newW = m_frame.width() * m_horizontalFactor;
+ const qreal newH = m_frame.height() * m_verticalFactor;
+ setMaximumSize(newW, newH); //This is a hack to force Qt to center the view in the scroll area
+ resize(newW, newH);
+ }
+}
+
+void VncView::updateConfiguration()
+{
+ RemoteView::updateConfiguration();
+
+ // Update the scaling mode in case KeepAspectRatio changed
+ scaleResize(parentWidget()->width(), parentWidget()->height());
+}
+
+void VncView::startQuitting()
+{
+ kDebug(5011) << "about to quit";
+
+ setStatus(Disconnecting);
+
+ m_quitFlag = true;
+
+ vncThread.stop();
+
+ unpressModifiers();
+
+ // Disconnect all signals so that we don't get any more callbacks from the client thread
+ bool imageUpdatedDisconnect = disconnect(&vncThread, SIGNAL(imageUpdated(int,int,int,int)), this, SLOT(updateImage(int,int,int,int)));
+ bool gotCutDisconnect = disconnect(&vncThread, SIGNAL(gotCut(QString)), this, SLOT(setCut(QString)));
+ bool passwordRequestDisconnect = disconnect(&vncThread, SIGNAL(passwordRequest()), this, SLOT(requestPassword()));
+ bool outputErrorMessageDisconnect = disconnect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));
+
+ kDebug(5011) << "Signals disconnected: imageUpdated: " << imageUpdatedDisconnect << "gotCut: " << gotCutDisconnect << "passwordRequest: " << passwordRequestDisconnect << "outputErrorMessage: " << outputErrorMessageDisconnect;
+
+ vncThread.quit();
+
+ const bool quitSuccess = vncThread.wait(1000);
+
+ kDebug(5011) << "Quit VNC thread success:" << quitSuccess;
+
+ setStatus(Disconnected);
+}
+
+bool VncView::isQuitting()
+{
+ return m_quitFlag;
+}
+
+bool VncView::start()
+{
+ vncThread.setHost(m_host);
+ vncThread.setPort(m_port);
+ RemoteView::Quality quality;
+#ifdef QTONLY
+ quality = (RemoteView::Quality)((QCoreApplication::arguments().count() > 2) ?
+ QCoreApplication::arguments().at(2).toInt() : 2);
+#else
+ quality = m_hostPreferences->quality();
+#endif
+
+ vncThread.setQuality(quality);
+
+ kDebug(5011) << "Host: " << m_host;
+ kDebug(5011) << "Port: " << m_port;
+ kDebug(5011) << "Quality: " << quality;
+
+ // set local cursor on by default because low quality mostly means slow internet connection
+ if (quality == RemoteView::Low) {
+ showDotCursor(RemoteView::CursorOn);
+#ifndef QTONLY
+ // KRDC does always just have one main window, so at(0) is safe
+ KXMLGUIClient *mainWindow = dynamic_cast<KXMLGUIClient*>(KMainWindow::memberList().at(0));
+ if (mainWindow)
+ mainWindow->actionCollection()->action("show_local_cursor")->setChecked(true);
+#endif
+ }
+
+ setStatus(Connecting);
+
+ vncThread.start();
+ return true;
+}
+
+bool VncView::supportsScaling() const
+{
+ return true;
+}
+
+bool VncView::supportsLocalCursor() const
+{
+ return true;
+}
+
+void VncView::requestPassword()
+{
+ kDebug(5011) << "request password";
+
+ setStatus(Authenticating);
+
+ kDebug(5011) << "After setting authentication state";
+/*#ifndef QTONLY
+ // just try to get the passwort from the wallet the first time, otherwise it will loop (see issue #226283)
+ if (m_firstPasswordTry && m_hostPreferences->walletSupport()) {
+ QString walletPassword = readWalletPassword();
+
+ if (!walletPassword.isNull()) {
+ vncThread.setPassword(walletPassword);
+ m_firstPasswordTry = false;
+ return;
+ }
+ }
+#endif
+
+ if (m_firstPasswordTry && !m_url.password().isNull()) {
+ vncThread.setPassword(m_url.password());
+ m_firstPasswordTry = false;
+ return;
+ }*/
+
+#ifdef QTONLY
+ bool ok = true;
+ QString password = QInputDialog::getText(this, //krazy:exclude=qclasses
+ tr("Password required"),
+ tr("Please enter the password for the remote desktop:"),
+ QLineEdit::Password, QString(), &ok);
+ m_firstPasswordTry = false;
+ if (ok)
+ vncThread.setPassword(password);
+ else
+ startQuitting();
+#else
+ KPasswordDialog dialog(this);
+ dialog.setPrompt(m_firstPasswordTry ? i18n("Access to the system requires a password.")
+ : i18n("Authentication failed. Please try again."));
+ if (dialog.exec() == KPasswordDialog::Accepted) {
+ m_firstPasswordTry = false;
+ vncThread.setPassword(dialog.password());
+ } else {
+ kDebug(5011) << "password dialog not accepted";
+ startQuitting();
+ }
+#endif
+}
+
+void VncView::outputErrorMessage(const QString &message)
+{
+ kDebug(5011) << message;
+
+ if (message == "INTERNAL:APPLE_VNC_COMPATIBILTY") {
+ setCursor(localDotCursor());
+ m_forceLocalCursor = true;
+ return;
+ }
+
+ startQuitting();
+
+#ifndef QTONLY
+ KMessageBox::error(this, message, i18n("VNC failure"));
+#endif
+ emit errorMessage(i18n("VNC failure"), message);
+}
+
+#ifndef QTONLY
+HostPreferences* VncView::hostPreferences()
+{
+ return m_hostPreferences;
+}
+#endif
+
+void VncView::updateImage(int x, int y, int w, int h)
+{
+// kDebug(5011) << "got update" << width() << height();
+
+ m_x = x;
+ m_y = y;
+ m_w = w;
+ m_h = h;
+
+ if (m_horizontalFactor != 1.0 || m_verticalFactor != 1.0) {
+ // If the view is scaled, grow the update rectangle to avoid artifacts
+ m_x-=1;
+ m_y-=1;
+ m_w+=2;
+ m_h+=2;
+ }
+
+ m_frame = vncThread.image();
+
+ if (!m_initDone) {
+ setAttribute(Qt::WA_StaticContents);
+ setAttribute(Qt::WA_OpaquePaintEvent);
+ installEventFilter(this);
+
+ setCursor(((m_dotCursorState == CursorOn) || m_forceLocalCursor) ? localDotCursor() : Qt::BlankCursor);
+
+ setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
+ setFocusPolicy(Qt::WheelFocus);
+ setStatus(Connected);
+ emit connected();
+
+ if (m_scale) {
+#ifndef QTONLY
+ kDebug(5011) << "Setting initial size w:" <<m_hostPreferences->width() << " h:" << m_hostPreferences->height();
+ emit framebufferSizeChanged(m_hostPreferences->width(), m_hostPreferences->height());
+ scaleResize(m_hostPreferences->width(), m_hostPreferences->height());
+ kDebug() << "m_frame.size():" << m_frame.size() << "size()" << size();
+#else
+//TODO: qtonly alternative
+#endif
+ }
+
+ m_initDone = true;
+
+#ifndef QTONLY
+ if (m_hostPreferences->walletSupport()) {
+ saveWalletPassword(vncThread.password());
+ }
+#endif
+ }
+
+ if ((y == 0 && x == 0) && (m_frame.size() != size())) {
+ kDebug(5011) << "Updating framebuffer size";
+ if (m_scale) {
+ setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+ if (parentWidget())
+ scaleResize(parentWidget()->width(), parentWidget()->height());
+ } else {
+ kDebug(5011) << "Resizing: " << m_frame.width() << m_frame.height();
+ resize(m_frame.width(), m_frame.height());
+ setMaximumSize(m_frame.width(), m_frame.height()); //This is a hack to force Qt to center the view in the scroll area
+ setMinimumSize(m_frame.width(), m_frame.height());
+ emit framebufferSizeChanged(m_frame.width(), m_frame.height());
+ }
+ }
+
+ m_repaint = true;
+ repaint(qRound(m_x * m_horizontalFactor), qRound(m_y * m_verticalFactor), qRound(m_w * m_horizontalFactor), qRound(m_h * m_verticalFactor));
+ m_repaint = false;
+}
+
+void VncView::setViewOnly(bool viewOnly)
+{
+ RemoteView::setViewOnly(viewOnly);
+
+ m_dontSendClipboard = viewOnly;
+
+ if (viewOnly)
+ setCursor(Qt::ArrowCursor);
+ else
+ setCursor(m_dotCursorState == CursorOn ? localDotCursor() : Qt::BlankCursor);
+}
+
+void VncView::showDotCursor(DotCursorState state)
+{
+ RemoteView::showDotCursor(state);
+
+ setCursor(state == CursorOn ? localDotCursor() : Qt::BlankCursor);
+}
+
+void VncView::enableScaling(bool scale)
+{
+ RemoteView::enableScaling(scale);
+
+ if (scale) {
+ setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+ setMinimumSize(1, 1);
+ if (parentWidget())
+ scaleResize(parentWidget()->width(), parentWidget()->height());
+ } else {
+ m_verticalFactor = 1.0;
+ m_horizontalFactor = 1.0;
+
+ setMaximumSize(m_frame.width(), m_frame.height()); //This is a hack to force Qt to center the view in the scroll area
+ setMinimumSize(m_frame.width(), m_frame.height());
+ resize(m_frame.width(), m_frame.height());
+ }
+}
+
+void VncView::setCut(const QString &text)
+{
+ m_dontSendClipboard = true;
+ m_clipboard->setText(text, QClipboard::Clipboard);
+ m_dontSendClipboard = false;
+}
+
+void VncView::paintEvent(QPaintEvent *event)
+{
+// kDebug(5011) << "paint event: x: " << m_x << ", y: " << m_y << ", w: " << m_w << ", h: " << m_h;
+ if (m_frame.isNull() || m_frame.format() == QImage::Format_Invalid) {
+ kDebug(5011) << "no valid image to paint";
+ RemoteView::paintEvent(event);
+ return;
+ }
+
+ event->accept();
+
+ QPainter painter(this);
+
+ if (m_repaint) {
+// kDebug(5011) << "normal repaint";
+ painter.drawImage(QRect(qRound(m_x*m_horizontalFactor), qRound(m_y*m_verticalFactor),
+ qRound(m_w*m_horizontalFactor), qRound(m_h*m_verticalFactor)),
+ m_frame.copy(m_x, m_y, m_w, m_h).scaled(qRound(m_w*m_horizontalFactor),
+ qRound(m_h*m_verticalFactor),
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ } else {
+// kDebug(5011) << "resize repaint";
+ QRect rect = event->rect();
+ if (rect.width() != width() || rect.height() != height()) {
+// kDebug(5011) << "Partial repaint";
+ const int sx = rect.x()/m_horizontalFactor;
+ const int sy = rect.y()/m_verticalFactor;
+ const int sw = rect.width()/m_horizontalFactor;
+ const int sh = rect.height()/m_verticalFactor;
+ painter.drawImage(rect,
+ m_frame.copy(sx, sy, sw, sh).scaled(rect.width(), rect.height(),
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ } else {
+// kDebug(5011) << "Full repaint" << width() << height() << m_frame.width() << m_frame.height();
+ painter.drawImage(QRect(0, 0, width(), height()),
+ m_frame.scaled(m_frame.width() * m_horizontalFactor, m_frame.height() * m_verticalFactor,
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ }
+ }
+
+ RemoteView::paintEvent(event);
+}
+
+void VncView::resizeEvent(QResizeEvent *event)
+{
+ RemoteView::resizeEvent(event);
+ update();
+}
+
+bool VncView::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+// kDebug(5011) << "keyEvent";
+ keyEventHandler(static_cast<QKeyEvent*>(event));
+ return true;
+ break;
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+// kDebug(5011) << "mouseEvent";
+ mouseEventHandler(static_cast<QMouseEvent*>(event));
+ return true;
+ break;
+ case QEvent::Wheel:
+// kDebug(5011) << "wheelEvent";
+ wheelEventHandler(static_cast<QWheelEvent*>(event));
+ return true;
+ break;
+ default:
+ return RemoteView::event(event);
+ }
+}
+
+void VncView::mouseEventHandler(QMouseEvent *e)
+{
+ if (e->type() != QEvent::MouseMove) {
+ if ((e->type() == QEvent::MouseButtonPress) ||
+ (e->type() == QEvent::MouseButtonDblClick)) {
+ if (e->button() & Qt::LeftButton)
+ m_buttonMask |= 0x01;
+ if (e->button() & Qt::MidButton)
+ m_buttonMask |= 0x02;
+ if (e->button() & Qt::RightButton)
+ m_buttonMask |= 0x04;
+ } else if (e->type() == QEvent::MouseButtonRelease) {
+ if (e->button() & Qt::LeftButton)
+ m_buttonMask &= 0xfe;
+ if (e->button() & Qt::MidButton)
+ m_buttonMask &= 0xfd;
+ if (e->button() & Qt::RightButton)
+ m_buttonMask &= 0xfb;
+ }
+ }
+
+ vncThread.mouseEvent(qRound(e->x() / m_horizontalFactor), qRound(e->y() / m_verticalFactor), m_buttonMask);
+}
+
+void VncView::wheelEventHandler(QWheelEvent *event)
+{
+ int eb = 0;
+ if (event->delta() < 0)
+ eb |= 0x10;
+ else
+ eb |= 0x8;
+
+ const int x = qRound(event->x() / m_horizontalFactor);
+ const int y = qRound(event->y() / m_verticalFactor);
+
+ vncThread.mouseEvent(x, y, eb | m_buttonMask);
+ vncThread.mouseEvent(x, y, m_buttonMask);
+}
+
+void VncView::keyEventHandler(QKeyEvent *e)
+{
+ // strip away autorepeating KeyRelease; see bug #206598
+ if (e->isAutoRepeat() && (e->type() == QEvent::KeyRelease))
+ return;
+
+// parts of this code are based on http://italc.sourcearchive.com/documentation/1.0.9.1/vncview_8cpp-source.html
+ rfbKeySym k = e->nativeVirtualKey();
+
+ // we do not handle Key_Backtab separately as the Shift-modifier
+ // is already enabled
+ if (e->key() == Qt::Key_Backtab) {
+ k = XK_Tab;
+ }
+
+ const bool pressed = (e->type() == QEvent::KeyPress);
+
+ // handle modifiers
+ if (k == XK_Shift_L || k == XK_Control_L || k == XK_Meta_L || k == XK_Alt_L) {
+ if (pressed) {
+ m_mods[k] = true;
+ } else if (m_mods.contains(k)) {
+ m_mods.remove(k);
+ } else {
+ unpressModifiers();
+ }
+ }
+
+ if (k) {
+ vncThread.keyEvent(k, pressed);
+ }
+}
+
+void VncView::unpressModifiers()
+{
+ const QList<unsigned int> keys = m_mods.keys();
+ QList<unsigned int>::const_iterator it = keys.constBegin();
+ while (it != keys.end()) {
+ vncThread.keyEvent(*it, false);
+ it++;
+ }
+ m_mods.clear();
+}
+
+void VncView::clipboardDataChanged()
+{
+ kDebug(5011);
+
+ if (m_status != Connected)
+ return;
+
+ if (m_clipboard->ownsClipboard() || m_dontSendClipboard)
+ return;
+
+ const QString text = m_clipboard->text(QClipboard::Clipboard);
+
+ vncThread.clientCut(text);
+}
+
+#include "moc_vncview.cpp"
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2007-2012 Urs Wolfer <uwolfer @ kde.org>
+**
+** This file is part of KDE.
+**
+** 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; see the file COPYING. If not, write to
+** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+** Boston, MA 02110-1301, USA.
+**
+****************************************************************************/
+
+#ifndef VNCVIEW_H
+#define VNCVIEW_H
+
+#include "remoteview.h"
+#include "vncclientthread.h"
+
+#ifdef QTONLY
+ class KConfigGroup{};
+#else
+ #include "vnchostpreferences.h"
+#endif
+
+#include <QClipboard>
+
+extern "C" {
+#include <rfb/rfbclient.h>
+}
+
+class VncView: public RemoteView
+{
+ Q_OBJECT
+
+public:
+ explicit VncView(QWidget *parent = 0, const KUrl &url = KUrl(), KConfigGroup configGroup = KConfigGroup());
+ ~VncView();
+
+ QSize framebufferSize();
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+ void startQuitting();
+ bool isQuitting();
+ bool start();
+ bool supportsScaling() const;
+ bool supportsLocalCursor() const;
+
+#ifndef QTONLY
+ HostPreferences* hostPreferences();
+#endif
+
+ void setViewOnly(bool viewOnly);
+ void showDotCursor(DotCursorState state);
+ void enableScaling(bool scale);
+
+ virtual void updateConfiguration();
+
+public slots:
+ void scaleResize(int w, int h);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+ bool event(QEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+ VncClientThread vncThread;
+ QClipboard *m_clipboard;
+ bool m_initDone;
+ int m_buttonMask;
+ QMap<unsigned int, bool> m_mods;
+ int m_x, m_y, m_w, m_h;
+ bool m_repaint;
+ bool m_quitFlag;
+ bool m_firstPasswordTry;
+ bool m_dontSendClipboard;
+ qreal m_horizontalFactor;
+ qreal m_verticalFactor;
+#ifndef QTONLY
+ VncHostPreferences *m_hostPreferences;
+#endif
+ QImage m_frame;
+ bool m_forceLocalCursor;
+
+ void keyEventHandler(QKeyEvent *e);
+ void unpressModifiers();
+ void wheelEventHandler(QWheelEvent *event);
+ void mouseEventHandler(QMouseEvent *event);
+
+private slots:
+ void updateImage(int x, int y, int w, int h);
+ void setCut(const QString &text);
+ void requestPassword();
+ void outputErrorMessage(const QString &message);
+ void clipboardDataChanged();
+};
+
+#endif
--- /dev/null
+<RCC>
+ <qresource prefix="/images">
+ <file>images/exit.png</file>
+ <file>images/icon_no_preview.png</file>
+ <file>images/new_vm.png</file>
+ <file>images/palacios.png</file>
+ <file>images/vm_pause.png</file>
+ <file>images/vm_refresh.png</file>
+ <file>images/vm_start.png</file>
+ <file>images/vm_stop.png</file>
+ <file>images/start_vm.png</file>
+ <file>images/stop_vm.png</file>
+ <file>images/activate_vm.png</file>
+ <file>images/pause_vm.png</file>
+ <file>images/reload_vm.png</file>
+ </qresource>
+</RCC>