From 10027ecc1c77c84febb9da2766147a66a1fa6a10 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 8 Apr 2026 10:31:40 +0100 Subject: [PATCH] feat(ipc): Stop bad version existing Core process --- src/apps/deskflow-core/deskflow-core.cpp | 3 ++ src/lib/deskflow/App.cpp | 6 ++++ src/lib/deskflow/App.h | 1 + src/lib/deskflow/ipc/CoreIpcServer.cpp | 8 ++++- src/lib/deskflow/ipc/IpcServer.cpp | 5 ++- src/lib/gui/core/CoreProcess.cpp | 43 +++++++++++++++++++++--- src/lib/gui/core/CoreProcess.h | 1 + src/lib/gui/ipc/CoreIpcClient.cpp | 5 +++ src/lib/gui/ipc/CoreIpcClient.h | 2 ++ src/lib/gui/ipc/IpcClient.cpp | 27 ++++++++++----- src/lib/gui/ipc/IpcClient.h | 2 ++ 11 files changed, 85 insertions(+), 18 deletions(-) diff --git a/src/apps/deskflow-core/deskflow-core.cpp b/src/apps/deskflow-core/deskflow-core.cpp index 2171a6ae6..8214135d5 100644 --- a/src/apps/deskflow-core/deskflow-core.cpp +++ b/src/apps/deskflow-core/deskflow-core.cpp @@ -132,6 +132,9 @@ int main(int argc, char **argv) QApplication::setApplicationName(QStringLiteral("%1 Core").arg(kAppName)); const auto ipcServer = new deskflow::core::ipc::CoreIpcServer(&app); // NOSONAR - Qt managed + QObject::connect( + ipcServer, &deskflow::core::ipc::IpcServer::stopProcessRequested, coreApp, &App::quit, Qt::DirectConnection + ); ipcServer->listen(); QThread coreThread; diff --git a/src/lib/deskflow/App.cpp b/src/lib/deskflow/App.cpp index c75e52072..da0dfecc9 100644 --- a/src/lib/deskflow/App.cpp +++ b/src/lib/deskflow/App.cpp @@ -168,6 +168,12 @@ void App::handleScreenError() const getEvents()->addEvent(Event(EventTypes::Quit)); } +void App::quit() +{ + LOG_INFO("quitting"); + getEvents()->addEvent(Event(EventTypes::Quit)); +} + void App::runEventsLoop(const void *) { int exitCode = m_events->loop(); diff --git a/src/lib/deskflow/App.h b/src/lib/deskflow/App.h index eb4dc907c..2700ca5dc 100644 --- a/src/lib/deskflow/App.h +++ b/src/lib/deskflow/App.h @@ -77,6 +77,7 @@ public: } void run(QThread &coreThread); + void quit(); void setupFileLogging(); void loggingFilterWarning() const; void initApp() override; diff --git a/src/lib/deskflow/ipc/CoreIpcServer.cpp b/src/lib/deskflow/ipc/CoreIpcServer.cpp index f0ad0a39d..eba8eb798 100644 --- a/src/lib/deskflow/ipc/CoreIpcServer.cpp +++ b/src/lib/deskflow/ipc/CoreIpcServer.cpp @@ -29,8 +29,14 @@ CoreIpcServer &CoreIpcServer::instance() void CoreIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) { - Q_UNUSED(clientSocket) Q_UNUSED(parts) + if (command == QStringLiteral("stop")) { + LOG_DEBUG("core ipc server got stop message"); + writeToClientSocket(clientSocket, QStringLiteral("ok")); + broadcastCommand(QStringLiteral("bye")); + Q_EMIT stopProcessRequested(); + return; + } LOG_WARN("core ipc server got unknown command: %s", command.toUtf8().constData()); } diff --git a/src/lib/deskflow/ipc/IpcServer.cpp b/src/lib/deskflow/ipc/IpcServer.cpp index 616a3f388..beef1f77c 100644 --- a/src/lib/deskflow/ipc/IpcServer.cpp +++ b/src/lib/deskflow/ipc/IpcServer.cpp @@ -125,13 +125,12 @@ void IpcServer::processMessage(QLocalSocket *clientSocket, const QString &messag LOG_DEBUG("%s ipc server got hello message (version: %s)", m_typeName.constData(), versionId.toUtf8().constData()); if (clientVersion != versionId) { - LOG_ERR( + LOG_WARN( "%s ipc client version mismatch (client: %s, server: %s)", m_typeName.constData(), clientVersion.toUtf8().constData(), versionId.toUtf8().constData() ); - writeToClientSocket(clientSocket, QStringLiteral("error=version mismatch, expected: %1").arg(versionId)); + writeToClientSocket(clientSocket, QStringLiteral("versionMismatch=%1").arg(versionId)); clientSocket->flush(); - clientSocket->disconnectFromServer(); return; } diff --git a/src/lib/gui/core/CoreProcess.cpp b/src/lib/gui/core/CoreProcess.cpp index bd514a7ac..be4e5d667 100644 --- a/src/lib/gui/core/CoreProcess.cpp +++ b/src/lib/gui/core/CoreProcess.cpp @@ -144,6 +144,33 @@ void CoreProcess::daemonIpcClientConnected() m_daemonIpcClient->requestLogPath(); } +void CoreProcess::checkExistingProcess() +{ + qInfo("checking existing core"); + + auto *client = new ipc::CoreIpcClient(this); + connect(client, &ipc::CoreIpcClient::connected, this, [client] { + qInfo("existing core has matching version, leaving it running"); + client->deleteLater(); + }); + connect(client, &ipc::CoreIpcClient::versionMismatch, this, [client] { + qInfo("existing core has mismatched version, asking it to stop"); + client->sendStop(); + }); + connect(client, &ipc::CoreIpcClient::serverShutdown, this, [this, client] { + qInfo("existing core stopped successfully"); + client->deleteLater(); + setProcessState(ProcessState::RetryPending); + m_retryTimer.setSingleShot(true); + m_retryTimer.start(kRetryDelay); + }); + connect(client, &ipc::CoreIpcClient::connectionFailed, this, [client] { + qCritical("could not contact existing core"); + client->deleteLater(); + }); + client->connectToServer(); +} + void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus) { using enum ProcessState; @@ -155,10 +182,11 @@ void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus) if (exitCode != s_exitSuccess) { setProcessState(Stopped); - if (exitCode == s_exitDuplicate) - qWarning("desktop process is already running"); - else - qWarning("desktop process exited with code: %d", exitCode); + if (exitCode == s_exitDuplicate) { + checkExistingProcess(); + return; + } + qWarning("desktop process exited with code: %d", exitCode); return; } @@ -399,10 +427,15 @@ void CoreProcess::start(std::optional processModeOption) m_coreIpcClient = new ipc::CoreIpcClient(this); connect(m_coreIpcClient, &ipc::CoreIpcClient::commandReceived, this, &CoreProcess::onCoreIpcMessageReceived); - connect(m_coreIpcClient, &ipc::CoreIpcClient::connected, this, [] { qInfo("connected to core ipc server"); }); + connect(m_coreIpcClient, &ipc::CoreIpcClient::connected, this, [] { + qDebug("connected to core ipc server"); + }); connect(m_coreIpcClient, &ipc::CoreIpcClient::connectionFailed, this, [] { qWarning("failed to establish core ipc connection"); }); + connect(m_coreIpcClient, &ipc::CoreIpcClient::serverShutdown, this, [] { + qDebug("core ipc server shut down cleanly"); + }); m_coreIpcClient->connectToServer(); }); diff --git a/src/lib/gui/core/CoreProcess.h b/src/lib/gui/core/CoreProcess.h index 4dc924d28..353dbc5f3 100644 --- a/src/lib/gui/core/CoreProcess.h +++ b/src/lib/gui/core/CoreProcess.h @@ -114,6 +114,7 @@ private: void handleLogLines(const QString &text); QString correctedAddress(const QString &address) const; void setupDaemonLogTail(const QString &logPath); + void checkExistingProcess(); static QString makeQuotedArgs(const QString &app, const QStringList &args); static QString processModeToString(const Settings::ProcessMode mode); static QString processStateToString(const CoreProcess::ProcessState state); diff --git a/src/lib/gui/ipc/CoreIpcClient.cpp b/src/lib/gui/ipc/CoreIpcClient.cpp index 5315a6854..cde731d8f 100644 --- a/src/lib/gui/ipc/CoreIpcClient.cpp +++ b/src/lib/gui/ipc/CoreIpcClient.cpp @@ -20,6 +20,11 @@ CoreIpcClient::CoreIpcClient(QObject *parent) : IpcClient(parent, kCoreIpcName, // do nothing } +void CoreIpcClient::sendStop() +{ + sendMessage(QStringLiteral("stop")); +} + void CoreIpcClient::processCommand(const QString &command, const QStringList &parts) { const auto args = parts.size() >= 2 ? parts.at(1) : QString(); diff --git a/src/lib/gui/ipc/CoreIpcClient.h b/src/lib/gui/ipc/CoreIpcClient.h index bccb1ccd4..c14fc300b 100644 --- a/src/lib/gui/ipc/CoreIpcClient.h +++ b/src/lib/gui/ipc/CoreIpcClient.h @@ -19,6 +19,8 @@ class CoreIpcClient : public IpcClient public: explicit CoreIpcClient(QObject *parent = nullptr); + void sendStop(); + Q_SIGNALS: void commandReceived(const QString &command, const QString &args); diff --git a/src/lib/gui/ipc/IpcClient.cpp b/src/lib/gui/ipc/IpcClient.cpp index 79a5d1187..93dcdea65 100644 --- a/src/lib/gui/ipc/IpcClient.cpp +++ b/src/lib/gui/ipc/IpcClient.cpp @@ -151,6 +151,13 @@ void IpcClient::handleReadyRead() continue; } + if (parts.at(0) == QStringLiteral("bye")) { + qDebug().noquote() << QStringLiteral("%1 ipc server is shutting down").arg(m_typeName); + disconnectFromServer(); + Q_EMIT serverShutdown(); + return; + } + processCommand(parts.at(0), parts); } @@ -169,6 +176,17 @@ void IpcClient::handleHandshakeMessage(const QStringList &parts) return; } + const auto versionId = QStringLiteral("%1+%2").arg(kVersion, kVersionGitSha); + + if (parts.at(0) == QStringLiteral("versionMismatch")) { + const auto serverVersion = parts.size() >= 2 ? parts.at(1) : QStringLiteral("unknown"); + qWarning().noquote( + ) << QStringLiteral("%1 ipc version mismatch (client: %2, server: %3)").arg(m_typeName, versionId, serverVersion); + m_state = State::Connected; + Q_EMIT versionMismatch(); + return; + } + if (parts.at(0) != QStringLiteral("hello")) { return; } @@ -180,15 +198,6 @@ void IpcClient::handleHandshakeMessage(const QStringList &parts) return; } - const auto versionId = QStringLiteral("%1+%2").arg(kVersion, kVersionGitSha); - if (const auto serverVersion = parts.at(1); serverVersion != versionId) { - qCritical().noquote( - ) << QStringLiteral("%1 ipc version mismatch (client: %2 , server: %3)").arg(m_typeName, versionId, serverVersion); - disconnectFromServer(); - Q_EMIT connectionFailed(); - return; - } - m_state = State::Connected; qDebug().noquote() << QStringLiteral("%1 ipc client connected").arg(m_typeName); Q_EMIT connected(); diff --git a/src/lib/gui/ipc/IpcClient.h b/src/lib/gui/ipc/IpcClient.h index 231b1600f..699319ef2 100644 --- a/src/lib/gui/ipc/IpcClient.h +++ b/src/lib/gui/ipc/IpcClient.h @@ -38,6 +38,8 @@ public: Q_SIGNALS: void connected(); void connectionFailed(); + void serverShutdown(); + void versionMismatch(); private Q_SLOTS: void handleDisconnected();