feat(ipc): Core IPC server and connected client
This commit is contained in:
parent
1ec36645e1
commit
62ccb543dc
14 changed files with 170 additions and 28 deletions
|
|
@ -15,6 +15,7 @@
|
||||||
#include "common/ExitCodes.h"
|
#include "common/ExitCodes.h"
|
||||||
#include "deskflow/ClientApp.h"
|
#include "deskflow/ClientApp.h"
|
||||||
#include "deskflow/ServerApp.h"
|
#include "deskflow/ServerApp.h"
|
||||||
|
#include "deskflow/ipc/CoreIpcServer.h"
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
#include "arch/win32/ArchMiscWindows.h"
|
#include "arch/win32/ArchMiscWindows.h"
|
||||||
|
|
@ -104,6 +105,9 @@ int main(int argc, char **argv)
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
QCoreApplication::setApplicationName(QStringLiteral("%1 Core").arg(kAppName));
|
QCoreApplication::setApplicationName(QStringLiteral("%1 Core").arg(kAppName));
|
||||||
|
|
||||||
|
const auto ipcServer = new deskflow::core::ipc::CoreIpcServer(&app); // NOSONAR - Qt managed
|
||||||
|
ipcServer->listen();
|
||||||
|
|
||||||
QThread coreThread;
|
QThread coreThread;
|
||||||
QObject::connect(&coreThread, &QThread::finished, &app, &QCoreApplication::quit);
|
QObject::connect(&coreThread, &QThread::finished, &app, &QCoreApplication::quit);
|
||||||
coreApp->run(coreThread);
|
coreApp->run(coreThread);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ const auto kCopyright = //
|
||||||
"Copyright (C) 2002-2009 Chris Schoeneman";
|
"Copyright (C) 2002-2009 Chris Schoeneman";
|
||||||
|
|
||||||
const auto kCoreBinName = "@CORE_BINARY@";
|
const auto kCoreBinName = "@CORE_BINARY@";
|
||||||
|
const auto kCoreIpcName = "@CMAKE_PROJECT_NAME@-core";
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ add_library(${lib_name} STATIC ${PLATFORM_CODE}
|
||||||
ipc/IpcServer.h
|
ipc/IpcServer.h
|
||||||
ipc/DaemonIpcServer.cpp
|
ipc/DaemonIpcServer.cpp
|
||||||
ipc/DaemonIpcServer.h
|
ipc/DaemonIpcServer.h
|
||||||
|
ipc/CoreIpcServer.cpp
|
||||||
|
ipc/CoreIpcServer.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${lib_name} PUBLIC common Qt6::Core Qt6::Network)
|
target_link_libraries(${lib_name} PUBLIC common Qt6::Core Qt6::Network)
|
||||||
|
|
|
||||||
28
src/lib/deskflow/ipc/CoreIpcServer.cpp
Normal file
28
src/lib/deskflow/ipc/CoreIpcServer.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CoreIpcServer.h"
|
||||||
|
|
||||||
|
#include "base/Log.h"
|
||||||
|
#include "common/Constants.h"
|
||||||
|
|
||||||
|
#include <QLocalSocket>
|
||||||
|
|
||||||
|
namespace deskflow::core::ipc {
|
||||||
|
|
||||||
|
CoreIpcServer::CoreIpcServer(QObject *parent) : IpcServer(parent, kCoreIpcName)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts)
|
||||||
|
{
|
||||||
|
Q_UNUSED(clientSocket)
|
||||||
|
Q_UNUSED(parts)
|
||||||
|
LOG_WARN("core ipc server got unknown command: %s", command.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace deskflow::core::ipc
|
||||||
29
src/lib/deskflow/ipc/CoreIpcServer.h
Normal file
29
src/lib/deskflow/ipc/CoreIpcServer.h
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IpcServer.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
class QLocalSocket;
|
||||||
|
|
||||||
|
namespace deskflow::core::ipc {
|
||||||
|
|
||||||
|
class CoreIpcServer : public IpcServer
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CoreIpcServer(QObject *parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace deskflow::core::ipc
|
||||||
|
|
@ -23,29 +23,14 @@ DaemonIpcServer::DaemonIpcServer(QObject *parent, const QString &logFilename)
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &message)
|
void DaemonIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts)
|
||||||
{
|
{
|
||||||
LOG_DEBUG1("daemon ipc server got message: %s", message.toUtf8().constData());
|
if (command == "logLevel") {
|
||||||
const auto parts = message.split('=');
|
|
||||||
if (parts.size() < 1) {
|
|
||||||
LOG_ERR("daemon ipc server got invalid message: %s", message.toUtf8().constData());
|
|
||||||
writeToClientSocket(clientSocket, kErrorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &command = parts[0];
|
|
||||||
if (command == "hello") { // NOSONAR - if-init is confusing here
|
|
||||||
LOG_DEBUG("daemon ipc server got hello message, sending hello back");
|
|
||||||
writeToClientSocket(clientSocket, "hello");
|
|
||||||
} else if (command == "noop") {
|
|
||||||
LOG_DEBUG("daemon ipc server got noop message");
|
|
||||||
writeToClientSocket(clientSocket, kAckMessage);
|
|
||||||
} else if (command == "logLevel") {
|
|
||||||
processLogLevel(clientSocket, parts);
|
processLogLevel(clientSocket, parts);
|
||||||
} else if (command == "elevate") {
|
} else if (command == "elevate") {
|
||||||
processElevate(clientSocket, parts);
|
processElevate(clientSocket, parts);
|
||||||
} else if (command == "command") {
|
} else if (command == "command") {
|
||||||
processCommand(clientSocket, parts);
|
processCommandMessage(clientSocket, parts);
|
||||||
} else if (command == "start") {
|
} else if (command == "start") {
|
||||||
LOG_DEBUG("daemon ipc server got start message");
|
LOG_DEBUG("daemon ipc server got start message");
|
||||||
Q_EMIT startProcessRequested();
|
Q_EMIT startProcessRequested();
|
||||||
|
|
@ -62,10 +47,8 @@ void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &
|
||||||
Q_EMIT clearSettingsRequested();
|
Q_EMIT clearSettingsRequested();
|
||||||
writeToClientSocket(clientSocket, kAckMessage);
|
writeToClientSocket(clientSocket, kAckMessage);
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN("daemon ipc server got unknown message: %s", message.toUtf8().constData());
|
LOG_WARN("daemon ipc server got unknown command: %s", command.toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
clientSocket->flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonIpcServer::processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
void DaemonIpcServer::processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||||
|
|
@ -108,7 +91,7 @@ void DaemonIpcServer::processElevate(QLocalSocket *&clientSocket, const QStringL
|
||||||
writeToClientSocket(clientSocket, kAckMessage);
|
writeToClientSocket(clientSocket, kAckMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonIpcServer::processCommand(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
void DaemonIpcServer::processCommandMessage(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||||
{
|
{
|
||||||
if (messageParts.size() < 2) {
|
if (messageParts.size() < 2) {
|
||||||
LOG_ERR("daemon ipc server got invalid command message");
|
LOG_ERR("daemon ipc server got invalid command message");
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ public:
|
||||||
explicit DaemonIpcServer(QObject *parent, const QString &logFilename);
|
explicit DaemonIpcServer(QObject *parent, const QString &logFilename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void processMessage(QLocalSocket *clientSocket, const QString &message) override;
|
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
||||||
void processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
void processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
void processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
void processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
void processCommand(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
void processCommandMessage(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QString m_logFilename;
|
const QString m_logFilename;
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@
|
||||||
|
|
||||||
namespace deskflow::core::ipc {
|
namespace deskflow::core::ipc {
|
||||||
|
|
||||||
const auto kAckMessage = "ok";
|
|
||||||
const auto kErrorMessage = "error";
|
|
||||||
|
|
||||||
IpcServer::IpcServer(QObject *parent, const QString &serverName)
|
IpcServer::IpcServer(QObject *parent, const QString &serverName)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
m_server{new QLocalServer(this)}, // NOSONAR - Qt memory
|
m_server{new QLocalServer(this)}, // NOSONAR - Qt memory
|
||||||
|
|
@ -102,6 +99,29 @@ void IpcServer::handleErrorOccurred()
|
||||||
clientSocket->deleteLater();
|
clientSocket->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IpcServer::processMessage(QLocalSocket *clientSocket, const QString &message)
|
||||||
|
{
|
||||||
|
LOG_DEBUG1("ipc server got message: %s", message.toUtf8().constData());
|
||||||
|
const auto parts = message.split('=');
|
||||||
|
if (parts.isEmpty()) {
|
||||||
|
LOG_ERR("ipc server got invalid message: %s", message.toUtf8().constData());
|
||||||
|
writeToClientSocket(clientSocket, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto &command = parts.at(0); command == "hello") {
|
||||||
|
LOG_DEBUG("ipc server got hello message, sending hello back");
|
||||||
|
writeToClientSocket(clientSocket, "hello");
|
||||||
|
} else if (command == "noop") {
|
||||||
|
LOG_DEBUG("ipc server got noop message");
|
||||||
|
writeToClientSocket(clientSocket, "ok");
|
||||||
|
} else {
|
||||||
|
processCommand(clientSocket, command, parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSocket->flush();
|
||||||
|
}
|
||||||
|
|
||||||
void IpcServer::writeToClientSocket(QLocalSocket *&clientSocket, const QString &message) const
|
void IpcServer::writeToClientSocket(QLocalSocket *&clientSocket, const QString &message) const
|
||||||
{
|
{
|
||||||
QByteArray messageData = message.toUtf8() + '\n';
|
QByteArray messageData = message.toUtf8() + '\n';
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ protected:
|
||||||
void writeToClientSocket(QLocalSocket *&clientSocket, const QString &message) const;
|
void writeToClientSocket(QLocalSocket *&clientSocket, const QString &message) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void processMessage(QLocalSocket *clientSocket, const QString &message) = 0;
|
void processMessage(QLocalSocket *clientSocket, const QString &message);
|
||||||
|
virtual void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) = 0;
|
||||||
void handleNewConnection();
|
void handleNewConnection();
|
||||||
void handleReadyRead();
|
void handleReadyRead();
|
||||||
void handleDisconnected();
|
void handleDisconnected();
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,8 @@ add_library(${target} STATIC
|
||||||
ipc/IpcClient.h
|
ipc/IpcClient.h
|
||||||
ipc/DaemonIpcClient.cpp
|
ipc/DaemonIpcClient.cpp
|
||||||
ipc/DaemonIpcClient.h
|
ipc/DaemonIpcClient.h
|
||||||
|
ipc/CoreIpcClient.cpp
|
||||||
|
ipc/CoreIpcClient.h
|
||||||
validators/AliasValidator.cpp
|
validators/AliasValidator.cpp
|
||||||
validators/AliasValidator.h
|
validators/AliasValidator.h
|
||||||
validators/ComputerNameValidator.cpp
|
validators/ComputerNameValidator.cpp
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "CoreProcess.h"
|
#include "CoreProcess.h"
|
||||||
|
|
||||||
#include "common/ExitCodes.h"
|
#include "common/ExitCodes.h"
|
||||||
|
#include "gui/ipc/CoreIpcClient.h"
|
||||||
#include "gui/ipc/DaemonIpcClient.h"
|
#include "gui/ipc/DaemonIpcClient.h"
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
|
|
@ -372,6 +373,21 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
|
||||||
startProcessFromDaemon(args);
|
startProcessFromDaemon(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't block the main GUI render thread when connecting to the Core IPC server.
|
||||||
|
QTimer::singleShot(kRetryDelay, this, [this] {
|
||||||
|
if (m_processState != ProcessState::Started) {
|
||||||
|
qWarning("core process failed to start, skipping core ipc connection");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_coreIpcClient = new ipc::CoreIpcClient(this);
|
||||||
|
if (m_coreIpcClient->connectToServer()) {
|
||||||
|
qInfo("connected to core ipc server");
|
||||||
|
} else {
|
||||||
|
qWarning("failed to establish core ipc connection");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
m_lastProcessMode = processMode;
|
m_lastProcessMode = processMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -384,6 +400,12 @@ void CoreProcess::stop(std::optional<ProcessMode> processModeOption)
|
||||||
|
|
||||||
qInfo("stopping core process (%s mode)", qPrintable(processModeToString(processMode)));
|
qInfo("stopping core process (%s mode)", qPrintable(processModeToString(processMode)));
|
||||||
|
|
||||||
|
if (m_coreIpcClient) {
|
||||||
|
m_coreIpcClient->disconnectFromServer();
|
||||||
|
delete m_coreIpcClient;
|
||||||
|
m_coreIpcClient = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_processState == ProcessState::Starting) {
|
if (m_processState == ProcessState::Starting) {
|
||||||
qDebug("core process is starting, cancelling");
|
qDebug("core process is starting, cancelling");
|
||||||
setProcessState(ProcessState::Stopped);
|
setProcessState(ProcessState::Stopped);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
namespace deskflow::gui {
|
namespace deskflow::gui {
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
class CoreIpcClient;
|
||||||
class DaemonIpcClient;
|
class DaemonIpcClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,6 +129,7 @@ private:
|
||||||
std::optional<ProcessMode> m_lastProcessMode = std::nullopt;
|
std::optional<ProcessMode> m_lastProcessMode = std::nullopt;
|
||||||
QTimer m_retryTimer;
|
QTimer m_retryTimer;
|
||||||
int m_connections = 0;
|
int m_connections = 0;
|
||||||
|
deskflow::gui::ipc::CoreIpcClient *m_coreIpcClient = nullptr;
|
||||||
deskflow::gui::ipc::DaemonIpcClient *m_daemonIpcClient = nullptr;
|
deskflow::gui::ipc::DaemonIpcClient *m_daemonIpcClient = nullptr;
|
||||||
FileTail *m_daemonFileTail = nullptr;
|
FileTail *m_daemonFileTail = nullptr;
|
||||||
QProcess *m_process = nullptr;
|
QProcess *m_process = nullptr;
|
||||||
|
|
|
||||||
25
src/lib/gui/ipc/CoreIpcClient.cpp
Normal file
25
src/lib/gui/ipc/CoreIpcClient.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CoreIpcClient.h"
|
||||||
|
|
||||||
|
#include "common/Constants.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace deskflow::gui::ipc {
|
||||||
|
|
||||||
|
const auto kTimeout = 1000;
|
||||||
|
|
||||||
|
CoreIpcClient::CoreIpcClient(QObject *parent) : IpcClient(parent, kCoreIpcName)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace deskflow::gui::ipc
|
||||||
23
src/lib/gui/ipc/CoreIpcClient.h
Normal file
23
src/lib/gui/ipc/CoreIpcClient.h
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IpcClient.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace deskflow::gui::ipc {
|
||||||
|
|
||||||
|
class CoreIpcClient : public IpcClient
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CoreIpcClient(QObject *parent = nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace deskflow::gui::ipc
|
||||||
Loading…
Reference in a new issue