refactor: Exclude current process when killing existing process from Windows daemon

This commit is contained in:
Nick Bolton 2025-02-07 12:50:25 +00:00
parent 24c57e46e2
commit 90e63e2e9e
4 changed files with 75 additions and 45 deletions

View file

@ -6,8 +6,8 @@
*/ */
#include "arch/Arch.h" #include "arch/Arch.h"
#include "base/EventQueue.h"
#include "base/Log.h" #include "base/Log.h"
#include "common/constants.h"
#include "deskflow/DaemonApp.h" #include "deskflow/DaemonApp.h"
#include "arch/Arch.h" #include "arch/Arch.h"
@ -43,13 +43,18 @@ int main(int argc, char **argv)
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
// Must be on the heap, as we're moving it to a thread. const auto pDaemon = &DaemonApp::instance();
DaemonApp *pDaemon = new DaemonApp(&events); QObject::connect(pDaemon, &DaemonApp::mainLoopFinished, &app, &QCoreApplication::quit);
QObject::connect(pDaemon, &DaemonApp::fatalErrorOccurred, &app, &QCoreApplication::quit);
QObject::connect(pDaemon, &DaemonApp::serviceInstalled, &app, &QCoreApplication::quit); QObject::connect(pDaemon, &DaemonApp::serviceInstalled, &app, &QCoreApplication::quit);
QObject::connect(pDaemon, &DaemonApp::serviceUninstalled, &app, &QCoreApplication::quit); QObject::connect(pDaemon, &DaemonApp::serviceUninstalled, &app, &QCoreApplication::quit);
pDaemon->init(argc, argv); pDaemon->init(&events, argc, argv);
QThread *thread = new QThread(); // Thread must be on the heap, as the thread needs to be deleted only when the loop finishes,
// and not when we go out of scope.
// Deliberately do not set ownership of the thread to the daemon, as we want to delete the thread
// when the loop finishes, and not when the daemon is deleted.
QThread *thread = new QThread(); // NOSONAR
pDaemon->moveToThread(thread); pDaemon->moveToThread(thread);
QObject::connect(thread, &QThread::started, pDaemon, &DaemonApp::run); QObject::connect(thread, &QThread::started, pDaemon, &DaemonApp::run);
QObject::connect(thread, &QThread::finished, pDaemon, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, pDaemon, &QObject::deleteLater);

View file

@ -81,11 +81,9 @@ bool isServerCommandLine(const std::vector<std::string> &cmd)
} // namespace } // namespace
DaemonApp *DaemonApp::s_instance = nullptr;
int mainLoopStatic() int mainLoopStatic()
{ {
DaemonApp::s_instance->mainLoop(true); DaemonApp::instance().mainLoop(true);
return kExitSuccess; return kExitSuccess;
} }
@ -101,19 +99,14 @@ int winMainLoopStatic(int, const char **)
} }
#endif #endif
DaemonApp::DaemonApp(IEventQueue *events) : m_ipcServer{new ipc::DaemonIpcServer(this)} DaemonApp::DaemonApp() : 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);
} }
DaemonApp::~DaemonApp() DaemonApp::~DaemonApp() = default;
{
s_instance = nullptr;
}
void DaemonApp::run() void DaemonApp::run()
{ {
@ -132,6 +125,8 @@ void DaemonApp::run()
ARCH->daemonize(kAppName, unixMainLoopStatic); ARCH->daemonize(kAppName, unixMainLoopStatic);
#endif #endif
} }
Q_EMIT mainLoopFinished();
} }
void DaemonApp::handleElevateModeChanged(int mode) void DaemonApp::handleElevateModeChanged(int mode)
@ -156,8 +151,14 @@ void DaemonApp::handleRestartRequested()
#endif #endif
} }
void DaemonApp::init(int argc, char **argv) void DaemonApp::init(IEventQueue *events, int argc, char **argv) // NOSONAR
{ {
if (events == nullptr) {
throw XDeskflow("event queue not set");
}
m_events = events;
bool isUninstalling = false; bool isUninstalling = false;
try { try {
// default log level to system setting. // default log level to system setting.
@ -186,7 +187,7 @@ void DaemonApp::init(int argc, char **argv)
stringstream ss; stringstream ss;
ss << "Unrecognized argument: " << arg; ss << "Unrecognized argument: " << arg;
foregroundError(ss.str().c_str()); foregroundError(ss.str().c_str());
Q_EMIT fatalError(); Q_EMIT fatalErrorOccurred();
} }
} }
@ -208,18 +209,22 @@ void DaemonApp::init(int argc, char **argv)
} else { } else {
foregroundError(message.c_str()); foregroundError(message.c_str());
} }
Q_EMIT fatalError(); Q_EMIT fatalErrorOccurred();
} catch (std::exception &e) { } catch (std::exception &e) {
foregroundError(e.what()); foregroundError(e.what());
Q_EMIT fatalError(); Q_EMIT fatalErrorOccurred();
} catch (...) { } catch (...) {
foregroundError("Unrecognized error."); foregroundError("Unrecognized error.");
Q_EMIT fatalError(); Q_EMIT fatalErrorOccurred();
} }
} }
void DaemonApp::mainLoop(bool logToFile, bool foreground) void DaemonApp::mainLoop(bool logToFile, bool foreground)
{ {
if (m_events == nullptr) {
throw XDeskflow("event queue not set");
}
try { try {
DAEMON_RUNNING(true); DAEMON_RUNNING(true);

View file

@ -37,18 +37,26 @@ class DaemonApp : public QObject
Q_OBJECT Q_OBJECT
public: public:
DaemonApp(IEventQueue *events); void init(IEventQueue *events, int argc, char **argv);
~DaemonApp();
void init(int argc, char **argv);
void run(); void run();
void mainLoop(bool logToFile, bool foreground = false); void mainLoop(bool logToFile, bool foreground = false);
static DaemonApp &instance()
{
static DaemonApp instance; // NOSONAR - Meyers' Singleton
return instance;
}
signals: signals:
void fatalError(); void mainLoopFinished();
void fatalErrorOccurred();
void serviceInstalled(); void serviceInstalled();
void serviceUninstalled(); void serviceUninstalled();
private: private:
explicit DaemonApp();
~DaemonApp() override;
void daemonize(); void daemonize();
void foregroundError(const char *message); void foregroundError(const char *message);
std::string logFilename(); std::string logFilename();
@ -59,9 +67,6 @@ private slots:
void handleCommandChanged(const QString &command); void handleCommandChanged(const QString &command);
void handleRestartRequested(); void handleRestartRequested();
public:
static DaemonApp *s_instance;
#if SYSAPI_WIN32 #if SYSAPI_WIN32
std::unique_ptr<MSWindowsWatchdog> m_watchdog; std::unique_ptr<MSWindowsWatchdog> m_watchdog;
#endif #endif

View file

@ -422,15 +422,36 @@ void MSWindowsWatchdog::outputLoop(void *)
} }
} }
HANDLE openProcessForKill(PROCESSENTRY32 entry)
{
// pid 0 is 'system idle process'
if (entry.th32ProcessID == 0)
return nullptr;
if (_stricmp(entry.szExeFile, "deskflow-client.exe") != 0 && //
_stricmp(entry.szExeFile, "deskflow-server.exe") != 0 && //
_stricmp(entry.szExeFile, "deskflow-core.exe") != 0) {
return nullptr;
}
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
if (handle == nullptr) {
LOG((CLOG_ERR "could not open process handle for kill"));
throw XArch(new XArchEvalWindows);
}
// only shut down if not current process (daemon is now the same unified binary).
if (entry.th32ProcessID == GetCurrentProcessId()) {
CloseHandle(handle);
return nullptr;
}
return handle;
}
void MSWindowsWatchdog::shutdownExistingProcesses() void MSWindowsWatchdog::shutdownExistingProcesses()
{ {
LOG_INFO("daemon shutting down existing processes"); LOG_DEBUG("shutting down existing processes");
if (m_process != nullptr) {
m_process->shutdown();
m_process.reset();
m_processStarted = false;
}
// first we need to take a snapshot of the running processes // first we need to take a snapshot of the running processes
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, CURRENT_PROCESS_ID); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, CURRENT_PROCESS_ID);
@ -454,17 +475,11 @@ void MSWindowsWatchdog::shutdownExistingProcesses()
DWORD pid = 0; DWORD pid = 0;
while (gotEntry) { while (gotEntry) {
// make sure we're not checking the system process HANDLE handle = openProcessForKill(entry);
if (entry.th32ProcessID != 0) { if (handle) {
LOG((CLOG_INFO "shutting down process, name=%s, pid=%d", entry.szExeFile, entry.th32ProcessID));
if (_stricmp(entry.szExeFile, "deskflow-client.exe") == 0 || deskflow::platform::MSWindowsProcess::shutdown(handle, entry.th32ProcessID);
_stricmp(entry.szExeFile, "deskflow-server.exe") == 0 || CloseHandle(handle);
_stricmp(entry.szExeFile, "deskflow-core.exe") == 0) {
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
deskflow::platform::MSWindowsProcess::shutdown(handle, entry.th32ProcessID);
CloseHandle(handle);
}
} }
// now move on to the next entry (if we're not at the end) // now move on to the next entry (if we're not at the end)