UI: Option to try to start a Service on startup

This commit is contained in:
Nodir Temirkhodjaev 2024-02-24 13:21:03 +03:00
parent 25f4b41de6
commit 4536d858c4
10 changed files with 230 additions and 104 deletions

View File

@ -43,3 +43,6 @@
;Try to install a driver on opening error.
;canInstallDriver=false
;Try to start a Service on startup.
;canStartService=false

View File

@ -198,6 +198,7 @@ SOURCES += \
util/osutil.cpp \
util/processinfo.cpp \
util/regkey.cpp \
util/service/servicehandle.cpp \
util/service/serviceinfo.cpp \
util/service/servicelistmonitor.cpp \
util/service/servicemanageriface.cpp \
@ -412,6 +413,7 @@ HEADERS += \
util/osutil.h \
util/processinfo.h \
util/regkey.h \
util/service/servicehandle.h \
util/service/serviceinfo.h \
util/service/servicelistmonitor.h \
util/service/servicemanageriface.h \

View File

@ -179,6 +179,7 @@ void FortManager::initialize()
loadConf();
checkInstallDriver();
checkStartService();
}
void FortManager::setupThreadPool()
@ -329,14 +330,29 @@ void FortManager::checkInstallDriver()
const auto settings = IoC<FortSettings>();
const bool canInstallDriver = (settings->canInstallDriver() || settings->isPortable())
&& settings->isMaster() && settings->isUserAdmin();
const bool canInstallDriver = (settings->canInstallDriver() || settings->isPortable());
const bool isMasterAdmin = (settings->isMaster() && settings->isUserAdmin());
if (canInstallDriver) {
if (canInstallDriver && isMasterAdmin) {
installDriver();
}
}
void FortManager::checkStartService()
{
const auto settings = IoC<FortSettings>();
if (settings->isMaster() || StartupUtil::isServiceRunning())
return;
const bool canStartService = settings->canStartService();
const bool isAdmin = settings->isUserAdmin();
if (canStartService && isAdmin) {
StartupUtil::startService();
}
}
void FortManager::setupEnvManager()
{
auto envManager = IoC<EnvManager>();

View File

@ -45,6 +45,7 @@ private:
void closeDriver();
void checkInstallDriver();
void checkStartService();
void setupEnvManager();
void setupConfManager();

View File

@ -119,6 +119,7 @@ void FortSettings::setupGlobal()
m_noCache = settings.value("global/noCache").toBool();
m_canInstallDriver = settings.value("global/canInstallDriver").toBool();
m_canStartService = settings.value("global/canStartService").toBool();
m_defaultLanguage = settings.value("global/defaultLanguage").toString();
m_profilePath = settings.value("global/profileDir").toString();

View File

@ -33,6 +33,7 @@ public:
bool noCache() const { return m_noCache; }
bool noSplash() const { return m_noSplash; }
bool canInstallDriver() const { return m_canInstallDriver; }
bool canStartService() const { return m_canStartService; }
bool isService() const { return m_isService; }
bool hasService() const { return m_hasService; }
@ -129,6 +130,7 @@ private:
uint m_noCache : 1 = false;
uint m_noSplash : 1 = false;
uint m_canInstallDriver : 1 = false;
uint m_canStartService : 1 = false;
uint m_isService : 1 = false;
uint m_hasService : 1 = false;
uint m_isUserAdmin : 1 = false;

View File

@ -0,0 +1,120 @@
#include "servicehandle.h"
#include <QThread>
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
#include "servicemanageriface.h"
ServiceHandle::ServiceHandle(
const wchar_t *serviceName, quint32 managerAccess, quint32 serviceAccess)
{
openService(serviceName, managerAccess, serviceAccess);
}
ServiceHandle::~ServiceHandle()
{
closeService();
}
bool ServiceHandle::queryIsRunning()
{
SERVICE_STATUS status;
if (QueryServiceStatus(SC_HANDLE(m_serviceHandle), &status)) {
return (status.dwCurrentState == SERVICE_RUNNING);
}
return false;
}
bool ServiceHandle::startService()
{
return StartServiceW(SC_HANDLE(m_serviceHandle), 0, nullptr);
}
bool ServiceHandle::stopService()
{
int n = 3; /* count of attempts to stop the service */
do {
SERVICE_STATUS status;
if (QueryServiceStatus(SC_HANDLE(m_serviceHandle), &status)
&& status.dwCurrentState == SERVICE_STOPPED)
return true;
const DWORD controlCode = (status.dwControlsAccepted & SERVICE_ACCEPT_STOP) != 0
? SERVICE_CONTROL_STOP
: FORT_SERVICE_CONTROL_UNINSTALL;
ControlService(SC_HANDLE(m_serviceHandle), controlCode, &status);
QThread::msleep(n * 100);
} while (--n > 0);
return false;
}
void ServiceHandle::createService(const wchar_t *serviceName, const wchar_t *serviceDisplay,
const wchar_t *serviceGroup, const wchar_t *dependencies, const QString &command)
{
m_serviceHandle = qintptr(CreateServiceW(SC_HANDLE(m_managerHandle), serviceName,
serviceDisplay, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL, (LPCWSTR) command.utf16(), serviceGroup, 0, dependencies, nullptr,
nullptr));
}
bool ServiceHandle::deleteService()
{
return DeleteService(SC_HANDLE(m_serviceHandle));
}
void ServiceHandle::setServiceDescription(const wchar_t *serviceDescription)
{
SERVICE_DESCRIPTION sd = { (LPWSTR) serviceDescription };
ChangeServiceConfig2(SC_HANDLE(m_serviceHandle), SERVICE_CONFIG_DESCRIPTION, &sd);
}
void ServiceHandle::setupServiceRestartConfig()
{
constexpr int actionsCount = 3;
SC_ACTION actions[actionsCount];
actions[0].Type = SC_ACTION_RESTART;
actions[0].Delay = 150;
actions[1].Type = SC_ACTION_NONE;
actions[1].Delay = 0;
actions[2].Type = SC_ACTION_NONE;
actions[2].Delay = 0;
SERVICE_FAILURE_ACTIONS sfa;
sfa.dwResetPeriod = 0;
sfa.lpCommand = NULL;
sfa.lpRebootMsg = NULL;
sfa.cActions = actionsCount;
sfa.lpsaActions = actions;
ChangeServiceConfig2(SC_HANDLE(m_serviceHandle), SERVICE_CONFIG_FAILURE_ACTIONS, &sfa);
}
void ServiceHandle::openService(
const wchar_t *serviceName, quint32 managerAccess, quint32 serviceAccess)
{
m_managerHandle = qintptr(OpenSCManagerW(nullptr, nullptr, managerAccess));
if (!m_managerHandle)
return;
if (serviceAccess != 0) {
m_serviceHandle =
qintptr(OpenServiceW(SC_HANDLE(m_managerHandle), serviceName, serviceAccess));
}
}
void ServiceHandle::closeService()
{
if (m_serviceHandle) {
CloseServiceHandle(SC_HANDLE(m_serviceHandle));
}
if (m_managerHandle) {
CloseServiceHandle(SC_HANDLE(m_managerHandle));
}
}

View File

@ -0,0 +1,37 @@
#ifndef SERVICEHANDLE_H
#define SERVICEHANDLE_H
#include <QObject>
class ServiceHandle final
{
public:
explicit ServiceHandle(
const wchar_t *serviceName, quint32 managerAccess = 0, quint32 serviceAccess = 0);
~ServiceHandle();
bool isManagerOpened() const { return m_managerHandle != 0; }
bool isServiceOpened() const { return m_serviceHandle != 0; }
bool queryIsRunning();
bool startService();
bool stopService();
void createService(const wchar_t *serviceName, const wchar_t *serviceDisplay,
const wchar_t *serviceGroup, const wchar_t *dependencies, const QString &command);
bool deleteService();
void setServiceDescription(const wchar_t *serviceDescription);
void setupServiceRestartConfig();
private:
void openService(const wchar_t *serviceName, quint32 managerAccess, quint32 serviceAccess);
void closeService();
private:
qintptr m_managerHandle = 0;
qintptr m_serviceHandle = 0;
};
#endif // SERVICEHANDLE_H

View File

@ -9,9 +9,10 @@
#include <fort_version_l.h>
#include "fileutil.h"
#include "regkey.h"
#include "service/servicemanageriface.h"
#include <util/fileutil.h>
#include <util/regkey.h>
#include <util/service/servicehandle.h>
#include <util/service/servicemanageriface.h>
namespace {
@ -91,87 +92,34 @@ void removeAutorunForAllUsers()
removeAutorunForUser(regAllUsersRoot, regAllUsersRun);
}
static void setupServiceRestartConfig(SC_HANDLE svc)
{
constexpr int actionsCount = 3;
SC_ACTION actions[actionsCount];
actions[0].Type = SC_ACTION_RESTART;
actions[0].Delay = 150;
actions[1].Type = SC_ACTION_NONE;
actions[1].Delay = 0;
actions[2].Type = SC_ACTION_NONE;
actions[2].Delay = 0;
SERVICE_FAILURE_ACTIONS sfa;
sfa.dwResetPeriod = 0;
sfa.lpCommand = NULL;
sfa.lpRebootMsg = NULL;
sfa.cActions = actionsCount;
sfa.lpsaActions = actions;
ChangeServiceConfig2(svc, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa);
}
bool installService(const wchar_t *serviceName, const wchar_t *serviceDisplay,
const wchar_t *serviceDescription, const wchar_t *serviceGroup, const wchar_t *dependencies,
const QString &command)
{
bool res = false;
const SC_HANDLE mngr = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (mngr) {
const SC_HANDLE svc = CreateServiceW(mngr, serviceName, serviceDisplay, SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
(LPCWSTR) command.utf16(), serviceGroup, 0, dependencies, nullptr, nullptr);
if (svc) {
SERVICE_DESCRIPTION sd = { (LPWSTR) serviceDescription };
ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd);
ServiceHandle svc(serviceName, SC_MANAGER_CREATE_SERVICE);
if (!svc.isManagerOpened())
return false;
setupServiceRestartConfig(svc);
svc.createService(serviceName, serviceDisplay, serviceGroup, dependencies, command);
if (!svc.isServiceOpened())
return false;
res = true;
CloseServiceHandle(svc);
}
CloseServiceHandle(mngr);
}
return res;
}
svc.setServiceDescription(serviceDescription);
svc.setupServiceRestartConfig();
bool stopServiceControl(SC_HANDLE svc)
{
int n = 3; /* count of attempts to stop the service */
do {
SERVICE_STATUS status;
if (QueryServiceStatus(svc, &status) && status.dwCurrentState == SERVICE_STOPPED)
return true;
const DWORD controlCode = (status.dwControlsAccepted & SERVICE_ACCEPT_STOP) != 0
? SERVICE_CONTROL_STOP
: FORT_SERVICE_CONTROL_UNINSTALL;
ControlService(svc, controlCode, &status);
QThread::msleep(n * 100);
} while (--n > 0);
return false;
return true;
}
bool uninstallService(const wchar_t *serviceName)
{
bool res = false;
const SC_HANDLE mngr = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (mngr) {
const SC_HANDLE svc = OpenServiceW(mngr, serviceName, SERVICE_ALL_ACCESS | DELETE);
if (svc) {
stopServiceControl(svc);
ServiceHandle svc(serviceName, SC_MANAGER_ALL_ACCESS, SERVICE_ALL_ACCESS | DELETE);
if (svc.isServiceOpened()) {
svc.stopService();
res = DeleteService(svc);
CloseServiceHandle(svc);
}
CloseServiceHandle(mngr);
return svc.deleteService();
}
return res;
return false;
}
}
@ -183,17 +131,9 @@ const wchar_t *StartupUtil::serviceName()
bool StartupUtil::isServiceInstalled()
{
bool res = false;
const SC_HANDLE mngr = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
if (mngr) {
const SC_HANDLE svc = OpenServiceW(mngr, serviceNameStr, SERVICE_INTERROGATE);
if (svc) {
res = true;
CloseServiceHandle(svc);
}
CloseServiceHandle(mngr);
}
return res;
ServiceHandle svc(serviceNameStr, SC_MANAGER_CONNECT, SERVICE_INTERROGATE);
return svc.isServiceOpened();
}
void StartupUtil::setServiceInstalled(bool install)
@ -209,36 +149,38 @@ void StartupUtil::setServiceInstalled(bool install)
serviceDependenciesStr, command);
startService();
QThread::msleep(100); // Let the service to start
}
bool StartupUtil::isServiceRunning()
{
ServiceHandle svc(serviceNameStr, SC_MANAGER_CONNECT, SERVICE_INTERROGATE);
if (svc.isServiceOpened()) {
return svc.queryIsRunning();
}
return false;
}
bool StartupUtil::startService()
{
bool res = false;
const SC_HANDLE mngr = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
if (mngr) {
const SC_HANDLE svc = OpenServiceW(mngr, serviceNameStr, SERVICE_START);
if (svc) {
res = StartServiceW(svc, 0, nullptr);
CloseServiceHandle(svc);
}
CloseServiceHandle(mngr);
ServiceHandle svc(serviceNameStr, SC_MANAGER_CONNECT, SERVICE_START);
if (svc.isServiceOpened()) {
return svc.startService();
}
return res;
return false;
}
bool StartupUtil::stopService()
{
bool res = false;
const SC_HANDLE mngr = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (mngr) {
const SC_HANDLE svc = OpenServiceW(mngr, serviceNameStr, SERVICE_ALL_ACCESS);
if (svc) {
res = stopServiceControl(svc);
}
CloseServiceHandle(mngr);
ServiceHandle svc(serviceNameStr, SC_MANAGER_ALL_ACCESS, SERVICE_ALL_ACCESS);
if (svc.isServiceOpened()) {
return svc.stopService();
}
return res;
return false;
}
StartupUtil::AutoRunMode StartupUtil::autoRunMode()

View File

@ -13,6 +13,8 @@ public:
static bool isServiceInstalled();
static void setServiceInstalled(bool install);
static bool isServiceRunning();
static bool startService();
static bool stopService();