feat(ipc): Stop bad version existing Core process

This commit is contained in:
Nick Bolton 2026-04-08 10:31:40 +01:00
parent e9868123df
commit 10027ecc1c
11 changed files with 85 additions and 18 deletions

View file

@ -132,6 +132,9 @@ int main(int argc, char **argv)
QApplication::setApplicationName(QStringLiteral("%1 Core").arg(kAppName)); QApplication::setApplicationName(QStringLiteral("%1 Core").arg(kAppName));
const auto ipcServer = new deskflow::core::ipc::CoreIpcServer(&app); // NOSONAR - Qt managed 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(); ipcServer->listen();
QThread coreThread; QThread coreThread;

View file

@ -168,6 +168,12 @@ void App::handleScreenError() const
getEvents()->addEvent(Event(EventTypes::Quit)); getEvents()->addEvent(Event(EventTypes::Quit));
} }
void App::quit()
{
LOG_INFO("quitting");
getEvents()->addEvent(Event(EventTypes::Quit));
}
void App::runEventsLoop(const void *) void App::runEventsLoop(const void *)
{ {
int exitCode = m_events->loop(); int exitCode = m_events->loop();

View file

@ -77,6 +77,7 @@ public:
} }
void run(QThread &coreThread); void run(QThread &coreThread);
void quit();
void setupFileLogging(); void setupFileLogging();
void loggingFilterWarning() const; void loggingFilterWarning() const;
void initApp() override; void initApp() override;

View file

@ -29,8 +29,14 @@ CoreIpcServer &CoreIpcServer::instance()
void CoreIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) void CoreIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts)
{ {
Q_UNUSED(clientSocket)
Q_UNUSED(parts) 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()); LOG_WARN("core ipc server got unknown command: %s", command.toUtf8().constData());
} }

View file

@ -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()); LOG_DEBUG("%s ipc server got hello message (version: %s)", m_typeName.constData(), versionId.toUtf8().constData());
if (clientVersion != versionId) { if (clientVersion != versionId) {
LOG_ERR( LOG_WARN(
"%s ipc client version mismatch (client: %s, server: %s)", m_typeName.constData(), "%s ipc client version mismatch (client: %s, server: %s)", m_typeName.constData(),
clientVersion.toUtf8().constData(), versionId.toUtf8().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->flush();
clientSocket->disconnectFromServer();
return; return;
} }

View file

@ -144,6 +144,33 @@ void CoreProcess::daemonIpcClientConnected()
m_daemonIpcClient->requestLogPath(); 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) void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus)
{ {
using enum ProcessState; using enum ProcessState;
@ -155,10 +182,11 @@ void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus)
if (exitCode != s_exitSuccess) { if (exitCode != s_exitSuccess) {
setProcessState(Stopped); setProcessState(Stopped);
if (exitCode == s_exitDuplicate) if (exitCode == s_exitDuplicate) {
qWarning("desktop process is already running"); checkExistingProcess();
else return;
qWarning("desktop process exited with code: %d", exitCode); }
qWarning("desktop process exited with code: %d", exitCode);
return; return;
} }
@ -399,10 +427,15 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
m_coreIpcClient = new ipc::CoreIpcClient(this); m_coreIpcClient = new ipc::CoreIpcClient(this);
connect(m_coreIpcClient, &ipc::CoreIpcClient::commandReceived, this, &CoreProcess::onCoreIpcMessageReceived); 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, [] { connect(m_coreIpcClient, &ipc::CoreIpcClient::connectionFailed, this, [] {
qWarning("failed to establish core ipc connection"); qWarning("failed to establish core ipc connection");
}); });
connect(m_coreIpcClient, &ipc::CoreIpcClient::serverShutdown, this, [] {
qDebug("core ipc server shut down cleanly");
});
m_coreIpcClient->connectToServer(); m_coreIpcClient->connectToServer();
}); });

View file

@ -114,6 +114,7 @@ private:
void handleLogLines(const QString &text); void handleLogLines(const QString &text);
QString correctedAddress(const QString &address) const; QString correctedAddress(const QString &address) const;
void setupDaemonLogTail(const QString &logPath); void setupDaemonLogTail(const QString &logPath);
void checkExistingProcess();
static QString makeQuotedArgs(const QString &app, const QStringList &args); static QString makeQuotedArgs(const QString &app, const QStringList &args);
static QString processModeToString(const Settings::ProcessMode mode); static QString processModeToString(const Settings::ProcessMode mode);
static QString processStateToString(const CoreProcess::ProcessState state); static QString processStateToString(const CoreProcess::ProcessState state);

View file

@ -20,6 +20,11 @@ CoreIpcClient::CoreIpcClient(QObject *parent) : IpcClient(parent, kCoreIpcName,
// do nothing // do nothing
} }
void CoreIpcClient::sendStop()
{
sendMessage(QStringLiteral("stop"));
}
void CoreIpcClient::processCommand(const QString &command, const QStringList &parts) void CoreIpcClient::processCommand(const QString &command, const QStringList &parts)
{ {
const auto args = parts.size() >= 2 ? parts.at(1) : QString(); const auto args = parts.size() >= 2 ? parts.at(1) : QString();

View file

@ -19,6 +19,8 @@ class CoreIpcClient : public IpcClient
public: public:
explicit CoreIpcClient(QObject *parent = nullptr); explicit CoreIpcClient(QObject *parent = nullptr);
void sendStop();
Q_SIGNALS: Q_SIGNALS:
void commandReceived(const QString &command, const QString &args); void commandReceived(const QString &command, const QString &args);

View file

@ -151,6 +151,13 @@ void IpcClient::handleReadyRead()
continue; 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); processCommand(parts.at(0), parts);
} }
@ -169,6 +176,17 @@ void IpcClient::handleHandshakeMessage(const QStringList &parts)
return; 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")) { if (parts.at(0) != QStringLiteral("hello")) {
return; return;
} }
@ -180,15 +198,6 @@ void IpcClient::handleHandshakeMessage(const QStringList &parts)
return; 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; m_state = State::Connected;
qDebug().noquote() << QStringLiteral("%1 ipc client connected").arg(m_typeName); qDebug().noquote() << QStringLiteral("%1 ipc client connected").arg(m_typeName);
Q_EMIT connected(); Q_EMIT connected();

View file

@ -38,6 +38,8 @@ public:
Q_SIGNALS: Q_SIGNALS:
void connected(); void connected();
void connectionFailed(); void connectionFailed();
void serverShutdown();
void versionMismatch();
private Q_SLOTS: private Q_SLOTS:
void handleDisconnected(); void handleDisconnected();