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);
|
||||
|
||||
// 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
|
||||
pDaemon->moveToThread(pDaemonThread);
|
||||
|
||||
@ -78,9 +80,14 @@ int main(int argc, char **argv)
|
||||
Qt::DirectConnection
|
||||
);
|
||||
QObject::connect(
|
||||
ipcServer, &ipc::DaemonIpcServer::restartRequested, pDaemon, &DaemonApp::restartCoreProcess, //
|
||||
ipcServer, &ipc::DaemonIpcServer::startProcessRequested, pDaemon, &DaemonApp::applyWatchdogCommand, //
|
||||
Qt::DirectConnection
|
||||
);
|
||||
QObject::connect(
|
||||
ipcServer, &ipc::DaemonIpcServer::stopProcessRequested, pDaemon, &DaemonApp::clearWatchdogCommand, //
|
||||
Qt::DirectConnection
|
||||
);
|
||||
|
||||
pDaemonThread->start();
|
||||
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
|
||||
m_watchdog->setCommand(m_command, m_elevate);
|
||||
#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
|
||||
}
|
||||
|
||||
|
||||
@ -54,6 +54,8 @@ public:
|
||||
void saveLogLevel(const QString &logLevel) const;
|
||||
void setElevate(bool elevate);
|
||||
void setCommand(const QString &command);
|
||||
void applyWatchdogCommand() const;
|
||||
void clearWatchdogCommand();
|
||||
|
||||
static DaemonApp &instance()
|
||||
{
|
||||
|
||||
@ -14,13 +14,18 @@
|
||||
|
||||
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.
|
||||
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
|
||||
connect(m_server, &QLocalServer::newConnection, this, &DaemonIpcServer::handleNewConnection);
|
||||
m_server->removeServer(kDaemonIpcName);
|
||||
QLocalServer::removeServer(kDaemonIpcName);
|
||||
if (m_server->listen(kDaemonIpcName)) {
|
||||
LOG_DEBUG("ipc server listening on: %s", kDaemonIpcName);
|
||||
} else {
|
||||
@ -68,7 +73,7 @@ void DaemonIpcServer::handleReadyRead()
|
||||
|
||||
// each message is delimited by a newline to keep the protocol super simple.
|
||||
while (data.contains('\n')) {
|
||||
int index = data.indexOf('\n');
|
||||
const auto index = data.indexOf('\n');
|
||||
QByteArray messageData = data.left(index);
|
||||
data.remove(0, index + 1);
|
||||
QString message = QString::fromUtf8(messageData);
|
||||
@ -94,53 +99,101 @@ void DaemonIpcServer::handleErrorOccurred()
|
||||
|
||||
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());
|
||||
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");
|
||||
} else if (message == "noop") {
|
||||
} else if (command == "noop") {
|
||||
clientSocket->write(kAckMessage);
|
||||
} else if (message.startsWith("logLevel=")) {
|
||||
const auto logLevel = message.split('=')[1];
|
||||
if (logLevel.isEmpty()) {
|
||||
LOG_ERR("ipc server got empty log level");
|
||||
clientSocket->write(kErrorMessage);
|
||||
} else {
|
||||
LOG_DEBUG("ipc server got new log level: %s", logLevel.toUtf8().constData());
|
||||
Q_EMIT logLevelChanged(logLevel);
|
||||
clientSocket->write(kAckMessage);
|
||||
}
|
||||
} 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();
|
||||
} else if (command == "logLevel") {
|
||||
processLogLevel(clientSocket, parts);
|
||||
} else if (command == "elevate") {
|
||||
processElevate(clientSocket, parts);
|
||||
} else if (command == "command") {
|
||||
processCommand(clientSocket, parts);
|
||||
} else if (command == "start") {
|
||||
LOG_DEBUG("ipc server got start message");
|
||||
Q_EMIT startProcessRequested();
|
||||
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 {
|
||||
LOG_WARN("ipc server got unknown message: %s", message.toUtf8().constData());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -19,17 +19,21 @@ class DaemonIpcServer : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DaemonIpcServer(QObject *parent);
|
||||
~DaemonIpcServer();
|
||||
explicit DaemonIpcServer(QObject *parent);
|
||||
~DaemonIpcServer() override;
|
||||
|
||||
signals:
|
||||
void logLevelChanged(const QString &logLevel);
|
||||
void elevateModeChanged(bool elevate);
|
||||
void commandChanged(const QString &command);
|
||||
void restartRequested();
|
||||
void startProcessRequested();
|
||||
void stopProcessRequested();
|
||||
|
||||
private:
|
||||
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:
|
||||
void handleNewConnection();
|
||||
|
||||
@ -291,8 +291,8 @@ void CoreProcess::startProcessFromDaemon(const QString &app, const QStringList &
|
||||
|
||||
qInfo("running command: %s", qPrintable(commandQuoted));
|
||||
|
||||
if (!m_daemonIpcClient->sendCommand(commandQuoted, m_appConfig.elevateMode())) {
|
||||
qCritical("aborting process start, ipc connect failed");
|
||||
if (!m_daemonIpcClient->sendStartProcess(commandQuoted, m_appConfig.elevateMode())) {
|
||||
qCritical("cannot start process, ipc command failed");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -325,12 +325,11 @@ void CoreProcess::stopProcessFromDaemon()
|
||||
qFatal("core process must be in stopping state");
|
||||
}
|
||||
|
||||
if (!m_pDeps->ipcClient().isConnected()) {
|
||||
qDebug("cannot stop process, ipc not connected");
|
||||
if (!m_daemonIpcClient->sendStopProcess()) {
|
||||
qCritical("cannot stop process, ipc command failed");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pDeps->ipcClient().sendCommand("", m_appConfig.elevateMode());
|
||||
setProcessState(ProcessState::Stopped);
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,9 @@ namespace deskflow::gui::ipc {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool DaemonIpcClient::sendCommand(const QString &command, ElevateMode elevateMode)
|
||||
bool DaemonIpcClient::sendStartProcess(const QString &command, ElevateMode elevateMode)
|
||||
{
|
||||
if (!keepAlive())
|
||||
return false;
|
||||
|
||||
sendMessage("elevate=" + (elevateMode == ElevateMode::kAlways ? QStringLiteral("yes") : QStringLiteral("no")));
|
||||
sendMessage("command=" + command);
|
||||
sendMessage("restart");
|
||||
return true;
|
||||
if (!sendMessage("elevate=" + (elevateMode == ElevateMode::kAlways ? QStringLiteral("yes") : QStringLiteral("no")))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@ -22,7 +22,8 @@ public:
|
||||
explicit DaemonIpcClient(QObject *parent = nullptr);
|
||||
bool connectToServer();
|
||||
bool sendLogLevel(const QString &logLevel);
|
||||
bool sendCommand(const QString &command, ElevateMode elevateMode);
|
||||
bool sendStartProcess(const QString &command, ElevateMode elevateMode);
|
||||
bool sendStopProcess();
|
||||
|
||||
bool isConnected() const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user