refactor: Send log level to daemon over IPC instead of when sending command

This commit is contained in:
Nick Bolton 2025-02-07 17:02:20 +00:00
parent 80cc323f5a
commit d6228416a1
8 changed files with 97 additions and 41 deletions

View file

@ -296,6 +296,7 @@ void MainWindow::connectSlots()
connect(&m_appConfig, &AppConfig::tlsChanged, this, &MainWindow::appConfigTlsChanged); connect(&m_appConfig, &AppConfig::tlsChanged, this, &MainWindow::appConfigTlsChanged);
connect(&m_appConfig, &AppConfig::screenNameChanged, this, &MainWindow::updateScreenName); connect(&m_appConfig, &AppConfig::screenNameChanged, this, &MainWindow::updateScreenName);
connect(&m_appConfig, &AppConfig::logLevelChanged, &m_coreProcess, &CoreProcess::applyLogLevel);
connect(&m_coreProcess, &CoreProcess::starting, this, &MainWindow::coreProcessStarting, Qt::DirectConnection); connect(&m_coreProcess, &CoreProcess::starting, this, &MainWindow::coreProcessStarting, Qt::DirectConnection);
connect(&m_coreProcess, &CoreProcess::error, this, &MainWindow::coreProcessError); connect(&m_coreProcess, &CoreProcess::error, this, &MainWindow::coreProcessError);
@ -631,6 +632,8 @@ void MainWindow::open()
qDebug() << "update check disabled"; qDebug() << "update check disabled";
} }
m_coreProcess.applyLogLevel();
if (m_appConfig.startedBefore()) { if (m_appConfig.startedBefore()) {
m_coreProcess.start(); m_coreProcess.start();
} }

View file

@ -14,6 +14,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QVariant> #include <QVariant>
#include <functional> #include <functional>
using namespace deskflow::gui; using namespace deskflow::gui;
@ -680,7 +681,10 @@ void AppConfig::setNetworkInterface(const QString &s)
void AppConfig::setLogLevel(int i) void AppConfig::setLogLevel(int i)
{ {
const auto changed = (m_LogLevel != i);
m_LogLevel = i; m_LogLevel = i;
if (changed)
Q_EMIT logLevelChanged();
} }
void AppConfig::setLogToFile(bool b) void AppConfig::setLogToFile(bool b)

View file

@ -342,4 +342,5 @@ private:
signals: signals:
void tlsChanged(); void tlsChanged();
void screenNameChanged(); void screenNameChanged();
void logLevelChanged();
}; };

View file

@ -1,6 +1,6 @@
/* /*
* Deskflow -- mouse and keyboard sharing utility * Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2016 Symless Ltd. * SPDX-FileCopyrightText: (C) 2016 - 2025 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/ */
@ -9,20 +9,20 @@
/** /**
* @brief The elevate mode tristate determines two behaviors on Windows. * @brief The elevate mode tristate determines two behaviors on Windows.
* *
* The first, switch-on-desk-switch (SodS), passed through deskflowd as a
* command line argument to deskflow core, determines if the server restarts
* when switching Windows desktops (e.g. when Windows UAC dialog pops up).
* The second, passed as a boolean flag to Deskflowd over the IPC inside
* IpcMessageType::CommandMessage, determines whether Deskflow should be started
* with elevated privileges.
*
* The matrix for these two behaviors is as follows: * The matrix for these two behaviors is as follows:
* *
* | SodS | Elevate | * | sods | elevate |
* |-----------|------------| * |-----------|------------|
* kAutomatic | true | false | * kAutomatic | true | false |
* kAlways | false | true | * kAlways | false | true |
* kNever | false | false | * kNever | false | false |
*
* The first, --stop-on-desk-switch (sods), is passed through the daemon as a
* command line argument to the server/client, and determines if it restarts
* when switching Windows desktops (e.g. when Windows UAC dialog pops up).
*
* The second, elevate, is passed as a boolean flag to the daemon over IPC,
* and determines whether the server/client should be started with elevated privileges.
*/ */
enum class ElevateMode enum class ElevateMode
{ {

View file

@ -242,6 +242,16 @@ void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus)
} }
} }
void CoreProcess::applyLogLevel()
{
if (m_appConfig.processMode() == ProcessMode::kService) {
qDebug() << "setting daemon log level:" << m_appConfig.logLevelText();
if (!m_daemonIpcClient->sendLogLevel(m_appConfig.logLevelText())) {
qCritical() << "failed to set daemon ipc log level";
}
}
}
void CoreProcess::startForegroundProcess(const QString &app, const QStringList &args) void CoreProcess::startForegroundProcess(const QString &app, const QStringList &args)
{ {
using enum ProcessState; using enum ProcessState;
@ -280,7 +290,12 @@ void CoreProcess::startProcessFromDaemon(const QString &app, const QStringList &
QString commandQuoted = makeQuotedArgs(app, args); QString commandQuoted = makeQuotedArgs(app, args);
qInfo("running command: %s", qPrintable(commandQuoted)); qInfo("running command: %s", qPrintable(commandQuoted));
m_daemonIpcClient->sendCommand(commandQuoted, m_appConfig.elevateMode());
if (!m_daemonIpcClient->sendCommand(commandQuoted, m_appConfig.elevateMode())) {
qCritical("aborting process start, ipc connect failed");
return;
}
setProcessState(Started); setProcessState(Started);
} }

View file

@ -90,6 +90,7 @@ public:
void stop(std::optional<ProcessMode> processMode = std::nullopt); void stop(std::optional<ProcessMode> processMode = std::nullopt);
void restart(); void restart();
void cleanup(); void cleanup();
void applyLogLevel();
// getters // getters
Mode mode() const Mode mode() const

View file

@ -11,6 +11,7 @@
#include <QDebug> #include <QDebug>
#include <QLocalSocket> #include <QLocalSocket>
#include <QObject> #include <QObject>
#include <QString>
namespace deskflow::gui::ipc { namespace deskflow::gui::ipc {
@ -20,27 +21,27 @@ DaemonIpcClient::DaemonIpcClient(QObject *parent) : QObject(parent), m_socket{ne
{ {
} }
DaemonIpcClient::~DaemonIpcClient() bool DaemonIpcClient::connectToServer()
{
}
void DaemonIpcClient::connectToServer()
{ {
qInfo("connecting to daemon ipc"); qInfo("connecting to daemon ipc");
m_socket->connectToServer(kDaemonIpcName); m_socket->connectToServer(kDaemonIpcName);
if (!m_socket->waitForConnected(kTimeout)) { if (!m_socket->waitForConnected(kTimeout)) {
qCritical() << "ipc client failed to connect to server:" << kDaemonIpcName; qWarning() << "ipc client failed to connect to server:" << kDaemonIpcName;
return; return false;
} }
sendMessage("hello", "hello", false); if (!sendMessage("hello", "hello", false)) {
qWarning() << "ipc client failed to send hello";
return false;
}
connect(m_socket, &QLocalSocket::disconnected, this, &DaemonIpcClient::handleDisconnected); connect(m_socket, &QLocalSocket::disconnected, this, &DaemonIpcClient::handleDisconnected);
connect(m_socket, &QLocalSocket::errorOccurred, this, &DaemonIpcClient::handleErrorOccurred); connect(m_socket, &QLocalSocket::errorOccurred, this, &DaemonIpcClient::handleErrorOccurred);
m_connected = true; m_connected = true;
qInfo() << "ipc client connected to server:" << kDaemonIpcName; qInfo() << "ipc client connected to server:" << kDaemonIpcName;
return true;
} }
void DaemonIpcClient::handleDisconnected() void DaemonIpcClient::handleDisconnected()
@ -55,50 +56,80 @@ void DaemonIpcClient::handleErrorOccurred()
m_connected = false; m_connected = false;
} }
void DaemonIpcClient::sendCommand(const QString &command, ElevateMode elevateMode) bool DaemonIpcClient::keepAlive()
{ {
sendMessage("elevate=" + QString::number(static_cast<int>(elevateMode))); if (!isConnected() && !connectToServer()) {
sendMessage("command=" + command); qWarning() << "ipc client keep alive failed to connect";
sendMessage("restart"); return false;
}
if (!sendMessage("noop")) {
qWarning() << "ipc client keep alive ping failed";
m_connected = false;
return false;
}
return true;
} }
void DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected) bool DaemonIpcClient::sendLogLevel(const QString &logLevel)
{
if (!keepAlive())
return false;
sendMessage("logLevel=" + logLevel);
return true;
}
bool DaemonIpcClient::sendCommand(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;
}
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
{ {
if (expectConnected && !m_connected) { if (expectConnected && !m_connected) {
qCritical() << "cannot send command, ipc not connected"; qWarning() << "cannot send command, ipc not connected";
return; return false;
} }
QByteArray messageData = message.toUtf8() + "\n"; QByteArray messageData = message.toUtf8() + "\n";
m_socket->write(messageData); m_socket->write(messageData);
if (!m_socket->waitForBytesWritten(kTimeout)) { if (!m_socket->waitForBytesWritten(kTimeout)) {
qCritical() << "ipc client failed to write command"; qWarning() << "ipc client failed to write command";
return; return false;
} }
if (!m_socket->waitForReadyRead(kTimeout)) { if (!m_socket->waitForReadyRead(kTimeout)) {
qCritical() << "ipc client failed to read response"; qWarning() << "ipc client failed to read response";
return; return false;
} }
QByteArray response = m_socket->readAll(); QByteArray response = m_socket->readAll();
if (response.isEmpty()) { if (response.isEmpty()) {
qCritical() << "ipc client got empty response"; qWarning() << "ipc client got empty response";
return; return false;
} }
QString responseData = QString::fromUtf8(response); QString responseData = QString::fromUtf8(response);
if (responseData.isEmpty()) { if (responseData.isEmpty()) {
qCritical() << "ipc client failed to convert response to string"; qWarning() << "ipc client failed to convert response to string";
return; return false;
} }
if (responseData != expectAck + "\n") { if (responseData != expectAck + "\n") {
qCritical() << "ipc client got unexpected response: " << responseData; qWarning() << "ipc client got unexpected response: " << responseData;
return; return false;
} }
qInfo() << "ipc client sent message: " << messageData; qDebug() << "ipc client sent message: " << messageData;
return true;
} }
} // namespace deskflow::gui::ipc } // namespace deskflow::gui::ipc

View file

@ -19,10 +19,10 @@ class DaemonIpcClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
DaemonIpcClient(QObject *parent = nullptr); explicit DaemonIpcClient(QObject *parent = nullptr);
~DaemonIpcClient(); bool connectToServer();
void connectToServer(); bool sendLogLevel(const QString &logLevel);
void sendCommand(const QString &command, ElevateMode elevateMode); bool sendCommand(const QString &command, ElevateMode elevateMode);
bool isConnected() const bool isConnected() const
{ {
@ -34,7 +34,8 @@ private slots:
void handleErrorOccurred(); void handleErrorOccurred();
private: private:
void sendMessage(const QString &message, const QString &expectAck = "ok", const bool expectConnected = true); bool keepAlive();
bool sendMessage(const QString &message, const QString &expectAck = "ok", const bool expectConnected = true);
private: private:
QLocalSocket *m_socket; QLocalSocket *m_socket;