UI: Prepare services monitoring

This commit is contained in:
Nodir Temirkhodjaev 2023-06-19 13:14:21 +03:00
parent a57aee92cc
commit 23695146eb
12 changed files with 434 additions and 16 deletions

View File

@ -182,7 +182,9 @@ SOURCES += \
util/processinfo.cpp \
util/regkey.cpp \
util/service/serviceinfo.cpp \
util/service/servicelistmonitor.cpp \
util/service/servicemanageriface.cpp \
util/service/servicemonitor.cpp \
util/service/serviceworker.cpp \
util/startuputil.cpp \
util/stringutil.cpp \
@ -375,7 +377,9 @@ HEADERS += \
util/processinfo.h \
util/regkey.h \
util/service/serviceinfo.h \
util/service/servicelistmonitor.h \
util/service/servicemanageriface.h \
util/service/servicemonitor.h \
util/service/serviceworker.h \
util/startuputil.h \
util/stringutil.h \

View File

@ -1153,17 +1153,22 @@ bool ConfManager::validateDriver()
void ConfManager::updateDriverServices()
{
auto serviceInfoManager = IoC<ServiceInfoManager>();
int runningServicesCount;
const QVector<ServiceInfo> services =
serviceInfoManager->loadServiceInfoList(ServiceInfo::TypeWin32, ServiceInfo::StateAll,
/*displayName=*/false, &runningServicesCount);
serviceInfoManager->monitorServices(services);
if (runningServicesCount == 0)
return;
ConfUtil confUtil;
QByteArray buf;
auto serviceInfoManager = IoC<ServiceInfoManager>();
const QVector<ServiceInfo> services = serviceInfoManager->loadServiceInfoList(
ServiceInfo::TypeWin32, ServiceInfo::StateActive, /*displayName=*/false);
if (services.isEmpty())
return;
const int outSize = confUtil.writeServices(services, buf);
const int outSize = confUtil.writeServices(services, runningServicesCount, buf);
auto driverManager = IoC<DriverManager>();
driverManager->writeServices(buf, outSize);

View File

@ -7,6 +7,8 @@
#include <util/fileutil.h>
#include <util/regkey.h>
#include <util/service/servicelistmonitor.h>
#include <util/service/servicemonitor.h>
namespace {
@ -64,7 +66,8 @@ bool checkIsSvcHostService(const RegKey &svcReg)
}
QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr, DWORD serviceType = SERVICE_WIN32,
DWORD state = SERVICE_STATE_ALL, bool displayName = true)
DWORD state = SERVICE_STATE_ALL, bool displayName = true,
int *runningServicesCount = nullptr)
{
QVector<ServiceInfo> infoList;
@ -76,6 +79,8 @@ QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr, DWORD serviceType = SERV
DWORD serviceCount = 0;
DWORD resumePoint = 0;
int runningCount = 0;
while (EnumServicesStatusExW(mngr, SC_ENUM_PROCESS_INFO, serviceType, state, (LPBYTE) buffer,
sizeof(buffer), &bytesRemaining, &serviceCount, &resumePoint, nullptr)
|| GetLastError() == ERROR_MORE_DATA) {
@ -97,6 +102,7 @@ QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr, DWORD serviceType = SERV
const quint32 trackFlags = svcReg.value(serviceTrackFlagsKey).toUInt();
ServiceInfo info;
info.isRunning = (service->ServiceStatusProcess.dwCurrentState == SERVICE_RUNNING);
info.trackFlags = trackFlags;
info.processId = service->ServiceStatusProcess.dwProcessId;
info.serviceName = serviceName;
@ -105,6 +111,10 @@ QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr, DWORD serviceType = SERV
info.displayName = QString::fromUtf16((const char16_t *) service->lpDisplayName);
}
if (info.isRunning) {
++runningCount;
}
infoList.append(info);
}
@ -112,6 +122,10 @@ QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr, DWORD serviceType = SERV
break;
}
if (runningServicesCount) {
*runningServicesCount = runningCount;
}
return infoList;
}
@ -119,14 +133,19 @@ QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr, DWORD serviceType = SERV
ServiceInfoManager::ServiceInfoManager(QObject *parent) : QObject(parent) { }
QVector<ServiceInfo> ServiceInfoManager::loadServiceInfoList(
ServiceInfo::Type serviceType, ServiceInfo::State state, bool displayName)
void ServiceInfoManager::setUp()
{
setupServiceListMonitor();
}
QVector<ServiceInfo> ServiceInfoManager::loadServiceInfoList(ServiceInfo::Type serviceType,
ServiceInfo::State state, bool displayName, int *runningServicesCount)
{
QVector<ServiceInfo> list;
const SC_HANDLE mngr =
OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
if (mngr) {
list = getServiceInfoList(mngr, serviceType, state, displayName);
list = getServiceInfoList(mngr, serviceType, state, displayName, runningServicesCount);
CloseServiceHandle(mngr);
}
return list;
@ -180,3 +199,73 @@ void ServiceInfoManager::revertService(const QString &serviceName)
svcReg.removeValue(serviceTrackFlagsKey);
}
void ServiceInfoManager::monitorServices(const QVector<ServiceInfo> &serviceInfoList)
{
for (const ServiceInfo &serviceInfo : serviceInfoList) {
setupServiceMonitor(serviceInfo.serviceName);
}
}
void ServiceInfoManager::setupServiceListMonitor()
{
m_serviceListMonitor = new ServiceListMonitor(this);
connect(m_serviceListMonitor, &ServiceListMonitor::servicesCreated, this,
&ServiceInfoManager::onServicesCreated);
m_serviceListMonitor->startMonitor();
}
void ServiceInfoManager::setupServiceMonitor(const QString &serviceName)
{
if (isServiceMonitoring(serviceName))
return;
auto serviceMonitor = new ServiceMonitor(serviceName, m_serviceListMonitor);
connect(serviceMonitor, &ServiceMonitor::stateChanged, this,
[=] { onServiceStateChanged(serviceMonitor); });
startServiceMonitor(serviceMonitor);
}
bool ServiceInfoManager::isServiceMonitoring(const QString &serviceName) const
{
return m_serviceMonitors.contains(serviceName);
}
void ServiceInfoManager::startServiceMonitor(ServiceMonitor *serviceMonitor)
{
m_serviceMonitors.insert(serviceMonitor->serviceName(), serviceMonitor);
serviceMonitor->startMonitor(m_serviceListMonitor->managerHandle());
}
void ServiceInfoManager::stopServiceMonitor(ServiceMonitor *serviceMonitor)
{
m_serviceMonitors.remove(serviceMonitor->serviceName());
delete serviceMonitor;
}
void ServiceInfoManager::onServicesCreated(const QStringList &serviceNames)
{
for (const QString &serviceName : serviceNames) {
setupServiceMonitor(serviceName);
}
}
void ServiceInfoManager::onServiceStateChanged(ServiceMonitor *serviceMonitor)
{
switch (serviceMonitor->state()) {
case ServiceMonitor::ServiceStateUnknown: {
} break;
case ServiceMonitor::ServiceRunning: {
emit serviceStarted(serviceMonitor->serviceName(), serviceMonitor->processId());
} break;
case ServiceMonitor::ServiceDeleting: {
stopServiceMonitor(serviceMonitor);
} break;
}
}

View File

@ -1,9 +1,14 @@
#ifndef SERVICEINFOMANAGER_H
#define SERVICEINFOMANAGER_H
#include <QHash>
#include <util/ioc/iocservice.h>
#include <util/service/serviceinfo.h>
class ServiceListMonitor;
class ServiceMonitor;
class ServiceInfoManager : public QObject, public IocService
{
Q_OBJECT
@ -11,15 +16,39 @@ class ServiceInfoManager : public QObject, public IocService
public:
explicit ServiceInfoManager(QObject *parent = nullptr);
void setUp() override;
static QVector<ServiceInfo> loadServiceInfoList(
ServiceInfo::Type serviceType = ServiceInfo::TypeWin32,
ServiceInfo::State state = ServiceInfo::StateAll, bool displayName = true);
ServiceInfo::State state = ServiceInfo::StateAll, bool displayName = true,
int *runningServicesCount = nullptr);
static QString getSvcHostServiceDll(const QString &serviceName);
signals:
void serviceStarted(const QString &serviceName, qint32 processId);
public slots:
virtual void trackService(const QString &serviceName);
virtual void revertService(const QString &serviceName);
void monitorServices(const QVector<ServiceInfo> &serviceInfoList);
protected:
virtual void setupServiceListMonitor();
void setupServiceMonitor(const QString &serviceName);
bool isServiceMonitoring(const QString &serviceName) const;
void startServiceMonitor(ServiceMonitor *serviceMonitor);
void stopServiceMonitor(ServiceMonitor *serviceMonitor);
private:
void onServicesCreated(const QStringList &serviceNames);
void onServiceStateChanged(ServiceMonitor *serviceMonitor);
private:
ServiceListMonitor *m_serviceListMonitor = nullptr;
QHash<QString, ServiceMonitor *> m_serviceMonitors;
};
#endif // SERVICEINFOMANAGER_H

View File

@ -13,6 +13,9 @@ public:
public slots:
void trackService(const QString &serviceName) override;
void revertService(const QString &serviceName) override;
protected:
void setupServiceListMonitor() override { }
};
#endif // SERVICEINFOMANAGERRPC_H

View File

@ -108,13 +108,17 @@ int ConfUtil::writeVersion(QByteArray &buf)
return verSize;
}
int ConfUtil::writeServices(const QVector<ServiceInfo> &services, QByteArray &buf)
int ConfUtil::writeServices(
const QVector<ServiceInfo> &services, int runningServicesCount, QByteArray &buf)
{
buf.reserve(FORT_SERVICE_INFO_LIST_MIN_SIZE);
int outSize = writeServicesHeader(buf.data(), services.size());
int outSize = writeServicesHeader(buf.data(), runningServicesCount);
for (const ServiceInfo &info : services) {
if (!info.isRunning)
continue;
buf.reserve(outSize + FORT_SERVICE_INFO_MAX_SIZE);
outSize += writeServiceInfo(buf.data() + outSize, info);

View File

@ -44,7 +44,8 @@ signals:
public slots:
int writeVersion(QByteArray &buf);
int writeServices(const QVector<ServiceInfo> &services, QByteArray &buf);
int writeServices(
const QVector<ServiceInfo> &services, int runningServicesCount, QByteArray &buf);
int write(const FirewallConf &conf, ConfAppsWalker *confAppsWalker, EnvManager &envManager,
QByteArray &buf);
int writeFlags(const FirewallConf &conf, QByteArray &buf);

View File

@ -22,6 +22,7 @@ public:
bool isTracked() const { return trackFlags != 0; }
bool isRunning = 0;
quint32 trackFlags = 0;
quint32 processId = 0;
QString serviceName;

View File

@ -0,0 +1,108 @@
#include "servicelistmonitor.h"
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
namespace {
inline void startManagerMonitor(const ServiceListMonitor &monitor, PSERVICE_NOTIFYW notifyBuffer)
{
const auto managerHandle = SC_HANDLE(monitor.managerHandle());
constexpr DWORD notifyMask = SERVICE_NOTIFY_CREATED;
NotifyServiceStatusChangeW(managerHandle, notifyMask, notifyBuffer);
}
void parseServiceNames(
PCWSTR names, QStringList &createdServiceNames, QStringList &deletedServiceNames)
{
while (names[0] != L'\0') {
const bool isCreated = (names[0] == L'/');
if (isCreated) {
++names;
}
const auto name = QString::fromWCharArray(names);
if (isCreated) {
createdServiceNames.append(name);
} else {
deletedServiceNames.append(name);
}
names += name.size() + 1;
}
}
void CALLBACK managerNotifyCallback(PVOID param)
{
auto notifyBuffer = PSERVICE_NOTIFYW(param);
auto monitor = static_cast<ServiceListMonitor *>(notifyBuffer->pContext);
QStringList createdServiceNames;
QStringList deletedServiceNames;
if (notifyBuffer->dwNotificationStatus == ERROR_SUCCESS) {
PWSTR names = notifyBuffer->pszServiceNames;
if (names) {
parseServiceNames(names, createdServiceNames, deletedServiceNames);
LocalFree(names);
notifyBuffer->pszServiceNames = nullptr;
}
}
QMetaObject::invokeMethod(
monitor, [=] { monitor->onManagerNotify(createdServiceNames); }, Qt::QueuedConnection);
}
}
ServiceListMonitor::ServiceListMonitor(QObject *parent) : QObject(parent) { }
ServiceListMonitor::~ServiceListMonitor()
{
stopMonitor();
}
void ServiceListMonitor::startMonitor()
{
m_managerHandle =
OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
if (!m_managerHandle)
return;
m_buffer.resize(sizeof(SERVICE_NOTIFYW));
auto notifyBuffer = PSERVICE_NOTIFYW(m_buffer.data());
RtlZeroMemory(notifyBuffer, sizeof(SERVICE_NOTIFYW));
notifyBuffer->dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
notifyBuffer->pfnNotifyCallback = &managerNotifyCallback;
notifyBuffer->pContext = this;
startManagerMonitor(*this, notifyBuffer);
}
void ServiceListMonitor::stopMonitor()
{
if (!m_managerHandle)
return;
CloseServiceHandle(SC_HANDLE(m_managerHandle));
m_managerHandle = nullptr;
}
void ServiceListMonitor::onManagerNotify(const QStringList &createdServiceNames)
{
if (!m_managerHandle)
return;
auto notifyBuffer = PSERVICE_NOTIFYW(m_buffer.data());
startManagerMonitor(*this, notifyBuffer);
if (!createdServiceNames.isEmpty()) {
emit servicesCreated(createdServiceNames);
}
}

View File

@ -0,0 +1,31 @@
#ifndef SERVICELISTMONITOR_H
#define SERVICELISTMONITOR_H
#include <QByteArray>
#include <QObject>
class ServiceListMonitor : public QObject
{
Q_OBJECT
public:
explicit ServiceListMonitor(QObject *parent = nullptr);
~ServiceListMonitor() override;
void *managerHandle() const { return m_managerHandle; }
void startMonitor();
void stopMonitor();
void onManagerNotify(const QStringList &createdServiceNames);
signals:
void servicesCreated(const QStringList &serviceNames);
private:
void *m_managerHandle = nullptr;
QByteArray m_buffer;
};
#endif // SERVICELISTMONITOR_H

View File

@ -0,0 +1,97 @@
#include "servicemonitor.h"
#include <QLoggingCategory>
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
namespace {
inline void startServiceMonitor(const ServiceMonitor &monitor, PSERVICE_NOTIFYW notifyBuffer)
{
const auto serviceHandle = SC_HANDLE(monitor.serviceHandle());
constexpr DWORD notifyMask = SERVICE_NOTIFY_RUNNING | SERVICE_NOTIFY_DELETE_PENDING;
NotifyServiceStatusChangeW(serviceHandle, notifyMask, notifyBuffer);
}
void CALLBACK serviceNotifyCallback(PVOID param)
{
auto notifyBuffer = PSERVICE_NOTIFYW(param);
auto monitor = static_cast<ServiceMonitor *>(notifyBuffer->pContext);
DWORD notificationTriggered = 0;
DWORD processId = 0;
if (notifyBuffer->dwNotificationStatus == ERROR_SUCCESS) {
notificationTriggered = notifyBuffer->dwNotificationTriggered;
processId = notifyBuffer->ServiceStatus.dwProcessId;
}
QMetaObject::invokeMethod(
monitor, [=] { monitor->onServiceNotify(notificationTriggered, processId); },
Qt::QueuedConnection);
}
}
ServiceMonitor::ServiceMonitor(const QString &serviceName, QObject *parent) :
QObject(parent), m_serviceName(serviceName)
{
}
ServiceMonitor::~ServiceMonitor()
{
stopMonitor();
}
void ServiceMonitor::startMonitor(void *managerHandle)
{
m_serviceHandle = OpenServiceW(SC_HANDLE(managerHandle), PCWSTR(serviceName().utf16()),
SERVICE_QUERY_STATUS | SERVICE_INTERROGATE);
if (!m_serviceHandle)
return;
m_buffer.resize(sizeof(SERVICE_NOTIFYW));
auto notifyBuffer = PSERVICE_NOTIFYW(m_buffer.data());
RtlZeroMemory(notifyBuffer, sizeof(SERVICE_NOTIFYW));
notifyBuffer->dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
notifyBuffer->pfnNotifyCallback = &serviceNotifyCallback;
notifyBuffer->pContext = this;
startServiceMonitor(*this, notifyBuffer);
}
void ServiceMonitor::stopMonitor()
{
if (!m_serviceHandle)
return;
CloseServiceHandle(SC_HANDLE(m_serviceHandle));
m_serviceHandle = nullptr;
}
void ServiceMonitor::onServiceNotify(quint32 notificationTriggered, qint32 processId)
{
if (!m_serviceHandle)
return;
auto notifyBuffer = PSERVICE_NOTIFYW(m_buffer.data());
startServiceMonitor(*this, notifyBuffer);
m_processId = processId;
switch (notificationTriggered) {
case SERVICE_NOTIFY_RUNNING: {
m_state = ServiceRunning;
} break;
case SERVICE_NOTIFY_DELETE_PENDING: {
m_state = ServiceDeleting;
} break;
}
emit stateChanged();
}

View File

@ -0,0 +1,46 @@
#ifndef SERVICEMONITOR_H
#define SERVICEMONITOR_H
#include <QByteArray>
#include <QObject>
class ServiceMonitor : public QObject
{
Q_OBJECT
public:
enum ServiceState : qint8 {
ServiceStateUnknown = 0,
ServiceRunning,
ServiceDeleting,
};
Q_ENUM(ServiceState)
explicit ServiceMonitor(const QString &serviceName, QObject *parent = nullptr);
~ServiceMonitor() override;
ServiceState state() const { return m_state; }
qint32 processId() const { return m_processId; }
void *serviceHandle() const { return m_serviceHandle; }
const QString &serviceName() const { return m_serviceName; }
void startMonitor(void *managerHandle);
void stopMonitor();
void onServiceNotify(quint32 notificationTriggered, qint32 processId);
signals:
void stateChanged();
private:
ServiceState m_state = ServiceStateUnknown;
qint32 m_processId = 0;
void *m_serviceHandle = nullptr;
const QString m_serviceName;
QByteArray m_buffer;
};
#endif // SERVICEMONITOR_H