feat(ipc): Messaging for connection states via IPC (async client)
This commit is contained in:
parent
62ccb543dc
commit
66c9b6607c
25 changed files with 376 additions and 308 deletions
|
|
@ -199,7 +199,6 @@ otherwise it expands to a call that doesn't.
|
|||
// end, then we resort to using non-numerical chars. this still works (since
|
||||
// to deduce the number we subtract octal \060, so '/' is -1, and ':' is 10
|
||||
|
||||
#define CLOG_IPC CLOG_TRACE "%z\056" // char is '' ?
|
||||
#define CLOG_PRINT CLOG_TRACE "%z\057" // char is '/'
|
||||
#define CLOG_CRIT CLOG_TRACE "%z\060" // char is '0'
|
||||
#define CLOG_ERR CLOG_TRACE "%z\061"
|
||||
|
|
@ -210,7 +209,6 @@ otherwise it expands to a call that doesn't.
|
|||
#define CLOG_DEBUG1 CLOG_TRACE "%z\066"
|
||||
#define CLOG_DEBUG2 CLOG_TRACE "%z\067"
|
||||
|
||||
#define LOG_IPC(...) LOG((CLOG_IPC __VA_ARGS__))
|
||||
#define LOG_PRINT(...) LOG((CLOG_PRINT __VA_ARGS__))
|
||||
#define LOG_CRIT(...) LOG((CLOG_CRIT __VA_ARGS__))
|
||||
#define LOG_ERR(...) LOG((CLOG_ERR __VA_ARGS__))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "base/NetworkProtocol.h"
|
||||
#include "client/ServerProxy.h"
|
||||
#include "common/Settings.h"
|
||||
#include "deskflow/ipc/CoreIpc.h"
|
||||
#include "deskflow/Clipboard.h"
|
||||
#include "deskflow/IPlatformScreen.h"
|
||||
#include "deskflow/PacketStreamFilter.h"
|
||||
|
|
@ -92,10 +93,11 @@ void Client::connect(size_t addressIndex)
|
|||
// m_serverAddress will be null if the hostname address is not reolved
|
||||
if (m_serverAddress.getAddress() != nullptr) {
|
||||
// to help users troubleshoot, show server host name (issue: 60)
|
||||
LOG_IPC(
|
||||
LOG_DEBUG(
|
||||
"connecting to '%s': %s:%i", m_serverAddress.getHostname().c_str(),
|
||||
ARCH->addrToString(m_serverAddress.getAddress()).c_str(), m_serverAddress.getPort()
|
||||
);
|
||||
ipcSendConnectionState(deskflow::core::ConnectionState::Connecting);
|
||||
}
|
||||
|
||||
// create the socket
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ add_library(${lib_name} STATIC ${PLATFORM_CODE}
|
|||
ipc/IpcServer.h
|
||||
ipc/DaemonIpcServer.cpp
|
||||
ipc/DaemonIpcServer.h
|
||||
ipc/CoreIpc.cpp
|
||||
ipc/CoreIpc.h
|
||||
ipc/CoreIpcServer.cpp
|
||||
ipc/CoreIpcServer.h
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "common/Settings.h"
|
||||
#include "deskflow/Screen.h"
|
||||
#include "deskflow/ScreenException.h"
|
||||
#include "deskflow/ipc/CoreIpc.h"
|
||||
#include "net/NetworkAddress.h"
|
||||
#include "net/SocketException.h"
|
||||
#include "net/SocketMultiplexer.h"
|
||||
|
|
@ -159,10 +160,8 @@ void ClientApp::handleClientRestart(const Event &, EventQueueTimer *timer)
|
|||
|
||||
void ClientApp::scheduleClientRestart(double retryTime)
|
||||
{
|
||||
if (Settings::value(Settings::Client::DynamicConnectionRetry).toBool())
|
||||
LOG_IPC("retry in %.0f seconds", retryTime);
|
||||
else
|
||||
LOG_DEBUG("retry in %.0f seconds", retryTime);
|
||||
ipcSendToClient("retryIn", QString::number(retryTime, 'f', 0));
|
||||
// install a timer and handler to retry later
|
||||
EventQueueTimer *timer = getEvents()->newOneShotTimer(retryTime, nullptr);
|
||||
getEvents()->addHandler(EventTypes::Timer, timer, [this, timer](const auto &e) { handleClientRestart(e, timer); });
|
||||
|
|
@ -170,7 +169,8 @@ void ClientApp::scheduleClientRestart(double retryTime)
|
|||
|
||||
void ClientApp::handleClientConnected()
|
||||
{
|
||||
LOG_IPC("connected to server");
|
||||
LOG_DEBUG("connected to server");
|
||||
ipcSendConnectionState(deskflow::core::ConnectionState::Connected);
|
||||
// Reset server index on successful connection
|
||||
m_currentServerIndex = 0;
|
||||
m_lastServerAddressIndex = 0;
|
||||
|
|
@ -223,7 +223,8 @@ void ClientApp::handleClientRefused(const Event &e)
|
|||
void ClientApp::handleClientDisconnected()
|
||||
{
|
||||
m_retryCount = 0;
|
||||
LOG_IPC("disconnected from server");
|
||||
LOG_DEBUG("disconnected from server");
|
||||
ipcSendConnectionState(deskflow::core::ConnectionState::Disconnected);
|
||||
if (!m_suspended) {
|
||||
scheduleClientRestart(retryTime());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "deskflow/ProtocolTypes.h"
|
||||
#include "deskflow/Screen.h"
|
||||
#include "deskflow/ScreenException.h"
|
||||
#include "deskflow/ipc/CoreIpc.h"
|
||||
#include "net/SocketException.h"
|
||||
#include "net/SocketMultiplexer.h"
|
||||
#include "net/TCPSocketFactory.h"
|
||||
|
|
@ -370,7 +371,8 @@ bool ServerApp::startServer()
|
|||
listener->setServer(m_server);
|
||||
m_server->setListener(listener);
|
||||
m_listener = listener;
|
||||
LOG_IPC("started server, waiting for clients");
|
||||
LOG_DEBUG("started server, waiting for clients");
|
||||
ipcSendConnectionState(deskflow::core::ConnectionState::Listening);
|
||||
m_serverState = Started;
|
||||
return true;
|
||||
} catch (SocketAddressInUseException &e) {
|
||||
|
|
|
|||
28
src/lib/deskflow/ipc/CoreIpc.cpp
Normal file
28
src/lib/deskflow/ipc/CoreIpc.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Symless Ltd.
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "CoreIpc.h"
|
||||
|
||||
#include "CoreIpcServer.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
void ipcSendToClient(const QString &command, const QString &args)
|
||||
{
|
||||
// Queued because callers may not be on the main thread,
|
||||
// and QLocalSocket can only be written to from its owning thread.
|
||||
auto &server = deskflow::core::ipc::CoreIpcServer::instance();
|
||||
QMetaObject::invokeMethod(
|
||||
&server, [command, args] { deskflow::core::ipc::CoreIpcServer::instance().broadcastCommand(command, args); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
}
|
||||
|
||||
void ipcSendConnectionState(deskflow::core::ConnectionState state)
|
||||
{
|
||||
const auto metaEnum = QMetaEnum::fromType<deskflow::core::ConnectionState>();
|
||||
ipcSendToClient("connectionState", metaEnum.valueToKey(static_cast<int>(state)));
|
||||
}
|
||||
14
src/lib/deskflow/ipc/CoreIpc.h
Normal file
14
src/lib/deskflow/ipc/CoreIpc.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Symless Ltd.
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Enums.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
void ipcSendToClient(const QString &command, const QString &args = "");
|
||||
void ipcSendConnectionState(deskflow::core::ConnectionState state);
|
||||
|
|
@ -13,9 +13,18 @@
|
|||
|
||||
namespace deskflow::core::ipc {
|
||||
|
||||
static CoreIpcServer *s_instance = nullptr;
|
||||
|
||||
CoreIpcServer::CoreIpcServer(QObject *parent) : IpcServer(parent, kCoreIpcName)
|
||||
{
|
||||
// do nothing
|
||||
assert(s_instance == nullptr);
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
CoreIpcServer &CoreIpcServer::instance()
|
||||
{
|
||||
assert(s_instance != nullptr);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
void CoreIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ class CoreIpcServer : public IpcServer
|
|||
public:
|
||||
explicit CoreIpcServer(QObject *parent);
|
||||
|
||||
static CoreIpcServer &instance();
|
||||
|
||||
private:
|
||||
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -112,6 +112,14 @@ void IpcServer::processMessage(QLocalSocket *clientSocket, const QString &messag
|
|||
if (const auto &command = parts.at(0); command == "hello") {
|
||||
LOG_DEBUG("ipc server got hello message, sending hello back");
|
||||
writeToClientSocket(clientSocket, "hello");
|
||||
|
||||
// Replay messages that were queued before any clients connected.
|
||||
LOG_DEBUG1("ipc server replaying %d pending messages", m_pendingMessages.size());
|
||||
for (const auto &pending : std::as_const(m_pendingMessages)) {
|
||||
LOG_DEBUG1("ipc server replaying: %s", pending.toUtf8().constData());
|
||||
writeToClientSocket(clientSocket, pending);
|
||||
}
|
||||
m_pendingMessages.clear();
|
||||
} else if (command == "noop") {
|
||||
LOG_DEBUG("ipc server got noop message");
|
||||
writeToClientSocket(clientSocket, "ok");
|
||||
|
|
@ -122,6 +130,23 @@ void IpcServer::processMessage(QLocalSocket *clientSocket, const QString &messag
|
|||
clientSocket->flush();
|
||||
}
|
||||
|
||||
void IpcServer::broadcastCommand(const QString &command, const QString &args)
|
||||
{
|
||||
const auto message = args.isEmpty() ? command : command + "=" + args;
|
||||
|
||||
if (m_clients.isEmpty()) {
|
||||
LOG_DEBUG1("ipc server has no clients, message queued: %s", message.toUtf8().constData());
|
||||
m_pendingMessages.append(message);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG1("ipc server broadcasting message to %d clients: %s", m_clients.size(), message.toUtf8().constData());
|
||||
for (auto *client : std::as_const(m_clients)) {
|
||||
writeToClientSocket(client, message);
|
||||
client->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void IpcServer::writeToClientSocket(QLocalSocket *&clientSocket, const QString &message) const
|
||||
{
|
||||
QByteArray messageData = message.toUtf8() + '\n';
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public:
|
|||
~IpcServer() override;
|
||||
|
||||
void listen();
|
||||
void broadcastCommand(const QString &command, const QString &args = "");
|
||||
|
||||
Q_SIGNALS:
|
||||
void logLevelChanged(const QString &logLevel);
|
||||
|
|
@ -52,6 +53,7 @@ private:
|
|||
QLocalServer *m_server;
|
||||
QSet<QLocalSocket *> m_clients;
|
||||
QString m_serverName;
|
||||
QStringList m_pendingMessages;
|
||||
};
|
||||
|
||||
} // namespace deskflow::core::ipc
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ void MainWindow::connectSlots()
|
|||
|
||||
connect(&m_serverConnection, &ServerConnection::configureClient, this, &MainWindow::serverConnectionConfigureClient);
|
||||
connect(&m_serverConnection, &ServerConnection::clientsChanged, this, &MainWindow::serverClientsChanged);
|
||||
connect(&m_coreProcess, &CoreProcess::connectedClientsChanged, this, &MainWindow::serverClientsChanged);
|
||||
connect(
|
||||
&m_serverConnection, &ServerConnection::requestNewClientPrompt, this, &MainWindow::handleNewClientPromptRequest
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMetaEnum>
|
||||
#include <QMutexLocker>
|
||||
#include <QRegularExpression>
|
||||
|
||||
|
|
@ -112,6 +113,7 @@ CoreProcess::CoreProcess(const IServerConfig &serverConfig)
|
|||
connect(
|
||||
m_daemonIpcClient, &ipc::DaemonIpcClient::connectionFailed, this, &CoreProcess::daemonIpcClientConnectionFailed
|
||||
);
|
||||
connect(m_daemonIpcClient, &ipc::DaemonIpcClient::logPathReceived, this, &CoreProcess::setupDaemonLogTail);
|
||||
|
||||
connect(&m_retryTimer, &QTimer::timeout, this, [this] {
|
||||
if (m_processState == ProcessState::RetryPending) {
|
||||
|
|
@ -139,20 +141,7 @@ void CoreProcess::onProcessReadyReadStandardError()
|
|||
void CoreProcess::daemonIpcClientConnected()
|
||||
{
|
||||
applyLogLevel();
|
||||
|
||||
const auto logPath = requestDaemonLogPath();
|
||||
if (logPath.isEmpty()) {
|
||||
qWarning() << "daemon no log path";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "daemon log path:" << logPath;
|
||||
if (m_daemonFileTail) {
|
||||
m_daemonFileTail->setWatchedFile(logPath);
|
||||
} else {
|
||||
m_daemonFileTail = new FileTail(logPath, this);
|
||||
connect(m_daemonFileTail, &FileTail::newLine, this, &CoreProcess::handleLogLines);
|
||||
}
|
||||
m_daemonIpcClient->requestLogPath();
|
||||
}
|
||||
|
||||
void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus)
|
||||
|
|
@ -190,9 +179,7 @@ void CoreProcess::applyLogLevel()
|
|||
const auto processMode = Settings::value(Settings::Core::ProcessMode).value<Settings::ProcessMode>();
|
||||
if (processMode == ProcessMode::Service) {
|
||||
qDebug() << "setting daemon log level:" << Settings::logLevelText();
|
||||
if (!m_daemonIpcClient->sendLogLevel(Settings::logLevelText())) {
|
||||
qWarning() << "failed to set daemon ipc log level";
|
||||
}
|
||||
m_daemonIpcClient->sendLogLevel(Settings::logLevelText());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -234,15 +221,22 @@ void CoreProcess::startProcessFromDaemon(const QStringList &args)
|
|||
}
|
||||
|
||||
QString commandQuoted = makeQuotedArgs(m_appPath, args);
|
||||
|
||||
qInfo("running command: %s", qPrintable(commandQuoted));
|
||||
|
||||
if (!m_daemonIpcClient->sendStartProcess(commandQuoted, Settings::value(Settings::Daemon::Elevate).toBool())) {
|
||||
qWarning("cannot start process, ipc command failed");
|
||||
return;
|
||||
}
|
||||
|
||||
auto sendStart = [this, commandQuoted] {
|
||||
m_daemonIpcClient->sendStartProcess(commandQuoted, Settings::value(Settings::Daemon::Elevate).toBool());
|
||||
setProcessState(ProcessState::Started);
|
||||
};
|
||||
|
||||
if (m_daemonIpcClient->isConnected()) {
|
||||
sendStart();
|
||||
} else {
|
||||
connect(
|
||||
m_daemonIpcClient, &ipc::DaemonIpcClient::connected, this, sendStart,
|
||||
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection | Qt::QueuedConnection)
|
||||
);
|
||||
m_daemonIpcClient->connectToServer();
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::stopForegroundProcess() const
|
||||
|
|
@ -271,12 +265,20 @@ void CoreProcess::stopProcessFromDaemon()
|
|||
qFatal("core process must be in stopping state");
|
||||
}
|
||||
|
||||
if (!m_daemonIpcClient->sendStopProcess()) {
|
||||
qWarning("cannot stop process, ipc command failed");
|
||||
return;
|
||||
}
|
||||
|
||||
auto sendStop = [this] {
|
||||
m_daemonIpcClient->sendStopProcess();
|
||||
setProcessState(ProcessState::Stopped);
|
||||
};
|
||||
|
||||
if (m_daemonIpcClient->isConnected()) {
|
||||
sendStop();
|
||||
} else {
|
||||
connect(
|
||||
m_daemonIpcClient, &ipc::DaemonIpcClient::connected, this, sendStop,
|
||||
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection | Qt::QueuedConnection)
|
||||
);
|
||||
m_daemonIpcClient->connectToServer();
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::handleLogLines(const QString &text)
|
||||
|
|
@ -293,9 +295,23 @@ void CoreProcess::handleLogLines(const QString &text)
|
|||
if (line.contains("calling TIS/TSM in non-main thread environment")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// the core process is not allowed to show the permission prompt
|
||||
// (called "notification permission") and the notification log line is emitted from
|
||||
// deep inside cocoa code in the core binary to stdout, so it can't be sent over
|
||||
// ipc from the core to the gui and instead the gui has to parse the core output.
|
||||
static const QString needle = "OSX Notification: ";
|
||||
if (line.contains(needle) && line.contains('|')) {
|
||||
const int delimiterPosition = line.indexOf('|');
|
||||
const int start = line.indexOf(needle);
|
||||
const QString title = line.mid(start + needle.length(), delimiterPosition - start - needle.length());
|
||||
const QString body = line.mid(delimiterPosition + 1, line.length() - delimiterPosition);
|
||||
if (!showOSXNotification(title, body)) {
|
||||
qDebug("osx notification was not shown");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
checkLogLine(line);
|
||||
Q_EMIT logLine(line);
|
||||
}
|
||||
}
|
||||
|
|
@ -366,6 +382,33 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
|
|||
qInfo().noquote() << "log file:" << logFile;
|
||||
}
|
||||
|
||||
// Wired before the start calls so it catches Started from both sync (desktop) and async (service) paths.
|
||||
connect(
|
||||
this, &CoreProcess::processStateChanged, this,
|
||||
[this](ProcessState state) {
|
||||
if (state != ProcessState::Started) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delay briefly to give the core process time to start its IPC server.
|
||||
QTimer::singleShot(kRetryDelay, this, [this] {
|
||||
if (m_processState != ProcessState::Started) {
|
||||
return;
|
||||
}
|
||||
|
||||
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::connectionFailed, this, [] {
|
||||
qWarning("failed to establish core ipc connection");
|
||||
});
|
||||
|
||||
m_coreIpcClient->connectToServer();
|
||||
});
|
||||
},
|
||||
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection | Qt::QueuedConnection)
|
||||
);
|
||||
|
||||
if (processMode == ProcessMode::Desktop) {
|
||||
startForegroundProcess(args);
|
||||
} else if (processMode == ProcessMode::Service) {
|
||||
|
|
@ -373,21 +416,6 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -500,35 +528,27 @@ void CoreProcess::setProcessState(ProcessState state)
|
|||
Q_EMIT processStateChanged(state);
|
||||
}
|
||||
|
||||
void CoreProcess::checkLogLine(const QString &line)
|
||||
void CoreProcess::onCoreIpcMessageReceived(const QString &command, const QString &args)
|
||||
{
|
||||
using enum ConnectionState;
|
||||
|
||||
if (line.contains("connected to server") || line.contains("has connected")) {
|
||||
m_connections++;
|
||||
setConnectionState(Connected);
|
||||
} else if (line.contains("started server")) {
|
||||
m_connections = 0;
|
||||
setConnectionState(Listening);
|
||||
} else if (line.contains("disconnected from server") || line.contains("process exited")) {
|
||||
m_connections = 0;
|
||||
setConnectionState(Disconnected);
|
||||
} else if (line.contains("connecting to")) {
|
||||
setConnectionState(Connecting);
|
||||
} else if (line.contains("has disconnected")) {
|
||||
m_connections--;
|
||||
if (m_connections < 1) {
|
||||
setConnectionState(Listening);
|
||||
if (command == "connectionState") {
|
||||
const auto metaEnum = QMetaEnum::fromType<ConnectionState>();
|
||||
bool ok = false;
|
||||
const auto state = static_cast<ConnectionState>(metaEnum.keyToValue(args.toUtf8().constData(), &ok));
|
||||
if (!ok) {
|
||||
qWarning("core ipc got unknown connection state: %s", args.toUtf8().constData());
|
||||
return;
|
||||
}
|
||||
setConnectionState(state);
|
||||
} else if (command == "connectedClients") {
|
||||
const auto clients = args.isEmpty() ? QStringList() : args.split(",");
|
||||
Q_EMIT connectedClientsChanged(clients);
|
||||
} else if (command == "secureSocket") {
|
||||
Q_EMIT secureSocket(true);
|
||||
if (args != m_secureSocketVersion) {
|
||||
m_secureSocketVersion = args;
|
||||
Q_EMIT securityLevelChanged(args);
|
||||
}
|
||||
}
|
||||
|
||||
checkSecureSocket(line);
|
||||
|
||||
// server and client processes are not allowed to show notifications.
|
||||
// process the log from it and show notification from deskflow instead.
|
||||
#ifdef Q_OS_MACOS
|
||||
checkOSXNotification(line);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CoreProcess::checkSecureSocket(const QString &line)
|
||||
|
|
@ -548,46 +568,30 @@ bool CoreProcess::checkSecureSocket(const QString &line)
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
void CoreProcess::checkOSXNotification(const QString &line)
|
||||
{
|
||||
static const QString needle = "OSX Notification: ";
|
||||
if (line.contains(needle) && line.contains('|')) {
|
||||
int delimiterPosition = line.indexOf('|');
|
||||
int start = line.indexOf(needle);
|
||||
QString title = line.mid(start + needle.length(), delimiterPosition - start - needle.length());
|
||||
QString body = line.mid(delimiterPosition + 1, line.length() - delimiterPosition);
|
||||
if (!showOSXNotification(title, body)) {
|
||||
qDebug("osx notification was not shown");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QString CoreProcess::correctedAddress(const QString &address) const
|
||||
{
|
||||
return wrapIpv6(address.simplified());
|
||||
}
|
||||
|
||||
QString CoreProcess::requestDaemonLogPath()
|
||||
void CoreProcess::setupDaemonLogTail(const QString &logPath)
|
||||
{
|
||||
qDebug() << "requesting daemon log path";
|
||||
const auto logPath = m_daemonIpcClient->requestLogPath();
|
||||
if (logPath.isEmpty()) {
|
||||
qCritical() << "failed to get daemon log path";
|
||||
return QString();
|
||||
}
|
||||
qDebug() << "daemon log path:" << logPath;
|
||||
|
||||
if (QFileInfo logFile(logPath); !logFile.isFile()) {
|
||||
auto file = QFile(logPath);
|
||||
if (!file.open(QFile::ReadWrite)) {
|
||||
qCritical() << "daemon log path file can not be written:" << logPath;
|
||||
return QString();
|
||||
return;
|
||||
}
|
||||
file.write(""); // Create an empty file
|
||||
}
|
||||
|
||||
return logPath;
|
||||
if (m_daemonFileTail) {
|
||||
m_daemonFileTail->setWatchedFile(logPath);
|
||||
} else {
|
||||
m_daemonFileTail = new FileTail(logPath, this);
|
||||
connect(m_daemonFileTail, &FileTail::newLine, this, &CoreProcess::handleLogLines);
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::clearSettings()
|
||||
|
|
@ -608,9 +612,7 @@ void CoreProcess::clearSettings()
|
|||
|
||||
void CoreProcess::retryDaemon()
|
||||
{
|
||||
if (m_daemonIpcClient->connectToServer()) {
|
||||
qInfo("successfully reconnected to daemon");
|
||||
}
|
||||
m_daemonIpcClient->connectToServer();
|
||||
}
|
||||
|
||||
} // namespace deskflow::gui
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace deskflow::gui {
|
|||
namespace ipc {
|
||||
class CoreIpcClient;
|
||||
class DaemonIpcClient;
|
||||
}
|
||||
} // namespace ipc
|
||||
|
||||
class CoreProcess : public QObject
|
||||
{
|
||||
|
|
@ -89,12 +89,14 @@ Q_SIGNALS:
|
|||
void processStateChanged(deskflow::core::ProcessState state);
|
||||
void secureSocket(bool enabled);
|
||||
void daemonIpcClientConnectionFailed();
|
||||
void connectedClientsChanged(const QStringList &clients);
|
||||
void securityLevelChanged(QString securityLevel);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onProcessFinished(int exitCode, QProcess::ExitStatus);
|
||||
void onProcessReadyReadStandardOutput();
|
||||
void onProcessReadyReadStandardError();
|
||||
void onCoreIpcMessageReceived(const QString &command, const QString &args);
|
||||
void daemonIpcClientConnected();
|
||||
|
||||
private:
|
||||
|
|
@ -105,20 +107,15 @@ private:
|
|||
QPair<bool, QString> persistServerConfig() const;
|
||||
void setConnectionState(ConnectionState state);
|
||||
void setProcessState(ProcessState state);
|
||||
void checkLogLine(const QString &line);
|
||||
bool checkSecureSocket(const QString &line);
|
||||
void handleLogLines(const QString &text);
|
||||
QString correctedAddress(const QString &address) const;
|
||||
QString requestDaemonLogPath();
|
||||
void setupDaemonLogTail(const QString &logPath);
|
||||
static QString makeQuotedArgs(const QString &app, const QStringList &args);
|
||||
static QString processModeToString(const Settings::ProcessMode mode);
|
||||
static QString processStateToString(const CoreProcess::ProcessState state);
|
||||
static QString wrapIpv6(const QString &address);
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
void checkOSXNotification(const QString &line);
|
||||
#endif
|
||||
|
||||
const IServerConfig &m_serverConfig;
|
||||
QString m_address;
|
||||
ProcessState m_processState = ProcessState::Stopped;
|
||||
|
|
@ -128,7 +125,6 @@ private:
|
|||
QString m_secureSocketVersion;
|
||||
std::optional<ProcessMode> m_lastProcessMode = std::nullopt;
|
||||
QTimer m_retryTimer;
|
||||
int m_connections = 0;
|
||||
deskflow::gui::ipc::CoreIpcClient *m_coreIpcClient = nullptr;
|
||||
deskflow::gui::ipc::DaemonIpcClient *m_daemonIpcClient = nullptr;
|
||||
FileTail *m_daemonFileTail = nullptr;
|
||||
|
|
|
|||
|
|
@ -15,11 +15,15 @@
|
|||
|
||||
namespace deskflow::gui::ipc {
|
||||
|
||||
const auto kTimeout = 1000;
|
||||
|
||||
CoreIpcClient::CoreIpcClient(QObject *parent) : IpcClient(parent, kCoreIpcName)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CoreIpcClient::processCommand(const QString &command, const QStringList &parts)
|
||||
{
|
||||
const auto args = parts.size() >= 2 ? parts[1] : QString();
|
||||
Q_EMIT commandReceived(command, args);
|
||||
}
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ class CoreIpcClient : public IpcClient
|
|||
|
||||
public:
|
||||
explicit CoreIpcClient(QObject *parent = nullptr);
|
||||
|
||||
Q_SIGNALS:
|
||||
void commandReceived(const QString &command, const QString &args);
|
||||
|
||||
protected:
|
||||
void processCommand(const QString &command, const QStringList &parts) override;
|
||||
};
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
|
|
|
|||
|
|
@ -9,93 +9,46 @@
|
|||
#include "common/Constants.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLocalSocket>
|
||||
#include <QMutexLocker>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
namespace deskflow::gui::ipc {
|
||||
|
||||
const auto kTimeout = 1000;
|
||||
|
||||
DaemonIpcClient::DaemonIpcClient(QObject *parent) : IpcClient(parent, kDaemonIpcName)
|
||||
{
|
||||
}
|
||||
|
||||
bool DaemonIpcClient::sendLogLevel(const QString &logLevel)
|
||||
void DaemonIpcClient::sendLogLevel(const QString &logLevel)
|
||||
{
|
||||
if (!keepAlive())
|
||||
return false;
|
||||
|
||||
sendMessage("logLevel=" + logLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DaemonIpcClient::sendStartProcess(const QString &command, bool elevate)
|
||||
void DaemonIpcClient::sendStartProcess(const QString &command, bool elevate)
|
||||
{
|
||||
if (!keepAlive())
|
||||
return false;
|
||||
|
||||
if (!sendMessage("elevate=" + (elevate ? QStringLiteral("yes") : QStringLiteral("no")))) {
|
||||
return false;
|
||||
const auto elevateStr = elevate ? QStringLiteral("yes") : QStringLiteral("no");
|
||||
sendMessage("elevate=" + elevateStr);
|
||||
sendMessage("command=" + command);
|
||||
sendMessage("start");
|
||||
}
|
||||
|
||||
if (!sendMessage("command=" + command)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sendMessage("start");
|
||||
}
|
||||
|
||||
bool DaemonIpcClient::sendStopProcess()
|
||||
void DaemonIpcClient::sendStopProcess()
|
||||
{
|
||||
return sendMessage("stop");
|
||||
sendMessage("stop");
|
||||
}
|
||||
|
||||
QString DaemonIpcClient::requestLogPath()
|
||||
void DaemonIpcClient::sendClearSettings()
|
||||
{
|
||||
if (!keepAlive())
|
||||
return QString();
|
||||
|
||||
if (!sendMessage("logPath", QString())) {
|
||||
return QString();
|
||||
sendMessage("clearSettings");
|
||||
}
|
||||
|
||||
if (!socket()->waitForReadyRead(kTimeout)) {
|
||||
qWarning() << "daemon ipc client failed to read log path response";
|
||||
return QString();
|
||||
}
|
||||
|
||||
QByteArray response = socket()->readAll();
|
||||
if (response.isEmpty()) {
|
||||
qWarning() << "daemon ipc client got empty log path response";
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString responseData = QString::fromUtf8(response);
|
||||
if (responseData.isEmpty()) {
|
||||
qWarning() << "daemon ipc client failed to convert log path response to string";
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Trimming removes newline from end of message.
|
||||
QStringList parts = responseData.trimmed().split("=");
|
||||
if (parts.size() != 2) {
|
||||
qWarning() << "daemon ipc client got invalid log path response:" << responseData;
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (parts[0] != "logPath") {
|
||||
qWarning() << "daemon ipc client got unexpected log path response:" << responseData;
|
||||
return QString();
|
||||
}
|
||||
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
bool DaemonIpcClient::sendClearSettings()
|
||||
void DaemonIpcClient::requestLogPath()
|
||||
{
|
||||
return sendMessage("clearSettings");
|
||||
sendMessage("logPath");
|
||||
}
|
||||
|
||||
void DaemonIpcClient::processCommand(const QString &command, const QStringList &parts)
|
||||
{
|
||||
if (command == "logPath" && parts.size() == 2) {
|
||||
Q_EMIT logPathReceived(parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
|
|
|
|||
|
|
@ -18,11 +18,17 @@ class DaemonIpcClient : public IpcClient
|
|||
|
||||
public:
|
||||
explicit DaemonIpcClient(QObject *parent = nullptr);
|
||||
bool sendLogLevel(const QString &logLevel);
|
||||
bool sendStartProcess(const QString &command, bool elevate);
|
||||
bool sendStopProcess();
|
||||
bool sendClearSettings();
|
||||
QString requestLogPath();
|
||||
void sendLogLevel(const QString &logLevel);
|
||||
void sendStartProcess(const QString &command, bool elevate);
|
||||
void sendStopProcess();
|
||||
void sendClearSettings();
|
||||
void requestLogPath();
|
||||
|
||||
Q_SIGNALS:
|
||||
void logPathReceived(const QString &logPath);
|
||||
|
||||
protected:
|
||||
void processCommand(const QString &command, const QStringList &parts) override;
|
||||
};
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QLocalSocket>
|
||||
#include <QMutexLocker>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
namespace deskflow::gui::ipc {
|
||||
|
||||
|
|
@ -24,13 +22,14 @@ IpcClient::IpcClient(QObject *parent, const QString &socketName)
|
|||
{
|
||||
connect(m_socket, &QLocalSocket::disconnected, this, &IpcClient::handleDisconnected);
|
||||
connect(m_socket, &QLocalSocket::errorOccurred, this, &IpcClient::handleErrorOccurred);
|
||||
connect(m_socket, &QLocalSocket::readyRead, this, &IpcClient::handleReadyRead);
|
||||
}
|
||||
|
||||
bool IpcClient::connectToServer()
|
||||
void IpcClient::connectToServer()
|
||||
{
|
||||
if (m_state == State::Connecting) {
|
||||
qWarning() << "ipc client already connecting to server";
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state != State::Unconnected) {
|
||||
|
|
@ -43,139 +42,129 @@ bool IpcClient::connectToServer()
|
|||
disconnectFromServer();
|
||||
}
|
||||
|
||||
for (int i = 0; i < kRetryLimit; ++i) {
|
||||
if (i == 0) {
|
||||
m_retryCount = 0;
|
||||
attemptConnection();
|
||||
}
|
||||
|
||||
void IpcClient::attemptConnection()
|
||||
{
|
||||
if (m_retryCount >= kRetryLimit) {
|
||||
qWarning() << "ipc client failed to connect after" << kRetryLimit << "attempts";
|
||||
m_state = State::Unconnected;
|
||||
Q_EMIT connectionFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_retryCount == 0) {
|
||||
qDebug() << "ipc client connecting to server:" << m_socketName;
|
||||
} else {
|
||||
qDebug() << "ipc client retrying connection, attempt:" << i + 1;
|
||||
qDebug() << "ipc client retrying connection, attempt:" << m_retryCount + 1;
|
||||
}
|
||||
|
||||
m_state = State::Connecting;
|
||||
m_retryCount++;
|
||||
|
||||
connect(
|
||||
m_socket, &QLocalSocket::connected, this,
|
||||
[this] {
|
||||
m_socket->write("hello\n");
|
||||
qDebug() << "ipc client sent hello";
|
||||
},
|
||||
Qt::SingleShotConnection
|
||||
);
|
||||
|
||||
connect(
|
||||
m_socket, &QLocalSocket::errorOccurred, this,
|
||||
[this] {
|
||||
qWarning() << "ipc client failed to connect:" << m_socket->errorString();
|
||||
m_socket->disconnectFromServer();
|
||||
m_state = State::Unconnected;
|
||||
QTimer::singleShot(0, this, &IpcClient::attemptConnection);
|
||||
},
|
||||
Qt::SingleShotConnection
|
||||
);
|
||||
|
||||
m_socket->connectToServer(m_socketName);
|
||||
|
||||
if (!m_socket->waitForConnected(kTimeout)) {
|
||||
qWarning() << "ipc client failed to connect";
|
||||
disconnectFromServer();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sendMessage("hello", "hello", false)) {
|
||||
qWarning() << "ipc client failed to send hello";
|
||||
disconnectFromServer();
|
||||
continue;
|
||||
}
|
||||
|
||||
m_state = State::Connected;
|
||||
qDebug() << "ipc client connected";
|
||||
Q_EMIT connected();
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << "ipc client failed to connect after" << kRetryLimit << "attempts";
|
||||
disconnectFromServer();
|
||||
Q_EMIT connectionFailed();
|
||||
return false;
|
||||
}
|
||||
|
||||
void IpcClient::disconnectFromServer()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
m_state = State::Disconnecting;
|
||||
qDebug() << "ipc client disconnecting from server";
|
||||
m_socket->disconnectFromServer();
|
||||
|
||||
if (m_socket->state() != QLocalSocket::UnconnectedState) {
|
||||
qDebug() << "ipc client waiting for socket to disconnect";
|
||||
m_socket->waitForDisconnected(kTimeout);
|
||||
qDebug() << "ipc client disconnected from server";
|
||||
} else {
|
||||
qDebug() << "ipc client socket already disconnected";
|
||||
}
|
||||
|
||||
m_state = State::Unconnected;
|
||||
}
|
||||
|
||||
void IpcClient::handleDisconnected()
|
||||
{
|
||||
qDebug() << "ipc client disconnected from server";
|
||||
if (m_state == State::Connected) {
|
||||
Q_EMIT connectionFailed();
|
||||
if (m_state == State::Connecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "ipc client disconnected from server";
|
||||
const auto wasConnected = m_state == State::Connected;
|
||||
m_state = State::Unconnected;
|
||||
|
||||
if (wasConnected) {
|
||||
Q_EMIT connectionFailed();
|
||||
}
|
||||
}
|
||||
|
||||
void IpcClient::handleErrorOccurred()
|
||||
{
|
||||
if (m_state == State::Connecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
qWarning() << "ipc client error:" << m_socket->errorString();
|
||||
disconnectFromServer();
|
||||
|
||||
if (m_state == State::Connected) {
|
||||
disconnectFromServer();
|
||||
Q_EMIT connectionFailed();
|
||||
}
|
||||
}
|
||||
|
||||
bool IpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
|
||||
void IpcClient::handleReadyRead()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
QByteArray data = m_readBuffer + m_socket->readAll();
|
||||
m_readBuffer.clear();
|
||||
|
||||
if (expectConnected && !isConnected()) {
|
||||
while (data.contains('\n')) {
|
||||
const auto index = data.indexOf('\n');
|
||||
const auto message = QString::fromUtf8(data.left(index));
|
||||
data.remove(0, index + 1);
|
||||
|
||||
qDebug("ipc client message: %s", message.toUtf8().constData());
|
||||
const auto parts = message.split('=');
|
||||
if (parts.isEmpty()) {
|
||||
qWarning("ipc client got invalid message: %s", message.toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_state == State::Connecting && parts.at(0) == "hello") {
|
||||
m_state = State::Connected;
|
||||
qDebug() << "ipc client connected";
|
||||
Q_EMIT connected();
|
||||
continue;
|
||||
}
|
||||
|
||||
processCommand(parts.at(0), parts);
|
||||
}
|
||||
|
||||
if (!data.isEmpty()) {
|
||||
m_readBuffer = data;
|
||||
}
|
||||
}
|
||||
|
||||
void IpcClient::sendMessage(const QString &message)
|
||||
{
|
||||
if (m_state != State::Connected) {
|
||||
qWarning() << "cannot send command, ipc client not connected";
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray messageData = message.toUtf8() + "\n";
|
||||
m_socket->write(messageData);
|
||||
if (!m_socket->waitForBytesWritten(kTimeout)) {
|
||||
qWarning() << "ipc client failed to write command";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!expectAck.isEmpty()) {
|
||||
qDebug() << "ipc client waiting for ack:" << expectAck;
|
||||
|
||||
if (!m_socket->waitForReadyRead(kTimeout)) {
|
||||
qWarning() << "ipc client socket ready read timed out";
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray response = m_socket->readAll();
|
||||
if (response.isEmpty()) {
|
||||
qWarning() << "ipc client got empty response";
|
||||
return false;
|
||||
}
|
||||
|
||||
QString responseData = QString::fromUtf8(response);
|
||||
if (responseData.isEmpty()) {
|
||||
qWarning() << "ipc client failed to convert response to string";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (responseData != expectAck + "\n") {
|
||||
qWarning() << "ipc client got unexpected response:" << responseData;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "ipc client sent message:" << messageData;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IpcClient::keepAlive()
|
||||
{
|
||||
if (!isConnected() && !connectToServer()) {
|
||||
qWarning() << "ipc client keep alive failed to connect";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sendMessage("noop")) {
|
||||
qWarning() << "ipc client keep alive ping failed, reconnecting";
|
||||
connectToServer();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
m_socket->write(message.toUtf8() + "\n");
|
||||
qDebug() << "ipc client sent message:" << message;
|
||||
}
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
|
||||
class QLocalSocket;
|
||||
|
|
@ -28,7 +27,7 @@ class IpcClient : public QObject
|
|||
|
||||
public:
|
||||
explicit IpcClient(QObject *parent, const QString &socketName);
|
||||
bool connectToServer();
|
||||
void connectToServer();
|
||||
void disconnectFromServer();
|
||||
|
||||
bool isConnected() const
|
||||
|
|
@ -43,21 +42,25 @@ Q_SIGNALS:
|
|||
private Q_SLOTS:
|
||||
void handleDisconnected();
|
||||
void handleErrorOccurred();
|
||||
void handleReadyRead();
|
||||
|
||||
protected:
|
||||
bool keepAlive();
|
||||
bool sendMessage(const QString &message, const QString &expectAck = "ok", const bool expectConnected = true);
|
||||
|
||||
QLocalSocket *socket() const
|
||||
virtual void processCommand(const QString &command, const QStringList &parts)
|
||||
{
|
||||
return m_socket;
|
||||
Q_UNUSED(command)
|
||||
Q_UNUSED(parts)
|
||||
}
|
||||
|
||||
void sendMessage(const QString &message);
|
||||
|
||||
private:
|
||||
void attemptConnection();
|
||||
|
||||
QLocalSocket *m_socket;
|
||||
State m_state{State::Unconnected};
|
||||
QString m_socketName;
|
||||
QMutex m_mutex;
|
||||
QByteArray m_readBuffer;
|
||||
int m_retryCount{0};
|
||||
};
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "base/IEventQueue.h"
|
||||
#include "base/Log.h"
|
||||
#include "common/Settings.h"
|
||||
#include "deskflow/ipc/CoreIpc.h"
|
||||
#include "mt/Lock.h"
|
||||
#include "net/FingerprintDatabase.h"
|
||||
#include "net/TCPSocket.h"
|
||||
|
|
@ -625,8 +626,9 @@ bool SecureSocket::verifyCertFingerprint(const QString &FingerprintDatabasePath)
|
|||
if (!sha256.isValid())
|
||||
return false;
|
||||
|
||||
// Gui Must Parse this line, DO NOT CHANGE
|
||||
LOG_IPC("peer fingerprint: %s", qPrintable(deskflow::formatSSLFingerprint(sha256.data, false)));
|
||||
const auto fingerprint = deskflow::formatSSLFingerprint(sha256.data, false);
|
||||
LOG_DEBUG("peer fingerprint: %s", qPrintable(fingerprint));
|
||||
ipcSendToClient("peerFingerprint", fingerprint);
|
||||
|
||||
QFile file(FingerprintDatabasePath);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <sstream>
|
||||
|
||||
#include <base/Log.h>
|
||||
#include <deskflow/ipc/CoreIpc.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
|
|
@ -105,13 +106,12 @@ void SslLogger::logSecureConnectInfo(const SSL *ssl)
|
|||
std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}
|
||||
};
|
||||
if (parts.size() > 2) {
|
||||
// log the section containing the protocol version
|
||||
LOG_INFO("network encryption protocol: %s", parts[1].c_str());
|
||||
LOG_DEBUG("network encryption protocol: %s", parts[1].c_str());
|
||||
ipcSendToClient("secureSocket", parts[1].c_str());
|
||||
} else {
|
||||
// log the error in spliting then display the whole description rather
|
||||
// then nothing
|
||||
LOG_ERR("could not split cipher for protocol");
|
||||
LOG_INFO("network encryption protocol: %s", msg);
|
||||
LOG_DEBUG("network encryption protocol: %s", msg);
|
||||
ipcSendToClient("secureSocket", msg);
|
||||
}
|
||||
} else {
|
||||
LOG_ERR("could not get secure socket cipher");
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ bool ClientProxy1_0::parseMessage(const uint8_t *code)
|
|||
|
||||
void ClientProxy1_0::handleDisconnect()
|
||||
{
|
||||
LOG_IPC("client \"%s\" has disconnected", getName().c_str());
|
||||
LOG_DEBUG("client \"%s\" has disconnected", getName().c_str());
|
||||
disconnect();
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ void ClientProxy1_0::handleWriteError()
|
|||
void ClientProxy1_0::handleFlatline()
|
||||
{
|
||||
// didn't get a heartbeat fast enough. assume client is dead.
|
||||
LOG_IPC("client \"%s\" is dead", getName().c_str());
|
||||
LOG_DEBUG("client \"%s\" is dead", getName().c_str());
|
||||
disconnect();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "base/IEventQueue.h"
|
||||
#include "base/Log.h"
|
||||
#include "deskflow/AppUtil.h"
|
||||
#include "deskflow/ipc/CoreIpc.h"
|
||||
#include "deskflow/DeskflowException.h"
|
||||
#include "deskflow/IPlatformScreen.h"
|
||||
#include "deskflow/OptionTypes.h"
|
||||
|
|
@ -233,7 +234,7 @@ void Server::adoptClient(BaseClientProxy *client)
|
|||
|
||||
// name must be in our configuration
|
||||
if (!m_config->isScreen(client->getName())) {
|
||||
LOG_IPC("unrecognised client name \"%s\", check server config", client->getName().c_str());
|
||||
LOG_WARN("unrecognised client name \"%s\", check server config", client->getName().c_str());
|
||||
closeClient(client, kMsgEUnknown);
|
||||
return;
|
||||
}
|
||||
|
|
@ -245,7 +246,9 @@ void Server::adoptClient(BaseClientProxy *client)
|
|||
closeClient(client, kMsgEBusy);
|
||||
return;
|
||||
}
|
||||
LOG_IPC("client \"%s\" has connected", getName(client).c_str());
|
||||
LOG_DEBUG("client \"%s\" has connected", getName(client).c_str());
|
||||
ipcSendConnectionState(deskflow::core::ConnectionState::Connected);
|
||||
sendConnectedClientsIpc();
|
||||
|
||||
// send configuration options to client
|
||||
sendOptions(client);
|
||||
|
|
@ -291,6 +294,18 @@ void Server::getClients(std::vector<std::string> &list) const
|
|||
}
|
||||
}
|
||||
|
||||
void Server::sendConnectedClientsIpc() const
|
||||
{
|
||||
const auto primaryName = getName(m_primaryClient);
|
||||
QStringList clientList;
|
||||
for (const auto &[name, _] : m_clients) {
|
||||
if (name != primaryName) {
|
||||
clientList.append(QString::fromStdString(name));
|
||||
}
|
||||
}
|
||||
ipcSendToClient("connectedClients", clientList.join(","));
|
||||
}
|
||||
|
||||
std::string Server::getName(const BaseClientProxy *client) const
|
||||
{
|
||||
std::string name = m_config->getCanonicalName(client->getName());
|
||||
|
|
@ -1285,6 +1300,11 @@ void Server::handleClientDisconnected(BaseClientProxy *client)
|
|||
removeActiveClient(client);
|
||||
removeOldClient(client);
|
||||
|
||||
// m_clients always contains the primary (server) screen, so 1 means no remote clients.
|
||||
using enum deskflow::core::ConnectionState;
|
||||
ipcSendConnectionState(m_clients.size() <= 1 ? Listening : Connected);
|
||||
sendConnectedClientsIpc();
|
||||
|
||||
delete client;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ public:
|
|||
Set the \c list to the names of the currently connected clients.
|
||||
*/
|
||||
void getClients(std::vector<std::string> &list) const;
|
||||
void sendConnectedClientsIpc() const;
|
||||
|
||||
//@}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue