mirror of
https://github.com/outbackdingo/sysadm.git
synced 2026-01-27 02:20:17 +00:00
Convert sysadm-server to run either in TCP or WebSocket mode.
New CLI flags: "-ws": Use the websocket protocols instead of tcp "-p <port number>": Use the designated port number for the server.
This commit is contained in:
@@ -13,30 +13,22 @@
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#define PORTNUMBER 12142
|
||||
|
||||
#define APPCAFEWORKING QString("/var/tmp/appcafe/dispatch-queue.working")
|
||||
|
||||
//=======================
|
||||
// PUBLIC
|
||||
//=======================
|
||||
WebServer::WebServer() : QWebSocketServer("sysadm-server", QWebSocketServer::NonSecureMode){
|
||||
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);
|
||||
|
||||
//Setup Connections
|
||||
connect(this, SIGNAL(closed()), this, SLOT(ServerClosed()) );
|
||||
connect(this, SIGNAL(serverError(QWebSocketProtocol::CloseCode)), this, SLOT(ServerError(QWebSocketProtocol::CloseCode)) );
|
||||
connect(this, SIGNAL(newConnection()), this, SLOT(NewSocketConnection()) );
|
||||
connect(this, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(NewConnectError(QAbstractSocket::SocketError)) );
|
||||
connect(this, SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)), this, SLOT(OriginAuthRequired(QWebSocketCorsAuthenticator*)) );
|
||||
connect(this, SIGNAL(peerVerifyError(const QSslError&)), this, SLOT(PeerVerifyError(const QSslError&)) );
|
||||
connect(this, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(SslErrors(const QList<QSslError>&)) );
|
||||
connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) );
|
||||
connect(watcher, SIGNAL(directoryChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) );
|
||||
}
|
||||
@@ -45,28 +37,60 @@ WebServer::~WebServer(){
|
||||
delete AUTH;
|
||||
}
|
||||
|
||||
bool WebServer::startServer(){
|
||||
bool ok = this->listen(QHostAddress::Any, PORTNUMBER);
|
||||
bool WebServer::startServer(quint16 port, bool websocket){
|
||||
bool ok = false;
|
||||
if(websocket){ ok = setupWebSocket(port); }
|
||||
else{ ok = setupTcp(port); }
|
||||
|
||||
if(ok){
|
||||
QCoreApplication::processEvents();
|
||||
qDebug() << "Server Started:" << QDateTime::currentDateTime().toString(Qt::ISODate);
|
||||
qDebug() << " Name:" << this->serverName() << "Port:" << this->serverPort();
|
||||
qDebug() << " URL:" << this->serverUrl().toString() << "Remote Address:" << this->serverAddress().toString();
|
||||
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
|
||||
}else{ qCritical() << "Could not start server - exiting..."; }
|
||||
}else{
|
||||
qCritical() << "Could not start server - exiting...";
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void WebServer::stopServer(){
|
||||
this->close();
|
||||
if(WSServer!=0){ WSServer->close(); } //note - this will throw the "closed()" signal when done
|
||||
else if(TCPServer!=0){ TCPServer->close(); QCoreApplication::exit(0);} //no corresponding signal
|
||||
}
|
||||
|
||||
//===================
|
||||
// PRIVATE
|
||||
//===================
|
||||
bool WebServer::setupWebSocket(quint16 port){
|
||||
WSServer = new QWebSocketServer("sysadm-server", QWebSocketServer::NonSecureMode, this);
|
||||
//Setup Connections
|
||||
connect(WSServer, SIGNAL(newConnection()), this, SLOT(NewSocketConnection()) );
|
||||
connect(WSServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(NewConnectError(QAbstractSocket::SocketError)) );
|
||||
// -- websocket specific signals
|
||||
connect(WSServer, SIGNAL(closed()), this, SLOT(ServerClosed()) );
|
||||
connect(WSServer, SIGNAL(serverError(QWebSocketProtocol::CloseCode)), this, SLOT(ServerError(QWebSocketProtocol::CloseCode)) );
|
||||
connect(WSServer, SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)), this, SLOT(OriginAuthRequired(QWebSocketCorsAuthenticator*)) );
|
||||
connect(WSServer, SIGNAL(peerVerifyError(const QSslError&)), this, SLOT(PeerVerifyError(const QSslError&)) );
|
||||
connect(WSServer, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(SslErrors(const QList<QSslError>&)) );
|
||||
//Now start the server
|
||||
return WSServer->listen(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
bool WebServer::setupTcp(quint16 port){
|
||||
TCPServer = new QTcpServer(this);
|
||||
//Setup Connections
|
||||
connect(TCPServer, SIGNAL(newConnection()), this, SLOT(NewSocketConnection()) );
|
||||
connect(TCPServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(NewConnectError(QAbstractSocket::SocketError)) );
|
||||
//Now start the server
|
||||
return TCPServer->listen(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
QString WebServer::generateID(){
|
||||
int id = 0;
|
||||
for(int i=0; i<OpenSockets.length(); i++){
|
||||
@@ -88,44 +112,39 @@ QString WebServer::readFile(QString path){
|
||||
//=======================
|
||||
// PRIVATE SLOTS
|
||||
//=======================
|
||||
// Overall Server signals
|
||||
void WebServer::ServerClosed(){
|
||||
qDebug() << "Server Closed:" << QDateTime::currentDateTime().toString(Qt::ISODate);
|
||||
QCoreApplication::exit(0);
|
||||
}
|
||||
|
||||
void WebServer::ServerError(QWebSocketProtocol::CloseCode code){
|
||||
qWarning() << "Server Error["+QString::number(code)+"]:" << this->errorString();
|
||||
}
|
||||
|
||||
//GENERIC SERVER SIGNALS
|
||||
// New Connection Signals
|
||||
void WebServer::NewSocketConnection(){
|
||||
if(!this->hasPendingConnections()){ return; }
|
||||
WebSocket *sock = 0;
|
||||
if(WSServer!=0){
|
||||
if(WSServer->hasPendingConnections()){ sock = new WebSocket( WSServer->nextPendingConnection(), generateID(), AUTH); }
|
||||
}else if(TCPServer!=0){
|
||||
if(TCPServer->hasPendingConnections()){ sock = new WebSocket( TCPServer->nextPendingConnection(), generateID(), AUTH); }
|
||||
}
|
||||
if(sock==0){ return; } //no new connection
|
||||
qDebug() << "New Socket Connection";
|
||||
//if(idletimer->isActive()){ idletimer->stop(); }
|
||||
QWebSocket *csock = this->nextPendingConnection();
|
||||
if(csock == 0){ qWarning() << " - new connection invalid, skipping..."; QTimer::singleShot(10, this, SLOT(NewSocketConnection())); return; }
|
||||
qDebug() << " - Accepting connection:" << csock->origin();
|
||||
WebSocket *sock = new WebSocket(csock, generateID(), AUTH);
|
||||
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
|
||||
OpenSockets << sock;
|
||||
}
|
||||
|
||||
void WebServer::NewConnectError(QAbstractSocket::SocketError err){
|
||||
//if(csock!=0){
|
||||
//qWarning() << "New Connection Error["+QString::number(err)+"]:" << csock->errorString();
|
||||
//csock->close();
|
||||
//}else{
|
||||
qWarning() << "New Connection Error["+QString::number(err)+"]:" << this->errorString();
|
||||
//}
|
||||
//csock = 0; //remove the current socket
|
||||
void WebServer::NewConnectError(QAbstractSocket::SocketError err){
|
||||
qWarning() << "New Connection Error["+QString::number(err)+"]:" << ( (WSServer!=0) ? WSServer->errorString() : TCPServer->errorString());
|
||||
QTimer::singleShot(0,this, SLOT(NewSocketConnection()) ); //check for a new connection
|
||||
|
||||
}
|
||||
|
||||
// SSL/Authentication Signals
|
||||
//WEBSOCKET SERVER SIGNALS
|
||||
// Overall Server signals
|
||||
void WebServer::ServerClosed(){
|
||||
QCoreApplication::exit(0);
|
||||
}
|
||||
|
||||
void WebServer::ServerError(QWebSocketProtocol::CloseCode code){
|
||||
qWarning() << "Server Error["+QString::number(code)+"]:" << ( (WSServer!=0) ? WSServer->errorString() : TCPServer->errorString());
|
||||
}
|
||||
|
||||
// - SSL/Authentication Signals (still websocket only)
|
||||
void WebServer::OriginAuthRequired(QWebSocketCorsAuthenticator *auth){
|
||||
qDebug() << "Origin Auth Required:" << auth->origin();
|
||||
//if(auth->origin() == this->serverAddress().toString()){
|
||||
@@ -151,6 +170,7 @@ void WebServer::SslErrors(const QList<QSslError> &list){
|
||||
}
|
||||
}
|
||||
|
||||
// - More Functions for all socket interactions
|
||||
void WebServer::SocketClosed(QString ID){
|
||||
for(int i=0; i<OpenSockets.length(); i++){
|
||||
if(OpenSockets[i]->ID()==ID){ delete OpenSockets.takeAt(i); break; }
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
#define _PCBSD_REST_WEB_SERVER_H
|
||||
|
||||
#include <QWebSocketServer>
|
||||
#include <QTcpServer>
|
||||
#include <QWebSocket>
|
||||
#include <QTcpSocket>
|
||||
#include <QWebSocketCorsAuthenticator>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QSslError>
|
||||
@@ -20,40 +22,45 @@
|
||||
#include "WebSocket.h"
|
||||
#include "AuthorizationManager.h"
|
||||
|
||||
class WebServer : public QWebSocketServer{
|
||||
class WebServer : public QObject{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebServer();
|
||||
~WebServer();
|
||||
|
||||
bool startServer();
|
||||
bool startServer(quint16 port, bool websocket);
|
||||
|
||||
public slots:
|
||||
void stopServer();
|
||||
|
||||
private:
|
||||
QWebSocketServer *WSServer;
|
||||
QTcpServer *TCPServer;
|
||||
QList<WebSocket*> OpenSockets;
|
||||
AuthorizationManager *AUTH;
|
||||
QFileSystemWatcher *watcher;
|
||||
QString lastDispatch;
|
||||
|
||||
//Server Setup functions
|
||||
bool setupWebSocket(quint16 port);
|
||||
bool setupTcp(quint16 port);
|
||||
|
||||
//Generic functions for either type of server
|
||||
QString generateID(); //generate a new ID for a socket
|
||||
QString readFile(QString path);
|
||||
|
||||
private slots:
|
||||
// Overall Server signals
|
||||
void ServerClosed(); //closed() signal
|
||||
void ServerError(QWebSocketProtocol::CloseCode); //serverError() signal
|
||||
|
||||
// New Connection Signals
|
||||
// Generic Server Slots
|
||||
void NewSocketConnection(); //newConnection() signal
|
||||
void NewConnectError(QAbstractSocket::SocketError); //acceptError() signal
|
||||
|
||||
// SSL/Authentication Signals
|
||||
// (WebSocket-only) Server signals/slots
|
||||
void ServerClosed(); //closed() signal
|
||||
void ServerError(QWebSocketProtocol::CloseCode); //serverError() signal
|
||||
// - SSL/Authentication Signals
|
||||
void OriginAuthRequired(QWebSocketCorsAuthenticator*); //originAuthenticationRequired() signal
|
||||
void PeerVerifyError(const QSslError&); //peerVerifyError() signal
|
||||
void SslErrors(const QList<QSslError>&); //sslErrors() signal
|
||||
|
||||
void SocketClosed(QString ID);
|
||||
|
||||
//File watcher signals
|
||||
|
||||
@@ -12,6 +12,7 @@ WebSocket::WebSocket(QWebSocket *sock, QString ID, AuthorizationManager *auth){
|
||||
SockID = ID;
|
||||
SockAuthToken.clear(); //nothing set initially
|
||||
SOCKET = sock;
|
||||
TSOCKET = 0;
|
||||
SendAppCafeEvents = false;
|
||||
AUTHSYSTEM = auth;
|
||||
idletimer = new QTimer(this);
|
||||
@@ -24,11 +25,31 @@ WebSocket::WebSocket(QWebSocket *sock, QString ID, AuthorizationManager *auth){
|
||||
idletimer->start();
|
||||
}
|
||||
|
||||
WebSocket::WebSocket(QTcpSocket *sock, QString ID, AuthorizationManager *auth){
|
||||
SockID = ID;
|
||||
SockAuthToken.clear(); //nothing set initially
|
||||
TSOCKET = sock;
|
||||
SOCKET = 0;
|
||||
SendAppCafeEvents = false;
|
||||
AUTHSYSTEM = auth;
|
||||
idletimer = new QTimer(this);
|
||||
idletimer->setInterval(IDLETIMEOUTMINS*60000); //connection timout for idle sockets
|
||||
idletimer->setSingleShot(true);
|
||||
connect(idletimer, SIGNAL(timeout()), this, SLOT(checkIdle()) );
|
||||
connect(TSOCKET, SIGNAL(readyRead()), this, SLOT(EvaluateTCPMessage()) );
|
||||
connect(TSOCKET, SIGNAL(aboutToClose()), this, SLOT(SocketClosing()) );
|
||||
idletimer->start();
|
||||
}
|
||||
|
||||
WebSocket::~WebSocket(){
|
||||
if(SOCKET!=0){
|
||||
SOCKET->close();
|
||||
delete SOCKET;
|
||||
}
|
||||
if(TSOCKET!=0){
|
||||
TSOCKET->close();
|
||||
delete TSOCKET;
|
||||
}
|
||||
delete SOCKET;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +220,8 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
|
||||
out.Header << "Content-Type: text/json; charset=utf-8";
|
||||
}
|
||||
//Return any information
|
||||
SOCKET->sendTextMessage(out.assembleMessage());
|
||||
if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); }
|
||||
else if(TSOCKET!=0){ TSOCKET->write(out.assembleMessage().toUtf8().data()); }
|
||||
}
|
||||
|
||||
// === SYSCACHE REQUEST INTERACTION ===
|
||||
@@ -303,6 +325,10 @@ void WebSocket::checkIdle(){
|
||||
qDebug() << " - Client Timeout: Closing connection...";
|
||||
SOCKET->close(); //timeout - close the connection to make way for others
|
||||
}
|
||||
if(TSOCKET !=0){
|
||||
qDebug() << " - Client Timeout: Closing connection...";
|
||||
TSOCKET->close(); //timeout - close the connection to make way for others
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocket::SocketClosing(){
|
||||
@@ -314,7 +340,9 @@ void WebSocket::SocketClosing(){
|
||||
//Stop any current requests
|
||||
|
||||
//Reset the pointer
|
||||
SOCKET = 0;
|
||||
if(SOCKET!=0){ SOCKET = 0; }
|
||||
if(TSOCKET!=0){ TSOCKET = 0; }
|
||||
|
||||
emit SocketClosed(SockID);
|
||||
}
|
||||
|
||||
@@ -334,6 +362,15 @@ void WebSocket::EvaluateMessage(const QString &msg){
|
||||
qDebug() << "Done with Message";
|
||||
}
|
||||
|
||||
void WebSocket::EvaluateTcpMessage(){
|
||||
//Need to read the data from the Tcp socket and turn it into a string
|
||||
qDebug() << "New TCP Message:";
|
||||
if(idletimer->isActive()){ idletimer->stop(); }
|
||||
EvaluateREST( QString(TSOCKET->readAll()) );
|
||||
idletimer->start();
|
||||
qDebug() << "Done with Message";
|
||||
}
|
||||
|
||||
// ======================
|
||||
// PUBLIC SLOTS
|
||||
// ======================
|
||||
@@ -359,5 +396,7 @@ void WebSocket::AppCafeStatusUpdate(QString msg){
|
||||
retdoc.setObject(ret);
|
||||
out.Body = retdoc.toJson();
|
||||
out.Header << "Content-Type: text/json; charset=utf-8";
|
||||
SOCKET->sendTextMessage(out.assembleMessage());
|
||||
//Now send the message back through the socket
|
||||
if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); }
|
||||
else if(TSOCKET!=0){ TSOCKET->write(out.assembleMessage().toUtf8().data()); }
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _PCBSD_REST_WEB_SOCKET_H
|
||||
|
||||
#include <QWebSocket>
|
||||
#include <QTcpSocket>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QJsonDocument>
|
||||
@@ -23,6 +24,7 @@ class WebSocket : public QObject{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebSocket(QWebSocket*, QString ID, AuthorizationManager *auth);
|
||||
WebSocket(QTcpSocket*, QString ID, AuthorizationManager *auth);
|
||||
~WebSocket();
|
||||
|
||||
QString ID();
|
||||
@@ -31,6 +33,7 @@ public:
|
||||
private:
|
||||
QTimer *idletimer;
|
||||
QWebSocket *SOCKET;
|
||||
QTcpSocket *TSOCKET;
|
||||
QString SockID, SockAuthToken, lastDispatchEvent;
|
||||
AuthorizationManager *AUTHSYSTEM;
|
||||
bool SendAppCafeEvents;
|
||||
@@ -54,6 +57,7 @@ private slots:
|
||||
//Currently connected socket signal/slot connections
|
||||
void EvaluateMessage(const QByteArray&);
|
||||
void EvaluateMessage(const QString&);
|
||||
void EvaluateTcpMessage();
|
||||
|
||||
public slots:
|
||||
void AppCafeStatusUpdate(QString msg = "");
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
#endif
|
||||
|
||||
#define DEBUG 1
|
||||
#define WSPORTNUMBER 12142 // WebSocket server default port
|
||||
#define PORTNUMBER 12142 // TCP server default port
|
||||
|
||||
QFile logfile("/var/log/sysadm-server.log");
|
||||
QFile logfile;
|
||||
void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg){
|
||||
QString txt;
|
||||
switch(type){
|
||||
@@ -53,9 +55,21 @@ int main( int argc, char ** argv )
|
||||
qDebug() << "sysadm-server must be started as root!";
|
||||
return 1;
|
||||
}
|
||||
//Setup the log file
|
||||
if(DEBUG){
|
||||
qDebug() << "Log File:" << logfile.fileName();
|
||||
//Evaluate input arguments
|
||||
bool websocket = false;
|
||||
quint16 port = 0;
|
||||
for(int i=1; i<argc; i++){
|
||||
if( QString(argv[i])=="-ws" ){ websocket = true; }
|
||||
else if( QString(argv[i])=="-p" && (i+1<argc) ){ i++; port = QString(argv[i]).toUInt(); }
|
||||
}
|
||||
if(port==0){
|
||||
if(websocket){ port = WSPORTNUMBER; }
|
||||
else{ port = PORTNUMBER; }
|
||||
}
|
||||
//Setup the log file
|
||||
if(!websocket){ logfile.setFileName("/var/log/sysadm-server-tcp.log"); }
|
||||
else{ logfile.setFileName("/var/log/sysadm-server-ws.log"); }
|
||||
if(DEBUG){ qDebug() << "Log File:" << logfile.fileName(); }
|
||||
if(QFile::exists(logfile.fileName()+".old")){ QFile::remove(logfile.fileName()+".old"); }
|
||||
if(logfile.exists()){ QFile::rename(logfile.fileName(), logfile.fileName()+".old"); }
|
||||
//Make sure the parent directory exists
|
||||
@@ -65,17 +79,20 @@ int main( int argc, char ** argv )
|
||||
}
|
||||
logfile.open(QIODevice::WriteOnly | QIODevice::Append);
|
||||
qInstallMessageHandler(MessageOutput);
|
||||
}
|
||||
|
||||
//Create and start the daemon
|
||||
qDebug() << "Starting the PC-BSD sysadm websocket daemon....";
|
||||
qDebug() << "Starting the PC-BSD sysadm server...." << (websocket ? "(WebSocket)" : "(TCP)");
|
||||
WebServer *w = new WebServer();
|
||||
if( w->startServer() ){
|
||||
if( w->startServer(port, websocket) ){
|
||||
//Now start the event loop
|
||||
int 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user