refactor: Move daemon process stop command to new Qt IPC
This commit is contained in:
@ -55,6 +55,8 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
// Thread must be heap-allocated for deferred deletion on thread exit.
|
||||||
|
// Avoid setting Qt ownership to prevent premature deletion (thread may run longer than Qt loop).
|
||||||
auto *pDaemonThread = new QThread(); // NOSONAR
|
auto *pDaemonThread = new QThread(); // NOSONAR
|
||||||
pDaemon->moveToThread(pDaemonThread);
|
pDaemon->moveToThread(pDaemonThread);
|
||||||
|
|
||||||
@ -78,9 +80,14 @@ int main(int argc, char **argv)
|
|||||||
Qt::DirectConnection
|
Qt::DirectConnection
|
||||||
);
|
);
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
ipcServer, &ipc::DaemonIpcServer::restartRequested, pDaemon, &DaemonApp::restartCoreProcess, //
|
ipcServer, &ipc::DaemonIpcServer::startProcessRequested, pDaemon, &DaemonApp::applyWatchdogCommand, //
|
||||||
Qt::DirectConnection
|
Qt::DirectConnection
|
||||||
);
|
);
|
||||||
|
QObject::connect(
|
||||||
|
ipcServer, &ipc::DaemonIpcServer::stopProcessRequested, pDaemon, &DaemonApp::clearWatchdogCommand, //
|
||||||
|
Qt::DirectConnection
|
||||||
|
);
|
||||||
|
|
||||||
pDaemonThread->start();
|
pDaemonThread->start();
|
||||||
return QCoreApplication::exec();
|
return QCoreApplication::exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -159,14 +159,25 @@ void DaemonApp::setCommand(const QString &command)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonApp::restartCoreProcess()
|
void DaemonApp::applyWatchdogCommand() const
|
||||||
{
|
{
|
||||||
LOG_DEBUG("service restart requested");
|
LOG_DEBUG("applying watchdog command");
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
m_watchdog->setCommand(m_command, m_elevate);
|
m_watchdog->setCommand(m_command, m_elevate);
|
||||||
#else
|
#else
|
||||||
LOG_ERR("restart not implemented on this platform");
|
LOG_ERR("applying watchdog command not implemented on this platform");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonApp::clearWatchdogCommand()
|
||||||
|
{
|
||||||
|
LOG_DEBUG("clearing watchdog command");
|
||||||
|
|
||||||
|
#if SYSAPI_WIN32
|
||||||
|
m_watchdog->setCommand("", false);
|
||||||
|
#else
|
||||||
|
LOG_ERR("clearing watchdog command not implemented on this platform");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,6 +54,8 @@ public:
|
|||||||
void saveLogLevel(const QString &logLevel) const;
|
void saveLogLevel(const QString &logLevel) const;
|
||||||
void setElevate(bool elevate);
|
void setElevate(bool elevate);
|
||||||
void setCommand(const QString &command);
|
void setCommand(const QString &command);
|
||||||
|
void applyWatchdogCommand() const;
|
||||||
|
void clearWatchdogCommand();
|
||||||
|
|
||||||
static DaemonApp &instance()
|
static DaemonApp &instance()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -14,13 +14,18 @@
|
|||||||
|
|
||||||
namespace deskflow::core::ipc {
|
namespace deskflow::core::ipc {
|
||||||
|
|
||||||
DaemonIpcServer::DaemonIpcServer(QObject *parent) : QObject(parent), m_server{new QLocalServer(this)}
|
const auto kAckMessage = "ok\n";
|
||||||
|
const auto kErrorMessage = "error\n";
|
||||||
|
|
||||||
|
DaemonIpcServer::DaemonIpcServer(QObject *parent)
|
||||||
|
: QObject(parent), //
|
||||||
|
m_server{new QLocalServer(this)} // NOSONAR
|
||||||
{
|
{
|
||||||
// Daemon runs as system, but GUI runs as regular user, so we need to allow world access.
|
// Daemon runs as system, but GUI runs as regular user, so we need to allow world access.
|
||||||
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
|
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||||
|
|
||||||
connect(m_server, &QLocalServer::newConnection, this, &DaemonIpcServer::handleNewConnection);
|
connect(m_server, &QLocalServer::newConnection, this, &DaemonIpcServer::handleNewConnection);
|
||||||
m_server->removeServer(kDaemonIpcName);
|
QLocalServer::removeServer(kDaemonIpcName);
|
||||||
if (m_server->listen(kDaemonIpcName)) {
|
if (m_server->listen(kDaemonIpcName)) {
|
||||||
LOG_DEBUG("ipc server listening on: %s", kDaemonIpcName);
|
LOG_DEBUG("ipc server listening on: %s", kDaemonIpcName);
|
||||||
} else {
|
} else {
|
||||||
@ -68,7 +73,7 @@ void DaemonIpcServer::handleReadyRead()
|
|||||||
|
|
||||||
// each message is delimited by a newline to keep the protocol super simple.
|
// each message is delimited by a newline to keep the protocol super simple.
|
||||||
while (data.contains('\n')) {
|
while (data.contains('\n')) {
|
||||||
int index = data.indexOf('\n');
|
const auto index = data.indexOf('\n');
|
||||||
QByteArray messageData = data.left(index);
|
QByteArray messageData = data.left(index);
|
||||||
data.remove(0, index + 1);
|
data.remove(0, index + 1);
|
||||||
QString message = QString::fromUtf8(messageData);
|
QString message = QString::fromUtf8(messageData);
|
||||||
@ -94,53 +99,101 @@ void DaemonIpcServer::handleErrorOccurred()
|
|||||||
|
|
||||||
void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &message)
|
void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &message)
|
||||||
{
|
{
|
||||||
const auto kAckMessage = "ok\n";
|
|
||||||
const auto kErrorMessage = "error\n";
|
|
||||||
|
|
||||||
LOG_DEBUG("ipc server got message: %s", message.toUtf8().constData());
|
LOG_DEBUG("ipc server got message: %s", message.toUtf8().constData());
|
||||||
if (message == "hello") {
|
const auto parts = message.split('=');
|
||||||
|
if (parts.size() < 1) {
|
||||||
|
LOG_ERR("ipc server got invalid message: %s", message.toUtf8().constData());
|
||||||
|
clientSocket->write(kErrorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &command = parts[0];
|
||||||
|
if (command == "hello") { // NOSONAR
|
||||||
clientSocket->write("hello\n");
|
clientSocket->write("hello\n");
|
||||||
} else if (message == "noop") {
|
} else if (command == "noop") {
|
||||||
clientSocket->write(kAckMessage);
|
clientSocket->write(kAckMessage);
|
||||||
} else if (message.startsWith("logLevel=")) {
|
} else if (command == "logLevel") {
|
||||||
const auto logLevel = message.split('=')[1];
|
processLogLevel(clientSocket, parts);
|
||||||
if (logLevel.isEmpty()) {
|
} else if (command == "elevate") {
|
||||||
LOG_ERR("ipc server got empty log level");
|
processElevate(clientSocket, parts);
|
||||||
clientSocket->write(kErrorMessage);
|
} else if (command == "command") {
|
||||||
} else {
|
processCommand(clientSocket, parts);
|
||||||
LOG_DEBUG("ipc server got new log level: %s", logLevel.toUtf8().constData());
|
} else if (command == "start") {
|
||||||
Q_EMIT logLevelChanged(logLevel);
|
LOG_DEBUG("ipc server got start message");
|
||||||
clientSocket->write(kAckMessage);
|
Q_EMIT startProcessRequested();
|
||||||
}
|
|
||||||
} else if (message.startsWith("elevate=")) {
|
|
||||||
const auto elevateMode = message.split('=')[1];
|
|
||||||
if (elevateMode != "yes" && elevateMode != "no") {
|
|
||||||
LOG_ERR("ipc server got invalid elevate value: %s", elevateMode.toUtf8().constData());
|
|
||||||
clientSocket->write(kErrorMessage);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG("ipc server got new elevate value: %s", elevateMode.toUtf8().constData());
|
|
||||||
Q_EMIT elevateModeChanged(elevateMode == "yes");
|
|
||||||
clientSocket->write(kAckMessage);
|
|
||||||
}
|
|
||||||
} else if (message.startsWith("command=")) {
|
|
||||||
const auto command = message.split('=')[1];
|
|
||||||
if (command.isEmpty()) {
|
|
||||||
LOG_ERR("ipc server got empty command");
|
|
||||||
clientSocket->write(kErrorMessage);
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG("ipc server got new command: %s", command.toUtf8().constData());
|
|
||||||
Q_EMIT commandChanged(command);
|
|
||||||
clientSocket->write(kAckMessage);
|
|
||||||
}
|
|
||||||
} else if (message == "restart") {
|
|
||||||
LOG_DEBUG("ipc server got restart message");
|
|
||||||
Q_EMIT restartRequested();
|
|
||||||
clientSocket->write(kAckMessage);
|
clientSocket->write(kAckMessage);
|
||||||
|
} else if (command == "stop") {
|
||||||
|
LOG_DEBUG("ipc server got stop message");
|
||||||
|
Q_EMIT stopProcessRequested();
|
||||||
|
clientSocket->write(kAckMessage);
|
||||||
|
} else if (command == "logPath") {
|
||||||
|
LOG_DEBUG("ipc server got log path request");
|
||||||
|
// TODO: send log path
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN("ipc server got unknown message: %s", message.toUtf8().constData());
|
LOG_WARN("ipc server got unknown message: %s", message.toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
clientSocket->flush();
|
clientSocket->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DaemonIpcServer::processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||||
|
{
|
||||||
|
if (messageParts.size() < 2) {
|
||||||
|
LOG_ERR("ipc server got invalid log level message");
|
||||||
|
clientSocket->write(kErrorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &logLevel = messageParts[1];
|
||||||
|
if (logLevel.isEmpty()) {
|
||||||
|
LOG_ERR("ipc server got empty log level");
|
||||||
|
clientSocket->write(kErrorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("ipc server got new log level: %s", logLevel.toUtf8().constData());
|
||||||
|
Q_EMIT logLevelChanged(logLevel);
|
||||||
|
clientSocket->write(kAckMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonIpcServer::processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||||
|
{
|
||||||
|
if (messageParts.size() < 2) {
|
||||||
|
LOG_ERR("ipc server got invalid elevate message");
|
||||||
|
clientSocket->write(kErrorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &elevate = messageParts[1];
|
||||||
|
if (elevate != "yes" && elevate != "no") {
|
||||||
|
LOG_ERR("ipc server got invalid elevate value: %s", elevate.toUtf8().constData());
|
||||||
|
clientSocket->write(kErrorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("ipc server got new elevate value: %s", elevate.toUtf8().constData());
|
||||||
|
Q_EMIT elevateModeChanged(elevate == "yes");
|
||||||
|
clientSocket->write(kAckMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonIpcServer::processCommand(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||||
|
{
|
||||||
|
if (messageParts.size() < 2) {
|
||||||
|
LOG_ERR("ipc server got invalid command message");
|
||||||
|
clientSocket->write(kErrorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &command = messageParts[1];
|
||||||
|
if (command.isEmpty()) {
|
||||||
|
LOG_ERR("ipc server got empty command");
|
||||||
|
clientSocket->write(kErrorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("ipc server got new command: %s", command.toUtf8().constData());
|
||||||
|
Q_EMIT commandChanged(command);
|
||||||
|
clientSocket->write(kAckMessage);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace deskflow::core::ipc
|
} // namespace deskflow::core::ipc
|
||||||
|
|||||||
@ -19,17 +19,21 @@ class DaemonIpcServer : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DaemonIpcServer(QObject *parent);
|
explicit DaemonIpcServer(QObject *parent);
|
||||||
~DaemonIpcServer();
|
~DaemonIpcServer() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void logLevelChanged(const QString &logLevel);
|
void logLevelChanged(const QString &logLevel);
|
||||||
void elevateModeChanged(bool elevate);
|
void elevateModeChanged(bool elevate);
|
||||||
void commandChanged(const QString &command);
|
void commandChanged(const QString &command);
|
||||||
void restartRequested();
|
void startProcessRequested();
|
||||||
|
void stopProcessRequested();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void processMessage(QLocalSocket *clientSocket, const QString &message);
|
void processMessage(QLocalSocket *clientSocket, const QString &message);
|
||||||
|
void processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
|
void processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
|
void processCommand(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleNewConnection();
|
void handleNewConnection();
|
||||||
|
|||||||
@ -291,8 +291,8 @@ void CoreProcess::startProcessFromDaemon(const QString &app, const QStringList &
|
|||||||
|
|
||||||
qInfo("running command: %s", qPrintable(commandQuoted));
|
qInfo("running command: %s", qPrintable(commandQuoted));
|
||||||
|
|
||||||
if (!m_daemonIpcClient->sendCommand(commandQuoted, m_appConfig.elevateMode())) {
|
if (!m_daemonIpcClient->sendStartProcess(commandQuoted, m_appConfig.elevateMode())) {
|
||||||
qCritical("aborting process start, ipc connect failed");
|
qCritical("cannot start process, ipc command failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,12 +325,11 @@ void CoreProcess::stopProcessFromDaemon()
|
|||||||
qFatal("core process must be in stopping state");
|
qFatal("core process must be in stopping state");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_pDeps->ipcClient().isConnected()) {
|
if (!m_daemonIpcClient->sendStopProcess()) {
|
||||||
qDebug("cannot stop process, ipc not connected");
|
qCritical("cannot stop process, ipc command failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pDeps->ipcClient().sendCommand("", m_appConfig.elevateMode());
|
|
||||||
setProcessState(ProcessState::Stopped);
|
setProcessState(ProcessState::Stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,9 @@ namespace deskflow::gui::ipc {
|
|||||||
|
|
||||||
const auto kTimeout = 1000;
|
const auto kTimeout = 1000;
|
||||||
|
|
||||||
DaemonIpcClient::DaemonIpcClient(QObject *parent) : QObject(parent), m_socket{new QLocalSocket(this)}
|
DaemonIpcClient::DaemonIpcClient(QObject *parent)
|
||||||
|
: QObject(parent), //
|
||||||
|
m_socket{new QLocalSocket(this)} // NOSONAR
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,15 +83,25 @@ bool DaemonIpcClient::sendLogLevel(const QString &logLevel)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DaemonIpcClient::sendCommand(const QString &command, ElevateMode elevateMode)
|
bool DaemonIpcClient::sendStartProcess(const QString &command, ElevateMode elevateMode)
|
||||||
{
|
{
|
||||||
if (!keepAlive())
|
if (!keepAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
sendMessage("elevate=" + (elevateMode == ElevateMode::kAlways ? QStringLiteral("yes") : QStringLiteral("no")));
|
if (!sendMessage("elevate=" + (elevateMode == ElevateMode::kAlways ? QStringLiteral("yes") : QStringLiteral("no")))) {
|
||||||
sendMessage("command=" + command);
|
return false;
|
||||||
sendMessage("restart");
|
}
|
||||||
return true;
|
|
||||||
|
if (!sendMessage("command=" + command)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendMessage("start");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonIpcClient::sendStopProcess()
|
||||||
|
{
|
||||||
|
return sendMessage("stop");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
|
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
|
||||||
|
|||||||
@ -22,7 +22,8 @@ public:
|
|||||||
explicit DaemonIpcClient(QObject *parent = nullptr);
|
explicit DaemonIpcClient(QObject *parent = nullptr);
|
||||||
bool connectToServer();
|
bool connectToServer();
|
||||||
bool sendLogLevel(const QString &logLevel);
|
bool sendLogLevel(const QString &logLevel);
|
||||||
bool sendCommand(const QString &command, ElevateMode elevateMode);
|
bool sendStartProcess(const QString &command, ElevateMode elevateMode);
|
||||||
|
bool sendStopProcess();
|
||||||
|
|
||||||
bool isConnected() const
|
bool isConnected() const
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user