refactor: Run legacy daemon loop in thread

This commit is contained in:
Nick Bolton
2025-02-06 18:13:54 +00:00
parent e72faf1446
commit 24c57e46e2
4 changed files with 81 additions and 40 deletions

View File

@ -5,6 +5,9 @@
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "arch/Arch.h"
#include "base/EventQueue.h"
#include "base/Log.h"
#include "deskflow/DaemonApp.h"
#include "arch/Arch.h"
@ -20,10 +23,13 @@
#endif
#include <QCoreApplication>
#include <QThread>
int main(int argc, char **argv)
{
#if SYSAPI_WIN32
// win32 instance needed for threading, etc.
// Save window instance for later use, e.g. `GetModuleFileName` which is used when installing the daemon.
ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr));
#endif
@ -33,8 +39,24 @@ int main(int argc, char **argv)
Log log;
EventQueue events;
DaemonApp app(&events, __argc, __argv);
return DaemonApp::exec();
LOG((CLOG_PRINT "%s daemon (v%s)", kAppName, kVersion));
QCoreApplication app(argc, argv);
// Must be on the heap, as we're moving it to a thread.
DaemonApp *pDaemon = new DaemonApp(&events);
QObject::connect(pDaemon, &DaemonApp::serviceInstalled, &app, &QCoreApplication::quit);
QObject::connect(pDaemon, &DaemonApp::serviceUninstalled, &app, &QCoreApplication::quit);
pDaemon->init(argc, argv);
QThread *thread = new QThread();
pDaemon->moveToThread(thread);
QObject::connect(thread, &QThread::started, pDaemon, &DaemonApp::run);
QObject::connect(thread, &QThread::finished, pDaemon, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
return QCoreApplication::exec();
}
#if SYSAPI_WIN32

View File

@ -13,6 +13,7 @@
#include "base/Log.h"
#include "base/TMethodEventJob.h"
#include "base/log_outputters.h"
#include "common/constants.h"
#include "common/ipc.h"
#include "deskflow/App.h"
#include "deskflow/ArgParser.h"
@ -42,6 +43,10 @@
#endif
#include <QCoreApplication>
#include <QObject>
#include <QThread>
#include <memory>
#include <sstream>
#include <string>
@ -96,16 +101,13 @@ int winMainLoopStatic(int, const char **)
}
#endif
DaemonApp::DaemonApp(IEventQueue *events, int argc, char **argv)
: QCoreApplication(argc, argv),
m_events(events),
m_ipcServer{new ipc::DaemonIpcServer(this)}
DaemonApp::DaemonApp(IEventQueue *events) : m_ipcServer{new ipc::DaemonIpcServer(this)}
{
s_instance = this;
connect(m_ipcServer, &ipc::DaemonIpcServer::elevateModeChanged, this, &DaemonApp::handleElevateModeChanged);
connect(m_ipcServer, &ipc::DaemonIpcServer::commandChanged, this, &DaemonApp::handleCommandChanged);
connect(m_ipcServer, &ipc::DaemonIpcServer::restartRequested, this, &DaemonApp::handleRestartRequested);
s_instance = this;
}
DaemonApp::~DaemonApp()
@ -113,6 +115,25 @@ DaemonApp::~DaemonApp()
s_instance = nullptr;
}
void DaemonApp::run()
{
if (m_foreground) {
LOG((CLOG_NOTE "starting daemon in foreground"));
// run process in foreground instead of daemonizing.
// useful for debugging.
mainLoop(false, m_foreground);
} else {
#if SYSAPI_WIN32
LOG((CLOG_NOTE "daemonizing windows service"));
ARCH->daemonize(kAppName, winMainLoopStatic);
#elif SYSAPI_UNIX
LOG((CLOG_NOTE "daemonizing unix service"));
ARCH->daemonize(kAppName, unixMainLoopStatic);
#endif
}
}
void DaemonApp::handleElevateModeChanged(int mode)
{
LOG_DEBUG("elevate mode changed: %d", mode);
@ -135,61 +156,50 @@ void DaemonApp::handleRestartRequested()
#endif
}
void DaemonApp::startAsync()
void DaemonApp::init(int argc, char **argv)
{
#if SYSAPI_WIN32
m_watchdog->startAsync();
#endif
}
int DaemonApp::init(int argc, char **argv)
{
bool uninstall = false;
bool isUninstalling = false;
try {
// default log level to system setting.
if (string logLevel = ARCH->setting("LogLevel"); logLevel != "")
CLOG->setFilter(logLevel.c_str());
bool foreground = false;
for (int i = 1; i < argc; ++i) {
string arg(argv[i]);
if (arg == "-f") {
foreground = true;
m_foreground = true;
}
#if SYSAPI_WIN32
else if (arg == "--install" || arg == "/install") {
LOG((CLOG_NOTE "installing windows daemon"));
uninstall = true;
ARCH->installDaemon();
return kExitSuccess;
Q_EMIT serviceInstalled();
} else if (arg == "--uninstall" || arg == "/uninstall") {
LOG((CLOG_NOTE "uninstalling windows daemon"));
isUninstalling = true;
ARCH->uninstallDaemon();
return kExitSuccess;
Q_EMIT serviceUninstalled();
}
#endif
else {
stringstream ss;
ss << "Unrecognized argument: " << arg;
foregroundError(ss.str().c_str());
return kExitArgs;
Q_EMIT fatalError();
}
}
if (!foreground) {
if (!m_foreground) {
#if SYSAPI_WIN32
// Only use MS debug outputter when the process is daemonized, since stdout won't be accessible
// in that case, but is accessible when running in the foreground.
CLOG->insert(new MSWindowsDebugOutputter()); // NOSONAR - Adopted by `Log`
#endif
}
return kExitSuccess;
} catch (XArch &e) {
std::string message = e.what();
if (uninstall && (message.find("The service has not been started") != std::string::npos)) {
if (isUninstalling && (message.find("The service has not been started") != std::string::npos)) {
// TODO: if we're keeping this use error code instead (what is it?!).
// HACK: this message happens intermittently, not sure where from but
// it's quite misleading for the user. they thing something has gone
@ -198,13 +208,13 @@ int DaemonApp::init(int argc, char **argv)
} else {
foregroundError(message.c_str());
}
return kExitFailed;
Q_EMIT fatalError();
} catch (std::exception &e) {
foregroundError(e.what());
return kExitFailed;
Q_EMIT fatalError();
} catch (...) {
foregroundError("Unrecognized error.");
return kExitFailed;
Q_EMIT fatalError();
}
}

View File

@ -11,7 +11,7 @@
#include <memory>
#include <string>
#include <QCoreApplication>
#include <QObject>
#include "common/common.h"
@ -20,6 +20,7 @@ class IEventQueue;
class IpcLogOutputter;
class FileLogOutputter;
class QLocalServer;
class QCoreApplication;
namespace deskflow::core::ipc {
class DaemonIpcServer;
@ -31,15 +32,22 @@ class MSWindowsWatchdog;
extern const char *const kLogFilename;
class DaemonApp : public QCoreApplication
class DaemonApp : public QObject
{
Q_OBJECT
public:
DaemonApp(IEventQueue *events, int argc, char **argv);
DaemonApp(IEventQueue *events);
~DaemonApp();
int init(int argc, char **argv);
void startAsync();
void init(int argc, char **argv);
void run();
void mainLoop(bool logToFile, bool foreground = false);
signals:
void fatalError();
void serviceInstalled();
void serviceUninstalled();
private:
void daemonize();
void foregroundError(const char *message);
@ -65,4 +73,5 @@ private:
deskflow::core::ipc::DaemonIpcServer *m_ipcServer = nullptr;
std::string m_command = "";
int m_elevateMode = 0;
bool m_foreground = false;
};

View File

@ -108,7 +108,7 @@ void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &
return;
} else {
LOG_DEBUG("ipc server got new elevate mode: %d", elevateMode);
Q_SIGNAL elevateModeChanged(elevateMode);
Q_EMIT elevateModeChanged(elevateMode);
clientSocket->write(kAckMessage);
}
} else if (message.startsWith("command=")) {
@ -118,12 +118,12 @@ void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &
clientSocket->write(kErrorMessage);
} else {
LOG_DEBUG("ipc server got new command: %s", command.toUtf8().constData());
Q_SIGNAL commandChanged(command);
Q_EMIT commandChanged(command);
clientSocket->write(kAckMessage);
}
} else if (message == "restart") {
LOG_DEBUG("ipc server got restart message");
Q_SIGNAL restartRequested();
Q_EMIT restartRequested();
clientSocket->write(kAckMessage);
} else {
LOG_WARN("ipc server got unknown message: %s", message.toUtf8().constData());