/* * Remote Laboratory Administration Console Part * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (c) 2012 Timothy Pearson * Raptor Engineering * http://www.raptorengineeringinc.com */ #include "define.h" #include "part.h" #include //::createAboutData() #include #include #include #include #include //::start() #include #include #include #include #include #include #include //encodeName() #include //postInit() hack #include #include #include #include #include #include #include #include #include #include #include #include //access() #include #include #include "tracewidget.h" #include "floatspinbox.h" #include "layout.h" #include "terminatedlg.h" #define NETWORK_COMM_TIMEOUT_MS 2500 enum connectionModes { ModeIdle = 0, ModeTerminate = 1 }; enum connectionStates { ModeIdle_None = 0, ModeIdle_StateTerminalListRequest = 1, ModeIdle_StateProcessTerminalList = 2, ModeIdle_StateWorkspaceListRequest = 3, ModeIdle_StateProcessWorkspaceList = 4, ModeIdle_StateDelay = 5, ModeTerminate_TerminalServices = 6, ModeTerminate_LaboratoryWorkspace = 7, ModeTerminate_CancelTerminationOfWorkspace = 8, ModeTerminate_ProcessResponse = 9 }; namespace RemoteLab { typedef KParts::GenericFactory Factory; #define CLIENT_LIBRARY "libremotelab_adminconsole" K_EXPORT_COMPONENT_FACTORY(libremotelab_adminconsole, RemoteLab::Factory) AdminConsolePart::AdminConsolePart(TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, const TQStringList&) : RemoteInstrumentPart( parent, name ), m_base(NULL), m_commHandlerState(0), m_connectionActiveAndValid(false), m_tickerState(0) { // Initialize important base class variables m_clientLibraryName = CLIENT_LIBRARY; // Initialize mutex m_connectionMutex = new TQMutex(false); // Initialize kpart setInstance(Factory::instance()); setWidget(new TQVBox(parentWidget, widgetName)); // Create timers m_forcedUpdateTimer = new TQTimer(this); connect(m_forcedUpdateTimer, SIGNAL(timeout()), this, SLOT(mainEventLoop())); m_updateTimeoutTimer = new TQTimer(this); connect(m_updateTimeoutTimer, SIGNAL(timeout()), this, SLOT(mainEventLoop())); m_pingDelayTimer = new TQTimer(this); connect(m_pingDelayTimer, SIGNAL(timeout()), this, SLOT(mainEventLoop())); // Create widgets m_base = new AdminConsoleBase(widget()); // Initialize widgets connect(m_base->ts_buttonKill, SIGNAL(clicked()), this, SLOT(terminalServiceKillButtonClicked())); connect(m_base->workspace_buttonTerminate, SIGNAL(clicked()), this, SLOT(workspaceTerminateButtonClicked())); connect(m_base->workspace_buttonCancelTermination, SIGNAL(clicked()), this, SLOT(workspaceCancelTerminationButtonClicked())); m_base->ts_list->setAllColumnsShowFocus(true); connect(m_base->ts_list, SIGNAL(selectionChanged()), this, SLOT(terminalServiceListSelect())); m_base->workspace_list->setAllColumnsShowFocus(true); connect(m_base->workspace_list, SIGNAL(selectionChanged()), this, SLOT(workspaceListSelect())); TQTimer::singleShot(0, this, TQT_SLOT(postInit())); } AdminConsolePart::~AdminConsolePart() { if (m_connectionMutex->locked()) { printf("[WARNING] Exiting when data transfer still in progress!\n\r"); fflush(stdout); } disconnectFromServer(); delete m_connectionMutex; } void AdminConsolePart::processLockouts() { TQWidget* mainWidget = widget(); if (mainWidget) { if ((m_socket) && (m_socket->state() == TQSocket::Connected) && (connToServerState > 0) && (connToServerConnecting == false) && (m_commHandlerNextMode == ModeIdle)) { mainWidget->setEnabled(true); } else { mainWidget->setEnabled(false); } } if ((m_connectionActiveAndValid == true) && (m_base->ts_list->selectedItem())) { m_base->ts_buttonKill->setEnabled(true); } else { m_base->ts_buttonKill->setEnabled(false); } if ((m_connectionActiveAndValid == true) && (m_base->workspace_list->selectedItem())) { m_base->workspace_buttonTerminate->setEnabled(true); } else { m_base->workspace_buttonTerminate->setEnabled(false); } if ((m_connectionActiveAndValid == true) && (m_base->workspace_list->selectedItem())) { m_base->workspace_buttonCancelTermination->setEnabled(true); } else { m_base->workspace_buttonCancelTermination->setEnabled(false); } } void AdminConsolePart::resizeToHint() { resize(widget()->sizeHint()); } void AdminConsolePart::connectionClosed() { closeURL(); } void AdminConsolePart::postInit() { setUsingFixedSize(false); } bool AdminConsolePart::openURL(const KURL &url) { int ret; ret = connectToServer(url.url()); processLockouts(); return (ret != 0); } bool AdminConsolePart::closeURL() { disconnectFromServer(); m_url = KURL(); return true; } void AdminConsolePart::disconnectFromServerCallback() { m_forcedUpdateTimer->stop(); m_updateTimeoutTimer->stop(); } void AdminConsolePart::connectionFinishedCallback() { connect(m_socket, SIGNAL(readyRead()), m_socket, SLOT(processPendingData())); m_socket->processPendingData(); connect(m_socket, SIGNAL(newDataReceived()), this, SLOT(mainEventLoop())); m_tickerState = 0; m_commHandlerState = ModeIdle_StateTerminalListRequest; m_commHandlerMode = ModeIdle; m_commHandlerNextState = ModeIdle_None; m_commHandlerNextMode = ModeIdle; m_socket->setDataTimeout(NETWORK_COMM_TIMEOUT_MS); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); processLockouts(); mainEventLoop(); return; } void AdminConsolePart::connectionStatusChangedCallback() { processLockouts(); } void AdminConsolePart::terminalServiceKillButtonClicked() { TQListViewItem* item = m_base->ts_list->selectedItem(); TerminateDialog termdlg(0); termdlg.setWarningLabelText(i18n("Configure termination of Terminal Services for user %1").arg(item->text(1))); termdlg.enableDelayedTermination(false); if (termdlg.exec() == TQDialog::Accepted) { terminateSessionID = item->text(0); terminateSessionDelayMinutes = termdlg.minutes(); m_commHandlerNextState = ModeTerminate_TerminalServices; m_commHandlerNextMode = ModeTerminate; } processLockouts(); } void AdminConsolePart::workspaceTerminateButtonClicked() { TQListViewItem* item = m_base->workspace_list->selectedItem(); TerminateDialog termdlg(0); termdlg.setWarningLabelText(i18n("Configure termination of Laboratory Workspace for user %1").arg(item->text(1))); termdlg.enableDelayedTermination(true); if (termdlg.exec() == TQDialog::Accepted) { terminateSessionID = item->text(0); terminateSessionDelayMinutes = termdlg.minutes(); m_commHandlerNextState = ModeTerminate_LaboratoryWorkspace; m_commHandlerNextMode = ModeTerminate; } processLockouts(); } void AdminConsolePart::workspaceCancelTerminationButtonClicked() { TQListViewItem* item = m_base->workspace_list->selectedItem(); terminateSessionID = item->text(0); m_commHandlerNextState = ModeTerminate_CancelTerminationOfWorkspace; m_commHandlerNextMode = ModeTerminate; processLockouts(); } void AdminConsolePart::terminalServiceListSelect() { // Highlight the matching user session in the workspace list, // or deselect all workspace list items if no matching workspace session is found TQListViewItem* item = m_base->ts_list->selectedItem(); if (item) { TQString username = item->text(1); TQListViewItemIterator it; it = TQListViewItemIterator(m_base->workspace_list); bool found = false; TQListViewItem* item2 = NULL; while (it.current()) { item2 = *it; if (item2->text(1) == username) { found = true; break; } ++it; } if (found) { m_base->workspace_list->setCurrentItem(item2); } else { m_base->workspace_list->clearSelection(); } } else { m_base->workspace_list->clearSelection(); } processLockouts(); } void AdminConsolePart::workspaceListSelect() { // Highlight the matching user session in the terminal services list, // or deselect all workspace list items if no matching terminal services session is found TQListViewItem* item = m_base->workspace_list->selectedItem(); if (item) { TQString username = item->text(1); TQListViewItemIterator it; it = TQListViewItemIterator(m_base->ts_list); bool found = false; TQListViewItem* item2 = NULL; while (it.current()) { item2 = *it; if (item2->text(1) == username) { found = true; break; } ++it; } if (found) { m_base->ts_list->setCurrentItem(item2); } else { m_base->ts_list->clearSelection(); } } else { m_base->ts_list->clearSelection(); } processLockouts(); } #define UPDATEDISPLAY_TIMEOUT m_connectionActiveAndValid = false; \ m_tickerState = 0; \ m_commHandlerState = ModeIdle_StateTerminalListRequest; \ m_commHandlerMode = ModeIdle; \ m_commHandlerNextState = ModeIdle_None; \ m_commHandlerNextMode = ModeIdle; \ m_socket->clearIncomingData(); \ setStatusMessage(i18n("Server ping timeout. Please verify the status of your network connection.")); \ m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); \ m_connectionMutex->unlock(); \ return; #define SET_WATCHDOG_TIMER if (!m_updateTimeoutTimer->isActive()) m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); #define PAT_WATCHDOG_TIMER m_updateTimeoutTimer->stop(); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); #define SET_NEXT_STATE(x) m_commHandlerState = x; #define EXEC_NEXT_STATE_IMMEDIATELY m_forcedUpdateTimer->start(0, TRUE); void AdminConsolePart::setTickerMessage(TQString message) { m_connectionActiveAndValid = true; TQString tickerChar; switch (m_tickerState) { case 0: tickerChar = "-"; break; case 1: tickerChar = "\\"; break; case 2: tickerChar = "|"; break; case 3: tickerChar = "/"; break; } setStatusMessage(message + TQString("... %1").arg(tickerChar)); m_tickerState++; if (m_tickerState > 3) { m_tickerState = 0; } } void AdminConsolePart::mainEventLoop() { TQDataStream ds(m_socket); ds.setPrintableData(true); if (!m_connectionMutex->tryLock()) { EXEC_NEXT_STATE_IMMEDIATELY return; } if (m_socket) { if (m_commHandlerMode == ModeIdle) { // Normal operation switch (m_commHandlerState) { case ModeIdle_StateTerminalListRequest: // Get status of remote system // Clear buffers to synchronize frames in case of data corruption m_socket->clearIncomingData(); ds << TQString("USERS"); ds << TQString("TERMINALS"); m_socket->writeEndOfFrame(); SET_NEXT_STATE(ModeIdle_StateProcessTerminalList) break; case ModeIdle_StateProcessTerminalList: // Get all data if (m_socket->canReadFrame()) { PAT_WATCHDOG_TIMER TQString status; TerminalServiceStatusList list; ds >> status; if (status == "OK") { ds >> list; m_socket->clearFrameTail(); TerminalServiceStatusList::iterator it; TQListViewItemIterator it2; for (it = list.begin(); it != list.end(); ++it) { TerminalServiceStatusType info = *it; it2 = TQListViewItemIterator(m_base->ts_list); bool found = false; TQListViewItem* item = NULL; while (it2.current()) { item = *it2; if (info.sessionID == item->text(0).toInt()) { found = true; break; } ++it2; } if (!found) { item = new TQListViewItem(m_base->ts_list); } item->setText(0, TQString("%1").arg(info.sessionID)); item->setText(1, info.username); item->setText(2, info.serverName); item->setText(3, TQString("%1").arg(info.serverPID)); item->setText(4, TQString("%1").arg(info.wmPID)); item->setText(5, TQString(":%1").arg(info.display)); item->setText(6, info.loginStamp.toString()); item->setText(7, info.activityStamp.toString()); } it2 = TQListViewItemIterator(m_base->ts_list); while (it2.current()) { TQListViewItem* item = *it2; bool found = false; for (it = list.begin(); it != list.end(); ++it) { TerminalServiceStatusType info = *it; if (info.sessionID == item->text(0).toInt()) { found = true; } } if (!found) { delete item; } ++it2; } setTickerMessage(i18n("Connected")); } SET_NEXT_STATE(ModeIdle_StateWorkspaceListRequest); EXEC_NEXT_STATE_IMMEDIATELY } else { if (!m_updateTimeoutTimer->isActive()) { UPDATEDISPLAY_TIMEOUT } } break; case ModeIdle_StateWorkspaceListRequest: // Get status of remote system // Clear buffers to synchronize frames in case of data corruption m_socket->clearIncomingData(); ds << TQString("USERS"); ds << TQString("WORKSPACES"); m_socket->writeEndOfFrame(); SET_NEXT_STATE(ModeIdle_StateProcessWorkspaceList) EXEC_NEXT_STATE_IMMEDIATELY break; case ModeIdle_StateProcessWorkspaceList: // Get all data if (m_socket->canReadFrame()) { PAT_WATCHDOG_TIMER TQString status; WorkspaceServiceStatusList list; ds >> status; if (status == "OK") { ds >> list; m_socket->clearFrameTail(); WorkspaceServiceStatusList::iterator it; TQListViewItemIterator it2; for (it = list.begin(); it != list.end(); ++it) { WorkspaceServiceStatusType info = *it; if (info.serviceID != 0) { continue; } it2 = TQListViewItemIterator(m_base->workspace_list); bool found = false; TQListViewItem* item = NULL; while (it2.current()) { item = *it2; if (info.sessionID == item->text(0).toInt()) { found = true; break; } ++it2; } if (!found) { item = new TQListViewItem(m_base->workspace_list); } item->setText(0, TQString("%1").arg(info.sessionID)); item->setText(1, info.username); item->setText(2, info.realmname); item->setText(3, TQString("%1").arg(info.stationName)); item->setText(4, info.loginStamp.toString()); item->setText(5, (info.terminateStamp.toTime_t()==0)?i18n("Not Set"):info.terminateStamp.toString()); } it2 = TQListViewItemIterator(m_base->workspace_list); while (it2.current()) { TQListViewItem* item = *it2; bool found = false; for (it = list.begin(); it != list.end(); ++it) { WorkspaceServiceStatusType info = *it; if (info.serviceID != 0) { continue; } if (info.sessionID == item->text(0).toInt()) { found = true; } } if (!found) { delete item; } ++it2; } setTickerMessage(i18n("Connected")); } if (m_commHandlerState == ModeIdle_StateProcessWorkspaceList) { m_pingDelayTimer->start(250, TRUE); SET_NEXT_STATE(ModeIdle_StateDelay); } } else { if (!m_updateTimeoutTimer->isActive()) { UPDATEDISPLAY_TIMEOUT } } break; case ModeIdle_StateDelay: if (m_commHandlerNextMode == ModeIdle) { // Let the client and server rest for a bit to lower CPU/network overhead if (!m_pingDelayTimer->isActive()) { EXEC_NEXT_STATE_IMMEDIATELY // Execute query on next event loop SET_NEXT_STATE(ModeIdle_StateTerminalListRequest); } PAT_WATCHDOG_TIMER } else { m_commHandlerMode = m_commHandlerNextMode; SET_NEXT_STATE(m_commHandlerNextState); EXEC_NEXT_STATE_IMMEDIATELY m_commHandlerNextState = ModeIdle_None; m_commHandlerNextMode = ModeIdle; } break; } } else if (m_commHandlerMode == ModeTerminate) { switch (m_commHandlerState) { case ModeTerminate_TerminalServices: ds << TQString("SESSION"); ds << TQString("KILL_TERMINAL"); ds << terminateSessionID; m_socket->writeEndOfFrame(); SET_NEXT_STATE(ModeTerminate_ProcessResponse) EXEC_NEXT_STATE_IMMEDIATELY break; case ModeTerminate_LaboratoryWorkspace: ds << TQString("SESSION"); ds << TQString("KILL_WORKSPACE"); ds << terminateSessionID; ds << terminateSessionDelayMinutes; m_socket->writeEndOfFrame(); SET_NEXT_STATE(ModeTerminate_ProcessResponse) EXEC_NEXT_STATE_IMMEDIATELY break; case ModeTerminate_CancelTerminationOfWorkspace: ds << TQString("SESSION"); ds << TQString("CANCEL_KILL_WORKSPACE"); ds << terminateSessionID; m_socket->writeEndOfFrame(); SET_NEXT_STATE(ModeTerminate_ProcessResponse) EXEC_NEXT_STATE_IMMEDIATELY break; case ModeTerminate_ProcessResponse: // Get all data if (m_socket->canReadFrame()) { PAT_WATCHDOG_TIMER TQString status; WorkspaceServiceStatusList list; ds >> status; if (status != "OK") { // Command failed! KMessageBox::error(0, i18n("Command Failure"), i18n("Unable to execute command!")); } m_commHandlerMode = ModeIdle; SET_NEXT_STATE(ModeIdle_StateTerminalListRequest); EXEC_NEXT_STATE_IMMEDIATELY } else { if (!m_updateTimeoutTimer->isActive()) { UPDATEDISPLAY_TIMEOUT } } break; } } processLockouts(); SET_WATCHDOG_TIMER } else { SET_NEXT_STATE(ModeIdle_StateTerminalListRequest); m_commHandlerMode = ModeIdle; } m_connectionMutex->unlock(); } KAboutData* AdminConsolePart::createAboutData() { return new KAboutData( APP_NAME, I18N_NOOP( APP_PRETTYNAME ), APP_VERSION ); } } //namespace RemoteLab #include "part.moc"