Properly handle recoverable MySQL connection interruptions

Properly track arbiter use
master
Timothy Pearson 10 years ago
parent b25a8045b8
commit 9ab208e78c

@ -20,7 +20,6 @@
* http://www.raptorengineeringinc.com * http://www.raptorengineeringinc.com
*/ */
/*!40101 SET NAMES utf8 */; /*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/; /*!40101 SET SQL_MODE=''*/;
@ -40,15 +39,26 @@ DROP TABLE IF EXISTS `activity`;
CREATE TABLE `activity` ( CREATE TABLE `activity` (
`pk` int(20) NOT NULL AUTO_INCREMENT, `pk` int(20) NOT NULL AUTO_INCREMENT,
`station` bigint(20) NOT NULL, `station` bigint(20) NOT NULL,
`arbiter` text,
`username` text, `username` text,
`realmname` text, `realmname` text,
`logontime` bigint(20) DEFAULT NULL, `logontime` bigint(20) DEFAULT NULL,
`serverid` bigint(20) DEFAULT NULL,
`serviceid` bigint(20) DEFAULT NULL, `serviceid` bigint(20) DEFAULT NULL,
`terminate` bigint(20) NOT NULL, `terminate` bigint(20) NOT NULL,
PRIMARY KEY (`pk`) PRIMARY KEY (`pk`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1; ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*Table structure for table `dbschema` */
DROP TABLE IF EXISTS `dbschema`;
CREATE TABLE `dbschema` (
`pk` bigint(20) NOT NULL,
`skey` text NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`pk`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*Table structure for table `permissions` */ /*Table structure for table `permissions` */
DROP TABLE IF EXISTS `permissions`; DROP TABLE IF EXISTS `permissions`;
@ -60,7 +70,7 @@ CREATE TABLE `permissions` (
`realm` text, `realm` text,
`restrictions` text, `restrictions` text,
PRIMARY KEY (`pk`) PRIMARY KEY (`pk`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;
/*Table structure for table `services` */ /*Table structure for table `services` */
@ -87,11 +97,7 @@ CREATE TABLE `servicetypes` (
`version` bigint(20) DEFAULT NULL, `version` bigint(20) DEFAULT NULL,
`single_instance` bigint(20) DEFAULT NULL, `single_instance` bigint(20) DEFAULT NULL,
PRIMARY KEY (`serviceid`) PRIMARY KEY (`serviceid`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1; ) ENGINE=InnoDB AUTO_INCREMENT=257 DEFAULT CHARSET=latin1;
/*Data for the table `servicetypes` */
insert into `servicetypes`(`serviceid`,`name`,`description`,`client_library`,`version`,`single_instance`) values (1,'Remote FPGA','Remote FPGA access','libremotelab_fpgaviewer',1,1),(2,'Spectrum Analyzer','GPIB spectrum analyzer access','libremotelab_commanalyzer',1,1),(3,'FPGA Programmer','Remote FPGA programming','libremotelab_fpgaprogrammer',1,1),(4,'Oscilloscope','GPIB oscilloscope access','libremotelab_scope',1,1),(5,'Sensor Monitor','Sensor monitor access','libremotelab_sensormonitor',1,1),(6,'Administration Console','View and control active sessions','libremotelab_adminconsole',1,0),(7,'User Management Console','Manage user permissions','libremotelab_adminusermgmt',1,0),(8,'Logic Analyzer','View digital waveforms','libremotelab_logicanalyzer',1,1),(9,'Serial Console','Communicate via a serial connection','libremotelab_serialconsole',1,1);
/*Table structure for table `stations` */ /*Table structure for table `stations` */
@ -102,7 +108,7 @@ CREATE TABLE `stations` (
`name` text, `name` text,
`description` text, `description` text,
PRIMARY KEY (`pk`) PRIMARY KEY (`pk`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=latin1;
/*Table structure for table `statistics` */ /*Table structure for table `statistics` */
@ -112,12 +118,13 @@ CREATE TABLE `statistics` (
`pk` bigint(20) NOT NULL AUTO_INCREMENT, `pk` bigint(20) NOT NULL AUTO_INCREMENT,
`timestamp` bigint(20) NOT NULL, `timestamp` bigint(20) NOT NULL,
`eventtypeid` bigint(20) NOT NULL, `eventtypeid` bigint(20) NOT NULL,
`serverid` bigint(20) DEFAULT NULL, `arbiter` text,
`stationid` bigint(20) DEFAULT NULL,
`sessionid` bigint(20) DEFAULT NULL, `sessionid` bigint(20) DEFAULT NULL,
`typeid` bigint(20) DEFAULT NULL, `typeid` bigint(20) DEFAULT NULL,
`userid` text, `userid` text,
PRIMARY KEY (`pk`) PRIMARY KEY (`pk`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1; ) ENGINE=InnoDB AUTO_INCREMENT=2339 DEFAULT CHARSET=latin1;
/*Table structure for table `status` */ /*Table structure for table `status` */
@ -128,7 +135,7 @@ CREATE TABLE `status` (
`hostname` text NOT NULL, `hostname` text NOT NULL,
`online` tinyint(4) NOT NULL, `online` tinyint(4) NOT NULL,
PRIMARY KEY (`pk`) PRIMARY KEY (`pk`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1; ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;

@ -6,6 +6,6 @@ bin_PROGRAMS = ulab_authserver
ulab_authserver_SOURCES = main.cpp auth_conn.cpp ulab_authserver_SOURCES = main.cpp auth_conn.cpp
ulab_authserver_METASOURCES = AUTO ulab_authserver_METASOURCES = AUTO
ulab_authserver_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor -ltdekrbsocket -ltqtrla ulab_authserver_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor -ltdekrbsocket -ltqtrla -ltdeldap
KDE_OPTIONS = nofinal KDE_OPTIONS = nofinal

@ -15,13 +15,20 @@
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* (c) 2012-2013 Timothy Pearson * (c) 2012-2014 Timothy Pearson
* Raptor Engineering * Raptor Engineering
* http://www.raptorengineeringinc.com * http://www.raptorengineeringinc.com
*/ */
#include <stdlib.h> #include <stdlib.h>
// getLocalMachineFQDN
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <grp.h> #include <grp.h>
#include <pwd.h> #include <pwd.h>
@ -37,6 +44,8 @@
#define STATISTICS_NEW_CONNECTION_EVENT 2 #define STATISTICS_NEW_CONNECTION_EVENT 2
#define STATISTICS_DISCONNECTION_EVENT 3 #define STATISTICS_DISCONNECTION_EVENT 3
#define DB_SCHEMA_VERSION 1
/* exception handling */ /* exception handling */
struct exit_exception { struct exit_exception {
int c; int c;
@ -48,8 +57,8 @@ struct exit_exception {
For every client that connects to the server, the server creates a new For every client that connects to the server, the server creates a new
instance of this class. instance of this class.
*/ */
AuthSocket::AuthSocket(int sock, int serverID, TQObject *parent, const char *name) : AuthSocket::AuthSocket(int sock, TQString localMachineFQDN, TQObject *parent, const char *name) :
TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_stationID(-1), m_bound(false), m_serviceID(0), m_serverID(serverID), m_pollInterval(10), 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<AuthServer*>(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_localMachineFQDN(localMachineFQDN), m_pollInterval(10), 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<AuthServer*>(parent)->m_config), m_database(NULL), m_databaseStationsCursor(NULL),
m_databaseServicesCursor(NULL), m_databaseServiceTypesCursor(NULL), m_databasePermissionsCursor(NULL), m_databaseActivityCursor(NULL), m_databaseStatisticsCursor(NULL), m_databaseStatusCursor(NULL) m_databaseServicesCursor(NULL), m_databaseServiceTypesCursor(NULL), m_databasePermissionsCursor(NULL), m_databaseActivityCursor(NULL), m_databaseStatisticsCursor(NULL), m_databaseStatusCursor(NULL)
{ {
// Read settings // Read settings
@ -368,7 +377,7 @@ void AuthSocket::pollFlags() {
void AuthSocket::updateStatistics(int eventType) { void AuthSocket::updateStatistics(int eventType) {
// Update statistics // Update statistics
long long sessionID = -1; long long sessionID = -1;
m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3' AND serverid='%4' AND serviceid='%5'").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName).arg(m_serverID).arg(m_serviceID)); m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3' AND arbiter='%4' AND serviceid='%5'").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName).arg(m_localMachineFQDN).arg(m_serviceID));
if (m_databaseActivityCursor->next()) { if (m_databaseActivityCursor->next()) {
sessionID = m_databaseActivityCursor->value("pk").toInt(); sessionID = m_databaseActivityCursor->value("pk").toInt();
} }
@ -376,7 +385,8 @@ void AuthSocket::updateStatistics(int eventType) {
TQSqlRecord *buffer = m_databaseStatisticsCursor->primeInsert(); TQSqlRecord *buffer = m_databaseStatisticsCursor->primeInsert();
buffer->setValue("timestamp", TQDateTime::currentDateTime().toTime_t()); buffer->setValue("timestamp", TQDateTime::currentDateTime().toTime_t());
buffer->setValue("eventtypeid", eventType); buffer->setValue("eventtypeid", eventType);
buffer->setValue("serverid", m_stationID); buffer->setValue("arbiter", m_localMachineFQDN);
buffer->setValue("stationid", m_stationID);
buffer->setValue("sessionid", sessionID); buffer->setValue("sessionid", sessionID);
buffer->setValue("typeid", m_serviceID); buffer->setValue("typeid", m_serviceID);
buffer->setValue("userid", userID); buffer->setValue("userid", userID);
@ -554,10 +564,10 @@ void AuthSocket::commandLoop() {
// Update database // Update database
TQSqlRecord *buffer = m_databaseActivityCursor->primeInsert(); TQSqlRecord *buffer = m_databaseActivityCursor->primeInsert();
buffer->setValue("station", m_stationID); buffer->setValue("station", m_stationID);
buffer->setValue("arbiter", m_localMachineFQDN);
buffer->setValue("username", m_authenticatedUserName); buffer->setValue("username", m_authenticatedUserName);
buffer->setValue("realmname", m_authenticatedRealmName); buffer->setValue("realmname", m_authenticatedRealmName);
buffer->setValue("logontime", TQDateTime::currentDateTime().toTime_t()); buffer->setValue("logontime", TQDateTime::currentDateTime().toTime_t());
buffer->setValue("serverid", m_serverID);
buffer->setValue("serviceid", m_serviceID); buffer->setValue("serviceid", m_serviceID);
buffer->setValue("terminate", 0); buffer->setValue("terminate", 0);
m_databaseActivityCursor->insert(); m_databaseActivityCursor->insert();
@ -645,10 +655,10 @@ void AuthSocket::commandLoop() {
m_serviceID = sid; m_serviceID = sid;
TQSqlRecord *buffer = m_databaseActivityCursor->primeInsert(); TQSqlRecord *buffer = m_databaseActivityCursor->primeInsert();
buffer->setValue("station", m_stationID); buffer->setValue("station", m_stationID);
buffer->setValue("arbiter", m_localMachineFQDN);
buffer->setValue("username", m_authenticatedUserName); buffer->setValue("username", m_authenticatedUserName);
buffer->setValue("realmname", m_authenticatedRealmName); buffer->setValue("realmname", m_authenticatedRealmName);
buffer->setValue("logontime", TQDateTime::currentDateTime().toTime_t()); buffer->setValue("logontime", TQDateTime::currentDateTime().toTime_t());
buffer->setValue("serverid", m_serverID);
buffer->setValue("serviceid", m_serviceID); buffer->setValue("serviceid", m_serviceID);
buffer->setValue("terminate", 0); buffer->setValue("terminate", 0);
m_databaseActivityCursor->insert(); m_databaseActivityCursor->insert();
@ -745,21 +755,32 @@ int AuthSocket::connectToDatabase() {
AuthServer::AuthServer(TQObject* parent) : AuthServer::AuthServer(TQObject* parent) :
TQServerSocket( 4004, 1, parent ), m_database(NULL) { TQServerSocket( 4004, 1, parent ), m_database(NULL) {
m_localMachineFQDN = getLocalMachineFQDN();
m_config = new KSimpleConfig("ulab_authserver.conf", false); m_config = new KSimpleConfig("ulab_authserver.conf", false);
if (connectToDatabase() != 0) { if (connectToDatabase() != 0) {
exit(1); exit(1);
} }
m_serverID = 0; // Verify schema version
TDECmdLineArgs* const args = TDECmdLineArgs::parsedArgs(); bool schemaValid = 0;
if ((args) && (args->count() > 0)) { uint schemaVersion = 0;
m_serverID = TQString(args->arg(0)).toInt(); TQSqlCursor databaseDBSchemaCursor("dbschema", TRUE, m_database);
databaseDBSchemaCursor.select(TQString("skey='revision'"));
if (databaseDBSchemaCursor.next()) {
schemaVersion = databaseDBSchemaCursor.value("value").toUInt();
if (schemaVersion == DB_SCHEMA_VERSION) {
schemaValid = 1;
}
}
if (!schemaValid) {
printf("[ERROR] Schema version not present or incorrect (got %d expected %d)\n\r", schemaVersion, DB_SCHEMA_VERSION);
exit(1);
} }
// Delete existing activity entries for this server ID // Delete existing activity entries for this server ID
TQSqlCursor databaseActivityCursor("activity", TRUE, m_database); TQSqlCursor databaseActivityCursor("activity", TRUE, m_database);
databaseActivityCursor.select(TQString("serverid='%1'").arg(m_serverID)); databaseActivityCursor.select(TQString("arbiter='%1'").arg(m_localMachineFQDN));
while (databaseActivityCursor.next()) { while (databaseActivityCursor.next()) {
databaseActivityCursor.primeDelete(); databaseActivityCursor.primeDelete();
databaseActivityCursor.del(false); databaseActivityCursor.del(false);
@ -805,12 +826,17 @@ AuthServer::~AuthServer() {
int AuthServer::connectToDatabase() { int AuthServer::connectToDatabase() {
m_config->setGroup("Database"); m_config->setGroup("Database");
m_database = TQSqlDatabase::addDatabase(m_config->readEntry("driver")); TQString databaseDriver = m_config->readEntry("driver");
m_database = TQSqlDatabase::addDatabase(databaseDriver);
m_database->setDatabaseName(m_config->readEntry("database")); m_database->setDatabaseName(m_config->readEntry("database"));
m_database->setUserName(m_config->readEntry("username")); m_database->setUserName(m_config->readEntry("username"));
m_database->setPassword(m_config->readEntry("password")); m_database->setPassword(m_config->readEntry("password"));
m_database->setHostName(m_config->readEntry("server")); m_database->setHostName(m_config->readEntry("server"));
if (databaseDriver.contains("MYSQL")) {
m_database->setConnectOptions("MYSQL_OPT_RECONNECT");
}
if(!m_database->open()) { if(!m_database->open()) {
printf("[ERROR] Failed to connect to control database on server '%s' [%s]\n\r", m_database->hostName().ascii(), m_database->lastError().text().ascii()); fflush(stdout); printf("[ERROR] Failed to connect to control database on server '%s' [%s]\n\r", m_database->hostName().ascii(), m_database->lastError().text().ascii()); fflush(stdout);
TQSqlDatabase::removeDatabase(m_database); TQSqlDatabase::removeDatabase(m_database);
@ -858,10 +884,8 @@ int AuthServer::connectToDatabase() {
return -1; return -1;
} }
// FIXME
// We currently have no way to handle something as simple as the database server going offline!
// Start database ping process // Start database ping process
// When combined with the MYSQL_OPT_RECONNECT flag passed above, this will keep the connection open even if the database server goes offline and then comes back online
m_sqlPingTimer = new TQTimer(); m_sqlPingTimer = new TQTimer();
connect(m_sqlPingTimer, SIGNAL(timeout()), this, SLOT(pingSQLServer())); connect(m_sqlPingTimer, SIGNAL(timeout()), this, SLOT(pingSQLServer()));
m_sqlPingTimer->start(60*1000); m_sqlPingTimer->start(60*1000);
@ -870,14 +894,35 @@ int AuthServer::connectToDatabase() {
} }
void AuthServer::pingSQLServer() { void AuthServer::pingSQLServer() {
// FIXME if (m_database) {
// We might as well gather statistics here... m_database->ping();
TQSqlQuery query; }
query.exec("SELECT * FROM activity"); }
TQString AuthServer::getLocalMachineFQDN() {
struct addrinfo hints, *res;
int err;
char hostname[1024];
hostname[1023] = '\0';
gethostname(hostname, 1023);
memset(&hints, 0, sizeof hints);
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_CANONNAME;
if ((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
return TQString::null;
}
TQString ret(res->ai_canonname);
freeaddrinfo(res);
return ret;
} }
void AuthServer::newConnection(int socket) { void AuthServer::newConnection(int socket) {
AuthSocket *s = new AuthSocket(socket, m_serverID, this); AuthSocket *s = new AuthSocket(socket, m_localMachineFQDN, this);
s->m_remoteHost = s->peerAddress().toString(); s->m_remoteHost = s->peerAddress().toString();
printf("[DEBUG] New connection from %s\n\r", s->m_remoteHost.ascii()); printf("[DEBUG] New connection from %s\n\r", s->m_remoteHost.ascii());
connect(s, SIGNAL(connectionClosed()), s, SLOT(deleteLater())); connect(s, SIGNAL(connectionClosed()), s, SLOT(deleteLater()));

@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* (c) 2012-2013 Timothy Pearson * (c) 2012-2014 Timothy Pearson
* Raptor Engineering * Raptor Engineering
* http://www.raptorengineeringinc.com * http://www.raptorengineeringinc.com
*/ */
@ -46,7 +46,7 @@ class AuthSocket : public TDEKerberosServerSocket
Q_OBJECT Q_OBJECT
public: public:
AuthSocket(int sock, int serverID, TQObject *parent=0, const char *name=0); AuthSocket(int sock, TQString localMachineFQDN, TQObject *parent=0, const char *name=0);
~AuthSocket(); ~AuthSocket();
public: public:
@ -72,7 +72,7 @@ class AuthSocket : public TDEKerberosServerSocket
int m_stationID; int m_stationID;
bool m_bound; bool m_bound;
int m_serviceID; int m_serviceID;
int m_serverID; TQString m_localMachineFQDN;
int m_pollInterval; int m_pollInterval;
TQ_ULLONG m_terminationStamp; TQ_ULLONG m_terminationStamp;
@ -120,11 +120,14 @@ class AuthServer : public TQServerSocket
signals: signals:
void newConnect(AuthSocket*); void newConnect(AuthSocket*);
private:
TQString getLocalMachineFQDN();
private: private:
KSimpleConfig* m_config; KSimpleConfig* m_config;
TQSqlDatabase* m_database; TQSqlDatabase* m_database;
TQTimer* m_sqlPingTimer; TQTimer* m_sqlPingTimer;
int m_serverID; TQString m_localMachineFQDN;
friend class AuthSocket; friend class AuthSocket;

@ -1,22 +1,24 @@
/*************************************************************************** /*
* Copyright (C) 2012 by Timothy Pearson * * Remote Laboratory Authentication Server
* kb9vqf@pearsoncomputing.net * *
* * * This program is free software; you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or
* the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version.
* (at your option) any later version. * *
* * * This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details.
* GNU General Public License for more details. * *
* * * You should have received a copy of the GNU General Public License along
* You should have received a copy of the GNU General Public License * * with this program; if not, write to the Free Software Foundation, Inc.,
* along with this program; if not, write to the * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Free Software Foundation, Inc., * *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * (c) 2012-2014 Timothy Pearson
***************************************************************************/ * Raptor Engineering
* http://www.raptorengineeringinc.com
*/
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>

Loading…
Cancel
Save