diff --git a/SMSubProcess.cpp b/SMSubProcess.cpp index de77f96..eef359a 100644 --- a/SMSubProcess.cpp +++ b/SMSubProcess.cpp @@ -1,106 +1,106 @@ -/***************************************************************************** -* smsub Server Manager Subprocess -* Copyright (C) 2020 Syping -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* This software is provided as-is, no warranties are given to you, we are not -* responsible for anything with use of the software, you are self responsible. -*****************************************************************************/ - -#include -#include -#include "SMSubProcess.h" -#include "smsub.h" - -SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout) : - termTimeout(termTimeout) -{ - // Set process executable, arguments and working directory - process.setProgram(executable); - process.setArguments(arguments); - process.setWorkingDirectory(workingDirectory); - - // stdout and stderr in same IO stream - process.setProcessChannelMode(QProcess::MergedChannels); - - // Connect process signal handlers - QObject::connect(&process, &QProcess::readyRead, this, &SMSubProcess::readyRead); - QObject::connect(&process, &QProcess::errorOccurred, this, &SMSubProcess::processError); - QObject::connect(&process, QOverload::of(&QProcess::finished), this, &SMSubProcess::processExit); -} - -void SMSubProcess::start() -{ - process.start(QIODevice::ReadWrite); -} - -void SMSubProcess::readyRead() -{ - // Read process output and emit event - while (process.canReadLine()) { - const QByteArray readData = process.readLine().trimmed(); - emit outputWritten(readData + '\n'); - } -} - -void SMSubProcess::processExit(int exitCode) -{ - // Exit with the same exit code as the process - emit processStopped(); - QCoreApplication::exit(exitCode); -} - -void SMSubProcess::processError(QProcess::ProcessError error) -{ - // Handle process errors - if (likely(error == QProcess::FailedToStart)) { - QTextStream(stderr) << "Process failed to start!" << endl; - QCoreApplication::exit(1); - } - else if (error == QProcess::UnknownError) { - QTextStream(stderr) << "Unknown error occurred!" << endl; - QCoreApplication::exit(1); - } -} - -void SMSubProcess::aboutToQuit() -{ - // Main process terminated, terminate subprocess with set timeout - if (process.state() == QProcess::Running) { - process.terminate(); - if (!process.waitForFinished(termTimeout)) { - QTextStream(stderr) << "Failed to terminate process!" << endl; - } - } -} - -void SMSubProcess::killProcess() -{ - // Kill process as requested - if (process.state() == QProcess::Running) { - process.kill(); - } -} - -void SMSubProcess::stopProcess() -{ - // Terminate process as requested - if (process.state() == QProcess::Running) { - process.terminate(); - } -} - -void SMSubProcess::writeInput(const QByteArray &input) -{ - // Write input from Unix/IPC socket to process - process.write(input); -} +/***************************************************************************** +* smsub Server Manager Subprocess +* Copyright (C) 2020 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include "SMSubProcess.h" +#include "smsub.h" + +SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout) : + termTimeout(termTimeout) +{ + // Set process executable, arguments and working directory + process.setProgram(executable); + process.setArguments(arguments); + process.setWorkingDirectory(workingDirectory); + + // stdout and stderr in same IO stream + process.setProcessChannelMode(QProcess::MergedChannels); + + // Connect process signal handlers + QObject::connect(&process, &QProcess::readyRead, this, &SMSubProcess::readyRead); + QObject::connect(&process, &QProcess::errorOccurred, this, &SMSubProcess::processError); + QObject::connect(&process, QOverload::of(&QProcess::finished), this, &SMSubProcess::processExit); +} + +void SMSubProcess::start() +{ + process.start(QIODevice::ReadWrite); +} + +void SMSubProcess::readyRead() +{ + // Read process output and emit event + while (process.canReadLine()) { + const QByteArray readData = process.readLine().trimmed(); + emit outputWritten(readData + '\n'); + } +} + +void SMSubProcess::processExit(int exitCode) +{ + // Exit with the same exit code as the process + emit processStopped(); + QCoreApplication::exit(exitCode); +} + +void SMSubProcess::processError(QProcess::ProcessError error) +{ + // Handle process errors + if (likely(error == QProcess::FailedToStart)) { + QTextStream(stderr) << "Process failed to start!" << endl; + QCoreApplication::exit(1); + } + else if (error == QProcess::UnknownError) { + QTextStream(stderr) << "Unknown error occurred!" << endl; + QCoreApplication::exit(1); + } +} + +void SMSubProcess::aboutToQuit() +{ + // Main process terminated, terminate subprocess with set timeout + if (process.state() == QProcess::Running) { + process.terminate(); + if (!process.waitForFinished(termTimeout)) { + QTextStream(stderr) << "Failed to terminate process!" << endl; + } + } +} + +void SMSubProcess::killProcess() +{ + // Kill process as requested + if (process.state() == QProcess::Running) { + process.kill(); + } +} + +void SMSubProcess::stopProcess() +{ + // Terminate process as requested + if (process.state() == QProcess::Running) { + process.terminate(); + } +} + +void SMSubProcess::writeInput(const QByteArray &input) +{ + // Write input from Unix/IPC socket to process + process.write(input); +} diff --git a/SMSubProcess.h b/SMSubProcess.h index c03bfeb..e78c364 100644 --- a/SMSubProcess.h +++ b/SMSubProcess.h @@ -1,53 +1,53 @@ -/***************************************************************************** -* smsub Server Manager Subprocess -* Copyright (C) 2020 Syping -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* This software is provided as-is, no warranties are given to you, we are not -* responsible for anything with use of the software, you are self responsible. -*****************************************************************************/ - -#ifndef SMSUBPROCESS_H -#define SMSUBPROCESS_H - -#include -#include - -class SMSubProcess : public QObject -{ - Q_OBJECT -public: - SMSubProcess(const QString &executable, const QStringList &arguments, const QString& workingDirectory, const int &termTimeout = 60000); - void start(); - -private: - QProcess process; - int termTimeout; - -public slots: - void aboutToQuit(); - void killProcess(); - void stopProcess(); - void writeInput(const QByteArray &input); - -private slots: - void readyRead(); - void processExit(int exitCode); - void processError(QProcess::ProcessError error); - -signals: - void outputWritten(const QByteArray &output); - void processStopped(); - -}; - -#endif // SMSUBPROCESS_H +/***************************************************************************** +* smsub Server Manager Subprocess +* Copyright (C) 2020 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef SMSUBPROCESS_H +#define SMSUBPROCESS_H + +#include +#include + +class SMSubProcess : public QObject +{ + Q_OBJECT +public: + SMSubProcess(const QString &executable, const QStringList &arguments, const QString& workingDirectory, const int &termTimeout = 60000); + void start(); + +private: + QProcess process; + int termTimeout; + +public slots: + void aboutToQuit(); + void killProcess(); + void stopProcess(); + void writeInput(const QByteArray &input); + +private slots: + void readyRead(); + void processExit(int exitCode); + void processError(QProcess::ProcessError error); + +signals: + void outputWritten(const QByteArray &output); + void processStopped(); + +}; + +#endif // SMSUBPROCESS_H diff --git a/SMSubServer.cpp b/SMSubServer.cpp index 2e6242e..25dfd74 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -1,241 +1,244 @@ -/***************************************************************************** -* smsub Server Manager Subprocess -* Copyright (C) 2020 Syping -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* This software is provided as-is, no warranties are given to you, we are not -* responsible for anything with use of the software, you are self responsible. -*****************************************************************************/ - -#include -#include -#include -#include "SMSubServer.h" -#include "smsub.h" - -SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &socket) : serverSettings(serverSettings) -{ - QLocalServer *localServer = new QLocalServer(this); - localServer->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption); - localServer->listen(socket); - - QObject::connect(localServer, &QLocalServer::newConnection, this, &SMSubServer::newConnection); - - type = ServerType::Local; - server = localServer; -} - -SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port) : serverSettings(serverSettings) -{ - QWebSocketServer *webSocketServer = new QWebSocketServer(serverName, QWebSocketServer::NonSecureMode, this); - webSocketServer->listen(QHostAddress::LocalHost, port); - - QObject::connect(webSocketServer, &QWebSocketServer::newConnection, this, &SMSubServer::newConnection); - - type = ServerType::WebSocket; - server = webSocketServer; -} - -bool SMSubServer::isListening() -{ - if (likely(type == ServerType::Local)) { - return static_cast(server)->isListening(); - } - else if (type == ServerType::WebSocket) { - return static_cast(server)->isListening(); - } - return false; -} - -void SMSubServer::newConnection() -{ - QObject *socket; - if (likely(type == ServerType::Local)) { - QLocalSocket *localSocket = static_cast(server)->nextPendingConnection(); - QObject::connect(localSocket, &QLocalSocket::readyRead, this, &SMSubServer::lsReadyRead); - QObject::connect(localSocket, &QLocalSocket::disconnected, this, &SMSubServer::deleteSocket); - localSocket->write(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); - socket = localSocket; - } - else if (type == ServerType::WebSocket) { - QWebSocket *webSocket = static_cast(server)->nextPendingConnection(); - QObject::connect(webSocket, &QWebSocket::binaryMessageReceived, this, &SMSubServer::wsMessageReceived); - QObject::connect(webSocket, &QWebSocket::disconnected, this, &SMSubServer::deleteSocket); - webSocket->sendBinaryMessage(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); - socket = webSocket; - } - else { - // Just for being sure - return; - } - - // Set authentication state - if (serverSettings->isLocal) { - socket->setProperty("Authenticated", true); - sockets << socket; - } - else { - socket->setProperty("Authenticated", false); - } -} - -void SMSubServer::messageReceived(QObject *socket, const QByteArray &message) -{ - // Only allow commands being sent if authenticated - bool isAuthenticated = socket->property("Authenticated").toBool(); - if (likely(isAuthenticated)) { - if (message.startsWith("+dbg")) { - socket->setProperty("ReceiveDbgMsg", true); - sendMessage(socket, "Debug messages enabled!\n"); - } - else if (message.startsWith("-dbg")) { - socket->setProperty("ReceiveDbgMsg", false); - sendMessage(socket, "Debug messages disabled!\n"); - } - else if (message.startsWith("+log")) { - socket->setProperty("ReceiveLog", true); - debugOutput(socket, "Log output enabled!"); - } - else if (message.startsWith("-log")) { - socket->setProperty("ReceiveLog", false); - debugOutput(socket, "Log output disabled!"); - } - else if (message.startsWith("+reg")) { - if (likely(serverSettings->canRegister)) { - QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128); - authUuid = QByteArray::fromHex(authUuid).toBase64(QByteArray::OmitTrailingEquals); - emit tokenRegistered(QString::fromUtf8(authUuid)); - sendMessage(socket, "Token: " + authUuid + '\n'); - } - else { - sendMessage(socket, "Permission denied!\n"); - } - } - else if (message.startsWith("kill")) { - emit killRequested(); - debugOutput(socket, "Killing server!"); - } - else if (message.startsWith("stop")) { - emit stopRequested(); - debugOutput(socket, "Stopping server!"); - } - else if (message.startsWith("wl")) { - const QByteArray writeData = message.mid(3); - emit inputWritten(writeData + '\n'); - debugOutput(socket, "Write line \"" + writeData + "\"!"); - } - else if (message.startsWith("w")) { - const QByteArray writeData = message.mid(2); - emit inputWritten(writeData); - debugOutput(socket, "Write \"" + writeData + "\"!"); - } - } - else { - // Authenticate when token is valid, otherwise disconnect - if (unlikely(tokens.contains(QString::fromUtf8(message)))) { - // Set client as authenticated and add it to vector - socket->setProperty("Authenticated", true); - sendMessage(socket, "Login successful!\n"); - isAuthenticated = true; - sockets << socket; - } - else { - // Stop receiving data and disconnect socket - if (likely(type == ServerType::Local)) { - QLocalSocket *localSocket = static_cast(socket); - QObject::disconnect(localSocket, &QLocalSocket::readyRead, this, &SMSubServer::lsReadyRead); - localSocket->write("Incorrect token!\n"); - localSocket->disconnectFromServer(); - } - else if (type == ServerType::WebSocket) { - QWebSocket *webSocket = static_cast(socket); - QObject::disconnect(webSocket, &QWebSocket::binaryMessageReceived, this, &SMSubServer::wsMessageReceived); - webSocket->sendBinaryMessage("Incorrect token!\n"); - webSocket->close(QWebSocketProtocol::CloseCodeNormal); - } - return; - } - } -} - -void SMSubServer::wsMessageReceived(const QByteArray &message) -{ - QWebSocket *socket = static_cast(sender()); - messageReceived(socket, message.trimmed()); -} - -void SMSubServer::lsReadyRead() -{ - QLocalSocket *socket = static_cast(sender()); - while (socket->canReadLine()) { - const QByteArray message = socket->readLine().trimmed(); - messageReceived(socket, message); - } -} - -void SMSubServer::deleteSocket() -{ - // Delete socket and remove from index - QObject *socket = sender(); - sockets.removeAll(socket); - socket->deleteLater(); -} - -void SMSubServer::debugOutput(QObject *socket, const QByteArray &message) -{ - // Only send debug messages when the client opted-in - const QVariant variant = socket->property("ReceiveDbgMsg"); - if (unlikely(variant.type() == QVariant::Bool)) { - bool receiveDbgMsg = variant.toBool(); - if (likely(receiveDbgMsg)) { - sendMessage(socket, message + '\n'); - } - } -} - -void SMSubServer::writeOutput(const QByteArray &output) -{ - // Read process output when client opted-in for log - QVector::const_iterator it = sockets.constBegin(); - QVector::const_iterator end = sockets.constEnd(); - while (it != end) { - const QVariant variant = (*it)->property("ReceiveLog"); - if (unlikely(variant.type() == QVariant::Bool)) { - bool receiveLog = variant.toBool(); - if (likely(receiveLog)) { - sendMessage(*it, output); - } - } - it++; - } -} - -void SMSubServer::sendMessage(QObject *socket, const QByteArray &message) -{ - if (likely(type == ServerType::Local)) { - QLocalSocket *localSocket = static_cast(socket); - localSocket->write(message); - } - else if (type == ServerType::WebSocket) { - QWebSocket *webSocket = static_cast(socket); - webSocket->sendBinaryMessage(message); - } -} - -void SMSubServer::registerToken(const QString &token) -{ - // Register temporary token for a secure remote connection - tokens << token; - QTimer::singleShot(30000, [this, token]() { - tokens.removeAll(token); - }); -} +/***************************************************************************** +* smsub Server Manager Subprocess +* Copyright (C) 2020 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include +#include "SMSubServer.h" +#include "smsub.h" + +SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &socket) : serverSettings(serverSettings) +{ + QLocalServer *localServer = new QLocalServer(this); + localServer->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption); + localServer->listen(socket); + + QObject::connect(localServer, &QLocalServer::newConnection, this, &SMSubServer::newConnection); + + type = ServerType::Local; + server = localServer; +} + +SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port) : serverSettings(serverSettings) +{ + QWebSocketServer *webSocketServer = new QWebSocketServer(serverName, QWebSocketServer::NonSecureMode, this); + webSocketServer->listen(QHostAddress::LocalHost, port); + + QObject::connect(webSocketServer, &QWebSocketServer::newConnection, this, &SMSubServer::newConnection); + + type = ServerType::WebSocket; + server = webSocketServer; +} + +bool SMSubServer::isListening() +{ + if (likely(type == ServerType::Local)) { + return static_cast(server)->isListening(); + } + else if (type == ServerType::WebSocket) { + return static_cast(server)->isListening(); + } + return false; +} + +void SMSubServer::newConnection() +{ + QObject *socket; + if (likely(type == ServerType::Local)) { + QLocalSocket *localSocket = static_cast(server)->nextPendingConnection(); + QObject::connect(localSocket, &QLocalSocket::readyRead, this, &SMSubServer::lsReadyRead); + QObject::connect(localSocket, &QLocalSocket::disconnected, this, &SMSubServer::deleteSocket); + localSocket->write(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); + socket = localSocket; + } + else if (type == ServerType::WebSocket) { + QWebSocket *webSocket = static_cast(server)->nextPendingConnection(); + QObject::connect(webSocket, &QWebSocket::binaryMessageReceived, this, &SMSubServer::wsMessageReceived); + QObject::connect(webSocket, &QWebSocket::disconnected, this, &SMSubServer::deleteSocket); + webSocket->sendBinaryMessage(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); + socket = webSocket; + } + else { + // Just for being sure + return; + } + + // Set authentication state + if (serverSettings->isLocal) { + socket->setProperty("Authenticated", true); + sockets << socket; + } + else { + socket->setProperty("Authenticated", false); + } +} + +bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) +{ + // Only allow commands being sent if authenticated + bool isAuthenticated = socket->property("Authenticated").toBool(); + if (likely(isAuthenticated)) { + if (message.startsWith("+dbg")) { + socket->setProperty("ReceiveDbgMsg", true); + sendMessage(socket, "Debug messages enabled!\n"); + } + else if (message.startsWith("-dbg")) { + socket->setProperty("ReceiveDbgMsg", false); + sendMessage(socket, "Debug messages disabled!\n"); + } + else if (message.startsWith("+log")) { + socket->setProperty("ReceiveLog", true); + debugOutput(socket, "Log output enabled!"); + } + else if (message.startsWith("-log")) { + socket->setProperty("ReceiveLog", false); + debugOutput(socket, "Log output disabled!"); + } + else if (message.startsWith("+reg")) { + if (likely(serverSettings->canRegister)) { + QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128); + authUuid = QByteArray::fromHex(authUuid).toBase64(QByteArray::OmitTrailingEquals); + emit tokenRegistered(QString::fromUtf8(authUuid)); + sendMessage(socket, "Token: " + authUuid + '\n'); + } + else { + sendMessage(socket, "Permission denied!\n"); + } + } + else if (message.startsWith("kill")) { + emit killRequested(); + debugOutput(socket, "Killing server!"); + } + else if (message.startsWith("stop")) { + emit stopRequested(); + debugOutput(socket, "Stopping server!"); + } + else if (message.startsWith("wl")) { + const QByteArray writeData = message.mid(3); + emit inputWritten(writeData + '\n'); + debugOutput(socket, "Write line \"" + writeData + "\"!"); + } + else if (message.startsWith("w")) { + const QByteArray writeData = message.mid(2); + emit inputWritten(writeData); + debugOutput(socket, "Write \"" + writeData + "\"!"); + } + } + else { + // Authenticate when token is valid, otherwise disconnect + if (unlikely(tokens.contains(QString::fromUtf8(message)))) { + // Set client as authenticated and add it to vector + socket->setProperty("Authenticated", true); + sendMessage(socket, "Login successful!\n"); + isAuthenticated = true; + sockets << socket; + } + else { + // Stop receiving data and disconnect socket + if (likely(type == ServerType::Local)) { + QLocalSocket *localSocket = static_cast(socket); + QObject::disconnect(localSocket, &QLocalSocket::readyRead, this, &SMSubServer::lsReadyRead); + localSocket->write("Incorrect token!\n"); + localSocket->disconnectFromServer(); + return false; + } + else if (type == ServerType::WebSocket) { + QWebSocket *webSocket = static_cast(socket); + QObject::disconnect(webSocket, &QWebSocket::binaryMessageReceived, this, &SMSubServer::wsMessageReceived); + webSocket->sendBinaryMessage("Incorrect token!\n"); + webSocket->close(QWebSocketProtocol::CloseCodeNormal); + return false; + } + } + } + return true; +} + +void SMSubServer::wsMessageReceived(const QByteArray &message) +{ + QWebSocket *socket = static_cast(sender()); + messageReceived(socket, message.trimmed()); +} + +void SMSubServer::lsReadyRead() +{ + QLocalSocket *socket = static_cast(sender()); + while (socket->canReadLine()) { + const QByteArray message = socket->readLine().trimmed(); + if (!messageReceived(socket, message)) + return; + } +} + +void SMSubServer::deleteSocket() +{ + // Delete socket and remove from index + QObject *socket = sender(); + sockets.removeAll(socket); + socket->deleteLater(); +} + +void SMSubServer::debugOutput(QObject *socket, const QByteArray &message) +{ + // Only send debug messages when the client opted-in + const QVariant variant = socket->property("ReceiveDbgMsg"); + if (unlikely(variant.type() == QVariant::Bool)) { + bool receiveDbgMsg = variant.toBool(); + if (likely(receiveDbgMsg)) { + sendMessage(socket, message + '\n'); + } + } +} + +void SMSubServer::writeOutput(const QByteArray &output) +{ + // Read process output when client opted-in for log + QVector::const_iterator it = sockets.constBegin(); + QVector::const_iterator end = sockets.constEnd(); + while (it != end) { + const QVariant variant = (*it)->property("ReceiveLog"); + if (unlikely(variant.type() == QVariant::Bool)) { + bool receiveLog = variant.toBool(); + if (likely(receiveLog)) { + sendMessage(*it, output); + } + } + it++; + } +} + +void SMSubServer::sendMessage(QObject *socket, const QByteArray &message) +{ + if (likely(type == ServerType::Local)) { + QLocalSocket *localSocket = static_cast(socket); + localSocket->write(message); + } + else if (type == ServerType::WebSocket) { + QWebSocket *webSocket = static_cast(socket); + webSocket->sendBinaryMessage(message); + } +} + +void SMSubServer::registerToken(const QString &token) +{ + // Register temporary token for a secure remote connection + tokens << token; + QTimer::singleShot(30000, [this, token]() { + tokens.removeAll(token); + }); +} diff --git a/SMSubServer.h b/SMSubServer.h index b6692dc..706612f 100644 --- a/SMSubServer.h +++ b/SMSubServer.h @@ -1,72 +1,72 @@ -/***************************************************************************** -* smsub Server Manager Subprocess -* Copyright (C) 2020 Syping -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* This software is provided as-is, no warranties are given to you, we are not -* responsible for anything with use of the software, you are self responsible. -*****************************************************************************/ - -#ifndef SMSUBSERVER_H -#define SMSUBSERVER_H - -#include -#include -#include -#include -#include - -struct SMSubServerSettings -{ - bool canRegister; - bool isLocal; -}; - -class SMSubServer : public QObject -{ - Q_OBJECT -public: - SMSubServer(SMSubServerSettings *serverSettings, const QString &socket); - SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port); - bool isListening(); - - enum ServerType { Local, WebSocket }; - Q_ENUM(ServerType) - -public slots: - void writeOutput(const QByteArray &output); - void registerToken(const QString &token); - -private slots: - void wsMessageReceived(const QByteArray &message); - void lsReadyRead(); - void newConnection(); - void deleteSocket(); - -private: - inline void debugOutput(QObject *socket, const QByteArray &message); - inline void sendMessage(QObject *socket, const QByteArray &message); - void messageReceived(QObject *socket, const QByteArray &message); - SMSubServerSettings *serverSettings; - QVector sockets; - QVector tokens; - ServerType type; - QObject *server; - -signals: - void tokenRegistered(const QString &password); - void inputWritten(const QByteArray &input); - void killRequested(); - void stopRequested(); -}; - -#endif // SMSUBSERVER_H +/***************************************************************************** +* smsub Server Manager Subprocess +* Copyright (C) 2020 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef SMSUBSERVER_H +#define SMSUBSERVER_H + +#include +#include +#include +#include +#include + +struct SMSubServerSettings +{ + bool canRegister; + bool isLocal; +}; + +class SMSubServer : public QObject +{ + Q_OBJECT +public: + SMSubServer(SMSubServerSettings *serverSettings, const QString &socket); + SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port); + bool isListening(); + + enum ServerType { Local, WebSocket }; + Q_ENUM(ServerType) + +public slots: + void writeOutput(const QByteArray &output); + void registerToken(const QString &token); + +private slots: + void wsMessageReceived(const QByteArray &message); + void lsReadyRead(); + void newConnection(); + void deleteSocket(); + +private: + inline void debugOutput(QObject *socket, const QByteArray &message); + inline void sendMessage(QObject *socket, const QByteArray &message); + bool messageReceived(QObject *socket, const QByteArray &message); + SMSubServerSettings *serverSettings; + QVector sockets; + QVector tokens; + ServerType type; + QObject *server; + +signals: + void tokenRegistered(const QString &password); + void inputWritten(const QByteArray &input); + void killRequested(); + void stopRequested(); +}; + +#endif // SMSUBSERVER_H diff --git a/main.cpp b/main.cpp index 28a7e1f..cdcf482 100644 --- a/main.cpp +++ b/main.cpp @@ -1,317 +1,317 @@ -/***************************************************************************** -* smsub Server Manager Subprocess -* Copyright (C) 2020 Syping -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* This software is provided as-is, no warranties are given to you, we are not -* responsible for anything with use of the software, you are self responsible. -*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "SMSubProcess.h" -#include "SMSubServer.h" -#include "smsub.h" - -#ifdef Q_OS_UNIX -#include -#include "signal.h" -#include "unistd.h" -#endif - -#ifdef Q_OS_UNIX -void catchUnixSignals(std::initializer_list quitSignals) { - auto handler = [](int sig) -> void { - QString unixSignal; - switch (sig) { - case SIGINT: - unixSignal = QLatin1String("SIGINT"); - break; - case SIGHUP: - unixSignal = QLatin1String("SIGHUP"); - break; - case SIGQUIT: - unixSignal = QLatin1String("SIGQUIT"); - break; - case SIGTERM: - unixSignal = QLatin1String("SIGTERM"); - break; - default: - unixSignal = QString::number(sig); - } - QTextStream(stderr) << "Received Unix signal: " << unixSignal << endl; - QCoreApplication::quit(); - }; - - sigset_t blocking_mask; - sigemptyset(&blocking_mask); - for (int sig : quitSignals) - sigaddset(&blocking_mask, sig); - - struct sigaction sa; - sa.sa_handler = handler; - sa.sa_mask = blocking_mask; - sa.sa_flags = 0; - - for (int sig : quitSignals) - sigaction(sig, &sa, nullptr); -} -#endif - -int main(int argc, char *argv[]) -{ - QCoreApplication a(argc, argv); - a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.3"); - -#ifdef Q_OS_UNIX - catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); -#endif - - QCommandLineParser commandLineParser; - commandLineParser.addHelpOption(); - commandLineParser.addVersionOption(); - - QCommandLineOption processManifest("json", "JSON process manifest.", "json"); - commandLineParser.addOption(processManifest); - - QCommandLineOption processExecutable(QStringList() << "exec" << "executable", "Process executable to run.", "exec"); - commandLineParser.addOption(processExecutable); - - QCommandLineOption processArguments(QStringList() << "args" << "arguments", "Arguments given to process.", "args"); - commandLineParser.addOption(processArguments); - -#ifdef Q_OS_WIN - QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "IPC socket used for local communication.", "sock"); -#else - QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "Unix socket used for local communication.", "sock"); -#endif - commandLineParser.addOption(subprocessSocket); - - QCommandLineOption subprocessRemotePort("rport", "WebSockets port used for remote communication.", "rport"); - commandLineParser.addOption(subprocessRemotePort); - -#ifdef Q_OS_WIN - QCommandLineOption subprocessRemoteSocket(QStringList() << "rsock" << "rsocket", "IPC socket used for remote communication.", "rsock"); -#else - QCommandLineOption subprocessRemoteSocket(QStringList() << "rsock" << "rsocket", "Unix socket used for remote communication.", "rsock"); -#endif - commandLineParser.addOption(subprocessRemoteSocket); - - QCommandLineOption processTimeout("timeout", "SMSub termination timeout.", "timeout"); - commandLineParser.addOption(processTimeout); - - commandLineParser.process(a); - - if (unlikely(commandLineParser.isSet(processManifest) && commandLineParser.isSet(processExecutable))) { - QTextStream(stderr) << "You can't define a Process executable and a JSON process manifest at the same time!" << endl; - return 1; - } - - if (unlikely(commandLineParser.isSet(subprocessRemotePort) && commandLineParser.isSet(subprocessRemoteSocket))) { -#ifdef Q_OS_WIN - QTextStream(stderr) << "You can't define a WebSockets port and a IPC socket at same time!" << endl; -#else - QTextStream(stderr) << "You can't define a WebSockets port and a Unix socket at same time!" << endl; -#endif - return 1; - } - - bool timeoutSet = false; - int termTimeout = 60000; - if (unlikely(commandLineParser.isSet(processTimeout))) { - bool ok; - const int _termTimeout = commandLineParser.value(processTimeout).toInt(&ok); - if (ok) { - termTimeout = _termTimeout; - timeoutSet = true; - } - } - - QString executable; - QString workingDirectory; - QStringList argumentList; - if (likely(commandLineParser.isSet(processManifest))) { - QFile manifestFile(commandLineParser.value(processManifest)); - if (likely(manifestFile.open(QIODevice::ReadOnly))) { - const QByteArray jsonData = manifestFile.readAll(); - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData); - QJsonObject jsonObject = jsonDocument.object(); - - if (likely(jsonObject.contains("Executable"))) { - const QJsonValue jsonExecutable = jsonObject.value("Executable"); - if (unlikely(!jsonExecutable.isString())) { - QTextStream(stderr) << "Executable is not a string in manifest, aborting!" << endl; - manifestFile.close(); - return 1; - } - executable = jsonExecutable.toString(); - } - else { - QTextStream(stderr) << "Executable is not defined in manifest, aborting!" << endl; - manifestFile.close(); - return 1; - } - - if (likely(jsonObject.contains("WorkingDirectory"))) { - const QJsonValue jsonWorkingDirectory = jsonObject.value("WorkingDirectory"); - if (unlikely(!jsonWorkingDirectory.isString())) { - QTextStream(stderr) << "Working Directory is not a string in manifest, aborting!" << endl; - manifestFile.close(); - return 1; - } - workingDirectory = jsonWorkingDirectory.toString(); - } - else { - workingDirectory = QFileInfo(executable).absolutePath(); - } - - if (likely(jsonObject.contains("Arguments"))) { - const QJsonValue jsonArguments = jsonObject.value("Arguments"); - if (likely(jsonArguments.isArray())) { - const QJsonArray jsonArray = jsonArguments.toArray(); - QJsonArray::const_iterator it = jsonArray.constBegin(); - QJsonArray::const_iterator end = jsonArray.constEnd(); - while (it != end) { - argumentList << it->toString(); - it++; - } - } - else { - QTextStream(stderr) << "Arguments is not a array in manifest, aborting!" << endl; - manifestFile.close(); - return 1; - } - } - - if (unlikely(!timeoutSet && jsonObject.contains("TerminationTimeout"))) { - const QJsonValue jsonTimeout = jsonObject.value("TerminationTimeout"); - if (unlikely(!jsonTimeout.isDouble())) { - termTimeout = qRound(jsonTimeout.toDouble()); - } - else { - QTextStream(stderr) << "Termination Timeout is not a number in manifest, aborting!" << endl; - return 1; - } - } - - manifestFile.close(); - } - } - else if (unlikely(commandLineParser.isSet(processArguments))) { - QTextStream(stderr) << "Arguments over command line are not supported yet!" << endl; - return 1; - } - - QString socket; - if (likely(commandLineParser.isSet(subprocessSocket))) { - socket = commandLineParser.value(subprocessSocket); - } - else { -#ifdef Q_OS_WIN - QTextStream(stderr) << "You must define at least a local IPC socket!" << endl; -#else - QTextStream(stderr) << "You must define at least a local Unix socket!" << endl; -#endif - return 1; - } - - SMSubServerSettings localSettings; - localSettings.isLocal = true; - - SMSubServer subLocal(&localSettings, socket); - if (unlikely(!subLocal.isListening())) { -#ifdef Q_OS_WIN - QTextStream(stderr) << "Failed to start local IPC socket!" << endl; -#else - QTextStream(stderr) << "Failed to start local Unix socket!" << endl; -#endif - return 1; - } - - QString rsocket; - if (unlikely(commandLineParser.isSet(subprocessRemoteSocket))) { - rsocket = commandLineParser.value(subprocessRemoteSocket); - } - - bool rportSet = false; - quint16 rport; - if (unlikely(commandLineParser.isSet(subprocessRemotePort))) { - bool ok; - rport = commandLineParser.value(subprocessRemotePort).toUShort(&ok); - if (!ok) { - QTextStream(stderr) << "WebSockets port is not valid!" << endl; - return 1; - } - else { - rportSet = true; - } - } - - SMSubProcess subProcess(executable, argumentList, workingDirectory, termTimeout); - - SMSubServerSettings remoteSettings; - remoteSettings.canRegister = false; - remoteSettings.isLocal = false; - - if (unlikely(!rsocket.isEmpty())) { - SMSubServer *subRemote = new SMSubServer(&remoteSettings, rsocket); - if (unlikely(!subRemote->isListening())) { -#ifdef Q_OS_WIN - QTextStream(stderr) << "Failed to start remote IPC socket!" << endl; -#else - QTextStream(stderr) << "Failed to start remote Unix socket!" << endl; -#endif - return 1; - } - localSettings.canRegister = true; - QObject::connect(&subLocal, &SMSubServer::tokenRegistered, subRemote, &SMSubServer::registerToken); - QObject::connect(&subProcess, &SMSubProcess::outputWritten, subRemote, &SMSubServer::writeOutput); - QObject::connect(subRemote, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); - QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); - QObject::connect(subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); - } - else if (unlikely(rportSet)) { - SMSubServer *subRemote = new SMSubServer(&remoteSettings, QString(), rport); - if (unlikely(!subRemote->isListening())) { - QTextStream(stderr) << "Failed to start remote WebSockets server!" << endl; - return 1; - } - localSettings.canRegister = true; - QObject::connect(&subLocal, &SMSubServer::tokenRegistered, subRemote, &SMSubServer::registerToken); - QObject::connect(&subProcess, &SMSubProcess::outputWritten, subRemote, &SMSubServer::writeOutput); - QObject::connect(subRemote, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); - QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); - QObject::connect(subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); - } - else { - localSettings.canRegister = false; - } - - QObject::connect(&subProcess, &SMSubProcess::outputWritten, &subLocal, &SMSubServer::writeOutput); - QObject::connect(&subLocal, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); - QObject::connect(&subLocal, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); - QObject::connect(&subLocal, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); - QObject::connect(&a, &QCoreApplication::aboutToQuit, &subProcess, &SMSubProcess::aboutToQuit); - - subProcess.start(); - - return a.exec(); -} +/***************************************************************************** +* smsub Server Manager Subprocess +* Copyright (C) 2020 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SMSubProcess.h" +#include "SMSubServer.h" +#include "smsub.h" + +#ifdef Q_OS_UNIX +#include +#include "signal.h" +#include "unistd.h" +#endif + +#ifdef Q_OS_UNIX +void catchUnixSignals(std::initializer_list quitSignals) { + auto handler = [](int sig) -> void { + QString unixSignal; + switch (sig) { + case SIGINT: + unixSignal = QLatin1String("SIGINT"); + break; + case SIGHUP: + unixSignal = QLatin1String("SIGHUP"); + break; + case SIGQUIT: + unixSignal = QLatin1String("SIGQUIT"); + break; + case SIGTERM: + unixSignal = QLatin1String("SIGTERM"); + break; + default: + unixSignal = QString::number(sig); + } + QTextStream(stderr) << "Received Unix signal: " << unixSignal << endl; + QCoreApplication::quit(); + }; + + sigset_t blocking_mask; + sigemptyset(&blocking_mask); + for (int sig : quitSignals) + sigaddset(&blocking_mask, sig); + + struct sigaction sa; + sa.sa_handler = handler; + sa.sa_mask = blocking_mask; + sa.sa_flags = 0; + + for (int sig : quitSignals) + sigaction(sig, &sa, nullptr); +} +#endif + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + a.setApplicationName("Server Manager Subprocess"); + a.setApplicationVersion("0.3.1"); + +#ifdef Q_OS_UNIX + catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); +#endif + + QCommandLineParser commandLineParser; + commandLineParser.addHelpOption(); + commandLineParser.addVersionOption(); + + QCommandLineOption processManifest("json", "JSON process manifest.", "json"); + commandLineParser.addOption(processManifest); + + QCommandLineOption processExecutable(QStringList() << "exec" << "executable", "Process executable to run.", "exec"); + commandLineParser.addOption(processExecutable); + + QCommandLineOption processArguments(QStringList() << "args" << "arguments", "Arguments given to process.", "args"); + commandLineParser.addOption(processArguments); + +#ifdef Q_OS_WIN + QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "IPC socket used for local communication.", "sock"); +#else + QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "Unix socket used for local communication.", "sock"); +#endif + commandLineParser.addOption(subprocessSocket); + + QCommandLineOption subprocessRemotePort("rport", "WebSockets port used for remote communication.", "rport"); + commandLineParser.addOption(subprocessRemotePort); + +#ifdef Q_OS_WIN + QCommandLineOption subprocessRemoteSocket(QStringList() << "rsock" << "rsocket", "IPC socket used for remote communication.", "rsock"); +#else + QCommandLineOption subprocessRemoteSocket(QStringList() << "rsock" << "rsocket", "Unix socket used for remote communication.", "rsock"); +#endif + commandLineParser.addOption(subprocessRemoteSocket); + + QCommandLineOption processTimeout("timeout", "SMSub termination timeout.", "timeout"); + commandLineParser.addOption(processTimeout); + + commandLineParser.process(a); + + if (unlikely(commandLineParser.isSet(processManifest) && commandLineParser.isSet(processExecutable))) { + QTextStream(stderr) << "You can't define a Process executable and a JSON process manifest at the same time!" << endl; + return 1; + } + + if (unlikely(commandLineParser.isSet(subprocessRemotePort) && commandLineParser.isSet(subprocessRemoteSocket))) { +#ifdef Q_OS_WIN + QTextStream(stderr) << "You can't define a WebSockets port and a IPC socket at same time!" << endl; +#else + QTextStream(stderr) << "You can't define a WebSockets port and a Unix socket at same time!" << endl; +#endif + return 1; + } + + bool timeoutSet = false; + int termTimeout = 60000; + if (unlikely(commandLineParser.isSet(processTimeout))) { + bool ok; + const int _termTimeout = commandLineParser.value(processTimeout).toInt(&ok); + if (ok) { + termTimeout = _termTimeout; + timeoutSet = true; + } + } + + QString executable; + QString workingDirectory; + QStringList argumentList; + if (likely(commandLineParser.isSet(processManifest))) { + QFile manifestFile(commandLineParser.value(processManifest)); + if (likely(manifestFile.open(QIODevice::ReadOnly))) { + const QByteArray jsonData = manifestFile.readAll(); + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObject = jsonDocument.object(); + + if (likely(jsonObject.contains("Executable"))) { + const QJsonValue jsonExecutable = jsonObject.value("Executable"); + if (unlikely(!jsonExecutable.isString())) { + QTextStream(stderr) << "Executable is not a string in manifest, aborting!" << endl; + manifestFile.close(); + return 1; + } + executable = jsonExecutable.toString(); + } + else { + QTextStream(stderr) << "Executable is not defined in manifest, aborting!" << endl; + manifestFile.close(); + return 1; + } + + if (likely(jsonObject.contains("WorkingDirectory"))) { + const QJsonValue jsonWorkingDirectory = jsonObject.value("WorkingDirectory"); + if (unlikely(!jsonWorkingDirectory.isString())) { + QTextStream(stderr) << "Working Directory is not a string in manifest, aborting!" << endl; + manifestFile.close(); + return 1; + } + workingDirectory = jsonWorkingDirectory.toString(); + } + else { + workingDirectory = QFileInfo(executable).absolutePath(); + } + + if (likely(jsonObject.contains("Arguments"))) { + const QJsonValue jsonArguments = jsonObject.value("Arguments"); + if (likely(jsonArguments.isArray())) { + const QJsonArray jsonArray = jsonArguments.toArray(); + QJsonArray::const_iterator it = jsonArray.constBegin(); + QJsonArray::const_iterator end = jsonArray.constEnd(); + while (it != end) { + argumentList << it->toString(); + it++; + } + } + else { + QTextStream(stderr) << "Arguments is not a array in manifest, aborting!" << endl; + manifestFile.close(); + return 1; + } + } + + if (unlikely(!timeoutSet && jsonObject.contains("TerminationTimeout"))) { + const QJsonValue jsonTimeout = jsonObject.value("TerminationTimeout"); + if (unlikely(!jsonTimeout.isDouble())) { + termTimeout = qRound(jsonTimeout.toDouble()); + } + else { + QTextStream(stderr) << "Termination Timeout is not a number in manifest, aborting!" << endl; + return 1; + } + } + + manifestFile.close(); + } + } + else if (unlikely(commandLineParser.isSet(processArguments))) { + QTextStream(stderr) << "Arguments over command line are not supported yet!" << endl; + return 1; + } + + QString socket; + if (likely(commandLineParser.isSet(subprocessSocket))) { + socket = commandLineParser.value(subprocessSocket); + } + else { +#ifdef Q_OS_WIN + QTextStream(stderr) << "You must define at least a local IPC socket!" << endl; +#else + QTextStream(stderr) << "You must define at least a local Unix socket!" << endl; +#endif + return 1; + } + + SMSubServerSettings localSettings; + localSettings.isLocal = true; + + SMSubServer subLocal(&localSettings, socket); + if (unlikely(!subLocal.isListening())) { +#ifdef Q_OS_WIN + QTextStream(stderr) << "Failed to start local IPC socket!" << endl; +#else + QTextStream(stderr) << "Failed to start local Unix socket!" << endl; +#endif + return 1; + } + + QString rsocket; + if (unlikely(commandLineParser.isSet(subprocessRemoteSocket))) { + rsocket = commandLineParser.value(subprocessRemoteSocket); + } + + bool rportSet = false; + quint16 rport; + if (unlikely(commandLineParser.isSet(subprocessRemotePort))) { + bool ok; + rport = commandLineParser.value(subprocessRemotePort).toUShort(&ok); + if (!ok) { + QTextStream(stderr) << "WebSockets port is not valid!" << endl; + return 1; + } + else { + rportSet = true; + } + } + + SMSubProcess subProcess(executable, argumentList, workingDirectory, termTimeout); + + SMSubServerSettings remoteSettings; + remoteSettings.canRegister = false; + remoteSettings.isLocal = false; + + if (unlikely(!rsocket.isEmpty())) { + SMSubServer *subRemote = new SMSubServer(&remoteSettings, rsocket); + if (unlikely(!subRemote->isListening())) { +#ifdef Q_OS_WIN + QTextStream(stderr) << "Failed to start remote IPC socket!" << endl; +#else + QTextStream(stderr) << "Failed to start remote Unix socket!" << endl; +#endif + return 1; + } + localSettings.canRegister = true; + QObject::connect(&subLocal, &SMSubServer::tokenRegistered, subRemote, &SMSubServer::registerToken); + QObject::connect(&subProcess, &SMSubProcess::outputWritten, subRemote, &SMSubServer::writeOutput); + QObject::connect(subRemote, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); + QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); + QObject::connect(subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); + } + else if (unlikely(rportSet)) { + SMSubServer *subRemote = new SMSubServer(&remoteSettings, QString(), rport); + if (unlikely(!subRemote->isListening())) { + QTextStream(stderr) << "Failed to start remote WebSockets server!" << endl; + return 1; + } + localSettings.canRegister = true; + QObject::connect(&subLocal, &SMSubServer::tokenRegistered, subRemote, &SMSubServer::registerToken); + QObject::connect(&subProcess, &SMSubProcess::outputWritten, subRemote, &SMSubServer::writeOutput); + QObject::connect(subRemote, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); + QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); + QObject::connect(subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); + } + else { + localSettings.canRegister = false; + } + + QObject::connect(&subProcess, &SMSubProcess::outputWritten, &subLocal, &SMSubServer::writeOutput); + QObject::connect(&subLocal, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); + QObject::connect(&subLocal, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); + QObject::connect(&subLocal, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); + QObject::connect(&a, &QCoreApplication::aboutToQuit, &subProcess, &SMSubProcess::aboutToQuit); + + subProcess.start(); + + return a.exec(); +} diff --git a/smsub.pro b/smsub.pro index 7ab8c6b..7fea340 100644 --- a/smsub.pro +++ b/smsub.pro @@ -1,35 +1,35 @@ -############################################################################### -# smsub Server Manager Subprocess -# Copyright (C) 2020 Syping -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# This software is provided as-is, no warranties are given to you, we are not -# responsible for anything with use of the software, you are self responsible. -############################################################################### - -QT -= gui -QT += network websockets - -CONFIG += c++11 console -CONFIG -= app_bundle - -SOURCES += main.cpp \ - SMSubProcess.cpp \ - SMSubServer.cpp - -HEADERS += smsub.h \ - SMSubProcess.h \ - SMSubServer.h - -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +############################################################################### +# smsub Server Manager Subprocess +# Copyright (C) 2020 Syping +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# This software is provided as-is, no warranties are given to you, we are not +# responsible for anything with use of the software, you are self responsible. +############################################################################### + +QT -= gui +QT += network websockets + +CONFIG += c++11 console +CONFIG -= app_bundle + +SOURCES += main.cpp \ + SMSubProcess.cpp \ + SMSubServer.cpp + +HEADERS += smsub.h \ + SMSubProcess.h \ + SMSubServer.h + +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target