From 82b549ead33d2effc32458aaa5df58b2f04a201d Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Fri, 15 Jan 2016 15:36:04 -0500 Subject: [PATCH] Split off the Events subsystem from the server class and set it up for easy expansion later. --- src/server/EventWatcher.cpp | 60 +++++++++++++++++++++++++++++++++++++ src/server/EventWatcher.h | 41 +++++++++++++++++++++++++ src/server/WebServer.cpp | 55 ++++------------------------------ src/server/WebServer.h | 21 ++----------- src/server/WebSocket.cpp | 36 ++++++++-------------- src/server/WebSocket.h | 17 ++--------- src/server/globals-qt.h | 43 ++++++++++++++++++++++++++ src/server/globals.h | 25 ++++++++++++++++ src/server/main.cpp | 34 ++++++++++----------- src/server/server.pro | 9 ++++-- 10 files changed, 214 insertions(+), 127 deletions(-) create mode 100644 src/server/EventWatcher.cpp create mode 100644 src/server/EventWatcher.h create mode 100644 src/server/globals-qt.h create mode 100644 src/server/globals.h diff --git a/src/server/EventWatcher.cpp b/src/server/EventWatcher.cpp new file mode 100644 index 0000000..da4edf5 --- /dev/null +++ b/src/server/EventWatcher.cpp @@ -0,0 +1,60 @@ +// =============================== +// PC-BSD REST API Server +// Available under the 3-clause BSD License +// Written by: Ken Moore 2015-2016 +// ================================= +#include "EventWatcher.h" + +// === PUBLIC === +EventWatcher::EventWatcher(){ + watcher = new QFileSystemWatcher(this); + + connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); + connect(watcher, SIGNAL(directoryChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); +} + +EventWatcher::~EventWatcher(){ + +} + +void EventWatcher::start(){ + // - DISPATCH Events + if(!QFile::exists(DISPATCHWORKING)){ QProcess::execute("touch "+DISPATCHWORKING); } + qDebug() << " Dispatcher Events:" << DISPATCHWORKING; + watcher->addPath(DISPATCHWORKING); + WatcherUpdate(DISPATCHWORKING); //load it initially +} + +QString EventWatcher::lastEvent(EVENT_TYPE type){ + if(HASH.contains(type)){ return HASH.value(type); } + else{ return ""; } +} + +// === PRIVATE === +QString EventWatcher::readFile(QString path){ + QFile file(path); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ return ""; } + QTextStream in(&file); + QString contents = in.readAll(); + file.close(); + if(contents.endsWith("\n")){ contents.chop(1); } + return contents; +} + +// === PRIVATE SLOTS === +void EventWatcher::WatcherUpdate(QString path){ + if(path==DISPATCHWORKING){ + //Read the file contents + QString stat = readFile(DISPATCHWORKING); + if(stat.simplified().isEmpty()){ stat = "idle"; } + qDebug() << "Dispatcher Update:" << stat; + HASH.insert(DISPATCHER,stat); //save for later + //Forward those contents on to the currently-open sockets + emit NewEvent(DISPATCHER, stat); + } + + //Make sure this file/dir is not removed from the watcher + if(!watcher->files().contains(path) && !watcher->directories().contains(path)){ + watcher->addPath(path); //re-add it to the watcher. This happens when the file is removed/re-created instead of just overwritten + } +} \ No newline at end of file diff --git a/src/server/EventWatcher.h b/src/server/EventWatcher.h new file mode 100644 index 0000000..a8923c1 --- /dev/null +++ b/src/server/EventWatcher.h @@ -0,0 +1,41 @@ +// =============================== +// PC-BSD REST API Server +// Available under the 3-clause BSD License +// Written by: Ken Moore 2015-2016 +// ================================= +#ifndef _PCBSD_SYSADM_EVENT_WATCHER_SYSTEM_H +#define _PCBSD_SYSADM_EVENT_WATCHER_SYSTEM_H + +#include "globals-qt.h" + +#define DISPATCHWORKING QString("/var/tmp/appcafe/dispatch-queue.working") + +class EventWatcher : public QObject{ + Q_OBJECT +public: + //Add more event types here as needed + enum EVENT_TYPE{ DISPATCHER }; + + EventWatcher(); + ~EventWatcher(); + + void start(); + + QString lastEvent(EVENT_TYPE type); + +private: + QFileSystemWatcher *watcher; + QHash HASH; + + QString readFile(QString path); + +public slots: + +private slots: + //File watcher signals + void WatcherUpdate(QString); + +signals: + void NewEvent(EVENT_TYPE ev, QString msg); //type/message +}; +#endif diff --git a/src/server/WebServer.cpp b/src/server/WebServer.cpp index e6c0bbe..63b1440 100644 --- a/src/server/WebServer.cpp +++ b/src/server/WebServer.cpp @@ -5,34 +5,20 @@ // ================================= #include "WebServer.h" -#include -#include -#include -#include -#include -#include -#include - #define DEBUG 0 -#define APPCAFEWORKING QString("/var/tmp/appcafe/dispatch-queue.working") - //======================= // PUBLIC //======================= WebServer::WebServer(){ //Setup all the various settings - //Any SSL changes - /*QSslConfiguration ssl = this->sslConfiguration(); - ssl.setProtocol(QSsl::SecureProtocols); - this->setSslConfiguration(ssl);*/ WSServer = 0; TCPServer = 0; AUTH = new AuthorizationManager(); - watcher = new QFileSystemWatcher(this); + //watcher = new QFileSystemWatcher(this); - connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); - connect(watcher, SIGNAL(directoryChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); + //connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); + //connect(watcher, SIGNAL(directoryChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); } WebServer::~WebServer(){ @@ -54,11 +40,7 @@ bool WebServer::startServer(quint16 port, bool websocket){ qDebug() << "Server Started:" << QDateTime::currentDateTime().toString(Qt::ISODate); qDebug() << " Port:" << port; if(WSServer!=0){ qDebug() << " URL:" << WSServer->serverUrl().toString(); } - - if(!QFile::exists(APPCAFEWORKING)){ QProcess::execute("touch "+APPCAFEWORKING); } - qDebug() << " Dispatcher Events:" << APPCAFEWORKING; - watcher->addPath(APPCAFEWORKING); - WatcherUpdate(APPCAFEWORKING); //load it initially + EVENTS->start(); }else{ qCritical() << "Could not start server - exiting..."; } @@ -127,16 +109,6 @@ QString WebServer::generateID(){ return QString::number(id); } -QString WebServer::readFile(QString path){ - QFile file(path); - if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ return ""; } - QTextStream in(&file); - QString contents = in.readAll(); - file.close(); - if(contents.endsWith("\n")){ contents.chop(1); } - return contents; -} - //======================= // PRIVATE SLOTS //======================= @@ -152,8 +124,7 @@ void WebServer::NewSocketConnection(){ if(sock==0){ return; } //no new connection qDebug() << "New Socket Connection"; connect(sock, SIGNAL(SocketClosed(QString)), this, SLOT(SocketClosed(QString)) ); - connect(this, SIGNAL(DispatchStatusUpdate(QString)), sock, SLOT(AppCafeStatusUpdate(QString)) ); - sock->setLastDispatch(lastDispatch); //make sure this socket is aware of the latest notification + connect(EVENTS, SIGNAL(NewEvent(EventWatcher::EVENT_TYPE, QString)), sock, SLOT(EventUpdate(EventWatcher::EVENT_TYPE, QString)) ); OpenSockets << sock; } @@ -205,19 +176,3 @@ void WebServer::SocketClosed(QString ID){ } QTimer::singleShot(0,this, SLOT(NewSocketConnection()) ); //check for a new connection } - -void WebServer::WatcherUpdate(QString path){ - if(path==APPCAFEWORKING){ - //Read the file contents - QString stat = readFile(APPCAFEWORKING); - if(stat.simplified().isEmpty()){ stat = "idle"; } - qDebug() << "Dispatcher Update:" << stat; - lastDispatch = stat; //save for later - //Forward those contents on to the currently-open sockets - emit DispatchStatusUpdate(stat); - } - //Make sure this file/dir is not removed from the watcher - if(!watcher->files().contains(path) && !watcher->directories().contains(path)){ - watcher->addPath(path); //re-add it to the watcher. This happens when the file is removed/re-created instead of just overwritten - } -} diff --git a/src/server/WebServer.h b/src/server/WebServer.h index 9c44486..6829f31 100644 --- a/src/server/WebServer.h +++ b/src/server/WebServer.h @@ -6,18 +6,7 @@ #ifndef _PCBSD_REST_WEB_SERVER_H #define _PCBSD_REST_WEB_SERVER_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include //for better syntax of qDebug() / qWarning() / qCritical() / qFatal() +#include "globals.h" #include "WebSocket.h" #include "AuthorizationManager.h" @@ -38,8 +27,6 @@ private: SslServer *TCPServer; QList OpenSockets; AuthorizationManager *AUTH; - QFileSystemWatcher *watcher; - QString lastDispatch; //Server Setup functions bool setupWebSocket(quint16 port); @@ -47,7 +34,6 @@ private: //Generic functions for either type of server QString generateID(); //generate a new ID for a socket - QString readFile(QString path); private slots: // Generic Server Slots @@ -63,11 +49,8 @@ private slots: void SslErrors(const QList&); //sslErrors() signal void SocketClosed(QString ID); - //File watcher signals - void WatcherUpdate(QString); - signals: - void DispatchStatusUpdate(QString); + //void DispatchStatusUpdate(QString); }; diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index a70ccc1..dc7c569 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -65,11 +65,6 @@ QString WebSocket::ID(){ return SockID; } -void WebSocket::setLastDispatch(QString msg){ - //used on initialization only - lastDispatchEvent = msg; -} - //======================= // PRIVATE //======================= @@ -340,25 +335,18 @@ void WebSocket::SslError(const QList &err){ //sslErrors() signal // ====================== // PUBLIC SLOTS // ====================== -void WebSocket::AppCafeStatusUpdate(QString msg){ - if(!msg.isEmpty()){ lastDispatchEvent = msg; } - else{ msg = lastDispatchEvent; } +void WebSocket::EventUpdate(EventWatcher::EVENT_TYPE evtype, QString msg){ + if(msg.isEmpty()){ msg = EVENTS->lastEvent(evtype); } //qDebug() << "Socket Status Update:" << msg; - if(!SendAppCafeEvents){ return; } //don't report events on this socket - RestOutputStruct out; - out.CODE = RestOutputStruct::OK; - out.in_struct.name = "dispatcher"; - out.in_struct.namesp = "events"; - //Pre-set any output fields - //QJsonObject outargs; - //outargs.insert("name", "dispatcher"); - // outargs.insert("args",QJsonValue(msg)); - out.out_args = QJsonValue(msg);//outargs; - - //Assemble the output JSON document/text + if(evtype==EventWatcher::DISPATCHER){ + if(!SendAppCafeEvents){ return; } //don't report events on this socket + RestOutputStruct out; + out.CODE = RestOutputStruct::OK; + out.in_struct.name = "dispatcher"; + out.in_struct.namesp = "events"; + out.out_args = QJsonValue(msg);//outargs; out.Header << "Content-Type: text/json; charset=utf-8"; //REST header info - //Now send the message back through the socket - this->sendReply(out.assembleMessage()); -/* if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); } - else if(TSOCKET!=0){ TSOCKET->write(out.assembleMessage().toUtf8().data()); }*/ + //Now send the message back through the socket + this->sendReply(out.assembleMessage()); + } //end of DISPATCH event } diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index 6941b90..cfc2300 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -6,17 +6,7 @@ #ifndef _PCBSD_REST_WEB_SOCKET_H #define _PCBSD_REST_WEB_SOCKET_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "globals.h" #include "RestStructs.h" #include "AuthorizationManager.h" @@ -29,13 +19,12 @@ public: ~WebSocket(); QString ID(); - void setLastDispatch(QString); //used on initialization only private: QTimer *idletimer; QWebSocket *SOCKET; QSslSocket *TSOCKET; - QString SockID, SockAuthToken, lastDispatchEvent; + QString SockID, SockAuthToken; AuthorizationManager *AUTHSYSTEM; bool SendAppCafeEvents; @@ -77,7 +66,7 @@ private slots: void SslError(const QList&); //sslErrors() signal public slots: - void AppCafeStatusUpdate(QString msg = ""); + void EventUpdate(EventWatcher::EVENT_TYPE, QString msg = ""); signals: void SocketClosed(QString); //ID diff --git a/src/server/globals-qt.h b/src/server/globals-qt.h new file mode 100644 index 0000000..b2679fe --- /dev/null +++ b/src/server/globals-qt.h @@ -0,0 +1,43 @@ +// =============================== +// PC-BSD REST API Server +// Available under the 3-clause BSD License +// Written by: Ken Moore 2015-2016 +// ================================= +#ifndef _PCBSD_SYSADM_SERVER_QT_GLOBALS_H +#define _PCBSD_SYSADM_SERVER_QT_GLOBALS_H + +//General-purpose includes +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#endif diff --git a/src/server/globals.h b/src/server/globals.h new file mode 100644 index 0000000..ad9ec30 --- /dev/null +++ b/src/server/globals.h @@ -0,0 +1,25 @@ +// =============================== +// PC-BSD REST API Server +// Available under the 3-clause BSD License +// Written by: Ken Moore 2015-2016 +// ================================= +#ifndef _PCBSD_SYSADM_SERVER_GLOBALS_H +#define _PCBSD_SYSADM_SERVER_GLOBALS_H + +#include "globals-qt.h" + +//Global variables/classes (intially created in main.cpp) +extern QSettings *CONFIG; + +#include "EventWatcher.h" +extern EventWatcher *EVENTS; +//#include "ProcessQueue.h" +//extern ProcessQueue *PQUEUE; +//#include "LogManager.h" +//extern LogManager *LOGS; + +//Special defines +#define WSPORTNUMBER 12150 // WebSocket server default port +#define PORTNUMBER 12151 // TCP server default port + +#endif diff --git a/src/server/main.cpp b/src/server/main.cpp index f6d0043..1d48206 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -3,24 +3,20 @@ // Available under the 3-clause BSD License // Written by: Ken Moore July 2015 // ================================= -#include -#include -#include -#include -#include +#include "globals.h" + #include #include #include "WebServer.h" -#ifndef PREFIX -#define PREFIX QString("/usr/local/") -#endif - #define DEBUG 1 -#define WSPORTNUMBER 12150 // WebSocket server default port -#define PORTNUMBER 12151 // TCP server default port +//Create any global classes +QSettings *CONFIG = new QSettings("PCBSD","sysadm"); +EventWatcher *EVENTS = new EventWatcher(); + +//Create the default logfile QFile logfile; void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg){ QString txt; @@ -80,19 +76,23 @@ int main( int argc, char ** argv ) logfile.open(QIODevice::WriteOnly | QIODevice::Append); qInstallMessageHandler(MessageOutput); - //Create and start the daemon + //Create the daemon qDebug() << "Starting the PC-BSD sysadm server...." << (websocket ? "(WebSocket)" : "(TCP)"); WebServer *w = new WebServer(); + //Start the daemon + int ret = 1; //error return value if( w->startServer(port, websocket) ){ //Now start the event loop - int ret = a.exec(); + ret = a.exec(); qDebug() << "Server Stopped:" << QDateTime::currentDateTime().toString(Qt::ISODate); - logfile.close(); - return ret; }else{ qDebug() << "[FATAL] Server could not be started:" << QDateTime::currentDateTime().toString(Qt::ISODate); qDebug() << " - Tried port:" << port; - logfile.close(); - return 1; } + //Cleanup any globals + delete CONFIG; + logfile.close(); + + //Return + return ret; } diff --git a/src/server/server.pro b/src/server/server.pro index 8ef8272..7b6fe98 100644 --- a/src/server/server.pro +++ b/src/server/server.pro @@ -4,13 +4,15 @@ LANGUAGE = C++ CONFIG += qt warn_on release QT = core network websockets -HEADERS += WebServer.h \ +HEADERS += globals.h globals-qt.h \ + WebServer.h \ WebSocket.h \ syscache-client.h \ dispatcher-client.h \ RestStructs.h \ AuthorizationManager.h \ - SslServer.h + SslServer.h \ + EventWatcher.h SOURCES += main.cpp \ WebServer.cpp \ @@ -18,7 +20,8 @@ SOURCES += main.cpp \ WebBackend.cpp \ syscache-client.cpp \ dispatcher-client.cpp \ - AuthorizationManager.cpp + AuthorizationManager.cpp \ + EventWatcher.cpp TARGET=sysadm-server