feat: Tail daemon log file instead of using IPC log outputter
This commit is contained in:
parent
5980fb741b
commit
5733541b2a
11 changed files with 202 additions and 39 deletions
|
|
@ -27,6 +27,8 @@
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
using namespace deskflow::core;
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
|
|
@ -51,13 +53,11 @@ int main(int argc, char **argv)
|
||||||
using enum DaemonApp::InitResult;
|
using enum DaemonApp::InitResult;
|
||||||
|
|
||||||
case StartDaemon: {
|
case StartDaemon: {
|
||||||
using namespace deskflow::core;
|
|
||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
// Thread must be heap-allocated for deferred deletion on thread exit.
|
// Thread must be heap-allocated for deferred deletion on thread exit.
|
||||||
// Avoid setting Qt ownership to prevent premature deletion (thread may run longer than Qt loop).
|
// Avoid setting Qt ownership to prevent premature deletion (thread may run longer than Qt loop).
|
||||||
auto *pDaemonThread = new QThread(); // NOSONAR
|
auto *pDaemonThread = new QThread(); // NOSONAR - Qt memory
|
||||||
pDaemon->moveToThread(pDaemonThread);
|
pDaemon->moveToThread(pDaemonThread);
|
||||||
|
|
||||||
QObject::connect(pDaemonThread, &QThread::started, pDaemon, &DaemonApp::run);
|
QObject::connect(pDaemonThread, &QThread::started, pDaemon, &DaemonApp::run);
|
||||||
|
|
@ -66,7 +66,7 @@ int main(int argc, char **argv)
|
||||||
QObject::connect(pDaemonThread, &QThread::finished, QCoreApplication::instance(), &QCoreApplication::quit);
|
QObject::connect(pDaemonThread, &QThread::finished, QCoreApplication::instance(), &QCoreApplication::quit);
|
||||||
|
|
||||||
// The daemon app is on it's own thread which doesn't have a Qt event loop, so we need to use direct connection.
|
// The daemon app is on it's own thread which doesn't have a Qt event loop, so we need to use direct connection.
|
||||||
auto *ipcServer = new ipc::DaemonIpcServer(&app); // NOSONAR
|
auto *ipcServer = new ipc::DaemonIpcServer(&app, QString::fromStdString(pDaemon->logFilename())); // NOSONAR
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
ipcServer, &ipc::DaemonIpcServer::logLevelChanged, pDaemon, &DaemonApp::saveLogLevel, //
|
ipcServer, &ipc::DaemonIpcServer::logLevelChanged, pDaemon, &DaemonApp::saveLogLevel, //
|
||||||
Qt::DirectConnection
|
Qt::DirectConnection
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@ public:
|
||||||
void applyWatchdogCommand() const;
|
void applyWatchdogCommand() const;
|
||||||
void clearWatchdogCommand();
|
void clearWatchdogCommand();
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
std::string logFilename();
|
||||||
|
|
||||||
static DaemonApp &instance()
|
static DaemonApp &instance()
|
||||||
{
|
{
|
||||||
static DaemonApp instance; // NOSONAR - Meyers' Singleton
|
static DaemonApp instance; // NOSONAR - Meyers' Singleton
|
||||||
|
|
@ -69,7 +72,6 @@ private:
|
||||||
|
|
||||||
void daemonize();
|
void daemonize();
|
||||||
void handleError(const char *message);
|
void handleError(const char *message);
|
||||||
std::string logFilename();
|
|
||||||
void handleIpcMessage(const Event &e, void *);
|
void handleIpcMessage(const Event &e, void *);
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,10 @@ namespace deskflow::core::ipc {
|
||||||
const auto kAckMessage = "ok\n";
|
const auto kAckMessage = "ok\n";
|
||||||
const auto kErrorMessage = "error\n";
|
const auto kErrorMessage = "error\n";
|
||||||
|
|
||||||
DaemonIpcServer::DaemonIpcServer(QObject *parent)
|
DaemonIpcServer::DaemonIpcServer(QObject *parent, const QString &logFilename)
|
||||||
: QObject(parent), //
|
: QObject(parent),
|
||||||
m_server{new QLocalServer(this)} // NOSONAR
|
m_logFilename(logFilename),
|
||||||
|
m_server{new QLocalServer(this)} // NOSONAR - Qt memory
|
||||||
{
|
{
|
||||||
// Daemon runs as system, but GUI runs as regular user, so we need to allow world access.
|
// Daemon runs as system, but GUI runs as regular user, so we need to allow world access.
|
||||||
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
|
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||||
|
|
@ -108,7 +109,7 @@ void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &command = parts[0];
|
const auto &command = parts[0];
|
||||||
if (command == "hello") { // NOSONAR
|
if (command == "hello") { // NOSONAR - if-init is confusing here
|
||||||
clientSocket->write("hello\n");
|
clientSocket->write("hello\n");
|
||||||
} else if (command == "noop") {
|
} else if (command == "noop") {
|
||||||
clientSocket->write(kAckMessage);
|
clientSocket->write(kAckMessage);
|
||||||
|
|
@ -128,7 +129,7 @@ void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &
|
||||||
clientSocket->write(kAckMessage);
|
clientSocket->write(kAckMessage);
|
||||||
} else if (command == "logPath") {
|
} else if (command == "logPath") {
|
||||||
LOG_DEBUG("ipc server got log path request");
|
LOG_DEBUG("ipc server got log path request");
|
||||||
// TODO: send log path
|
clientSocket->write("logPath=" + m_logFilename.toUtf8());
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN("ipc server got unknown message: %s", message.toUtf8().constData());
|
LOG_WARN("ipc server got unknown message: %s", message.toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class DaemonIpcServer : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DaemonIpcServer(QObject *parent);
|
explicit DaemonIpcServer(QObject *parent, const QString &logFilename);
|
||||||
~DaemonIpcServer() override;
|
~DaemonIpcServer() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
@ -42,6 +42,7 @@ private slots:
|
||||||
void handleErrorOccurred();
|
void handleErrorOccurred();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const QString m_logFilename;
|
||||||
QLocalServer *m_server;
|
QLocalServer *m_server;
|
||||||
QSet<QLocalSocket *> m_clients;
|
QSet<QLocalSocket *> m_clients;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ add_library(${target} STATIC
|
||||||
dotenv.cpp
|
dotenv.cpp
|
||||||
dotenv.h
|
dotenv.h
|
||||||
env_vars.h
|
env_vars.h
|
||||||
|
FileTail.cpp
|
||||||
|
FileTail.h
|
||||||
Logger.cpp
|
Logger.cpp
|
||||||
Logger.h
|
Logger.h
|
||||||
messages.cpp
|
messages.cpp
|
||||||
|
|
|
||||||
45
src/lib/gui/FileTail.cpp
Normal file
45
src/lib/gui/FileTail.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FileTail.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
namespace deskflow::gui {
|
||||||
|
|
||||||
|
FileTail::FileTail(const QString &filePath, QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_file(filePath),
|
||||||
|
m_watcher(new QFileSystemWatcher(this))
|
||||||
|
{
|
||||||
|
if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
qCritical() << "failed to open file for tail:" << filePath;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "starting file tail:" << filePath;
|
||||||
|
m_watcher->addPath(filePath);
|
||||||
|
m_lastPos = m_file.size();
|
||||||
|
|
||||||
|
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &FileTail::handleFileChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTail::handleFileChanged(const QString &)
|
||||||
|
{
|
||||||
|
m_file.seek(m_lastPos);
|
||||||
|
QTextStream stream(&m_file);
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
QString line = stream.readLine();
|
||||||
|
Q_EMIT newLine(line);
|
||||||
|
}
|
||||||
|
m_lastPos = m_file.pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace deskflow::gui
|
||||||
35
src/lib/gui/FileTail.h
Normal file
35
src/lib/gui/FileTail.h
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QFileSystemWatcher;
|
||||||
|
|
||||||
|
namespace deskflow::gui {
|
||||||
|
|
||||||
|
class FileTail : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileTail(const QString &filePath, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void newLine(const QString &line);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleFileChanged(const QString &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFile m_file;
|
||||||
|
QFileSystemWatcher *m_watcher = nullptr;
|
||||||
|
qint64 m_lastPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace deskflow::gui
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
* SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
|
* SPDX-FileCopyrightText: (C) 2024 - 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -158,6 +158,15 @@ CoreProcess::CoreProcess(const IAppConfig &appConfig, const IServerConfig &serve
|
||||||
m_pDeps(deps),
|
m_pDeps(deps),
|
||||||
m_daemonIpcClient{new ipc::DaemonIpcClient(this)}
|
m_daemonIpcClient{new ipc::DaemonIpcClient(this)}
|
||||||
{
|
{
|
||||||
|
if (m_appConfig.processMode() == ProcessMode::kService) {
|
||||||
|
const auto logPath = requestDaemonLogPath();
|
||||||
|
if (!logPath.isEmpty()) {
|
||||||
|
qInfo() << "daemon log path:" << logPath;
|
||||||
|
m_daemonFileTail = new FileTail(logPath, this);
|
||||||
|
connect(m_daemonFileTail, &FileTail::newLine, this, &CoreProcess::handleLogLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connect(&m_pDeps->process(), &QProcessProxy::finished, this, &CoreProcess::onProcessFinished);
|
connect(&m_pDeps->process(), &QProcessProxy::finished, this, &CoreProcess::onProcessFinished);
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
|
|
@ -743,4 +752,21 @@ QString CoreProcess::correctedAddress() const
|
||||||
return wrapIpv6(m_address);
|
return wrapIpv6(m_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CoreProcess::requestDaemonLogPath()
|
||||||
|
{
|
||||||
|
qDebug() << "requesting daemon log path";
|
||||||
|
const auto logPath = m_daemonIpcClient->requestLogPath();
|
||||||
|
if (logPath.isEmpty()) {
|
||||||
|
qCritical() << "failed to get daemon log path";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFileInfo logFile(logPath); !logFile.exists() || !logFile.isFile()) {
|
||||||
|
qWarning() << "daemon log path file does not exist:" << logPath;
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return logPath;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace deskflow::gui
|
} // namespace deskflow::gui
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
* SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
|
* SPDX-FileCopyrightText: (C) 2024 - 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "gui/FileTail.h"
|
||||||
#include "gui/config/IAppConfig.h"
|
#include "gui/config/IAppConfig.h"
|
||||||
#include "gui/config/IServerConfig.h"
|
#include "gui/config/IServerConfig.h"
|
||||||
#include "gui/ipc/QIpcClient.h"
|
#include "gui/ipc/QIpcClient.h"
|
||||||
#include "gui/proxy/QProcessProxy.h"
|
#include "gui/proxy/QProcessProxy.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace deskflow::gui {
|
namespace deskflow::gui {
|
||||||
|
|
||||||
|
|
@ -158,6 +161,7 @@ private:
|
||||||
void handleLogLines(const QString &text);
|
void handleLogLines(const QString &text);
|
||||||
QString correctedInterface() const;
|
QString correctedInterface() const;
|
||||||
QString correctedAddress() const;
|
QString correctedAddress() const;
|
||||||
|
QString requestDaemonLogPath();
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
void checkOSXNotification(const QString &line);
|
void checkOSXNotification(const QString &line);
|
||||||
|
|
@ -176,6 +180,7 @@ private:
|
||||||
QTimer m_retryTimer;
|
QTimer m_retryTimer;
|
||||||
int m_connections = 0;
|
int m_connections = 0;
|
||||||
deskflow::gui::ipc::DaemonIpcClient *m_daemonIpcClient = nullptr;
|
deskflow::gui::ipc::DaemonIpcClient *m_daemonIpcClient = nullptr;
|
||||||
|
FileTail *m_daemonFileTail = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace deskflow::gui
|
} // namespace deskflow::gui
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ namespace deskflow::gui::ipc {
|
||||||
const auto kTimeout = 1000;
|
const auto kTimeout = 1000;
|
||||||
|
|
||||||
DaemonIpcClient::DaemonIpcClient(QObject *parent)
|
DaemonIpcClient::DaemonIpcClient(QObject *parent)
|
||||||
: QObject(parent), //
|
: QObject(parent),
|
||||||
m_socket{new QLocalSocket(this)} // NOSONAR
|
m_socket{new QLocalSocket(this)} // NOSONAR - Qt memory
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,15 +58,59 @@ void DaemonIpcClient::handleErrorOccurred()
|
||||||
m_connected = false;
|
m_connected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
|
||||||
|
{
|
||||||
|
if (expectConnected && !m_connected) {
|
||||||
|
qWarning() << "cannot send command, ipc not connected";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray messageData = message.toUtf8() + "\n";
|
||||||
|
m_socket->write(messageData);
|
||||||
|
if (!m_socket->waitForBytesWritten(kTimeout)) {
|
||||||
|
qWarning() << "ipc failed to write command";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expectAck.isEmpty()) {
|
||||||
|
qDebug() << "ipc waiting for ack: " << expectAck;
|
||||||
|
|
||||||
|
if (!m_socket->waitForReadyRead(kTimeout)) {
|
||||||
|
qWarning() << "ipc failed to read response";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray response = m_socket->readAll();
|
||||||
|
if (response.isEmpty()) {
|
||||||
|
qWarning() << "ipc got empty response";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString responseData = QString::fromUtf8(response);
|
||||||
|
if (responseData.isEmpty()) {
|
||||||
|
qWarning() << "ipc failed to convert response to string";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseData != expectAck + "\n") {
|
||||||
|
qWarning() << "ipc got unexpected response: " << responseData;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "ipc sent message: " << messageData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool DaemonIpcClient::keepAlive()
|
bool DaemonIpcClient::keepAlive()
|
||||||
{
|
{
|
||||||
if (!isConnected() && !connectToServer()) {
|
if (!isConnected() && !connectToServer()) {
|
||||||
qWarning() << "ipc client keep alive failed to connect";
|
qWarning() << "ipc keep alive failed to connect";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sendMessage("noop")) {
|
if (!sendMessage("noop")) {
|
||||||
qWarning() << "ipc client keep alive ping failed";
|
qWarning() << "ipc keep alive ping failed";
|
||||||
m_connected = false;
|
m_connected = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -104,44 +148,45 @@ bool DaemonIpcClient::sendStopProcess()
|
||||||
return sendMessage("stop");
|
return sendMessage("stop");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
|
QString DaemonIpcClient::requestLogPath()
|
||||||
{
|
{
|
||||||
if (expectConnected && !m_connected) {
|
if (!keepAlive())
|
||||||
qWarning() << "cannot send command, ipc not connected";
|
return QString();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray messageData = message.toUtf8() + "\n";
|
if (!sendMessage("logPath", QString())) {
|
||||||
m_socket->write(messageData);
|
return QString();
|
||||||
if (!m_socket->waitForBytesWritten(kTimeout)) {
|
|
||||||
qWarning() << "ipc client failed to write command";
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_socket->waitForReadyRead(kTimeout)) {
|
if (!m_socket->waitForReadyRead(kTimeout)) {
|
||||||
qWarning() << "ipc client failed to read response";
|
qWarning() << "ipc failed to read log path response";
|
||||||
return false;
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray response = m_socket->readAll();
|
QByteArray response = m_socket->readAll();
|
||||||
if (response.isEmpty()) {
|
if (response.isEmpty()) {
|
||||||
qWarning() << "ipc client got empty response";
|
qWarning() << "ipc got empty log path response";
|
||||||
return false;
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString responseData = QString::fromUtf8(response);
|
QString responseData = QString::fromUtf8(response);
|
||||||
if (responseData.isEmpty()) {
|
if (responseData.isEmpty()) {
|
||||||
qWarning() << "ipc client failed to convert response to string";
|
qWarning() << "ipc failed to convert log path response to string";
|
||||||
return false;
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseData != expectAck + "\n") {
|
// Trimming removes newline from end of message.
|
||||||
qWarning() << "ipc client got unexpected response: " << responseData;
|
QStringList parts = responseData.trimmed().split("=");
|
||||||
return false;
|
if (parts.size() != 2) {
|
||||||
|
qWarning() << "ipc got invalid log path response: " << responseData;
|
||||||
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "ipc client sent message: " << messageData;
|
if (parts[0] != "logPath") {
|
||||||
return true;
|
qWarning() << "ipc got unexpected log path response: " << responseData;
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace deskflow::gui::ipc
|
} // namespace deskflow::gui::ipc
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ public:
|
||||||
bool sendLogLevel(const QString &logLevel);
|
bool sendLogLevel(const QString &logLevel);
|
||||||
bool sendStartProcess(const QString &command, ElevateMode elevateMode);
|
bool sendStartProcess(const QString &command, ElevateMode elevateMode);
|
||||||
bool sendStopProcess();
|
bool sendStopProcess();
|
||||||
|
QString requestLogPath();
|
||||||
|
|
||||||
bool isConnected() const
|
bool isConnected() const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue