UI: Service: Add ability to pause/continue client connections

This commit is contained in:
Nodir Temirkhodjaev 2023-02-15 18:42:02 +03:00
parent ca4240dfb1
commit 92869ea5a6
14 changed files with 251 additions and 97 deletions

View File

@ -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 \

View File

@ -63,7 +63,10 @@ bool ControlManager::listen()
{
const auto settings = IoC<FortSettings>();
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<FortSettings>();
@ -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";

View File

@ -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:

View File

@ -156,6 +156,10 @@ bool ControlWorker::reconnectToServer()
m_isReconnecting = false;
if (!connectedToServer) {
startReconnectTimer();
}
return connectedToServer;
}

View File

@ -18,6 +18,7 @@
#include <manager/hotkeymanager.h>
#include <manager/logger.h>
#include <manager/nativeeventfilter.h>
#include <manager/servicemanager.h>
#include <manager/translationmanager.h>
#include <model/zonelistmodel.h>
#include <rpc/appinfomanagerrpc.h>
@ -157,6 +158,9 @@ void FortManager::createManagers()
if (settings->isService()) {
windowManager = new WindowManagerFake();
// For Service only
ioc->setService(new ServiceManager());
} else {
windowManager = new WindowManager();

View File

@ -0,0 +1,70 @@
#include "servicemanager.h"
#include <QCoreApplication>
#include <QLoggingCategory>
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
#include <control/controlmanager.h>
#include <util/ioc/ioccontainer.h>
#include <util/startuputil.h>
namespace {
const QLoggingCategory LC("manager.serviceManager");
}
ServiceManager::ServiceManager(QObject *parent) : QObject(parent) { }
ServiceManager::~ServiceManager() { }
void ServiceManager::setUp()
{
auto controlManager = IoC()->setUpDependency<ControlManager>();
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);
}

View File

@ -0,0 +1,30 @@
#ifndef SERVICEMANAGER_H
#define SERVICEMANAGER_H
#include <QObject>
#include <util/classhelpers.h>
#include <util/ioc/iocservice.h>
#include <util/service/servicemanageriface.h>
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

View File

@ -403,7 +403,7 @@ void RpcManager::setupClient()
[&] { invokeOnServer(Control::Rpc_RpcManager_initClient); });
client()->setIsTryReconnect(true);
client()->connectToServer();
client()->reconnectToServer();
}
void RpcManager::closeClient()

View File

@ -0,0 +1,33 @@
#include "servicemanageriface.h"
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
/* 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);
}

View File

@ -0,0 +1,22 @@
#ifndef SERVICEMANAGERIFACE_H
#define SERVICEMANAGERIFACE_H
#include <QObject>
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

View File

@ -0,0 +1,50 @@
#include "serviceworker.h"
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
#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);
}

View File

@ -3,10 +3,12 @@
#include <QObject>
class ServiceManagerIface;
class ServiceWorker
{
public:
static void run();
static void run(ServiceManagerIface *manager);
};
#endif // SERVICEWORKER_H

View File

@ -1,78 +0,0 @@
#include "serviceworker.h"
#include <QCoreApplication>
#include <QLoggingCategory>
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
#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);
}

View File

@ -12,9 +12,10 @@
#include <fortmanager.h>
#include <fortsettings.h>
#include <manager/envmanager.h>
#include <manager/servicemanager.h>
#include <util/fileutil.h>
#include <util/ioc/ioccontainer.h>
#include <util/serviceworker.h>
#include <util/service/serviceworker.h>
#include <util/startuputil.h>
namespace {
@ -135,7 +136,7 @@ int main(int argc, char *argv[])
fortManager.initialize();
if (settings.isService()) {
ServiceWorker::run();
ServiceWorker::run(IoC<ServiceManager>());
} else {
fortManager.show();
}