From 92869ea5a68b33aed346b06b6acb3fb88f1e7897 Mon Sep 17 00:00:00 2001 From: Nodir Temirkhodjaev Date: Wed, 15 Feb 2023 18:42:02 +0300 Subject: [PATCH] UI: Service: Add ability to pause/continue client connections --- src/ui/FortFirewallUI.pro | 8 ++- src/ui/control/controlmanager.cpp | 31 +++++--- src/ui/control/controlmanager.h | 7 +- src/ui/control/controlworker.cpp | 4 ++ src/ui/fortmanager.cpp | 4 ++ src/ui/manager/servicemanager.cpp | 70 ++++++++++++++++++ src/ui/manager/servicemanager.h | 30 ++++++++ src/ui/rpc/rpcmanager.cpp | 2 +- src/ui/util/service/servicemanageriface.cpp | 33 +++++++++ src/ui/util/service/servicemanageriface.h | 22 ++++++ src/ui/util/service/serviceworker.cpp | 50 +++++++++++++ src/ui/util/{ => service}/serviceworker.h | 4 +- src/ui/util/serviceworker.cpp | 78 --------------------- src/ui_bin/main.cpp | 5 +- 14 files changed, 251 insertions(+), 97 deletions(-) create mode 100644 src/ui/manager/servicemanager.cpp create mode 100644 src/ui/manager/servicemanager.h create mode 100644 src/ui/util/service/servicemanageriface.cpp create mode 100644 src/ui/util/service/servicemanageriface.h create mode 100644 src/ui/util/service/serviceworker.cpp rename src/ui/util/{ => service}/serviceworker.h (62%) delete mode 100644 src/ui/util/serviceworker.cpp diff --git a/src/ui/FortFirewallUI.pro b/src/ui/FortFirewallUI.pro index 9c307844..de2cdb85 100644 --- a/src/ui/FortFirewallUI.pro +++ b/src/ui/FortFirewallUI.pro @@ -104,6 +104,7 @@ SOURCES += \ manager/hotkeymanager.cpp \ manager/logger.cpp \ manager/nativeeventfilter.cpp \ + manager/servicemanager.cpp \ manager/translationmanager.cpp \ manager/windowmanager.cpp \ model/applistmodel.cpp \ @@ -163,7 +164,8 @@ SOURCES += \ util/osutil.cpp \ util/processinfo.cpp \ util/regkey.cpp \ - util/serviceworker.cpp \ + util/service/servicemanageriface.cpp \ + util/service/serviceworker.cpp \ util/startuputil.cpp \ util/stringutil.cpp \ util/textareautil.cpp \ @@ -273,6 +275,7 @@ HEADERS += \ manager/hotkeymanager.h \ manager/logger.h \ manager/nativeeventfilter.h \ + manager/servicemanager.h \ manager/translationmanager.h \ manager/windowmanager.h \ model/applistmodel.h \ @@ -335,7 +338,8 @@ HEADERS += \ util/osutil.h \ util/processinfo.h \ util/regkey.h \ - util/serviceworker.h \ + util/service/servicemanageriface.h \ + util/service/serviceworker.h \ util/startuputil.h \ util/stringutil.h \ util/textareautil.h \ diff --git a/src/ui/control/controlmanager.cpp b/src/ui/control/controlmanager.cpp index fabf8673..cbab17c6 100644 --- a/src/ui/control/controlmanager.cpp +++ b/src/ui/control/controlmanager.cpp @@ -63,7 +63,10 @@ bool ControlManager::listen() { const auto settings = IoC(); - Q_ASSERT(!m_server); + if (m_server) { + return m_server->isListening(); + } + m_server = new QLocalServer(this); m_server->setMaxPendingConnections(maxClientsCount); m_server->setSocketOptions( @@ -79,6 +82,21 @@ bool ControlManager::listen() return true; } +void ControlManager::close() +{ + if (m_server) { + m_server->close(); + m_server = nullptr; + } +} + +void ControlManager::closeAllClients() +{ + for (ControlWorker *w : m_clients) { + w->close(); + } +} + bool ControlManager::postCommand() { const auto settings = IoC(); @@ -124,7 +142,7 @@ void ControlManager::onNewConnection() m_clients.append(w); - qCDebug(LC) << "Client connected:" << w->id(); + qCDebug(LC) << "Client connected: id:" << w->id() << "count:" << m_clients.size(); } } @@ -137,7 +155,7 @@ void ControlManager::onDisconnected() w->deleteLater(); m_clients.removeOne(w); - qCDebug(LC) << "Client disconnected:" << w->id(); + qCDebug(LC) << "Client disconnected: id:" << w->id(); } bool ControlManager::processRequest(Control::Command command, const QVariantList &args) @@ -204,13 +222,6 @@ bool ControlManager::processCommandProg(const ProcessCommandArgs &p) return false; } -void ControlManager::close() -{ - if (m_server) { - m_server->close(); - } -} - QString ControlManager::getServerName(bool isService) { return QLatin1String(APP_BASE) + (isService ? "Svc" : OsUtil::userName()) + "Pipe"; diff --git a/src/ui/control/controlmanager.h b/src/ui/control/controlmanager.h index b370a692..c8475367 100644 --- a/src/ui/control/controlmanager.h +++ b/src/ui/control/controlmanager.h @@ -10,7 +10,6 @@ #include "control.h" QT_FORWARD_DECLARE_CLASS(QLocalServer) -QT_FORWARD_DECLARE_CLASS(QLocalSocket) class ControlWorker; @@ -40,6 +39,10 @@ public: ControlWorker *newServiceClient(QObject *parent = nullptr) const; bool listen(); + void close(); + + void closeAllClients(); + bool postCommand(); private slots: @@ -52,8 +55,6 @@ private: bool processCommand(const ProcessCommandArgs &p); bool processCommandProg(const ProcessCommandArgs &p); - void close(); - static QString getServerName(bool isService = false); private: diff --git a/src/ui/control/controlworker.cpp b/src/ui/control/controlworker.cpp index 2726a2d4..6fab3567 100644 --- a/src/ui/control/controlworker.cpp +++ b/src/ui/control/controlworker.cpp @@ -156,6 +156,10 @@ bool ControlWorker::reconnectToServer() m_isReconnecting = false; + if (!connectedToServer) { + startReconnectTimer(); + } + return connectedToServer; } diff --git a/src/ui/fortmanager.cpp b/src/ui/fortmanager.cpp index a8b65efa..35a5f773 100644 --- a/src/ui/fortmanager.cpp +++ b/src/ui/fortmanager.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,9 @@ void FortManager::createManagers() if (settings->isService()) { windowManager = new WindowManagerFake(); + + // For Service only + ioc->setService(new ServiceManager()); } else { windowManager = new WindowManager(); diff --git a/src/ui/manager/servicemanager.cpp b/src/ui/manager/servicemanager.cpp new file mode 100644 index 00000000..4f28bee7 --- /dev/null +++ b/src/ui/manager/servicemanager.cpp @@ -0,0 +1,70 @@ +#include "servicemanager.h" + +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include +#include + +namespace { + +const QLoggingCategory LC("manager.serviceManager"); + +} + +ServiceManager::ServiceManager(QObject *parent) : QObject(parent) { } + +ServiceManager::~ServiceManager() { } + +void ServiceManager::setUp() +{ + auto controlManager = IoC()->setUpDependency(); + + connect(this, &ServiceManager::pauseRequested, controlManager, [controlManager] { + controlManager->close(); + controlManager->closeAllClients(); + }); + connect(this, &ServiceManager::continueRequested, controlManager, &ControlManager::listen); +} + +const wchar_t *ServiceManager::serviceName() const +{ + return StartupUtil::serviceName(); +} + +void ServiceManager::processControl(quint32 code) +{ + DWORD state = SERVICE_RUNNING; + + switch (code) { + case SERVICE_CONTROL_PAUSE: { + qCDebug(LC) << "Pause due service control"; + + emit pauseRequested(); + + state = SERVICE_PAUSED; + } break; + case SERVICE_CONTROL_CONTINUE: { + qCDebug(LC) << "Continue due service control"; + + emit continueRequested(); + + state = SERVICE_RUNNING; + } break; + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: { + qCDebug(LC) << "Quit due service control"; + + qApp->connect(qApp, &QObject::destroyed, [] { reportStatus(SERVICE_STOPPED); }); + QCoreApplication::quit(); // it's threadsafe + + state = SERVICE_STOP_PENDING; + } break; + } + + reportStatus(state); +} diff --git a/src/ui/manager/servicemanager.h b/src/ui/manager/servicemanager.h new file mode 100644 index 00000000..8f94aef0 --- /dev/null +++ b/src/ui/manager/servicemanager.h @@ -0,0 +1,30 @@ +#ifndef SERVICEMANAGER_H +#define SERVICEMANAGER_H + +#include + +#include +#include +#include + +class ServiceManager : public QObject, public ServiceManagerIface, public IocService +{ + Q_OBJECT + +public: + explicit ServiceManager(QObject *parent = nullptr); + ~ServiceManager() override; + CLASS_DELETE_COPY_MOVE(ServiceManager) + + void setUp() override; + + const wchar_t *serviceName() const override; + + void processControl(quint32 code) override; + +signals: + void pauseRequested(); + void continueRequested(); +}; + +#endif // SERVICEMANAGER_H diff --git a/src/ui/rpc/rpcmanager.cpp b/src/ui/rpc/rpcmanager.cpp index b20bc326..ae348646 100644 --- a/src/ui/rpc/rpcmanager.cpp +++ b/src/ui/rpc/rpcmanager.cpp @@ -403,7 +403,7 @@ void RpcManager::setupClient() [&] { invokeOnServer(Control::Rpc_RpcManager_initClient); }); client()->setIsTryReconnect(true); - client()->connectToServer(); + client()->reconnectToServer(); } void RpcManager::closeClient() diff --git a/src/ui/util/service/servicemanageriface.cpp b/src/ui/util/service/servicemanageriface.cpp new file mode 100644 index 00000000..b938b60b --- /dev/null +++ b/src/ui/util/service/servicemanageriface.cpp @@ -0,0 +1,33 @@ +#include "servicemanageriface.h" + +#define WIN32_LEAN_AND_MEAN +#include + +/* Service global structure */ +static struct +{ + SERVICE_STATUS_HANDLE hstatus; + SERVICE_STATUS status; +} g_service; + +void ServiceManagerIface::initialize(qintptr hstatus) +{ + g_service.hstatus = SERVICE_STATUS_HANDLE(hstatus); + + g_service.status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + g_service.status.dwControlsAccepted = + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; + g_service.status.dwWin32ExitCode = NO_ERROR; + g_service.status.dwServiceSpecificExitCode = 0; + g_service.status.dwCheckPoint = 0; + g_service.status.dwWaitHint = 3000; + + reportStatus(SERVICE_RUNNING); +} + +void ServiceManagerIface::reportStatus(quint32 code) +{ + g_service.status.dwCurrentState = code; + + SetServiceStatus(g_service.hstatus, &g_service.status); +} diff --git a/src/ui/util/service/servicemanageriface.h b/src/ui/util/service/servicemanageriface.h new file mode 100644 index 00000000..b0dad061 --- /dev/null +++ b/src/ui/util/service/servicemanageriface.h @@ -0,0 +1,22 @@ +#ifndef SERVICEMANAGERIFACE_H +#define SERVICEMANAGERIFACE_H + +#include + +class ServiceManagerIface +{ +public: + ServiceManagerIface() = default; + virtual ~ServiceManagerIface() = default; + + void initialize(qintptr hstatus); + + virtual const wchar_t *serviceName() const = 0; + + virtual void processControl(quint32 code) = 0; + +protected: + static void reportStatus(quint32 code); +}; + +#endif // SERVICEMANAGERIFACE_H diff --git a/src/ui/util/service/serviceworker.cpp b/src/ui/util/service/serviceworker.cpp new file mode 100644 index 00000000..69914235 --- /dev/null +++ b/src/ui/util/service/serviceworker.cpp @@ -0,0 +1,50 @@ +#include "serviceworker.h" + +#define WIN32_LEAN_AND_MEAN +#include + +#include "servicemanageriface.h" + +namespace { + +ServiceManagerIface *g_manager = nullptr; + +void WINAPI serviceCtrlHandler(DWORD code) +{ + g_manager->processControl(code); +} + +void WINAPI serviceMain(DWORD argc, wchar_t *argv[]) +{ + Q_UNUSED(argc); + Q_UNUSED(argv); + + const SERVICE_STATUS_HANDLE hstatus = + RegisterServiceCtrlHandler(g_manager->serviceName(), serviceCtrlHandler); + + if (hstatus) { + g_manager->initialize(qintptr(hstatus)); + } +} + +DWORD WINAPI serviceStart(void *arg) +{ + Q_UNUSED(arg); + + wchar_t name[2] = { 0 }; // ignored for SERVICE_WIN32_OWN_PROCESS + SERVICE_TABLE_ENTRY table[] = { { name, serviceMain }, { nullptr, nullptr } }; + + return !StartServiceCtrlDispatcher(table); +} + +} + +void ServiceWorker::run(ServiceManagerIface *manager) +{ + Q_ASSERT(manager); + g_manager = manager; + + DWORD id; + const HANDLE hThr = CreateThread(nullptr, 8192, serviceStart, nullptr, 0, &id); + CloseHandle(hThr); +} diff --git a/src/ui/util/serviceworker.h b/src/ui/util/service/serviceworker.h similarity index 62% rename from src/ui/util/serviceworker.h rename to src/ui/util/service/serviceworker.h index b3d01ab3..8b3de2f1 100644 --- a/src/ui/util/serviceworker.h +++ b/src/ui/util/service/serviceworker.h @@ -3,10 +3,12 @@ #include +class ServiceManagerIface; + class ServiceWorker { public: - static void run(); + static void run(ServiceManagerIface *manager); }; #endif // SERVICEWORKER_H diff --git a/src/ui/util/serviceworker.cpp b/src/ui/util/serviceworker.cpp deleted file mode 100644 index 0025c034..00000000 --- a/src/ui/util/serviceworker.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "serviceworker.h" - -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include - -#include "startuputil.h" - -namespace { - -const QLoggingCategory LC("util.serviceWorker"); - -/* Service global structure */ -static struct -{ - SERVICE_STATUS status; - SERVICE_STATUS_HANDLE hstatus; -} g_service; - -void reportServiceStatus(DWORD state) -{ - g_service.status.dwCurrentState = state; - SetServiceStatus(g_service.hstatus, &g_service.status); -} - -void WINAPI serviceCtrlHandler(DWORD code) -{ - switch (code) { - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - qCDebug(LC) << "Quit due service control"; - - qApp->connect(qApp, &QObject::destroyed, [] { reportServiceStatus(SERVICE_STOPPED); }); - QCoreApplication::quit(); // it's threadsafe - - reportServiceStatus(SERVICE_STOP_PENDING); - break; - } -} - -void WINAPI serviceMain(DWORD argc, wchar_t *argv[]) -{ - Q_UNUSED(argc); - Q_UNUSED(argv); - - g_service.hstatus = RegisterServiceCtrlHandler(StartupUtil::serviceName(), serviceCtrlHandler); - if (g_service.hstatus) { - g_service.status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - g_service.status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; - g_service.status.dwWin32ExitCode = NO_ERROR; - g_service.status.dwServiceSpecificExitCode = 0; - g_service.status.dwCheckPoint = 0; - g_service.status.dwWaitHint = 3000; - - reportServiceStatus(SERVICE_RUNNING); - } -} - -DWORD WINAPI serviceStart(void *arg) -{ - Q_UNUSED(arg); - - wchar_t name[2] = { 0 }; // ignored for SERVICE_WIN32_OWN_PROCESS - SERVICE_TABLE_ENTRY table[] = { { name, serviceMain }, { nullptr, nullptr } }; - - return !StartServiceCtrlDispatcher(table); -} - -} - -void ServiceWorker::run() -{ - DWORD id; - const HANDLE hThr = CreateThread(nullptr, 8192, serviceStart, nullptr, 0, &id); - CloseHandle(hThr); -} diff --git a/src/ui_bin/main.cpp b/src/ui_bin/main.cpp index 47a5baef..e077051c 100644 --- a/src/ui_bin/main.cpp +++ b/src/ui_bin/main.cpp @@ -12,9 +12,10 @@ #include #include #include +#include #include #include -#include +#include #include namespace { @@ -135,7 +136,7 @@ int main(int argc, char *argv[]) fortManager.initialize(); if (settings.isService()) { - ServiceWorker::run(); + ServiceWorker::run(IoC()); } else { fortManager.show(); }