diff --git a/src/apps/deskflow-daemon/deskflow-daemon.cpp b/src/apps/deskflow-daemon/deskflow-daemon.cpp index a0165c83f..044291e38 100644 --- a/src/apps/deskflow-daemon/deskflow-daemon.cpp +++ b/src/apps/deskflow-daemon/deskflow-daemon.cpp @@ -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 +#include + 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 diff --git a/src/lib/deskflow/DaemonApp.cpp b/src/lib/deskflow/DaemonApp.cpp index 9654f9e62..f16f185a3 100644 --- a/src/lib/deskflow/DaemonApp.cpp +++ b/src/lib/deskflow/DaemonApp.cpp @@ -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 +#include +#include + #include #include #include @@ -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(); } } diff --git a/src/lib/deskflow/DaemonApp.h b/src/lib/deskflow/DaemonApp.h index 55b3de317..0c130e0b4 100644 --- a/src/lib/deskflow/DaemonApp.h +++ b/src/lib/deskflow/DaemonApp.h @@ -11,7 +11,7 @@ #include #include -#include +#include #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; }; diff --git a/src/lib/deskflow/ipc/DaemonIpcServer.cpp b/src/lib/deskflow/ipc/DaemonIpcServer.cpp index 0fc366868..4f8e25f20 100644 --- a/src/lib/deskflow/ipc/DaemonIpcServer.cpp +++ b/src/lib/deskflow/ipc/DaemonIpcServer.cpp @@ -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());