From 9178fa8161fa97247a4b660e0eaa5c1a763969d6 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Tue, 18 Sep 2012 10:06:27 -0500 Subject: [PATCH] Add logout timer to remote server and client --- clients/tde/src/app/remotemdi.cpp | 83 +++++++++++++++++++ clients/tde/src/app/remotemdi.h | 2 + protocol.txt | 4 + .../src/admin_sys_ctl.cpp | 61 +++++++++++++- servers/auth_server_lin/src/auth_conn.cpp | 65 ++++++++++++--- servers/auth_server_lin/src/auth_conn.h | 4 + 6 files changed, 204 insertions(+), 15 deletions(-) diff --git a/clients/tde/src/app/remotemdi.cpp b/clients/tde/src/app/remotemdi.cpp index 669350f..223996e 100644 --- a/clients/tde/src/app/remotemdi.cpp +++ b/clients/tde/src/app/remotemdi.cpp @@ -29,12 +29,17 @@ using namespace std; #include "views/instrumentview.h" #include "dialogs/selectserverdlg.h" +#define STATUSBAR_TIMEOUT_ID 5 + RemoteMDI::RemoteMDI() : KMdiMainFrm(0, "RemoteMDI", KMdi::ChildframeMode), m_children(0), m_rsvSvrSocket(NULL), connToServerConnecting(false), connToServerState(-1), connToServerTimeoutTimer(NULL) { setXMLFile("remotelabui.rc"); setIcon(SmallIcon("remote_laboratory_client")); + masterPollTimer = new TQTimer(); + connect(masterPollTimer, SIGNAL(timeout()), this, SLOT(masterPoll())); + // Create some actions KStdAction::close(this, SLOT(closeCurrent()), actionCollection()); KStdAction::quit(this, SLOT(close()), actionCollection()); @@ -67,6 +72,10 @@ RemoteMDI::RemoteMDI() // Create the status bar updateStatusBarMainMessage(i18n("No active instruments")); + KStatusBar* sb = statusBar(); + if (sb) { + sb->insertItem(i18n("Unknown Time Remaining"), STATUSBAR_TIMEOUT_ID, 0, true); + } processActions(); @@ -77,6 +86,11 @@ RemoteMDI::RemoteMDI() RemoteMDI::~RemoteMDI() { + if (masterPollTimer) { + masterPollTimer->stop(); + delete masterPollTimer; + } + while (m_pCurrentWindow) { closeCurrent(); } @@ -255,6 +269,7 @@ void RemoteMDI::finishConnectingToServer() { } connToServerState = 3; connToServerConnecting = false; + masterPollTimer->start(0, TRUE); processLockouts(); break; } @@ -263,6 +278,67 @@ void RemoteMDI::finishConnectingToServer() { } } +void RemoteMDI::masterPoll() { + // Query current termination timestamp + if (m_rsvSvrSocket) { + if (m_rsvSvrSocket->state() == TQSocket::Connected) { + TQDataStream ds(m_rsvSvrSocket); + ds.setPrintableData(true); + TQ_ULLONG terminationStamp; + long long currentStamp; + ds << TQString("TSTP"); + m_rsvSvrSocket->writeEndOfFrame(); + while (!m_rsvSvrSocket->canReadFrame()) { + tqApp->processEvents(); + if (!m_rsvSvrSocket) { + masterPollTimer->start(1000, TRUE); + return; + } + } + ds >> terminationStamp; + m_rsvSvrSocket->clearFrameTail(); + currentStamp = TQDateTime::currentDateTime().toTime_t(); + + KStatusBar* sb = statusBar(); + if (sb) { + if (terminationStamp == 0) { + sb->changeItem(i18n("Unlimited Time Remaining"), STATUSBAR_TIMEOUT_ID); + } + else { + long long difference = terminationStamp - currentStamp; + int seconds = 0; + int minutes = 0; + int hours = 0; + int days = 0; + if (difference >= 0) { + days = (difference / 86400); + difference = difference - (days * 86400); + hours = (difference / 3600); + difference = difference - (hours * 3600); + minutes = (difference / 60); + difference = difference - (minutes * 60); + seconds = difference; + } + TQString differenceString; + if (days > 0) { + differenceString.append(i18n("%1 day(s), ").arg(days)); + } + if ((days > 0) || (hours > 0)) { + differenceString.append(i18n("%1 hours(s), ").arg(hours)); + } + if ((days > 0) || (hours > 0) || (minutes > 0)) { + differenceString.append(i18n("%1 minutes(s), ").arg(minutes)); + } + differenceString.append(i18n("%1 seconds(s)").arg(seconds)); + sb->changeItem(i18n("%1 Remaining").arg(differenceString), STATUSBAR_TIMEOUT_ID); + } + } + } + } + + masterPollTimer->start(1000, TRUE); +} + void RemoteMDI::connectToServer() { if (m_rsvSvrSocket) { if (m_rsvSvrSocket->state() != TQSocket::Idle) { @@ -394,6 +470,13 @@ void RemoteMDI::processLockouts() { for (TQPtrList::Iterator it(m_instrumentActionList.begin()); it != m_instrumentActionList.end(); ++it) { (*it)->setEnabled(connected); } + + if (!connected) { + KStatusBar* sb = statusBar(); + if (sb) { + sb->changeItem(i18n("Unknown Time Remaining"), STATUSBAR_TIMEOUT_ID); + } + } } void RemoteMDI::configToolbars() { diff --git a/clients/tde/src/app/remotemdi.h b/clients/tde/src/app/remotemdi.h index c2c8884..ff93ea2 100644 --- a/clients/tde/src/app/remotemdi.h +++ b/clients/tde/src/app/remotemdi.h @@ -69,6 +69,7 @@ class RemoteMDI : public KMdiMainFrm void processLockouts(); void processActions(); void startModule(); + void masterPoll(); protected: virtual bool queryClose(); @@ -86,6 +87,7 @@ class RemoteMDI : public KMdiMainFrm bool connToServerConnecting; int connToServerState; TQTimer *connToServerTimeoutTimer; + TQTimer *masterPollTimer; KAction *connect_action; KAction *disconnect_action; diff --git a/protocol.txt b/protocol.txt index dda7c2d..c520be2 100644 --- a/protocol.txt +++ b/protocol.txt @@ -61,6 +61,10 @@ If BIND was previously commanded on this connection, the server must respond wit Example: SERV EOF libremotelab_fpgaviewer EOF +TSTP: +Returns a long long value containing the termination timestamp, if set +If an administrator has enabled automatic termination of the client's session, a UNIX timestamp with the exact termination time must be returned. If automatic termination has not been enabled, the server must return zero. + QUIT: Gracefully terminates the connection. The server should return the case-sensitive text "OK" and must immediately close all active connections for the current user. diff --git a/servers/admin_sys_ctl_server_lin/src/admin_sys_ctl.cpp b/servers/admin_sys_ctl_server_lin/src/admin_sys_ctl.cpp index 3cce4b5..f0cf958 100644 --- a/servers/admin_sys_ctl_server_lin/src/admin_sys_ctl.cpp +++ b/servers/admin_sys_ctl_server_lin/src/admin_sys_ctl.cpp @@ -181,6 +181,8 @@ void SysCtlSocket::commandLoop() { ds >> subCommand; if (subCommand == "TERMINALS") { clearFrameTail(); + ds << TQString("OK"); + writeEndOfFrame(); TQSqlCursor databaseActivityCursor("sessions", TRUE, m_terminals_database); databaseActivityCursor.select(); while (databaseActivityCursor.next()) { @@ -205,6 +207,8 @@ void SysCtlSocket::commandLoop() { } else if (subCommand == "WORKSPACES") { clearFrameTail(); + ds << TQString("OK"); + writeEndOfFrame(); TQSqlCursor databaseActivityCursor("activity", TRUE, m_workspaces_database); databaseActivityCursor.select(); while (databaseActivityCursor.next()) { @@ -224,6 +228,8 @@ void SysCtlSocket::commandLoop() { } else { clearFrameTail(); + ds << TQString("ERRINVCMD"); + writeEndOfFrame(); } } else if (command == "SESSION") { @@ -235,22 +241,69 @@ void SysCtlSocket::commandLoop() { TQ_UINT32 delay; ds >> delay; clearFrameTail(); - // FIXME + // FIXME UNIMPLEMENTED + ds << TQString("ERRINVCMD"); + writeEndOfFrame(); } else if (subCommand == "CANCEL_LOGOFF_TERMINAL") { clearFrameTail(); - // FIXME + // FIXME UNIMPLEMENTED + ds << TQString("ERRINVCMD"); + writeEndOfFrame(); } else if (subCommand == "KILL_TERMINAL") { clearFrameTail(); - // FIXME + TQSqlCursor databaseActivityCursor("sessions", TRUE, m_terminals_database); + databaseActivityCursor.select(TQString("pk=%1").arg(sessionID)); + if (databaseActivityCursor.next()) { + // Gather server information + TQString server_name = databaseActivityCursor.value("servername").toString(); + int server_pid = databaseActivityCursor.value("server_pid").toInt(); + // Kill server process + TQString command = TQString("ssh root@%1 'kill -9 %2'").arg(server_name).arg(server_pid); + if (system(command.ascii()) == 0) { + // Remove database entry + databaseActivityCursor.select(TQString("pk=%1").arg(sessionID)); + if (databaseActivityCursor.next()) { + databaseActivityCursor.primeDelete(); + databaseActivityCursor.del(true); + } + ds << TQString("OK"); + } + else { + ds << TQString("ERRFAILED"); + } + writeEndOfFrame(); + } + else { + ds << TQString("ERRINVCMD"); + } + writeEndOfFrame(); } else if (subCommand == "KILL_WORKSPACE") { clearFrameTail(); - // FIXME + TQSqlCursor databaseActivityCursor("activity", TRUE, m_workspaces_database); + databaseActivityCursor.select(TQString("pk=%1").arg(sessionID)); + if (databaseActivityCursor.next()) { + databaseActivityCursor.select(TQString("pk=%1").arg(sessionID)); + if (databaseActivityCursor.next()) { + TQSqlRecord *buffer = databaseActivityCursor.primeUpdate(); + buffer->setValue("terminate", true); + databaseActivityCursor.update(); + } + + ds << TQString("OK"); + writeEndOfFrame(); + } + else { + ds << TQString("ERRINVCMD"); + } + writeEndOfFrame(); } else { clearFrameTail(); + ds << TQString("ERRINVCMD"); + writeEndOfFrame(); } } else { diff --git a/servers/auth_server_lin/src/auth_conn.cpp b/servers/auth_server_lin/src/auth_conn.cpp index f3f3474..2ed9860 100644 --- a/servers/auth_server_lin/src/auth_conn.cpp +++ b/servers/auth_server_lin/src/auth_conn.cpp @@ -41,7 +41,7 @@ struct exit_exception { instance of this class. */ AuthSocket::AuthSocket(int sock, int serverID, TQObject *parent, const char *name) : - TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_stationID(-1), m_bound(false), m_serverID(serverID), m_servActive(false), m_servState(0), m_servClientSocket(NULL), m_servClientTimeout(NULL), m_loopTimer(NULL), m_config(static_cast(parent)->m_config), m_database(NULL), m_databaseStationsCursor(NULL), + TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_stationID(-1), m_bound(false), m_serviceID(0), m_serverID(serverID), m_terminationStamp(0), m_servActive(false), m_servState(0), m_servClientSocket(NULL), m_servClientTimeout(NULL), m_loopTimer(NULL), m_pollTimer(NULL), m_config(static_cast(parent)->m_config), m_database(NULL), m_databaseStationsCursor(NULL), m_databaseServicesCursor(NULL), m_databaseServiceTypesCursor(NULL), m_databasePermissionsCursor(NULL), m_databaseActivityCursor(NULL) { @@ -59,6 +59,10 @@ AuthSocket::AuthSocket(int sock, int serverID, TQObject *parent, const char *nam if (connectToDatabase() != 0) { exit(1); } + + m_pollTimer = new TQTimer(); + connect(m_pollTimer, SIGNAL(timeout()), this, SLOT(pollFlags())); + m_pollTimer->start(0, TRUE); } AuthSocket::~AuthSocket() { @@ -67,6 +71,11 @@ AuthSocket::~AuthSocket() { delete m_kerberosInitTimer; m_kerberosInitTimer = NULL; } + if (m_pollTimer) { + m_pollTimer->stop(); + delete m_pollTimer; + m_pollTimer = NULL; + } if (m_loopTimer) { m_loopTimer->stop(); delete m_loopTimer; @@ -102,13 +111,11 @@ void AuthSocket::close() { void AuthSocket::connectionClosedHandler() { printf("[DEBUG] Connection from %s closed\n\r", m_remoteHost.ascii()); - if (m_bound) { - // Update database - m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3'").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName)); - if (m_databaseActivityCursor->next()) { - m_databaseActivityCursor->primeDelete(); - m_databaseActivityCursor->del(true); - } + // Update database + m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3' AND serviceid=%4").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName).arg(m_serviceID)); + if (m_databaseActivityCursor->next()) { + m_databaseActivityCursor->primeDelete(); + m_databaseActivityCursor->del(true); } if (m_criticalSection > 0) { @@ -321,6 +328,24 @@ int AuthSocket::servLoop() { } } +void AuthSocket::pollFlags() { + if ((m_bound) || (m_servActive)) { + long long timestamp = TQDateTime::currentDateTime().toTime_t(); + m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3' AND serviceid=%4").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName).arg(m_serviceID)); + if (m_databaseActivityCursor->next()) { + m_terminationStamp = m_databaseActivityCursor->value("terminate").toLongLong(); + if (m_terminationStamp > 0) { + if (m_terminationStamp <= timestamp) { + printf("[DEBUG] Got termination request from database (%lld <= %lld)\n\r", m_terminationStamp, timestamp); fflush(stdout); + close(); + } + } + } + } + + m_pollTimer->start(1000, TRUE); +} + void AuthSocket::commandLoop() { bool transferred_data; @@ -447,6 +472,8 @@ void AuthSocket::commandLoop() { buffer->setValue("realmname", m_authenticatedRealmName); buffer->setValue("logontime", TQDateTime::currentDateTime().toTime_t()); buffer->setValue("serverid", m_serverID); + buffer->setValue("serviceid", 0); + buffer->setValue("terminate", 0); m_databaseActivityCursor->insert(); ds << TQString("OK"); @@ -465,7 +492,7 @@ void AuthSocket::commandLoop() { clearFrameTail(); printf("[DEBUG] SERV command parameter was %s from user %s@%s\n\r", libname.ascii(), m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); - m_databaseActivityCursor->select(TQString("username='%1' AND realmname='%2'").arg(m_authenticatedUserName).arg(m_authenticatedRealmName)); + m_databaseActivityCursor->select(TQString("username='%1' AND realmname='%2' AND serviceid=0").arg(m_authenticatedUserName).arg(m_authenticatedRealmName)); if (m_databaseActivityCursor->next()) { m_stationID = m_databaseActivityCursor->value("station").toInt(); } @@ -497,6 +524,18 @@ void AuthSocket::commandLoop() { m_srvServiceHostName = m_databaseServicesCursor->value("hostname").toString(); m_srvServicePort = m_databaseServicesCursor->value("port").toInt(); + // Update database + m_serviceID = sid; + TQSqlRecord *buffer = m_databaseActivityCursor->primeInsert(); + buffer->setValue("station", m_stationID); + buffer->setValue("username", m_authenticatedUserName); + buffer->setValue("realmname", m_authenticatedRealmName); + buffer->setValue("logontime", TQDateTime::currentDateTime().toTime_t()); + buffer->setValue("serverid", m_serverID); + buffer->setValue("serviceid", m_serviceID); + buffer->setValue("terminate", 0); + m_databaseActivityCursor->insert(); + if (!m_servClientSocket) m_servClientSocket = new TDEKerberosClientSocket; m_servClientSocket->setServiceName("remotefpga"); @@ -514,6 +553,10 @@ void AuthSocket::commandLoop() { } } } + else if (command == "TSTP") { + ds << m_terminationStamp; + writeEndOfFrame(); + } else { ds << TQString("ERRINVCMD"); writeEndOfFrame(); @@ -589,9 +632,9 @@ AuthServer::AuthServer(TQObject* parent) : // Delete existing activity entries for this server ID TQSqlCursor databaseActivityCursor("activity", TRUE, m_database); databaseActivityCursor.select(TQString("serverid='%1'").arg(m_serverID)); - if (databaseActivityCursor.next()) { + while (databaseActivityCursor.next()) { databaseActivityCursor.primeDelete(); - databaseActivityCursor.del(true); + databaseActivityCursor.del(false); } if ( !ok() ) { diff --git a/servers/auth_server_lin/src/auth_conn.h b/servers/auth_server_lin/src/auth_conn.h index d1245be..af816b9 100644 --- a/servers/auth_server_lin/src/auth_conn.h +++ b/servers/auth_server_lin/src/auth_conn.h @@ -60,6 +60,7 @@ class AuthSocket : public TDEKerberosServerSocket void connectionClosedHandler(); void commandLoop(); int servLoop(); + void pollFlags(); private: int line; @@ -67,7 +68,9 @@ class AuthSocket : public TDEKerberosServerSocket TQString m_remoteHost; int m_stationID; bool m_bound; + int m_serviceID; int m_serverID; + TQ_ULLONG m_terminationStamp; bool m_servActive; int m_servState; @@ -78,6 +81,7 @@ class AuthSocket : public TDEKerberosServerSocket TQTimer* m_kerberosInitTimer; TQTimer* m_loopTimer; + TQTimer* m_pollTimer; TQByteArray m_loopBuffer; KSimpleConfig* m_config;