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. ;Try to install a driver on opening error.
;canInstallDriver=false ;canInstallDriver=false
;Try to start a Service on startup.
;canStartService=false

View File

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

View File

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

View File

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

View File

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

View File

@ -33,6 +33,7 @@ public:
bool noCache() const { return m_noCache; } bool noCache() const { return m_noCache; }
bool noSplash() const { return m_noSplash; } bool noSplash() const { return m_noSplash; }
bool canInstallDriver() const { return m_canInstallDriver; } bool canInstallDriver() const { return m_canInstallDriver; }
bool canStartService() const { return m_canStartService; }
bool isService() const { return m_isService; } bool isService() const { return m_isService; }
bool hasService() const { return m_hasService; } bool hasService() const { return m_hasService; }
@ -129,6 +130,7 @@ private:
uint m_noCache : 1 = false; uint m_noCache : 1 = false;
uint m_noSplash : 1 = false; uint m_noSplash : 1 = false;
uint m_canInstallDriver : 1 = false; uint m_canInstallDriver : 1 = false;
uint m_canStartService : 1 = false;
uint m_isService : 1 = false; uint m_isService : 1 = false;
uint m_hasService : 1 = false; uint m_hasService : 1 = false;
uint m_isUserAdmin : 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 <fort_version_l.h>
#include "fileutil.h" #include <util/fileutil.h>
#include "regkey.h" #include <util/regkey.h>
#include "service/servicemanageriface.h" #include <util/service/servicehandle.h>
#include <util/service/servicemanageriface.h>
namespace { namespace {
@ -91,87 +92,34 @@ void removeAutorunForAllUsers()
removeAutorunForUser(regAllUsersRoot, regAllUsersRun); 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, bool installService(const wchar_t *serviceName, const wchar_t *serviceDisplay,
const wchar_t *serviceDescription, const wchar_t *serviceGroup, const wchar_t *dependencies, const wchar_t *serviceDescription, const wchar_t *serviceGroup, const wchar_t *dependencies,
const QString &command) const QString &command)
{ {
bool res = false; ServiceHandle svc(serviceName, SC_MANAGER_CREATE_SERVICE);
const SC_HANDLE mngr = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (!svc.isManagerOpened())
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);
setupServiceRestartConfig(svc);
res = true;
CloseServiceHandle(svc);
}
CloseServiceHandle(mngr);
}
return res;
}
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 false;
svc.createService(serviceName, serviceDisplay, serviceGroup, dependencies, command);
if (!svc.isServiceOpened())
return false;
svc.setServiceDescription(serviceDescription);
svc.setupServiceRestartConfig();
return true;
} }
bool uninstallService(const wchar_t *serviceName) bool uninstallService(const wchar_t *serviceName)
{ {
bool res = false; ServiceHandle svc(serviceName, SC_MANAGER_ALL_ACCESS, SERVICE_ALL_ACCESS | DELETE);
const SC_HANDLE mngr = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (svc.isServiceOpened()) {
if (mngr) { svc.stopService();
const SC_HANDLE svc = OpenServiceW(mngr, serviceName, SERVICE_ALL_ACCESS | DELETE);
if (svc) {
stopServiceControl(svc);
res = DeleteService(svc); return svc.deleteService();
CloseServiceHandle(svc);
} }
CloseServiceHandle(mngr);
} return false;
return res;
} }
} }
@ -183,17 +131,9 @@ const wchar_t *StartupUtil::serviceName()
bool StartupUtil::isServiceInstalled() bool StartupUtil::isServiceInstalled()
{ {
bool res = false; ServiceHandle svc(serviceNameStr, SC_MANAGER_CONNECT, SERVICE_INTERROGATE);
const SC_HANDLE mngr = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
if (mngr) { return svc.isServiceOpened();
const SC_HANDLE svc = OpenServiceW(mngr, serviceNameStr, SERVICE_INTERROGATE);
if (svc) {
res = true;
CloseServiceHandle(svc);
}
CloseServiceHandle(mngr);
}
return res;
} }
void StartupUtil::setServiceInstalled(bool install) void StartupUtil::setServiceInstalled(bool install)
@ -209,36 +149,38 @@ void StartupUtil::setServiceInstalled(bool install)
serviceDependenciesStr, command); serviceDependenciesStr, command);
startService(); startService();
QThread::msleep(100); // Let the service to start 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 StartupUtil::startService()
{ {
bool res = false; ServiceHandle svc(serviceNameStr, SC_MANAGER_CONNECT, SERVICE_START);
const SC_HANDLE mngr = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); if (svc.isServiceOpened()) {
if (mngr) { return svc.startService();
const SC_HANDLE svc = OpenServiceW(mngr, serviceNameStr, SERVICE_START);
if (svc) {
res = StartServiceW(svc, 0, nullptr);
CloseServiceHandle(svc);
} }
CloseServiceHandle(mngr);
} return false;
return res;
} }
bool StartupUtil::stopService() bool StartupUtil::stopService()
{ {
bool res = false; ServiceHandle svc(serviceNameStr, SC_MANAGER_ALL_ACCESS, SERVICE_ALL_ACCESS);
const SC_HANDLE mngr = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (svc.isServiceOpened()) {
if (mngr) { return svc.stopService();
const SC_HANDLE svc = OpenServiceW(mngr, serviceNameStr, SERVICE_ALL_ACCESS);
if (svc) {
res = stopServiceControl(svc);
} }
CloseServiceHandle(mngr);
} return false;
return res;
} }
StartupUtil::AutoRunMode StartupUtil::autoRunMode() StartupUtil::AutoRunMode StartupUtil::autoRunMode()

View File

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