UI: Prepare ServiceInfoMonitor

This commit is contained in:
Nodir Temirkhodjaev 2021-12-26 21:08:02 +03:00
parent e372bf8c1c
commit ef1fcf84b9
15 changed files with 363 additions and 118 deletions

View File

@ -111,6 +111,7 @@ SOURCES += \
rpc/windowmanagerfake.cpp \
serviceinfo/serviceinfo.cpp \
serviceinfo/serviceinfomanager.cpp \
serviceinfo/serviceinfomonitor.cpp \
stat/quotamanager.cpp \
stat/statmanager.cpp \
stat/statsql.cpp \
@ -264,6 +265,7 @@ HEADERS += \
rpc/windowmanagerfake.h \
serviceinfo/serviceinfo.h \
serviceinfo/serviceinfomanager.h \
serviceinfo/serviceinfomonitor.h \
stat/quotamanager.h \
stat/statmanager.h \
stat/statsql.h \

View File

@ -22,7 +22,6 @@ FirewallConf::FirewallConf(Settings *settings, QObject *parent) :
m_logBlockedIp(false),
m_appBlockAll(true),
m_appAllowAll(false),
m_filterServices(false),
m_activePeriodEnabled(false),
m_ini(settings)
{
@ -102,11 +101,6 @@ void FirewallConf::setAppAllowAll(bool appAllowAll)
m_appAllowAll = appAllowAll;
}
void FirewallConf::setFilterServices(bool filterServices)
{
m_filterServices = filterServices;
}
void FirewallConf::setActivePeriodEnabled(bool activePeriodEnabled)
{
m_activePeriodEnabled = activePeriodEnabled;
@ -295,8 +289,6 @@ void FirewallConf::copyFlags(const FirewallConf &o)
m_appBlockAll = o.appBlockAll();
m_appAllowAll = o.appAllowAll();
m_filterServices = o.filterServices();
m_activePeriodEnabled = o.activePeriodEnabled();
m_activePeriodFrom = o.activePeriodFrom();
m_activePeriodTo = o.activePeriodTo();
@ -345,8 +337,6 @@ QVariant FirewallConf::flagsToVariant() const
map["appBlockAll"] = appBlockAll();
map["appAllowAll"] = appAllowAll();
map["filterServices"] = filterServices();
map["activePeriodEnabled"] = activePeriodEnabled();
map["activePeriodFrom"] = activePeriodFrom();
map["activePeriodTo"] = activePeriodTo();
@ -377,8 +367,6 @@ void FirewallConf::flagsFromVariant(const QVariant &v)
m_appBlockAll = map["appBlockAll"].toBool();
m_appAllowAll = map["appAllowAll"].toBool();
m_filterServices = map["filterServices"].toBool();
m_activePeriodEnabled = map["activePeriodEnabled"].toBool();
m_activePeriodFrom = map["activePeriodFrom"].toString();
m_activePeriodTo = map["activePeriodTo"].toString();

View File

@ -85,9 +85,6 @@ public:
bool appAllowAll() const { return m_appAllowAll; }
void setAppAllowAll(bool appAllowAll);
bool filterServices() const { return m_filterServices; }
void setFilterServices(bool filterServices);
bool activePeriodEnabled() const { return m_activePeriodEnabled; }
void setActivePeriodEnabled(bool activePeriodEnabled);
@ -185,8 +182,6 @@ private:
uint m_appBlockAll : 1;
uint m_appAllowAll : 1;
uint m_filterServices : 1;
uint m_activePeriodEnabled : 1;
quint32 m_appGroupBits = 0;

View File

@ -31,11 +31,11 @@ ServiceInfoManager *ServicesPage::serviceInfoManager() const
void ServicesPage::onRetranslateUi()
{
m_btRefresh->setText(tr("Refresh"));
m_btEdit->setText(tr("Edit"));
m_actEditService->setText(tr("Edit Service"));
m_btOptions->setText(tr("Options"));
m_cbFilterServices->setText(tr("Filter Services"));
serviceListModel()->refresh();
}
@ -61,6 +61,9 @@ QLayout *ServicesPage::setupHeader()
{
auto layout = new QHBoxLayout();
m_btRefresh = ControlUtil::createButton(
":/icons/arrow_refresh_small.png", [&] { serviceListModel()->initialize(); });
// Edit Menu
auto editMenu = new QMenu(this);
@ -80,6 +83,7 @@ QLayout *ServicesPage::setupHeader()
// Options
setupOptions();
layout->addWidget(m_btRefresh);
layout->addWidget(m_btEdit);
layout->addStretch();
layout->addWidget(m_btOptions);
@ -89,10 +93,8 @@ QLayout *ServicesPage::setupHeader()
void ServicesPage::setupOptions()
{
setupFilterServices();
// Menu
const QList<QWidget *> menuWidgets = { m_cbFilterServices };
const QList<QWidget *> menuWidgets = {};
auto layout = ControlUtil::createLayoutByWidgets(menuWidgets);
auto menu = ControlUtil::createMenuByLayout(layout, this);
@ -101,19 +103,6 @@ void ServicesPage::setupOptions()
m_btOptions->setMenu(menu);
}
void ServicesPage::setupFilterServices()
{
m_cbFilterServices = ControlUtil::createCheckBox(conf()->filterServices(), [&](bool checked) {
if (conf()->filterServices() == checked)
return;
conf()->setFilterServices(checked);
ctrl()->setFlagsEdited();
updateFilterServices();
});
}
void ServicesPage::setupTableServiceList()
{
m_serviceListView = new TableView();
@ -133,15 +122,10 @@ void ServicesPage::setupTableServiceListHeader()
header->setSectionResizeMode(0, QHeaderView::Interactive);
header->setSectionResizeMode(1, QHeaderView::Stretch);
header->setSectionResizeMode(2, QHeaderView::Interactive);
header->setSectionResizeMode(3, QHeaderView::Interactive);
header->resizeSection(0, 250);
header->resizeSection(1, 350);
header->resizeSection(2, 90);
}
void ServicesPage::updateFilterServices()
{
if (conf()->filterServices()) {
serviceInfoManager()->setEnabled(true);
}
header->resizeSection(0, 240);
header->resizeSection(1, 290);
header->resizeSection(2, 80);
header->resizeSection(3, 90);
}

View File

@ -24,19 +24,16 @@ private:
void setupUi();
QLayout *setupHeader();
void setupOptions();
void setupFilterServices();
void setupTableServiceList();
void setupTableServiceListHeader();
void updateFilterServices();
private:
ServiceListModel *m_serviceListModel;
QPushButton *m_btRefresh = nullptr;
QPushButton *m_btEdit = nullptr;
QAction *m_actEditService = nullptr;
QPushButton *m_btOptions = nullptr;
QCheckBox *m_cbFilterServices = nullptr;
TableView *m_serviceListView = nullptr;
};

View File

@ -84,6 +84,7 @@ void FortManager::initialize()
setupConfManager();
setupQuotaManager();
setupTaskManager();
setupServiceInfoManager();
setupDriver();
loadConf();
@ -258,7 +259,6 @@ void FortManager::setupConfManager()
const FirewallConf *conf = IoC<ConfManager>()->conf();
updateLogger(conf);
updateServiceInfoManager(conf);
if (!onlyFlags || conf->flagsEdited()) {
updateDriverConf(onlyFlags);
@ -287,12 +287,11 @@ void FortManager::setupTaskManager()
IoC<ConfManager>(), &ConfManager::updateDriverZones);
}
void FortManager::updateServiceInfoManager(const FirewallConf *conf)
void FortManager::setupServiceInfoManager()
{
if (!conf->flagsEdited())
return;
auto serviceInfoManager = IoC<ServiceInfoManager>();
IoC<ServiceInfoManager>()->setEnabled(conf->filterServices());
serviceInfoManager->setMonitorEnabled(IoC<FortSettings>()->isMaster());
}
void FortManager::setupTranslationManager()

View File

@ -46,8 +46,7 @@ private:
void setupConfManager();
void setupQuotaManager();
void setupTaskManager();
void updateServiceInfoManager(const FirewallConf *conf);
void setupServiceInfoManager();
void setupTranslationManager();

View File

@ -347,7 +347,6 @@ void FortSettings::writeConfIni(const FirewallConf &conf)
setIniValue("logBlockedIp", conf.logBlockedIp());
setIniValue("appBlockAll", conf.appBlockAll());
setIniValue("appAllowAll", conf.appAllowAll());
setIniValue("filterServices", conf.filterServices());
setIniValue("appGroupBits", conf.appGroupBits(), DEFAULT_APP_GROUP_BITS);
ini()->endGroup();

View File

@ -25,20 +25,21 @@ FirewallConf *ServiceListModel::conf() const
void ServiceListModel::initialize()
{
connect(serviceInfoManager(), &ServiceInfoManager::servicesChanged, this,
&ServiceListModel::reset);
m_services = serviceInfoManager()->loadServiceInfoList();
reset();
}
int ServiceListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return serviceInfoManager()->services().size();
return services().size();
}
int ServiceListModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : 3;
return parent.isValid() ? 0 : 4;
}
QVariant ServiceListModel::headerData(int section, Qt::Orientation orientation, int role) const
@ -50,6 +51,8 @@ QVariant ServiceListModel::headerData(int section, Qt::Orientation orientation,
case 1:
return tr("Display Name");
case 2:
return tr("Process ID");
case 3:
return tr("Group");
}
}
@ -76,7 +79,7 @@ QVariant ServiceListModel::dataDisplay(const QModelIndex &index) const
const int row = index.row();
const int column = index.column();
const auto info = serviceInfoManager()->serviceInfoAt(row);
const auto info = serviceInfoAt(row);
switch (column) {
case 0:
@ -84,18 +87,36 @@ QVariant ServiceListModel::dataDisplay(const QModelIndex &index) const
case 1:
return info.displayName;
case 2:
return dataDisplayProcessId(info);
case 3:
return dataDisplayAppGroup(info);
}
return QVariant();
}
QVariant ServiceListModel::dataDisplayProcessId(const ServiceInfo &info) const
{
return (info.processId == 0) ? QVariant() : QVariant(info.processId);
}
QVariant ServiceListModel::dataDisplayAppGroup(const ServiceInfo &info) const
{
return (info.groupIndex < 0) ? QVariant() : conf()->appGroupAt(info.groupIndex)->name();
const int groupIndex = serviceInfoManager()->groupIndexByName(info.serviceName);
return (groupIndex < 0) ? QVariant() : conf()->appGroupAt(groupIndex)->name();
}
bool ServiceListModel::updateTableRow(int /*row*/) const
{
return true;
}
const ServiceInfo &ServiceListModel::serviceInfoAt(int index) const
{
if (index < 0 || index >= services().size()) {
static const ServiceInfo g_nullServiceInfo;
return g_nullServiceInfo;
}
return services()[index];
}

View File

@ -3,6 +3,7 @@
#include <QVector>
#include <serviceinfo/serviceinfo.h>
#include <util/model/tableitemmodel.h>
class ConfManager;
@ -36,9 +37,15 @@ protected:
private:
QVariant dataDisplay(const QModelIndex &index) const;
QVariant dataDisplayProcessId(const ServiceInfo &info) const;
QVariant dataDisplayAppGroup(const ServiceInfo &info) const;
const QVector<ServiceInfo> &services() const { return m_services; }
const ServiceInfo &serviceInfoAt(int index) const;
private:
QVector<ServiceInfo> m_services;
mutable TableRow m_serviceRow;
};

View File

@ -6,11 +6,13 @@
class ServiceInfo
{
public:
int groupIndex = -1;
enum State {
StateActive = 0x01,
StateInactive = 0x02,
StateDeleted = 0x04,
};
quint32 processId = 0;
quint64 id = 0;
QString serviceName;
QString displayName;
};

View File

@ -1,10 +1,16 @@
#include "serviceinfomanager.h"
#include <QLoggingCategory>
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
#include "serviceinfomonitor.h"
namespace {
const QLoggingCategory LC("serviceInfo.serviceInfoManager");
QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr)
{
QVector<ServiceInfo> infoList;
@ -43,54 +49,94 @@ QVector<ServiceInfo> getServiceInfoList(SC_HANDLE mngr)
ServiceInfoManager::ServiceInfoManager(QObject *parent) : QObject(parent) { }
void ServiceInfoManager::setEnabled(bool v)
ServiceInfoManager::~ServiceInfoManager()
{
if (m_enabled != v) {
m_enabled = v;
updateWorker();
updateServices();
clearServiceMonitors();
}
void ServiceInfoManager::setMonitorEnabled(bool v)
{
if (m_monitorEnabled != v) {
m_monitorEnabled = v;
setupServiceMonitors();
}
}
const ServiceInfo &ServiceInfoManager::serviceInfoAt(int index) const
int ServiceInfoManager::groupIndexByName(const QString &name) const
{
if (index < 0 || index >= services().size()) {
static const ServiceInfo g_nullServiceInfo;
return g_nullServiceInfo;
}
return services()[index];
return m_serviceGroups.value(name, -1);
}
void ServiceInfoManager::updateWorker()
{
if (enabled()) {
startWorker();
} else {
stopWorker();
}
}
void ServiceInfoManager::startWorker() { }
void ServiceInfoManager::stopWorker() { }
void ServiceInfoManager::updateServices()
{
services().clear();
if (enabled()) {
loadServices();
}
emit servicesChanged();
}
void ServiceInfoManager::loadServices()
QVector<ServiceInfo> ServiceInfoManager::loadServiceInfoList()
{
QVector<ServiceInfo> list;
const SC_HANDLE mngr =
OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
if (mngr) {
m_services = getServiceInfoList(mngr);
list = getServiceInfoList(mngr);
CloseServiceHandle(mngr);
}
return list;
}
void ServiceInfoManager::setupServiceMonitors()
{
clearServiceMonitors();
if (!monitorEnabled())
return;
const SC_HANDLE mngr =
OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
if (!mngr) {
qCCritical(LC) << "Open manager error:" << GetLastError();
return;
}
const auto services = loadServiceInfoList();
for (const auto &info : services) {
const auto m = startServiceMonitor(info, mngr);
m_serviceMonitors.insert(info.serviceName, m);
}
CloseServiceHandle(mngr);
}
void ServiceInfoManager::clearServiceMonitors()
{
const auto moniros = m_serviceMonitors.values();
for (ServiceInfoMonitor *m : moniros) {
stopServiceMonitor(m);
}
m_serviceMonitors.clear();
}
ServiceInfoMonitor *ServiceInfoManager::startServiceMonitor(
const ServiceInfo &info, void *managerHandle)
{
const auto m = new ServiceInfoMonitor(info.processId, info.serviceName, managerHandle);
connect(m, &ServiceInfoMonitor::stateChanged, this, &ServiceInfoManager::onServiceStateChanged);
return m;
}
void ServiceInfoManager::stopServiceMonitor(ServiceInfoMonitor *m)
{
m->terminate();
m->deleteLater();
}
void ServiceInfoManager::onServiceStateChanged(ServiceInfo::State state)
{
const auto m = qobject_cast<ServiceInfoMonitor *>(sender());
Q_ASSERT(m);
if (state == ServiceInfo::StateDeleted) {
stopServiceMonitor(m);
m_serviceMonitors.remove(m->name());
}
}

View File

@ -8,41 +8,40 @@
#include "serviceinfo.h"
class ServiceInfoMonitor;
class ServiceInfoManager : public QObject, public IocService
{
Q_OBJECT
public:
explicit ServiceInfoManager(QObject *parent = nullptr);
~ServiceInfoManager() override;
bool enabled() const { return m_enabled; }
void setEnabled(bool v);
bool monitorEnabled() const { return m_monitorEnabled; }
void setMonitorEnabled(bool v);
QHash<QString, int> &serviceGroups() { return m_serviceGroups; }
const QHash<QString, int> &serviceGroups() const { return m_serviceGroups; }
int groupIndexByName(const QString &name) const;
QVector<ServiceInfo> &services() { return m_services; }
const QVector<ServiceInfo> &services() const { return m_services; }
const ServiceInfo &serviceInfoAt(int index) const;
static QVector<ServiceInfo> loadServiceInfoList();
signals:
void servicesChanged();
private:
void updateWorker();
void startWorker();
void stopWorker();
void setupServiceMonitors();
void clearServiceMonitors();
void updateServices();
void loadServices();
void loadServiceInfos();
ServiceInfoMonitor *startServiceMonitor(const ServiceInfo &info, void *managerHandle);
void stopServiceMonitor(ServiceInfoMonitor *m);
void onServiceStateChanged(ServiceInfo::State state);
private:
bool m_enabled = false;
bool m_monitorEnabled = false;
QHash<QString, int> m_serviceGroups;
QVector<ServiceInfo> m_services;
QHash<QString, ServiceInfoMonitor *> m_serviceMonitors;
};
#endif // SERVICEINFOMANAGER_H

View File

@ -0,0 +1,155 @@
#include "serviceinfomonitor.h"
#include <QLoggingCategory>
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
#define asServiceHandle(h) static_cast<SC_HANDLE>((h))
#define serviceHandle() asServiceHandle(m_serviceHandle)
namespace {
const QLoggingCategory LC("serviceInfo.serviceInfoMonitor");
static void CALLBACK notifyCallback(PVOID parameter)
{
PSERVICE_NOTIFYW notify = static_cast<PSERVICE_NOTIFYW>(parameter);
ServiceInfoMonitor *m = static_cast<ServiceInfoMonitor *>(notify->pContext);
if (notify->dwNotificationStatus != ERROR_SUCCESS) {
if (notify->dwNotificationStatus == ERROR_SERVICE_MARKED_FOR_DELETE) {
emit m->stateChanged(ServiceInfo::StateDeleted);
}
return;
}
m->setProcessId(notify->ServiceStatus.dwProcessId);
const ServiceInfo::State state = (notify->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
? ServiceInfo::StateInactive
: ServiceInfo::StateActive;
emit m->stateChanged(state);
// NotifyServiceStatusChange() must not be called from the callback
m->requestStartNotifier();
}
PSERVICE_NOTIFYW getNotifyBuffer(ServiceInfoMonitor *m)
{
QByteArray &buffer = m->notifyBuffer();
if (buffer.isNull()) {
buffer = QByteArray(sizeof(SERVICE_NOTIFYW), '\0');
}
PSERVICE_NOTIFYW notify = reinterpret_cast<PSERVICE_NOTIFYW>(buffer.data());
notify->dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
notify->pfnNotifyCallback = notifyCallback;
notify->pContext = m;
return notify;
}
}
ServiceInfoMonitor::ServiceInfoMonitor(
quint32 processId, const QString &name, void *managerHandle, QObject *parent) :
QObject(parent),
m_terminated(false),
m_isReopening(false),
m_startNotifierRequested(false),
m_processId(processId),
m_name(name)
{
openService(managerHandle);
}
ServiceInfoMonitor::~ServiceInfoMonitor()
{
closeService();
}
void ServiceInfoMonitor::terminate()
{
m_terminated = true;
closeService();
}
void ServiceInfoMonitor::requestStartNotifier()
{
if (m_startNotifierRequested)
return;
m_startNotifierRequested = true;
QMetaObject::invokeMethod(this, &ServiceInfoMonitor::startNotifier, Qt::QueuedConnection);
}
void ServiceInfoMonitor::openService(void *managerHandle)
{
const SC_HANDLE mngr = managerHandle ? asServiceHandle(managerHandle)
: OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
if (mngr) {
m_serviceHandle = OpenServiceW(mngr, (LPCWSTR) name().utf16(), SERVICE_QUERY_STATUS);
if (!managerHandle) {
CloseServiceHandle(mngr);
}
}
if (!m_serviceHandle) {
qCCritical(LC) << "Open service error:" << name() << GetLastError();
return;
}
startNotifier();
}
void ServiceInfoMonitor::closeService()
{
if (m_serviceHandle) {
CloseServiceHandle(serviceHandle());
m_serviceHandle = nullptr;
}
}
void ServiceInfoMonitor::reopenService()
{
if (m_isReopening) {
qCCritical(LC) << "Reopen service error:" << name();
return;
}
m_isReopening = true;
closeService();
openService();
m_isReopening = false;
}
void ServiceInfoMonitor::startNotifier()
{
if (m_terminated)
return;
m_startNotifierRequested = false;
constexpr DWORD mask = SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING;
PSERVICE_NOTIFYW notify = getNotifyBuffer(this);
const DWORD res = NotifyServiceStatusChangeW(serviceHandle(), mask, notify);
if (res != ERROR_SUCCESS) {
if (res == ERROR_SERVICE_NOTIFY_CLIENT_LAGGING) {
qCDebug(LC) << "Notifier is lagging:" << name();
reopenService();
} else {
qCCritical(LC) << "Start notifier error:" << name() << res;
}
}
}

View File

@ -0,0 +1,52 @@
#ifndef SERVICEINFOMONITOR_H
#define SERVICEINFOMONITOR_H
#include <QObject>
#include "serviceinfo.h"
class ServiceInfoMonitor : public QObject
{
Q_OBJECT
public:
explicit ServiceInfoMonitor(quint32 processId, const QString &name,
void *managerHandle = nullptr, QObject *parent = nullptr);
~ServiceInfoMonitor() override;
quint32 processId() const { return m_processId; }
void setProcessId(quint32 v) { m_processId = v; }
QString name() const { return m_name; }
QByteArray &notifyBuffer() { return m_notifyBuffer; }
signals:
void stateChanged(ServiceInfo::State state);
public slots:
void terminate();
void requestStartNotifier();
private:
void openService(void *managerHandle = nullptr);
void closeService();
void reopenService();
void startNotifier();
private:
bool m_terminated : 1;
bool m_isReopening : 1;
bool m_startNotifierRequested : 1;
quint32 m_processId = 0;
void *m_serviceHandle = nullptr;
QByteArray m_notifyBuffer;
const QString m_name;
};
#endif // SERVICEINFOMONITOR_H