From 684e3962de16d39c785d0d24d4b9b85df9a558c3 Mon Sep 17 00:00:00 2001 From: Syping Date: Thu, 8 Jul 2021 02:57:49 +0200 Subject: [PATCH 01/10] envMode now finally working --- main.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/main.cpp b/main.cpp index 1baf24b..d35eef6 100644 --- a/main.cpp +++ b/main.cpp @@ -135,7 +135,7 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.5"); + a.setApplicationVersion("0.5.1"); #ifdef Q_OS_UNIX catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); @@ -164,6 +164,7 @@ int main(int argc, char *argv[]) if (unlikely(envEnvironmentMode == "1" || envEnvironmentMode.toLower() == "true")) { if (likely(envExecutable.isEmpty() && envArguments.isEmpty())) { QStringList arguments = a.arguments(); + arguments.removeFirst(); executable = arguments.takeFirst(); argumentList = arguments; } @@ -227,13 +228,14 @@ int main(int argc, char *argv[]) if (unlikely(!envRemotePort.isEmpty())) { bool ok; - rport = envRemotePort.toUShort(&ok); - if (!ok) { - QTextStream(stderr) << "WebSockets port is not valid in environment!" << smsub_endl; - return 1; + const quint16 _rport = envRemotePort.toUShort(&ok); + if (ok) { + rport = _rport; + rportSet = true; } else { - rportSet = true; + QTextStream(stderr) << "WebSockets port is not valid in environment!" << smsub_endl; + return 1; } } } @@ -448,24 +450,26 @@ int main(int argc, char *argv[]) if (unlikely(commandLineParser.isSet(subprocessRemotePort))) { bool ok; - rport = commandLineParser.value(subprocessRemotePort).toUShort(&ok); - if (!ok) { - QTextStream(stderr) << "WebSockets port is not valid in arguments!" << smsub_endl; - return 1; + const quint16 _rport = commandLineParser.value(subprocessRemotePort).toUShort(&ok); + if (ok) { + rport = _rport; + rportSet = true; } else { - rportSet = true; + QTextStream(stderr) << "WebSockets port is not valid in arguments!" << smsub_endl; + return 1; } } else if (!envRemotePort.isEmpty()) { bool ok; - rport = envRemotePort.toUShort(&ok); - if (!ok) { - QTextStream(stderr) << "WebSockets port is not valid in environment!" << smsub_endl; - return 1; + const quint16 _rport = envRemotePort.toUShort(&ok); + if (ok) { + rport = _rport; + rportSet = true; } else { - rportSet = true; + QTextStream(stderr) << "WebSockets port is not valid in arguments!" << smsub_endl; + return 1; } } } From 529a4a8be9a8a3403b8bcb2e1689012a9a43ed10 Mon Sep 17 00:00:00 2001 From: Syping Date: Mon, 20 Mar 2023 08:28:37 +0100 Subject: [PATCH 02/10] improve debugging, fix likely mess --- SMSubProcess.cpp | 20 ++++++++- SMSubProcess.h | 2 +- SMSubServer.cpp | 40 +++++++++++------ SMSubServer.h | 2 +- main.cpp | 109 ++++++++++++++++++++++++----------------------- smsub.h | 20 +-------- 6 files changed, 105 insertions(+), 88 deletions(-) diff --git a/SMSubProcess.cpp b/SMSubProcess.cpp index 9a67656..7ee45cc 100644 --- a/SMSubProcess.cpp +++ b/SMSubProcess.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2021 Syping +* Copyright (C) 2020-2023 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -30,13 +30,20 @@ SMSubProcess::SMSubProcess(const QString &executable, const QStringList &argumen process.setArguments(arguments); process.setWorkingDirectory(workingDirectory); + // manage input channel + process.setInputChannelMode(QProcess::ManagedInputChannel); + // 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, &QProcess::started, this, [=]() { + QTextStream(stderr) << "Subprocess started!" << smsub_endl; + }); QObject::connect(&process, QOverload::of(&QProcess::finished), this, [=](int exitCode) { + QTextStream(stderr) << "Subprocess exited!" << smsub_endl; // Exit with the same exit code as the process emit processStopped(); QCoreApplication::exit(exitCode); @@ -50,8 +57,14 @@ void SMSubProcess::start() void SMSubProcess::readyRead() { +#ifdef SMSUB_IODEBUG + QTextStream(stderr) << "Subprocess I/O RR!" << smsub_endl; +#endif // Read process output and emit event while (process.canReadLine()) { +#ifdef SMSUB_IODEBUG + QTextStream(stderr) << "Subprocess I/O WL!" << smsub_endl; +#endif const QByteArray readData = process.readLine().trimmed(); emit outputWritten(readData + '\n'); } @@ -60,7 +73,7 @@ void SMSubProcess::readyRead() void SMSubProcess::processError(QProcess::ProcessError error) { // Handle process errors - if (likely(error == QProcess::FailedToStart)) { + if (Q_LIKELY(error == QProcess::FailedToStart)) { QTextStream(stderr) << "Process failed to start!" << smsub_endl; QCoreApplication::exit(1); } @@ -99,6 +112,9 @@ void SMSubProcess::stopProcess() void SMSubProcess::writeInput(const QByteArray &input) { +#ifdef SMSUB_IODEBUG + QTextStream(stderr) << "Subprocess I/O IN!" << smsub_endl; +#endif // Write input from Unix/IPC socket to process process.write(input); } diff --git a/SMSubProcess.h b/SMSubProcess.h index dedd81c..5f36a0f 100644 --- a/SMSubProcess.h +++ b/SMSubProcess.h @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2021 Syping +* Copyright (C) 2020-2023 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: diff --git a/SMSubServer.cpp b/SMSubServer.cpp index 962829a..dd92038 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2021 Syping +* Copyright (C) 2020-2023 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -47,7 +47,7 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &ser bool SMSubServer::isListening() { - if (likely(type == ServerType::Local)) { + if (Q_LIKELY(type == ServerType::Local)) { return static_cast(server)->isListening(); } else if (type == ServerType::WebSocket) { @@ -59,12 +59,13 @@ bool SMSubServer::isListening() void SMSubServer::newConnection() { QObject *socket; - if (likely(type == ServerType::Local)) { + if (Q_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; + QTextStream(stderr) << "LocalSocket connected!" << smsub_endl; } else if (type == ServerType::WebSocket) { QWebSocket *webSocket = static_cast(server)->nextPendingConnection(); @@ -72,6 +73,7 @@ void SMSubServer::newConnection() QObject::connect(webSocket, &QWebSocket::disconnected, this, &SMSubServer::deleteSocket); webSocket->sendBinaryMessage(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); socket = webSocket; + QTextStream(stderr) << QString("WebSocket %1:%2 connected!").arg(webSocket->peerName(), QString::number(webSocket->peerPort())) << smsub_endl; } else { // Just for being sure @@ -92,7 +94,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) { // Only allow commands being sent if authenticated const bool isAuthenticated = socket->property("Authenticated").toBool(); - if (likely(isAuthenticated)) { + if (Q_LIKELY(isAuthenticated)) { if (message.startsWith("+dbg")) { socket->setProperty("ReceiveDbgMsg", true); sendMessage(socket, "Debug messages enabled!\n"); @@ -110,7 +112,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) debugOutput(socket, "Log output disabled!"); } else if (message.startsWith("+reg")) { - if (likely(serverSettings->canRegister)) { + if (Q_LIKELY(serverSettings->canRegister)) { QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128); authUuid = QByteArray::fromHex(authUuid).toBase64(QByteArray::OmitTrailingEquals); emit tokenRegistered(QString::fromUtf8(authUuid)); @@ -141,7 +143,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) } else { // Authenticate when token is valid, otherwise disconnect - if (unlikely(tokens.contains(QString::fromUtf8(message)))) { + if (Q_UNLIKELY(tokens.contains(QString::fromUtf8(message)))) { // Set client as authenticated and add it to vector socket->setProperty("Authenticated", true); sendMessage(socket, "Login successful!\n"); @@ -149,7 +151,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) } else { // Stop receiving data and disconnect socket - if (likely(type == ServerType::Local)) { + if (Q_LIKELY(type == ServerType::Local)) { QLocalSocket *localSocket = static_cast(socket); QObject::disconnect(localSocket, &QLocalSocket::readyRead, this, &SMSubServer::lsReadyRead); localSocket->write("Incorrect token!\n"); @@ -177,7 +179,13 @@ void SMSubServer::wsMessageReceived(const QByteArray &message) void SMSubServer::lsReadyRead() { QLocalSocket *socket = static_cast(sender()); +#ifdef SMSUB_IODEBUG + QTextStream(stderr) << "LocalSocket I/O RR!" << smsub_endl; +#endif while (socket->canReadLine()) { +#ifdef SMSUB_IODEBUG + QTextStream(stderr) << "LocalSocket I/O WL!" << smsub_endl; +#endif const QByteArray message = socket->readLine().trimmed(); if (!messageReceived(socket, message)) return; @@ -196,9 +204,13 @@ 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)) { +#if QT_VERSION >= 0x060000 + if (Q_UNLIKELY(variant.typeId() == QMetaType::Bool)) { +#else + if (Q_UNLIKELY(variant.type() == QVariant::Bool)) { +#endif bool receiveDbgMsg = variant.toBool(); - if (likely(receiveDbgMsg)) { + if (Q_LIKELY(receiveDbgMsg)) { sendMessage(socket, message + '\n'); } } @@ -209,9 +221,13 @@ void SMSubServer::writeOutput(const QByteArray &output) // Read process output when client opted-in for log for (auto it = sockets.constBegin(); it != sockets.constEnd(); it++) { const QVariant variant = (*it)->property("ReceiveLog"); - if (unlikely(variant.type() == QVariant::Bool)) { +#if QT_VERSION >= 0x060000 + if (Q_UNLIKELY(variant.typeId() == QMetaType::Bool)) { +#else + if (Q_UNLIKELY(variant.type() == QVariant::Bool)) { +#endif bool receiveLog = variant.toBool(); - if (likely(receiveLog)) { + if (Q_LIKELY(receiveLog)) { sendMessage(*it, output); } } @@ -220,7 +236,7 @@ void SMSubServer::writeOutput(const QByteArray &output) void SMSubServer::sendMessage(QObject *socket, const QByteArray &message) { - if (likely(type == ServerType::Local)) { + if (Q_LIKELY(type == ServerType::Local)) { QLocalSocket *localSocket = static_cast(socket); localSocket->write(message); } diff --git a/SMSubServer.h b/SMSubServer.h index f5f9936..29de80a 100644 --- a/SMSubServer.h +++ b/SMSubServer.h @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2021 Syping +* Copyright (C) 2020-2023 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: diff --git a/main.cpp b/main.cpp index d35eef6..8575ff7 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2021 Syping +* Copyright (C) 2020-2023 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -33,7 +33,6 @@ #ifdef Q_OS_UNIX #include #include "signal.h" -#include "unistd.h" #endif #ifdef Q_OS_UNIX @@ -135,7 +134,7 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.5.1"); + a.setApplicationVersion("0.6"); #ifdef Q_OS_UNIX catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); @@ -151,25 +150,25 @@ int main(int argc, char *argv[]) QString workingDirectory; QStringList argumentList; - QByteArray envEnvironmentMode = qgetenv("SMSUB_ENVIRONMENT_MODE"); - QByteArray envManifest = qgetenv("SMSUB_JSON"); - QByteArray envExecutable = qgetenv("SMSUB_EXEC"); - QByteArray envArguments = qgetenv("SMSUB_ARGS"); - QByteArray envSocket = qgetenv("SMSUB_SOCK"); - QByteArray envRemotePort = qgetenv("SMSUB_RPORT"); - QByteArray envRemoteSocket = qgetenv("SMSUB_RSOCK"); - QByteArray envTimeout = qgetenv("SMSUB_TIMEOUT"); - QByteArray envWorkDir = qgetenv("SMSUB_WORKDIR"); + const QByteArray envEnvironmentMode = qgetenv("SMSUB_ENVIRONMENT_MODE"); + const QByteArray envManifest = qgetenv("SMSUB_JSON"); + const QByteArray envExecutable = qgetenv("SMSUB_EXEC"); + const QByteArray envArguments = qgetenv("SMSUB_ARGS"); + const QByteArray envSocket = qgetenv("SMSUB_SOCK"); + const QByteArray envRemotePort = qgetenv("SMSUB_RPORT"); + const QByteArray envRemoteSocket = qgetenv("SMSUB_RSOCK"); + const QByteArray envTimeout = qgetenv("SMSUB_TIMEOUT"); + const QByteArray envWorkDir = qgetenv("SMSUB_WORKDIR"); - if (unlikely(envEnvironmentMode == "1" || envEnvironmentMode.toLower() == "true")) { - if (likely(envExecutable.isEmpty() && envArguments.isEmpty())) { + if (envEnvironmentMode == "1" || envEnvironmentMode.toLower() == "true") { + if (envExecutable.isEmpty() && envArguments.isEmpty()) { QStringList arguments = a.arguments(); arguments.removeFirst(); executable = arguments.takeFirst(); argumentList = arguments; } else { - if (likely(!envExecutable.isEmpty())) { + if (!envExecutable.isEmpty()) { executable = QString::fromUtf8(envExecutable); } else { @@ -177,7 +176,7 @@ int main(int argc, char *argv[]) return 1; } - if (likely(!envArguments.isEmpty())) { + if (!envArguments.isEmpty()) { argumentList = parseStringArguments(QString::fromUtf8(envArguments)); if (argumentList.empty()) { QTextStream(stderr) << "Arguments can't be parsed properly!" << smsub_endl; @@ -190,7 +189,7 @@ int main(int argc, char *argv[]) } } - if (unlikely(!envTimeout.isEmpty())) { + if (!envTimeout.isEmpty()) { bool ok; const int _termTimeout = envTimeout.toInt(&ok); if (ok) { @@ -203,14 +202,14 @@ int main(int argc, char *argv[]) } } - if (unlikely(!envWorkDir.isEmpty())) { + if (!envWorkDir.isEmpty()) { workingDirectory = QString::fromUtf8(envWorkDir); } else { workingDirectory = QFileInfo(executable).absolutePath(); } - if (likely(!envSocket.isEmpty())) { + if (!envSocket.isEmpty()) { socket = QString::fromUtf8(envSocket); } else { @@ -222,11 +221,11 @@ int main(int argc, char *argv[]) return 1; } - if (unlikely(!envRemoteSocket.isEmpty())) { + if (!envRemoteSocket.isEmpty()) { rsocket = QString::fromUtf8(envRemoteSocket); } - if (unlikely(!envRemotePort.isEmpty())) { + if (!envRemotePort.isEmpty()) { bool ok; const quint16 _rport = envRemotePort.toUShort(&ok); if (ok) { @@ -275,26 +274,26 @@ int main(int argc, char *argv[]) commandLineParser.process(a); - if (unlikely(commandLineParser.isSet(processManifest) && commandLineParser.isSet(processExecutable) || + if (commandLineParser.isSet(processManifest) && commandLineParser.isSet(processExecutable) || !envManifest.isEmpty() && !envExecutable.isEmpty() || commandLineParser.isSet(processManifest) && !envExecutable.isEmpty() || - !envManifest.isEmpty() && commandLineParser.isSet(processExecutable))) { + !envManifest.isEmpty() && commandLineParser.isSet(processExecutable)) { QTextStream(stderr) << "You can't define a Process executable and a JSON process manifest at the same time!" << smsub_endl; return 1; } - if (unlikely(commandLineParser.isSet(processManifest) && commandLineParser.isSet(processArguments) || + if (commandLineParser.isSet(processManifest) && commandLineParser.isSet(processArguments) || !envManifest.isEmpty() && !envArguments.isEmpty() || commandLineParser.isSet(processManifest) && !envArguments.isEmpty() || - !envManifest.isEmpty() && commandLineParser.isSet(processArguments))) { + !envManifest.isEmpty() && commandLineParser.isSet(processArguments)) { QTextStream(stderr) << "You can't define a Process arguments and a JSON process manifest at the same time!" << smsub_endl; return 1; } - if (unlikely(commandLineParser.isSet(subprocessRemotePort) && commandLineParser.isSet(subprocessRemoteSocket) || + if (commandLineParser.isSet(subprocessRemotePort) && commandLineParser.isSet(subprocessRemoteSocket) || !envRemotePort.isEmpty() && !envRemoteSocket.isEmpty() || commandLineParser.isSet(subprocessRemotePort) && !envRemoteSocket.isEmpty() || - !envRemotePort.isEmpty() && commandLineParser.isSet(subprocessRemoteSocket))) { + !envRemotePort.isEmpty() && commandLineParser.isSet(subprocessRemoteSocket)) { #ifdef Q_OS_WIN QTextStream(stderr) << "You can't define a WebSockets port and a IPC socket at same time!" << smsub_endl; #else @@ -303,7 +302,7 @@ int main(int argc, char *argv[]) return 1; } - if (unlikely(commandLineParser.isSet(processTimeout))) { + if (commandLineParser.isSet(processTimeout)) { bool ok; const int _termTimeout = commandLineParser.value(processTimeout).toInt(&ok); if (ok) { @@ -315,7 +314,7 @@ int main(int argc, char *argv[]) return 1; } } - else if (unlikely(!envTimeout.isEmpty())) { + else if (!envTimeout.isEmpty()) { bool ok; const int _termTimeout = envTimeout.toInt(&ok); if (ok) { @@ -328,14 +327,14 @@ int main(int argc, char *argv[]) } } - if (unlikely(commandLineParser.isSet(processExecutable))) { + if (commandLineParser.isSet(processExecutable)) { executable = commandLineParser.value(processExecutable); } - else if (unlikely(!envExecutable.isEmpty())) { + else if (!envExecutable.isEmpty()) { executable = QString::fromUtf8(envExecutable); } - if (unlikely(!envWorkDir.isEmpty())) { + if (!envWorkDir.isEmpty()) { workingDirectory = QString::fromUtf8(envWorkDir); } else { @@ -343,23 +342,23 @@ int main(int argc, char *argv[]) } QString manifestPath; - if (likely(commandLineParser.isSet(processManifest))) { + if (commandLineParser.isSet(processManifest)) { manifestPath = commandLineParser.value(processManifest); } else if (!envManifest.isEmpty()) { manifestPath = QString::fromUtf8(envManifest); } - if (likely(!manifestPath.isEmpty())) { + if (!manifestPath.isEmpty()) { QFile manifestFile(manifestPath); - if (likely(manifestFile.open(QIODevice::ReadOnly))) { + if (manifestFile.open(QIODevice::ReadOnly)) { const QByteArray jsonData = manifestFile.readAll(); QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData); QJsonObject jsonObject = jsonDocument.object(); - if (likely(jsonObject.contains("Executable"))) { + if (jsonObject.contains("Executable")) { const QJsonValue jsonExecutable = jsonObject.value("Executable"); - if (unlikely(!jsonExecutable.isString())) { + if (!jsonExecutable.isString()) { QTextStream(stderr) << "Executable is not a string in manifest, aborting!" << smsub_endl; manifestFile.close(); return 1; @@ -372,9 +371,9 @@ int main(int argc, char *argv[]) return 1; } - if (likely(jsonObject.contains("WorkingDirectory"))) { + if (jsonObject.contains("WorkingDirectory")) { const QJsonValue jsonWorkingDirectory = jsonObject.value("WorkingDirectory"); - if (unlikely(!jsonWorkingDirectory.isString())) { + if (!jsonWorkingDirectory.isString()) { QTextStream(stderr) << "Working Directory is not a string in manifest, aborting!" << smsub_endl; manifestFile.close(); return 1; @@ -382,9 +381,9 @@ int main(int argc, char *argv[]) workingDirectory = jsonWorkingDirectory.toString(); } - if (likely(jsonObject.contains("Arguments"))) { + if (jsonObject.contains("Arguments")) { const QJsonValue jsonArguments = jsonObject.value("Arguments"); - if (likely(jsonArguments.isArray())) { + if (jsonArguments.isArray()) { const QJsonArray jsonArray = jsonArguments.toArray(); for (auto it = jsonArray.constBegin(); it != jsonArray.constEnd(); it++) { argumentList << it->toString(); @@ -397,9 +396,9 @@ int main(int argc, char *argv[]) } } - if (unlikely(!timeoutSet && jsonObject.contains("TerminationTimeout"))) { + if (!timeoutSet && jsonObject.contains("TerminationTimeout")) { const QJsonValue jsonTimeout = jsonObject.value("TerminationTimeout"); - if (unlikely(!jsonTimeout.isDouble())) { + if (!jsonTimeout.isDouble()) { termTimeout = qRound(jsonTimeout.toDouble()); } else { @@ -411,14 +410,14 @@ int main(int argc, char *argv[]) manifestFile.close(); } } - else if (unlikely(commandLineParser.isSet(processArguments))) { + else if (commandLineParser.isSet(processArguments)) { argumentList = parseStringArguments(commandLineParser.value(processArguments)); if (argumentList.empty()) { QTextStream(stderr) << "Arguments can't be parsed properly!" << smsub_endl; return 1; } } - else if (unlikely(!envArguments.isEmpty())) { + else if (!envArguments.isEmpty()) { argumentList = parseStringArguments(QString::fromUtf8(envArguments)); if (argumentList.empty()) { QTextStream(stderr) << "Arguments can't be parsed properly!" << smsub_endl; @@ -426,7 +425,7 @@ int main(int argc, char *argv[]) } } - if (likely(commandLineParser.isSet(subprocessSocket))) { + if (commandLineParser.isSet(subprocessSocket)) { socket = commandLineParser.value(subprocessSocket); } else if (!envSocket.isEmpty()) { @@ -441,14 +440,14 @@ int main(int argc, char *argv[]) return 1; } - if (unlikely(commandLineParser.isSet(subprocessRemoteSocket))) { + if (commandLineParser.isSet(subprocessRemoteSocket)) { rsocket = commandLineParser.value(subprocessRemoteSocket); } - else if (unlikely(!envRemoteSocket.isEmpty())) { + else if (!envRemoteSocket.isEmpty()) { rsocket = QString::fromUtf8(envRemoteSocket); } - if (unlikely(commandLineParser.isSet(subprocessRemotePort))) { + if (commandLineParser.isSet(subprocessRemotePort)) { bool ok; const quint16 _rport = commandLineParser.value(subprocessRemotePort).toUShort(&ok); if (ok) { @@ -478,7 +477,7 @@ int main(int argc, char *argv[]) localSettings.isLocal = true; SMSubServer subLocal(&localSettings, socket); - if (unlikely(!subLocal.isListening())) { + if (!subLocal.isListening()) { #ifdef Q_OS_WIN QTextStream(stderr) << "Failed to start local IPC socket!" << smsub_endl; #else @@ -493,9 +492,9 @@ int main(int argc, char *argv[]) remoteSettings.canRegister = false; remoteSettings.isLocal = false; - if (unlikely(!rsocket.isEmpty())) { + if (!rsocket.isEmpty()) { SMSubServer *subRemote = new SMSubServer(&remoteSettings, rsocket); - if (unlikely(!subRemote->isListening())) { + if (!subRemote->isListening()) { #ifdef Q_OS_WIN QTextStream(stderr) << "Failed to start remote IPC socket!" << smsub_endl; #else @@ -510,9 +509,9 @@ int main(int argc, char *argv[]) QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); QObject::connect(subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); } - else if (unlikely(rportSet)) { + else if (rportSet) { SMSubServer *subRemote = new SMSubServer(&remoteSettings, QString(), rport); - if (unlikely(!subRemote->isListening())) { + if (!subRemote->isListening()) { QTextStream(stderr) << "Failed to start remote WebSockets server!" << smsub_endl; return 1; } @@ -535,5 +534,7 @@ int main(int argc, char *argv[]) subProcess.start(); + QTextStream(stderr) << QString("SMSub Version %1 initialized!").arg(QCoreApplication::applicationVersion()) << smsub_endl; + return a.exec(); } diff --git a/smsub.h b/smsub.h index d05e8bc..9b93a6b 100644 --- a/smsub.h +++ b/smsub.h @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2021 Syping +* Copyright (C) 2020-2023 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -20,23 +20,7 @@ #define SMSUB_H #include -#ifndef SMSUB_WITHOUT_EXPECT -#ifndef likely -#define likely(x) __builtin_expect((x),1) -#endif -#ifndef unlikely -#define unlikely(x) __builtin_expect((x),0) -#endif -#else -#ifndef likely -#define likely(x) (x) -#endif -#ifndef unlikely -#define unlikely(x) (x) -#endif -#endif - -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +#if QT_VERSION >= 0x050F00 #define smsub_endl Qt::endl #else #define smsub_endl endl From 95db23458ff7af8cb376e1979fb3154d404fd9e7 Mon Sep 17 00:00:00 2001 From: Syping Date: Wed, 9 Aug 2023 20:58:21 +0200 Subject: [PATCH 03/10] add KeepAlive, Status and Buffered Reads --- CMakeLists.txt | 2 +- SMSubProcess.cpp | 52 ++++++++++++++++++++++++++++++------------------ SMSubProcess.h | 10 +++++----- SMSubServer.cpp | 29 ++++++++++++++++++++++++--- SMSubServer.h | 5 ++++- main.cpp | 44 +++++++++++++++++++++++++++++++++------- 6 files changed, 106 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c83979b..d25973c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(smsub LANGUAGES CXX) +project(smsub LANGUAGES CXX VERSION 0.7) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/SMSubProcess.cpp b/SMSubProcess.cpp index 7ee45cc..14ef091 100644 --- a/SMSubProcess.cpp +++ b/SMSubProcess.cpp @@ -22,8 +22,8 @@ #include "smsub.h" #include -SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout) : - termTimeout(termTimeout) +SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout, const bool &keepAlive) : + termTimeout(termTimeout), keepAlive(keepAlive) { // Set process executable, arguments and working directory process.setProgram(executable); @@ -41,26 +41,31 @@ SMSubProcess::SMSubProcess(const QString &executable, const QStringList &argumen QObject::connect(&process, &QProcess::errorOccurred, this, &SMSubProcess::processError); QObject::connect(&process, &QProcess::started, this, [=]() { QTextStream(stderr) << "Subprocess started!" << smsub_endl; + emit statusUpdated(true); }); QObject::connect(&process, QOverload::of(&QProcess::finished), this, [=](int exitCode) { QTextStream(stderr) << "Subprocess exited!" << smsub_endl; // Exit with the same exit code as the process - emit processStopped(); - QCoreApplication::exit(exitCode); + emit statusUpdated(false); + if (!keepAlive) + QCoreApplication::exit(exitCode); }); } -void SMSubProcess::start() -{ - process.start(QIODevice::ReadWrite); -} - void SMSubProcess::readyRead() { #ifdef SMSUB_IODEBUG QTextStream(stderr) << "Subprocess I/O RR!" << smsub_endl; #endif // Read process output and emit event +#ifdef SMSUB_BUFFERED_READS +#ifdef SMSUB_IODEBUG + QTextStream(stderr) << "Subprocess I/O W!" << smsub_endl; +#endif + const QByteArray readData = process.read(1024); + if (!readData.isEmpty()) + emit outputWritten(readData); +#else while (process.canReadLine()) { #ifdef SMSUB_IODEBUG QTextStream(stderr) << "Subprocess I/O WL!" << smsub_endl; @@ -68,6 +73,7 @@ void SMSubProcess::readyRead() const QByteArray readData = process.readLine().trimmed(); emit outputWritten(readData + '\n'); } +#endif } void SMSubProcess::processError(QProcess::ProcessError error) @@ -75,11 +81,13 @@ void SMSubProcess::processError(QProcess::ProcessError error) // Handle process errors if (Q_LIKELY(error == QProcess::FailedToStart)) { QTextStream(stderr) << "Process failed to start!" << smsub_endl; - QCoreApplication::exit(1); + if (!keepAlive) + QCoreApplication::exit(1); } else if (error == QProcess::UnknownError) { QTextStream(stderr) << "Unknown error occurred!" << smsub_endl; - QCoreApplication::exit(1); + if (!keepAlive) + QCoreApplication::exit(1); } } @@ -94,20 +102,25 @@ void SMSubProcess::aboutToQuit() } } -void SMSubProcess::killProcess() +void SMSubProcess::startProcess() { - // Kill process as requested - if (process.state() == QProcess::Running) { - process.kill(); - } + // Start process as requested + if (process.state() == QProcess::NotRunning) + process.start(QIODevice::ReadWrite); } void SMSubProcess::stopProcess() { // Terminate process as requested - if (process.state() == QProcess::Running) { + if (process.state() == QProcess::Running) process.terminate(); - } +} + +void SMSubProcess::killProcess() +{ + // Kill process as requested + if (process.state() == QProcess::Running) + process.kill(); } void SMSubProcess::writeInput(const QByteArray &input) @@ -116,5 +129,6 @@ void SMSubProcess::writeInput(const QByteArray &input) QTextStream(stderr) << "Subprocess I/O IN!" << smsub_endl; #endif // Write input from Unix/IPC socket to process - process.write(input); + if (process.state() == QProcess::Running) + process.write(input); } diff --git a/SMSubProcess.h b/SMSubProcess.h index 5f36a0f..cd2dddc 100644 --- a/SMSubProcess.h +++ b/SMSubProcess.h @@ -26,17 +26,18 @@ class SMSubProcess : public QObject { Q_OBJECT public: - SMSubProcess(const QString &executable, const QStringList &arguments, const QString& workingDirectory, const int &termTimeout = 60000); - void start(); + SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout = 60000, const bool &keepAlive = false); private: QProcess process; int termTimeout; + bool keepAlive; public slots: void aboutToQuit(); - void killProcess(); + void startProcess(); void stopProcess(); + void killProcess(); void writeInput(const QByteArray &input); private slots: @@ -45,8 +46,7 @@ private slots: signals: void outputWritten(const QByteArray &output); - void processStopped(); - + void statusUpdated(const bool status); }; #endif // SMSUBPROCESS_H diff --git a/SMSubServer.cpp b/SMSubServer.cpp index dd92038..c753b79 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -32,6 +32,7 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &soc type = ServerType::Local; server = localServer; + status = false; } SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port) : serverSettings(serverSettings) @@ -43,6 +44,7 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &ser type = ServerType::WebSocket; server = webSocketServer; + status = false; } bool SMSubServer::isListening() @@ -122,14 +124,30 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) sendMessage(socket, "Permission denied!\n"); } } - else if (message.startsWith("kill")) { - emit killRequested(); - debugOutput(socket, "Killing server!"); + else if (message.startsWith("status")) { + if (status) { + sendMessage(socket, "Status: on\n"); + } + else { + sendMessage(socket, "Status: off\n"); + } + } + else if (message.startsWith("start")) { + emit startRequested(); + debugOutput(socket, "Starting server!"); } else if (message.startsWith("stop")) { emit stopRequested(); debugOutput(socket, "Stopping server!"); } + else if (message.startsWith("kill")) { + emit killRequested(); + debugOutput(socket, "Killing server!"); + } + else if (message.startsWith("quit")) { + QTimer::singleShot(0, qApp, &QCoreApplication::quit); + debugOutput(socket, "Qutting smsub!"); + } else if (message.startsWith("wl")) { const QByteArray writeData = message.mid(3); emit inputWritten(writeData + '\n'); @@ -254,3 +272,8 @@ void SMSubServer::registerToken(const QString &token) tokens.removeAll(token); }); } + +void SMSubServer::statusUpdated(const bool status_) +{ + status = status_; +} diff --git a/SMSubServer.h b/SMSubServer.h index 29de80a..daabb22 100644 --- a/SMSubServer.h +++ b/SMSubServer.h @@ -45,6 +45,7 @@ public: public slots: void writeOutput(const QByteArray &output); void registerToken(const QString &token); + void statusUpdated(const bool status); private slots: void wsMessageReceived(const QByteArray &message); @@ -61,12 +62,14 @@ private: QVector tokens; ServerType type; QObject *server; + bool status; signals: void tokenRegistered(const QString &password); void inputWritten(const QByteArray &input); - void killRequested(); + void startRequested(); void stopRequested(); + void killRequested(); }; #endif // SMSUBSERVER_H diff --git a/main.cpp b/main.cpp index 8575ff7..aebda4d 100644 --- a/main.cpp +++ b/main.cpp @@ -134,13 +134,14 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.6"); + a.setApplicationVersion("0.7"); #ifdef Q_OS_UNIX catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); #endif bool rportSet = false; + bool keepAlive = false; bool timeoutSet = false; int termTimeout = 60000; quint16 rport; @@ -151,6 +152,7 @@ int main(int argc, char *argv[]) QStringList argumentList; const QByteArray envEnvironmentMode = qgetenv("SMSUB_ENVIRONMENT_MODE"); + const QByteArray envKeepAlive = qgetenv("SMSUB_KEEPALIVE"); const QByteArray envManifest = qgetenv("SMSUB_JSON"); const QByteArray envExecutable = qgetenv("SMSUB_EXEC"); const QByteArray envArguments = qgetenv("SMSUB_ARGS"); @@ -160,7 +162,7 @@ int main(int argc, char *argv[]) const QByteArray envTimeout = qgetenv("SMSUB_TIMEOUT"); const QByteArray envWorkDir = qgetenv("SMSUB_WORKDIR"); - if (envEnvironmentMode == "1" || envEnvironmentMode.toLower() == "true") { + if (envEnvironmentMode == "1" || envEnvironmentMode.toLower() == "true" || envEnvironmentMode.toLower() == "yes") { if (envExecutable.isEmpty() && envArguments.isEmpty()) { QStringList arguments = a.arguments(); arguments.removeFirst(); @@ -202,6 +204,12 @@ int main(int argc, char *argv[]) } } + if (!envKeepAlive.isEmpty()) { + if (envKeepAlive == "1" || envKeepAlive.toLower() == "true" || envKeepAlive.toLower() == "yes") { + keepAlive = true; + } + } + if (!envWorkDir.isEmpty()) { workingDirectory = QString::fromUtf8(envWorkDir); } @@ -334,6 +342,12 @@ int main(int argc, char *argv[]) executable = QString::fromUtf8(envExecutable); } + if (!envKeepAlive.isEmpty()) { + if (envKeepAlive == "1" || envKeepAlive.toLower() == "true" || envKeepAlive.toLower() == "yes") { + keepAlive = true; + } + } + if (!envWorkDir.isEmpty()) { workingDirectory = QString::fromUtf8(envWorkDir); } @@ -371,6 +385,16 @@ int main(int argc, char *argv[]) return 1; } + if (jsonObject.contains("KeepAlive")) { + const QJsonValue jsonKeepAlive = jsonObject.value("KeepAlive"); + if (!jsonKeepAlive.isBool()) { + QTextStream(stderr) << "KeepAlive is not a bool in manifest, aborting!" << smsub_endl; + manifestFile.close(); + return 1; + } + keepAlive = jsonKeepAlive.toBool(); + } + if (jsonObject.contains("WorkingDirectory")) { const QJsonValue jsonWorkingDirectory = jsonObject.value("WorkingDirectory"); if (!jsonWorkingDirectory.isString()) { @@ -486,7 +510,7 @@ int main(int argc, char *argv[]) return 1; } - SMSubProcess subProcess(executable, argumentList, workingDirectory, termTimeout); + SMSubProcess subProcess(executable, argumentList, workingDirectory, termTimeout, keepAlive); SMSubServerSettings remoteSettings; remoteSettings.canRegister = false; @@ -505,9 +529,11 @@ int main(int argc, char *argv[]) localSettings.canRegister = true; QObject::connect(&subLocal, &SMSubServer::tokenRegistered, subRemote, &SMSubServer::registerToken); QObject::connect(&subProcess, &SMSubProcess::outputWritten, subRemote, &SMSubServer::writeOutput); + QObject::connect(&subProcess, &SMSubProcess::statusUpdated, subRemote, &SMSubServer::statusUpdated); QObject::connect(subRemote, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); - QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); + QObject::connect(subRemote, &SMSubServer::startRequested, &subProcess, &SMSubProcess::startProcess); QObject::connect(subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); + QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); } else if (rportSet) { SMSubServer *subRemote = new SMSubServer(&remoteSettings, QString(), rport); @@ -518,21 +544,25 @@ int main(int argc, char *argv[]) localSettings.canRegister = true; QObject::connect(&subLocal, &SMSubServer::tokenRegistered, subRemote, &SMSubServer::registerToken); QObject::connect(&subProcess, &SMSubProcess::outputWritten, subRemote, &SMSubServer::writeOutput); + QObject::connect(&subProcess, &SMSubProcess::statusUpdated, subRemote, &SMSubServer::statusUpdated); QObject::connect(subRemote, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); - QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); + QObject::connect(subRemote, &SMSubServer::startRequested, &subProcess, &SMSubProcess::startProcess); QObject::connect(subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); + QObject::connect(subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); } else { localSettings.canRegister = false; } QObject::connect(&subProcess, &SMSubProcess::outputWritten, &subLocal, &SMSubServer::writeOutput); + QObject::connect(&subProcess, &SMSubProcess::statusUpdated, &subLocal, &SMSubServer::statusUpdated); QObject::connect(&subLocal, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput); - QObject::connect(&subLocal, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); + QObject::connect(&subLocal, &SMSubServer::startRequested, &subProcess, &SMSubProcess::startProcess); QObject::connect(&subLocal, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess); + QObject::connect(&subLocal, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); QObject::connect(&a, &QCoreApplication::aboutToQuit, &subProcess, &SMSubProcess::aboutToQuit); - subProcess.start(); + subProcess.startProcess(); QTextStream(stderr) << QString("SMSub Version %1 initialized!").arg(QCoreApplication::applicationVersion()) << smsub_endl; From d37369ad4f989102a08f5986cf6115313497ad35 Mon Sep 17 00:00:00 2001 From: Syping Date: Thu, 25 Apr 2024 23:11:46 +0200 Subject: [PATCH 04/10] add AutoStart option, Last Start and Last Stop --- CMakeLists.txt | 2 +- SMSubProcess.cpp | 8 ++++---- SMSubProcess.h | 4 ++-- SMSubServer.cpp | 13 +++++++++---- SMSubServer.h | 6 ++++-- main.cpp | 31 ++++++++++++++++++++++++++++--- 6 files changed, 48 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d25973c..bf8df83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(smsub LANGUAGES CXX VERSION 0.7) +project(smsub LANGUAGES CXX VERSION 0.8) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/SMSubProcess.cpp b/SMSubProcess.cpp index 14ef091..e9016c6 100644 --- a/SMSubProcess.cpp +++ b/SMSubProcess.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2023 Syping +* Copyright (C) 2020-2024 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -18,9 +18,9 @@ #include #include +#include #include "SMSubProcess.h" #include "smsub.h" -#include SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout, const bool &keepAlive) : termTimeout(termTimeout), keepAlive(keepAlive) @@ -41,12 +41,12 @@ SMSubProcess::SMSubProcess(const QString &executable, const QStringList &argumen QObject::connect(&process, &QProcess::errorOccurred, this, &SMSubProcess::processError); QObject::connect(&process, &QProcess::started, this, [=]() { QTextStream(stderr) << "Subprocess started!" << smsub_endl; - emit statusUpdated(true); + emit statusUpdated(true, QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); }); QObject::connect(&process, QOverload::of(&QProcess::finished), this, [=](int exitCode) { QTextStream(stderr) << "Subprocess exited!" << smsub_endl; // Exit with the same exit code as the process - emit statusUpdated(false); + emit statusUpdated(false, QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); if (!keepAlive) QCoreApplication::exit(exitCode); }); diff --git a/SMSubProcess.h b/SMSubProcess.h index cd2dddc..568cf9a 100644 --- a/SMSubProcess.h +++ b/SMSubProcess.h @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2023 Syping +* Copyright (C) 2020-2024 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -46,7 +46,7 @@ private slots: signals: void outputWritten(const QByteArray &output); - void statusUpdated(const bool status); + void statusUpdated(const bool status, const qint64 time); }; #endif // SMSUBPROCESS_H diff --git a/SMSubServer.cpp b/SMSubServer.cpp index c753b79..3e8402a 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2023 Syping +* Copyright (C) 2020-2024 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -33,6 +33,8 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &soc type = ServerType::Local; server = localServer; status = false; + startTime = QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); + stopTime = startTime; } SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port) : serverSettings(serverSettings) @@ -45,6 +47,8 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &ser type = ServerType::WebSocket; server = webSocketServer; status = false; + startTime = QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); + stopTime = startTime; } bool SMSubServer::isListening() @@ -126,10 +130,10 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) } else if (message.startsWith("status")) { if (status) { - sendMessage(socket, "Status: on\n"); + sendMessage(socket, QString("Status: on\nLast Start: %1\nLast Stop: %2\n").arg(QString::number(startTime), QString::number(stopTime)).toUtf8()); } else { - sendMessage(socket, "Status: off\n"); + sendMessage(socket, QString("Status: off\nLast Start: %1\nLast Stop: %2\n").arg(QString::number(startTime), QString::number(stopTime)).toUtf8()); } } else if (message.startsWith("start")) { @@ -273,7 +277,8 @@ void SMSubServer::registerToken(const QString &token) }); } -void SMSubServer::statusUpdated(const bool status_) +void SMSubServer::statusUpdated(const bool status_, const qint64 time) { status = status_; + status ? (startTime = time) : (stopTime = time); } diff --git a/SMSubServer.h b/SMSubServer.h index daabb22..dc1b743 100644 --- a/SMSubServer.h +++ b/SMSubServer.h @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2023 Syping +* Copyright (C) 2020-2024 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -45,7 +45,7 @@ public: public slots: void writeOutput(const QByteArray &output); void registerToken(const QString &token); - void statusUpdated(const bool status); + void statusUpdated(const bool status, const qint64 time); private slots: void wsMessageReceived(const QByteArray &message); @@ -60,6 +60,8 @@ private: SMSubServerSettings *serverSettings; QVector sockets; QVector tokens; + qint64 startTime; + qint64 stopTime; ServerType type; QObject *server; bool status; diff --git a/main.cpp b/main.cpp index aebda4d..adf5f7b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * smsub Server Manager Subprocess -* Copyright (C) 2020-2023 Syping +* Copyright (C) 2020-2024 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -134,13 +134,14 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.7"); + a.setApplicationVersion("0.8"); #ifdef Q_OS_UNIX catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); #endif bool rportSet = false; + bool autoStart = true; bool keepAlive = false; bool timeoutSet = false; int termTimeout = 60000; @@ -152,6 +153,7 @@ int main(int argc, char *argv[]) QStringList argumentList; const QByteArray envEnvironmentMode = qgetenv("SMSUB_ENVIRONMENT_MODE"); + const QByteArray envAutoStart = qgetenv("SMSUB_AUTOSTART"); const QByteArray envKeepAlive = qgetenv("SMSUB_KEEPALIVE"); const QByteArray envManifest = qgetenv("SMSUB_JSON"); const QByteArray envExecutable = qgetenv("SMSUB_EXEC"); @@ -204,6 +206,12 @@ int main(int argc, char *argv[]) } } + if (!envAutoStart.isEmpty()) { + if (envAutoStart == "0" || envAutoStart.toLower() == "false" || envAutoStart.toLower() == "no") { + autoStart = false; + } + } + if (!envKeepAlive.isEmpty()) { if (envKeepAlive == "1" || envKeepAlive.toLower() == "true" || envKeepAlive.toLower() == "yes") { keepAlive = true; @@ -342,6 +350,12 @@ int main(int argc, char *argv[]) executable = QString::fromUtf8(envExecutable); } + if (!envAutoStart.isEmpty()) { + if (envAutoStart == "0" || envAutoStart.toLower() == "false" || envAutoStart.toLower() == "no") { + autoStart = false; + } + } + if (!envKeepAlive.isEmpty()) { if (envKeepAlive == "1" || envKeepAlive.toLower() == "true" || envKeepAlive.toLower() == "yes") { keepAlive = true; @@ -385,6 +399,16 @@ int main(int argc, char *argv[]) return 1; } + if (jsonObject.contains("AutoStart")) { + const QJsonValue jsonAutoStart = jsonObject.value("AutoStart"); + if (!jsonAutoStart.isBool()) { + QTextStream(stderr) << "AutoStart is not a bool in manifest, aborting!" << smsub_endl; + manifestFile.close(); + return 1; + } + autoStart = jsonAutoStart.toBool(); + } + if (jsonObject.contains("KeepAlive")) { const QJsonValue jsonKeepAlive = jsonObject.value("KeepAlive"); if (!jsonKeepAlive.isBool()) { @@ -562,7 +586,8 @@ int main(int argc, char *argv[]) QObject::connect(&subLocal, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess); QObject::connect(&a, &QCoreApplication::aboutToQuit, &subProcess, &SMSubProcess::aboutToQuit); - subProcess.startProcess(); + if (autoStart) + subProcess.startProcess(); QTextStream(stderr) << QString("SMSub Version %1 initialized!").arg(QCoreApplication::applicationVersion()) << smsub_endl; From 1ddc4ff755484dd850dc45de16c0eac81fa1b73a Mon Sep 17 00:00:00 2001 From: Syping Date: Tue, 7 May 2024 17:29:23 +0200 Subject: [PATCH 05/10] add BufferedReads, add AutoStart and KeepAlive command line options --- CMakeLists.txt | 2 +- SMSubProcess.cpp | 33 ++++++++++++++++++--------------- SMSubProcess.h | 3 ++- SMSubServer.cpp | 33 +++++++++++++++++---------------- main.cpp | 48 ++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 78 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf8df83..9486a27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(smsub LANGUAGES CXX VERSION 0.8) +project(smsub LANGUAGES CXX VERSION 0.9) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/SMSubProcess.cpp b/SMSubProcess.cpp index e9016c6..9b19b51 100644 --- a/SMSubProcess.cpp +++ b/SMSubProcess.cpp @@ -22,8 +22,8 @@ #include "SMSubProcess.h" #include "smsub.h" -SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout, const bool &keepAlive) : - termTimeout(termTimeout), keepAlive(keepAlive) +SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int termTimeout, const bool buffReads, const bool keepAlive) : + termTimeout(termTimeout), buffReads(buffReads), keepAlive(keepAlive) { // Set process executable, arguments and working directory process.setProgram(executable); @@ -33,7 +33,7 @@ SMSubProcess::SMSubProcess(const QString &executable, const QStringList &argumen // manage input channel process.setInputChannelMode(QProcess::ManagedInputChannel); - // stdout and stderr in same IO stream + // stdout and stderr in same I/O stream process.setProcessChannelMode(QProcess::MergedChannels); // Connect process signal handlers @@ -58,22 +58,25 @@ void SMSubProcess::readyRead() QTextStream(stderr) << "Subprocess I/O RR!" << smsub_endl; #endif // Read process output and emit event -#ifdef SMSUB_BUFFERED_READS + if (buffReads) { + while (process.bytesAvailable()) { #ifdef SMSUB_IODEBUG - QTextStream(stderr) << "Subprocess I/O W!" << smsub_endl; + QTextStream(stderr) << "Subprocess I/O W!" << smsub_endl; #endif - const QByteArray readData = process.read(1024); - if (!readData.isEmpty()) - emit outputWritten(readData); -#else - while (process.canReadLine()) { -#ifdef SMSUB_IODEBUG - QTextStream(stderr) << "Subprocess I/O WL!" << smsub_endl; -#endif - const QByteArray readData = process.readLine().trimmed(); - emit outputWritten(readData + '\n'); + const QByteArray readData = process.read(1024); + if (!readData.isEmpty()) + emit outputWritten(readData); + } } + else { + while (process.canReadLine()) { +#ifdef SMSUB_IODEBUG + QTextStream(stderr) << "Subprocess I/O WL!" << smsub_endl; #endif + const QByteArray readData = process.readLine().trimmed(); + emit outputWritten(readData + '\n'); + } + } } void SMSubProcess::processError(QProcess::ProcessError error) diff --git a/SMSubProcess.h b/SMSubProcess.h index 568cf9a..0402192 100644 --- a/SMSubProcess.h +++ b/SMSubProcess.h @@ -26,11 +26,12 @@ class SMSubProcess : public QObject { Q_OBJECT public: - SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout = 60000, const bool &keepAlive = false); + SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int termTimeout = 60000, const bool buffReads = false, const bool keepAlive = false); private: QProcess process; int termTimeout; + bool buffReads; bool keepAlive; public slots: diff --git a/SMSubServer.cpp b/SMSubServer.cpp index 3e8402a..d08f727 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -79,7 +79,7 @@ void SMSubServer::newConnection() QObject::connect(webSocket, &QWebSocket::disconnected, this, &SMSubServer::deleteSocket); webSocket->sendBinaryMessage(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); socket = webSocket; - QTextStream(stderr) << QString("WebSocket %1:%2 connected!").arg(webSocket->peerName(), QString::number(webSocket->peerPort())) << smsub_endl; + QTextStream(stderr) << QString("WebSocket %1:%2 connected!").arg(webSocket->peerAddress().toString(), QString::number(webSocket->peerPort())) << smsub_endl; } else { // Just for being sure @@ -101,25 +101,26 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) // Only allow commands being sent if authenticated const bool isAuthenticated = socket->property("Authenticated").toBool(); if (Q_LIKELY(isAuthenticated)) { - if (message.startsWith("+dbg")) { + if (message == "+dbg") { socket->setProperty("ReceiveDbgMsg", true); sendMessage(socket, "Debug messages enabled!\n"); } - else if (message.startsWith("-dbg")) { + else if (message == "-dbg") { socket->setProperty("ReceiveDbgMsg", false); sendMessage(socket, "Debug messages disabled!\n"); } - else if (message.startsWith("+log")) { + else if (message == "+log") { socket->setProperty("ReceiveLog", true); debugOutput(socket, "Log output enabled!"); } - else if (message.startsWith("-log")) { + else if (message == "-log") { socket->setProperty("ReceiveLog", false); debugOutput(socket, "Log output disabled!"); } - else if (message.startsWith("+reg")) { + else if (message == "+reg") { if (Q_LIKELY(serverSettings->canRegister)) { - QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128); + QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128) + + QUuid::createUuid().toByteArray(QUuid::Id128); authUuid = QByteArray::fromHex(authUuid).toBase64(QByteArray::OmitTrailingEquals); emit tokenRegistered(QString::fromUtf8(authUuid)); sendMessage(socket, "Token: " + authUuid + '\n'); @@ -128,7 +129,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) sendMessage(socket, "Permission denied!\n"); } } - else if (message.startsWith("status")) { + else if (message == "status") { if (status) { sendMessage(socket, QString("Status: on\nLast Start: %1\nLast Stop: %2\n").arg(QString::number(startTime), QString::number(stopTime)).toUtf8()); } @@ -136,31 +137,31 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) sendMessage(socket, QString("Status: off\nLast Start: %1\nLast Stop: %2\n").arg(QString::number(startTime), QString::number(stopTime)).toUtf8()); } } - else if (message.startsWith("start")) { + else if (message == "start") { emit startRequested(); debugOutput(socket, "Starting server!"); } - else if (message.startsWith("stop")) { + else if (message == "stop") { emit stopRequested(); debugOutput(socket, "Stopping server!"); } - else if (message.startsWith("kill")) { + else if (message == "kill") { emit killRequested(); debugOutput(socket, "Killing server!"); } - else if (message.startsWith("quit")) { + else if (message == "quit") { QTimer::singleShot(0, qApp, &QCoreApplication::quit); debugOutput(socket, "Qutting smsub!"); } - else if (message.startsWith("wl")) { + else if (message.startsWith("wl ")) { const QByteArray writeData = message.mid(3); emit inputWritten(writeData + '\n'); - debugOutput(socket, "Write line \"" + writeData + "\"!"); + debugOutput(socket, "Write line: " + writeData); } - else if (message.startsWith("w")) { + else if (message.startsWith("w ")) { const QByteArray writeData = message.mid(2); emit inputWritten(writeData); - debugOutput(socket, "Write \"" + writeData + "\"!"); + debugOutput(socket, "Write: " + writeData); } } else { diff --git a/main.cpp b/main.cpp index adf5f7b..3b52d92 100644 --- a/main.cpp +++ b/main.cpp @@ -134,7 +134,7 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.8"); + a.setApplicationVersion("0.9"); #ifdef Q_OS_UNIX catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); @@ -142,9 +142,10 @@ int main(int argc, char *argv[]) bool rportSet = false; bool autoStart = true; + bool buffReads = false; bool keepAlive = false; bool timeoutSet = false; - int termTimeout = 60000; + size_t termTimeout = 60000; quint16 rport; QString socket; QString rsocket; @@ -154,6 +155,7 @@ int main(int argc, char *argv[]) const QByteArray envEnvironmentMode = qgetenv("SMSUB_ENVIRONMENT_MODE"); const QByteArray envAutoStart = qgetenv("SMSUB_AUTOSTART"); + const QByteArray envBuffReads = qgetenv("SMSUB_BUFFERED_READS"); const QByteArray envKeepAlive = qgetenv("SMSUB_KEEPALIVE"); const QByteArray envManifest = qgetenv("SMSUB_JSON"); const QByteArray envExecutable = qgetenv("SMSUB_EXEC"); @@ -212,6 +214,12 @@ int main(int argc, char *argv[]) } } + if (!envBuffReads.isEmpty()) { + if (envBuffReads == "1" || envBuffReads.toLower() == "true" || envBuffReads.toLower() == "yes") { + buffReads = true; + } + } + if (!envKeepAlive.isEmpty()) { if (envKeepAlive == "1" || envKeepAlive.toLower() == "true" || envKeepAlive.toLower() == "yes") { keepAlive = true; @@ -285,6 +293,12 @@ int main(int argc, char *argv[]) #endif commandLineParser.addOption(subprocessRemoteSocket); + QCommandLineOption processAutoStart("autostart", "SMSub autostart mode setting.", "autostart"); + commandLineParser.addOption(processAutoStart); + + QCommandLineOption processKeepAlive("keepalive", "SMSub keepalive mode setting.", "keepalive"); + commandLineParser.addOption(processKeepAlive); + QCommandLineOption processTimeout("timeout", "SMSub termination timeout.", "timeout"); commandLineParser.addOption(processTimeout); @@ -302,7 +316,7 @@ int main(int argc, char *argv[]) !envManifest.isEmpty() && !envArguments.isEmpty() || commandLineParser.isSet(processManifest) && !envArguments.isEmpty() || !envManifest.isEmpty() && commandLineParser.isSet(processArguments)) { - QTextStream(stderr) << "You can't define a Process arguments and a JSON process manifest at the same time!" << smsub_endl; + QTextStream(stderr) << "You can't define Process arguments and a JSON process manifest at the same time!" << smsub_endl; return 1; } @@ -320,7 +334,7 @@ int main(int argc, char *argv[]) if (commandLineParser.isSet(processTimeout)) { bool ok; - const int _termTimeout = commandLineParser.value(processTimeout).toInt(&ok); + const int _termTimeout = commandLineParser.value(processTimeout).toInt(&ok, 10); if (ok) { termTimeout = _termTimeout; timeoutSet = true; @@ -332,7 +346,7 @@ int main(int argc, char *argv[]) } else if (!envTimeout.isEmpty()) { bool ok; - const int _termTimeout = envTimeout.toInt(&ok); + const int _termTimeout = envTimeout.toInt(&ok, 10); if (ok) { termTimeout = _termTimeout; timeoutSet = true; @@ -350,13 +364,31 @@ int main(int argc, char *argv[]) executable = QString::fromUtf8(envExecutable); } - if (!envAutoStart.isEmpty()) { + if (commandLineParser.isSet(processAutoStart)) { + const QString claAutoStart = commandLineParser.value(processAutoStart); + if (claAutoStart == "0" || claAutoStart.toLower() == "false" || claAutoStart.toLower() == "no") { + autoStart = false; + } + } + else if (!envAutoStart.isEmpty()) { if (envAutoStart == "0" || envAutoStart.toLower() == "false" || envAutoStart.toLower() == "no") { autoStart = false; } } - if (!envKeepAlive.isEmpty()) { + if (!envBuffReads.isEmpty()) { + if (envBuffReads == "1" || envBuffReads.toLower() == "true" || envBuffReads.toLower() == "yes") { + buffReads = true; + } + } + + if (commandLineParser.isSet(processKeepAlive)) { + const QString claKeepAlive = commandLineParser.value(processKeepAlive); + if (claKeepAlive != "0" || claKeepAlive.toLower() != "false" || claKeepAlive.toLower() != "no") { + keepAlive = true; + } + } + else if (!envKeepAlive.isEmpty()) { if (envKeepAlive == "1" || envKeepAlive.toLower() == "true" || envKeepAlive.toLower() == "yes") { keepAlive = true; } @@ -534,7 +566,7 @@ int main(int argc, char *argv[]) return 1; } - SMSubProcess subProcess(executable, argumentList, workingDirectory, termTimeout, keepAlive); + SMSubProcess subProcess(executable, argumentList, workingDirectory, termTimeout, buffReads, keepAlive); SMSubServerSettings remoteSettings; remoteSettings.canRegister = false; From 9e2518fa9bfaf204a775d9d3c51b9b2439010c4a Mon Sep 17 00:00:00 2001 From: Syping Date: Tue, 7 May 2024 17:39:45 +0200 Subject: [PATCH 06/10] change command line KeepAlive implementation --- main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.cpp b/main.cpp index 3b52d92..619be37 100644 --- a/main.cpp +++ b/main.cpp @@ -293,10 +293,10 @@ int main(int argc, char *argv[]) #endif commandLineParser.addOption(subprocessRemoteSocket); - QCommandLineOption processAutoStart("autostart", "SMSub autostart mode setting.", "autostart"); + QCommandLineOption processAutoStart("autostart", "SMSub autostart mode.", "autostart"); commandLineParser.addOption(processAutoStart); - QCommandLineOption processKeepAlive("keepalive", "SMSub keepalive mode setting.", "keepalive"); + QCommandLineOption processKeepAlive("keepalive", "SMSub keepalive mode.", "keepalive"); commandLineParser.addOption(processKeepAlive); QCommandLineOption processTimeout("timeout", "SMSub termination timeout.", "timeout"); @@ -384,7 +384,7 @@ int main(int argc, char *argv[]) if (commandLineParser.isSet(processKeepAlive)) { const QString claKeepAlive = commandLineParser.value(processKeepAlive); - if (claKeepAlive != "0" || claKeepAlive.toLower() != "false" || claKeepAlive.toLower() != "no") { + if (claKeepAlive == "1" || claKeepAlive.toLower() == "true" || claKeepAlive.toLower() == "yes") { keepAlive = true; } } From 4981372e8fca8f30e20e73a5d0ada27d0313e21f Mon Sep 17 00:00:00 2001 From: Syping Date: Thu, 9 May 2024 21:16:50 +0200 Subject: [PATCH 07/10] add +json -json +status -status --- CMakeLists.txt | 24 +++++--- SMSubServer.cpp | 154 ++++++++++++++++++++++++++++++++++++++++-------- SMSubServer.h | 1 + main.cpp | 2 +- 4 files changed, 149 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9486a27..1655bc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(smsub LANGUAGES CXX VERSION 0.9) +project(smsub LANGUAGES CXX VERSION 0.10) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -18,6 +18,18 @@ else() find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) endif() find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network WebSockets REQUIRED) +set(SMSUB_LIBS Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::WebSockets) + +option(WITH_BOOST "Use Boost library" ON) +if(WITH_BOOST) + find_package(Boost COMPONENTS json REQUIRED) + list(APPEND SMSUB_LIBS + Boost::json + ) + list(APPEND SMSUB_DEFINES + BOOST_JSON + ) +endif() set(SMSUB_SOURCES main.cpp @@ -31,11 +43,7 @@ set(SMSUB_HEADERS SMSubServer.h ) -add_executable(smsub - ${SMSUB_HEADERS} - ${SMSUB_SOURCES} -) - -target_link_libraries(smsub PRIVATE Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::WebSockets) - +add_executable(smsub ${SMSUB_HEADERS} ${SMSUB_SOURCES}) +target_compile_definitions(smsub PRIVATE ${SMSUB_DEFINES}) +target_link_libraries(smsub PRIVATE ${SMSUB_LIBS}) install(TARGETS smsub DESTINATION bin) diff --git a/SMSubServer.cpp b/SMSubServer.cpp index d08f727..8548165 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -21,6 +21,12 @@ #include #include "SMSubServer.h" #include "smsub.h" +#ifdef BOOST_JSON +#include +#else +#include +#include +#endif SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &socket) : serverSettings(serverSettings) { @@ -40,6 +46,7 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &soc SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port) : serverSettings(serverSettings) { QWebSocketServer *webSocketServer = new QWebSocketServer(serverName, QWebSocketServer::NonSecureMode, this); + webSocketServer->setSupportedSubprotocols(QStringList() << "smsub" << "smsub_json"); webSocketServer->listen(QHostAddress::LocalHost, port); QObject::connect(webSocketServer, &QWebSocketServer::newConnection, this, &SMSubServer::newConnection); @@ -53,7 +60,7 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &ser bool SMSubServer::isListening() { - if (Q_LIKELY(type == ServerType::Local)) { + if (type == ServerType::Local) { return static_cast(server)->isListening(); } else if (type == ServerType::WebSocket) { @@ -80,6 +87,9 @@ void SMSubServer::newConnection() webSocket->sendBinaryMessage(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); socket = webSocket; QTextStream(stderr) << QString("WebSocket %1:%2 connected!").arg(webSocket->peerAddress().toString(), QString::number(webSocket->peerPort())) << smsub_endl; + + if (webSocket->subprotocol() == "smsub_json") + socket->setProperty("ReceiveJson", true); } else { // Just for being sure @@ -100,7 +110,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) { // Only allow commands being sent if authenticated const bool isAuthenticated = socket->property("Authenticated").toBool(); - if (Q_LIKELY(isAuthenticated)) { + if (isAuthenticated) { if (message == "+dbg") { socket->setProperty("ReceiveDbgMsg", true); sendMessage(socket, "Debug messages enabled!\n"); @@ -109,6 +119,14 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) socket->setProperty("ReceiveDbgMsg", false); sendMessage(socket, "Debug messages disabled!\n"); } + else if (message == "+json") { + socket->setProperty("ReceiveJson", true); + debugOutput(socket, "JSON output enabled!"); + } + else if (message == "-json") { + socket->setProperty("ReceiveJson", false); + debugOutput(socket, "JSON output disabled!"); + } else if (message == "+log") { socket->setProperty("ReceiveLog", true); debugOutput(socket, "Log output enabled!"); @@ -117,8 +135,16 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) socket->setProperty("ReceiveLog", false); debugOutput(socket, "Log output disabled!"); } + else if (message == "+status") { + socket->setProperty("ReceiveStatus", true); + debugOutput(socket, "Status updates enabled!"); + } + else if (message == "-status") { + socket->setProperty("ReceiveStatus", false); + debugOutput(socket, "Status updates disabled!"); + } else if (message == "+reg") { - if (Q_LIKELY(serverSettings->canRegister)) { + if (serverSettings->canRegister) { QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128) + QUuid::createUuid().toByteArray(QUuid::Id128); authUuid = QByteArray::fromHex(authUuid).toBase64(QByteArray::OmitTrailingEquals); @@ -130,11 +156,26 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) } } else if (message == "status") { - if (status) { - sendMessage(socket, QString("Status: on\nLast Start: %1\nLast Stop: %2\n").arg(QString::number(startTime), QString::number(stopTime)).toUtf8()); + if (isVariantTrue(socket->property("ReceiveJson"))) { +#ifdef BOOST_JSON + boost::json::object object; + object["type"] = "status"; + object["status"] = status ? "on" : "off"; + object["start"] = startTime; + object["stop"] = stopTime; + const std::string json = boost::json::serialize(object) + '\n'; + sendMessage(socket, QByteArray::fromRawData(json.data(), json.size())); +#else + QJsonObject object; + object["type"] = "status"; + object["status"] = status ? "on" : "off"; + object["start"] = startTime; + object["stop"] = stopTime; + sendMessage(socket, QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'); +#endif } else { - sendMessage(socket, QString("Status: off\nLast Start: %1\nLast Stop: %2\n").arg(QString::number(startTime), QString::number(stopTime)).toUtf8()); + sendMessage(socket, QString("Status: %1\nLast Start: %2\nLast Stop: %3\n").arg(status ? "on" : "off", QString::number(startTime), QString::number(stopTime)).toUtf8()); } } else if (message == "start") { @@ -153,20 +194,24 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) QTimer::singleShot(0, qApp, &QCoreApplication::quit); debugOutput(socket, "Qutting smsub!"); } + else if (message == "wl") { + emit inputWritten("\n"); + debugOutput(socket, "Writing line!"); + } else if (message.startsWith("wl ")) { const QByteArray writeData = message.mid(3); emit inputWritten(writeData + '\n'); - debugOutput(socket, "Write line: " + writeData); + debugOutput(socket, "Writing line: " + writeData); } else if (message.startsWith("w ")) { const QByteArray writeData = message.mid(2); emit inputWritten(writeData); - debugOutput(socket, "Write: " + writeData); + debugOutput(socket, "Writing: " + writeData); } } else { // Authenticate when token is valid, otherwise disconnect - if (Q_UNLIKELY(tokens.contains(QString::fromUtf8(message)))) { + if (tokens.contains(QString::fromUtf8(message))) { // Set client as authenticated and add it to vector socket->setProperty("Authenticated", true); sendMessage(socket, "Login successful!\n"); @@ -174,7 +219,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) } else { // Stop receiving data and disconnect socket - if (Q_LIKELY(type == ServerType::Local)) { + if (type == ServerType::Local) { QLocalSocket *localSocket = static_cast(socket); QObject::disconnect(localSocket, &QLocalSocket::readyRead, this, &SMSubServer::lsReadyRead); localSocket->write("Incorrect token!\n"); @@ -193,6 +238,18 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) return true; } +bool SMSubServer::isVariantTrue(const QVariant &variant) +{ +#if QT_VERSION >= 0x060000 + if (variant.typeId() == QMetaType::Bool) { +#else + if (variant.type() == QVariant::Bool) { +#endif + return variant.toBool(); + } + return false; +} + void SMSubServer::wsMessageReceived(const QByteArray &message) { QWebSocket *socket = static_cast(sender()); @@ -226,14 +283,22 @@ void SMSubServer::deleteSocket() void SMSubServer::debugOutput(QObject *socket, const QByteArray &message) { // Only send debug messages when the client opted-in - const QVariant variant = socket->property("ReceiveDbgMsg"); -#if QT_VERSION >= 0x060000 - if (Q_UNLIKELY(variant.typeId() == QMetaType::Bool)) { + if (isVariantTrue(socket->property("ReceiveDbgMsg"))) { + if (isVariantTrue(socket->property("ReceiveJson"))) { +#ifdef BOOST_JSON + boost::json::object object; + object["type"] = "debug"; + object["message"] = message.toBase64(QByteArray::OmitTrailingEquals).constData(); + const std::string json = boost::json::serialize(object) + '\n'; + sendMessage(socket, QByteArray::fromRawData(json.data(), json.size())); #else - if (Q_UNLIKELY(variant.type() == QVariant::Bool)) { + QJsonObject object; + object["type"] = "debug"; + object["message"] = QString::fromUtf8(message.toBase64(QByteArray::OmitTrailingEquals)); + sendMessage(socket, QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'); #endif - bool receiveDbgMsg = variant.toBool(); - if (Q_LIKELY(receiveDbgMsg)) { + } + else { sendMessage(socket, message + '\n'); } } @@ -242,15 +307,26 @@ void SMSubServer::debugOutput(QObject *socket, const QByteArray &message) void SMSubServer::writeOutput(const QByteArray &output) { // Read process output when client opted-in for log + QByteArray json_output; for (auto it = sockets.constBegin(); it != sockets.constEnd(); it++) { - const QVariant variant = (*it)->property("ReceiveLog"); -#if QT_VERSION >= 0x060000 - if (Q_UNLIKELY(variant.typeId() == QMetaType::Bool)) { + if (isVariantTrue((*it)->property("ReceiveLog"))) { + if (isVariantTrue((*it)->property("ReceiveJson"))) { + if (json_output.isEmpty()) { +#ifdef BOOST_JSON + boost::json::object object; + object["type"] = "log"; + object["message"] = output.toBase64(QByteArray::OmitTrailingEquals).constData(); + json_output = QByteArray::fromStdString(boost::json::serialize(object) + '\n'); #else - if (Q_UNLIKELY(variant.type() == QVariant::Bool)) { + QJsonObject object; + object["type"] = "log"; + object["message"] = QString::fromUtf8(output.toBase64(QByteArray::OmitTrailingEquals)); + json_output = QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'; #endif - bool receiveLog = variant.toBool(); - if (Q_LIKELY(receiveLog)) { + } + sendMessage(*it, json_output); + } + else { sendMessage(*it, output); } } @@ -259,7 +335,7 @@ void SMSubServer::writeOutput(const QByteArray &output) void SMSubServer::sendMessage(QObject *socket, const QByteArray &message) { - if (Q_LIKELY(type == ServerType::Local)) { + if (type == ServerType::Local) { QLocalSocket *localSocket = static_cast(socket); localSocket->write(message); } @@ -282,4 +358,36 @@ void SMSubServer::statusUpdated(const bool status_, const qint64 time) { status = status_; status ? (startTime = time) : (stopTime = time); + + QByteArray output, json_output; + for (auto it = sockets.constBegin(); it != sockets.constEnd(); it++) { + if (isVariantTrue((*it)->property("ReceiveStatus"))) { + if (isVariantTrue((*it)->property("ReceiveJson"))) { + if (json_output.isEmpty()) { +#ifdef BOOST_JSON + boost::json::object object; + object["type"] = "status"; + object["status"] = status ? "on" : "off"; + object["start"] = startTime; + object["stop"] = stopTime; + json_output = QByteArray::fromStdString(boost::json::serialize(object) + '\n'); +#else + QJsonObject object; + object["type"] = "status"; + object["status"] = status ? "on" : "off"; + object["start"] = startTime; + object["stop"] = stopTime; + json_output = QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'; +#endif + } + sendMessage(*it, json_output); + } + else { + if (output.isEmpty()) { + output = QString("Status: %1\nLast Start: %2\nLast Stop: %3\n").arg(status ? "on" : "off", QString::number(startTime), QString::number(stopTime)).toUtf8(); + } + sendMessage(*it, json_output); + } + } + } } diff --git a/SMSubServer.h b/SMSubServer.h index dc1b743..f86786e 100644 --- a/SMSubServer.h +++ b/SMSubServer.h @@ -57,6 +57,7 @@ private: inline void debugOutput(QObject *socket, const QByteArray &message); inline void sendMessage(QObject *socket, const QByteArray &message); bool messageReceived(QObject *socket, const QByteArray &message); + inline bool isVariantTrue(const QVariant &variant); SMSubServerSettings *serverSettings; QVector sockets; QVector tokens; diff --git a/main.cpp b/main.cpp index 619be37..815a281 100644 --- a/main.cpp +++ b/main.cpp @@ -134,7 +134,7 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.9"); + a.setApplicationVersion("0.10"); #ifdef Q_OS_UNIX catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM}); From 6595738d64ad4dd9a8caedec7b6708f3d56f996c Mon Sep 17 00:00:00 2001 From: Syping Date: Thu, 9 May 2024 21:27:58 +0200 Subject: [PATCH 08/10] restore compatibility with older Qt --- SMSubServer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SMSubServer.cpp b/SMSubServer.cpp index 8548165..aa1cf6e 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -46,7 +46,9 @@ SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &soc SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &serverName, const quint16 &port) : serverSettings(serverSettings) { QWebSocketServer *webSocketServer = new QWebSocketServer(serverName, QWebSocketServer::NonSecureMode, this); +#if QT_VERSION >= 0x060400 webSocketServer->setSupportedSubprotocols(QStringList() << "smsub" << "smsub_json"); +#endif webSocketServer->listen(QHostAddress::LocalHost, port); QObject::connect(webSocketServer, &QWebSocketServer::newConnection, this, &SMSubServer::newConnection); @@ -88,8 +90,10 @@ void SMSubServer::newConnection() socket = webSocket; QTextStream(stderr) << QString("WebSocket %1:%2 connected!").arg(webSocket->peerAddress().toString(), QString::number(webSocket->peerPort())) << smsub_endl; +#if QT_VERSION >= 0x060400 if (webSocket->subprotocol() == "smsub_json") socket->setProperty("ReceiveJson", true); +#endif } else { // Just for being sure From 0198da65952ecbd9c924d881bbc1e04411f9f9e2 Mon Sep 17 00:00:00 2001 From: Syping Date: Thu, 9 May 2024 21:59:57 +0200 Subject: [PATCH 09/10] fix JSON status bool --- SMSubServer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SMSubServer.cpp b/SMSubServer.cpp index aa1cf6e..20a5672 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -164,7 +164,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) #ifdef BOOST_JSON boost::json::object object; object["type"] = "status"; - object["status"] = status ? "on" : "off"; + object["status"] = status; object["start"] = startTime; object["stop"] = stopTime; const std::string json = boost::json::serialize(object) + '\n'; @@ -172,7 +172,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) #else QJsonObject object; object["type"] = "status"; - object["status"] = status ? "on" : "off"; + object["status"] = status; object["start"] = startTime; object["stop"] = stopTime; sendMessage(socket, QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'); @@ -371,14 +371,14 @@ void SMSubServer::statusUpdated(const bool status_, const qint64 time) #ifdef BOOST_JSON boost::json::object object; object["type"] = "status"; - object["status"] = status ? "on" : "off"; + object["status"] = status; object["start"] = startTime; object["stop"] = stopTime; json_output = QByteArray::fromStdString(boost::json::serialize(object) + '\n'); #else QJsonObject object; object["type"] = "status"; - object["status"] = status ? "on" : "off"; + object["status"] = status; object["start"] = startTime; object["stop"] = stopTime; json_output = QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'; From fd57796114177c36cc08b1be2c63a2aa6c423c20 Mon Sep 17 00:00:00 2001 From: Syping Date: Sat, 18 May 2024 05:06:56 +0200 Subject: [PATCH 10/10] store tokens in QByteArray, minor fixes --- CMakeLists.txt | 2 +- SMSubServer.cpp | 12 ++++++------ SMSubServer.h | 6 +++--- main.cpp | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1655bc2..dea9e62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(smsub LANGUAGES CXX VERSION 0.10) +project(smsub LANGUAGES CXX VERSION 0.10.1) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/SMSubServer.cpp b/SMSubServer.cpp index 20a5672..6ea0b0f 100644 --- a/SMSubServer.cpp +++ b/SMSubServer.cpp @@ -113,15 +113,15 @@ void SMSubServer::newConnection() bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) { // Only allow commands being sent if authenticated - const bool isAuthenticated = socket->property("Authenticated").toBool(); + const bool isAuthenticated = isVariantTrue(socket->property("Authenticated")); if (isAuthenticated) { if (message == "+dbg") { socket->setProperty("ReceiveDbgMsg", true); - sendMessage(socket, "Debug messages enabled!\n"); + debugOutput(socket, "Debug messages enabled!"); } else if (message == "-dbg") { + debugOutput(socket, "Debug messages disabled!"); socket->setProperty("ReceiveDbgMsg", false); - sendMessage(socket, "Debug messages disabled!\n"); } else if (message == "+json") { socket->setProperty("ReceiveJson", true); @@ -152,7 +152,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128) + QUuid::createUuid().toByteArray(QUuid::Id128); authUuid = QByteArray::fromHex(authUuid).toBase64(QByteArray::OmitTrailingEquals); - emit tokenRegistered(QString::fromUtf8(authUuid)); + emit tokenRegistered(authUuid); sendMessage(socket, "Token: " + authUuid + '\n'); } else { @@ -215,7 +215,7 @@ bool SMSubServer::messageReceived(QObject *socket, const QByteArray &message) } else { // Authenticate when token is valid, otherwise disconnect - if (tokens.contains(QString::fromUtf8(message))) { + if (tokens.contains(message)) { // Set client as authenticated and add it to vector socket->setProperty("Authenticated", true); sendMessage(socket, "Login successful!\n"); @@ -349,7 +349,7 @@ void SMSubServer::sendMessage(QObject *socket, const QByteArray &message) } } -void SMSubServer::registerToken(const QString &token) +void SMSubServer::registerToken(const QByteArray &token) { // Register temporary token for a secure remote connection tokens << token; diff --git a/SMSubServer.h b/SMSubServer.h index f86786e..d7f452a 100644 --- a/SMSubServer.h +++ b/SMSubServer.h @@ -44,7 +44,7 @@ public: public slots: void writeOutput(const QByteArray &output); - void registerToken(const QString &token); + void registerToken(const QByteArray &token); void statusUpdated(const bool status, const qint64 time); private slots: @@ -60,7 +60,7 @@ private: inline bool isVariantTrue(const QVariant &variant); SMSubServerSettings *serverSettings; QVector sockets; - QVector tokens; + QVector tokens; qint64 startTime; qint64 stopTime; ServerType type; @@ -68,7 +68,7 @@ private: bool status; signals: - void tokenRegistered(const QString &password); + void tokenRegistered(const QByteArray &token); void inputWritten(const QByteArray &input); void startRequested(); void stopRequested(); diff --git a/main.cpp b/main.cpp index 815a281..a3d8eee 100644 --- a/main.cpp +++ b/main.cpp @@ -134,7 +134,7 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setApplicationName("Server Manager Subprocess"); - a.setApplicationVersion("0.10"); + a.setApplicationVersion("0.10.1"); #ifdef Q_OS_UNIX catchUnixSignals({SIGINT, SIGHUP, SIGQUIT, SIGTERM});