/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "ServiceWin32.h"
#include // for std::size
#include
#include
#include
#include
#include
namespace
{
_TCHAR* ServiceLongName;
_TCHAR* ServiceName;
_TCHAR* ServiceDescription;
int(*ServiceEntryPoint)(int argc, char** argv);
int* ServiceStatusPtr;
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE ServiceStatusHandle = nullptr;
}
typedef BOOL (WINAPI *CSD_T)(SC_HANDLE, DWORD, LPCVOID);
void Trinity::Service::Init(_TCHAR* serviceLongName, _TCHAR* serviceName, _TCHAR* serviceDescription, int(* entryPoint)(int argc, char** argv), int* status)
{
ServiceLongName = serviceLongName;
ServiceName = serviceName;
ServiceDescription = serviceDescription;
ServiceEntryPoint = entryPoint;
ServiceStatusPtr = status;
}
int32 Trinity::Service::Install()
{
SC_HANDLE serviceControlManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_CREATE_SERVICE);
if (serviceControlManager)
{
TCHAR path[_MAX_PATH + 10];
if (GetModuleFileName(nullptr, path, std::size(path)) > 0)
{
_tcscat(path, _T(" --service run"));
SC_HANDLE service = CreateService(serviceControlManager,
ServiceName, // name of service
ServiceLongName, // service name to display
SERVICE_ALL_ACCESS, // desired access
// service type
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START, // start type
SERVICE_ERROR_IGNORE, // error control type
path, // service's binary
nullptr, // no load ordering group
nullptr, // no tag identifier
nullptr, // no dependencies
nullptr, // LocalSystem account
nullptr); // no password
if (service)
{
SERVICE_DESCRIPTION sdBuf;
sdBuf.lpDescription = ServiceDescription;
ChangeServiceConfig2(
service, // handle to service
SERVICE_CONFIG_DESCRIPTION, // change: description
&sdBuf); // new data
SC_ACTION _action[1];
_action[0].Type = SC_ACTION_RESTART;
_action[0].Delay = 10000;
SERVICE_FAILURE_ACTIONS sfa;
ZeroMemory(&sfa, sizeof(SERVICE_FAILURE_ACTIONS));
sfa.lpsaActions = _action;
sfa.cActions = 1;
sfa.dwResetPeriod = INFINITE;
ChangeServiceConfig2(
service, // handle to service
SERVICE_CONFIG_FAILURE_ACTIONS, // information level
&sfa); // new data
CloseServiceHandle(service);
}
}
CloseServiceHandle(serviceControlManager);
}
printf("Service installed\n");
return 0;
}
int32 Trinity::Service::Uninstall()
{
SC_HANDLE serviceControlManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT);
if (serviceControlManager)
{
SC_HANDLE service = OpenService(serviceControlManager,
ServiceName, SERVICE_QUERY_STATUS | DELETE);
if (service)
{
SERVICE_STATUS serviceStatus2;
if (QueryServiceStatus(service, &serviceStatus2))
{
if (serviceStatus2.dwCurrentState == SERVICE_STOPPED)
DeleteService(service);
}
CloseServiceHandle(service);
}
CloseServiceHandle(serviceControlManager);
}
printf("Service uninstalled\n");
return 0;
}
void WINAPI ServiceControlHandler(DWORD controlCode)
{
switch (controlCode)
{
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
*ServiceStatusPtr = 0;
return;
case SERVICE_CONTROL_PAUSE:
*ServiceStatusPtr = 2;
ServiceStatus.dwCurrentState = SERVICE_PAUSED;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
break;
case SERVICE_CONTROL_CONTINUE:
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
*ServiceStatusPtr = 1;
break;
default:
if (controlCode >= 128 && controlCode <= 255)
// user defined control code
break;
else
// unrecognized control code
break;
}
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
}
template
void TCharToChar(TCHAR const* src, char(&dst)[size])
{
if constexpr (std::is_same_v)
::strcpy_s(dst, src);
else
::wcstombs_s(nullptr, dst, src, _TRUNCATE);
}
void WINAPI ServiceMain(DWORD /*argc*/, TCHAR *argv[])
{
// initialise service status
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
ServiceStatus.dwWin32ExitCode = NO_ERROR;
ServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
ServiceStatusHandle = RegisterServiceCtrlHandler(ServiceName, ServiceControlHandler);
if (ServiceStatusHandle)
{
TCHAR path[_MAX_PATH + 1];
size_t last_slash = 0;
size_t pathLen = GetModuleFileName(nullptr, path, std::size(path));
for (size_t i = 0; i < pathLen; i++)
if (path[i] == '\\')
last_slash = i;
path[last_slash] = 0;
// service is starting
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
// do initialisation here
SetCurrentDirectory(path);
// running
ServiceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
////////////////////////
// service main cycle //
////////////////////////
*ServiceStatusPtr = 1;
char cArg[_MAX_PATH + 1];
TCharToChar(argv[0], cArg);
char* cArgv[] = { cArg };
ServiceEntryPoint(1, cArgv);
// service was stopped
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
// do cleanup here
// service is now stopped
ServiceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
}
}
int32 Trinity::Service::Run()
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ ServiceName, ServiceMain },
{ nullptr, nullptr }
};
if (!StartServiceCtrlDispatcher(serviceTable))
{
printf("StartService Failed. Error [%u]", uint32(::GetLastError()));
return 1;
}
return 0;
}