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 * 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 "deskflow/DaemonApp.h"
#include "arch/Arch.h" #include "arch/Arch.h"
@ -20,10 +23,13 @@
#endif #endif
#include <QCoreApplication>
#include <QThread>
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
#if SYSAPI_WIN32 #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)); ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr));
#endif #endif
@ -33,8 +39,24 @@ int main(int argc, char **argv)
Log log; Log log;
EventQueue events; EventQueue events;
DaemonApp app(&events, __argc, __argv); LOG((CLOG_PRINT "%s daemon (v%s)", kAppName, kVersion));
return DaemonApp::exec();
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 #if SYSAPI_WIN32

View file

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

View file

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

View file

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