1bc12ba2b3
* Clean up cRoot & main * Move some OS-specifics into OSSupport
174 lines
4.1 KiB
C++
174 lines
4.1 KiB
C++
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <csignal>
|
|
|
|
|
|
|
|
|
|
|
|
class cStartAsService
|
|
{
|
|
public:
|
|
|
|
/** Make a Windows service. */
|
|
template <auto UniversalMain>
|
|
static bool MakeIntoService()
|
|
{
|
|
SERVICE_TABLE_ENTRY ServiceTable[] =
|
|
{
|
|
{ g_ServiceName, (LPSERVICE_MAIN_FUNCTION)serviceMain<UniversalMain> },
|
|
{ nullptr, nullptr }
|
|
};
|
|
|
|
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
|
|
{
|
|
throw std::system_error(GetLastError(), std::system_category());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
|
|
/** Set the internal status of the service */
|
|
static void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode)
|
|
{
|
|
SERVICE_STATUS serviceStatus = {};
|
|
serviceStatus.dwCheckPoint = 0;
|
|
serviceStatus.dwControlsAccepted = acceptedControls;
|
|
serviceStatus.dwCurrentState = newState;
|
|
serviceStatus.dwServiceSpecificExitCode = 0;
|
|
serviceStatus.dwServiceType = SERVICE_WIN32;
|
|
serviceStatus.dwWaitHint = 0;
|
|
serviceStatus.dwWin32ExitCode = exitCode;
|
|
|
|
if (SetServiceStatus(g_StatusHandle, &serviceStatus) == FALSE)
|
|
{
|
|
LOGERROR("SetServiceStatus() failed\n");
|
|
}
|
|
}
|
|
|
|
/** Handle stop events from the Service Control Manager */
|
|
static void WINAPI serviceCtrlHandler(DWORD CtrlCode)
|
|
{
|
|
if (CtrlCode == SERVICE_CONTROL_STOP)
|
|
{
|
|
std::raise(SIGINT);
|
|
serviceSetState(0, SERVICE_STOP_PENDING, 0);
|
|
}
|
|
}
|
|
|
|
/* Startup logic for running as a service */
|
|
template <auto MainFunction>
|
|
static void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
|
|
{
|
|
wchar_t applicationFilename[MAX_PATH];
|
|
wchar_t applicationDirectory[MAX_PATH];
|
|
|
|
// Get this binary's file path:
|
|
if (GetModuleFileName(nullptr, applicationFilename, std::size(applicationFilename)) == 0)
|
|
{
|
|
serviceSetState(0, SERVICE_STOPPED, GetLastError());
|
|
return;
|
|
}
|
|
|
|
const auto LastComponent = wcsrchr(applicationFilename, L'\\');
|
|
if (LastComponent == nullptr)
|
|
{
|
|
serviceSetState(0, SERVICE_STOPPED, E_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
const auto LengthToLastComponent = LastComponent - applicationFilename;
|
|
|
|
// Strip off the filename, keep only the path:
|
|
wcsncpy(applicationDirectory, applicationFilename, LengthToLastComponent);
|
|
applicationDirectory[LengthToLastComponent] = L'\0'; // Make sure new path is null terminated
|
|
|
|
// Services are run by the SCM, and inherit its working directory - usually System32.
|
|
// Set the working directory to the same location as the binary.
|
|
if (SetCurrentDirectory(applicationDirectory) == FALSE)
|
|
{
|
|
serviceSetState(0, SERVICE_STOPPED, GetLastError());
|
|
return;
|
|
}
|
|
|
|
|
|
g_StatusHandle = RegisterServiceCtrlHandler(g_ServiceName, serviceCtrlHandler);
|
|
if (g_StatusHandle == nullptr)
|
|
{
|
|
OutputDebugStringA("RegisterServiceCtrlHandler() failed\n");
|
|
serviceSetState(0, SERVICE_STOPPED, GetLastError());
|
|
return;
|
|
}
|
|
|
|
serviceSetState(SERVICE_ACCEPT_STOP, SERVICE_RUNNING, 0);
|
|
|
|
char MultibyteArgV0[MAX_PATH];
|
|
char * MultibyteArgV[] = { MultibyteArgV0 };
|
|
|
|
const auto OutputSize = std::size(MultibyteArgV0);
|
|
const auto TranslateResult = wcstombs(MultibyteArgV0, argv[0], OutputSize);
|
|
|
|
if (TranslateResult == static_cast<size_t>(-1))
|
|
{
|
|
// Translation failed entirely (!):
|
|
MultibyteArgV0[0] = '\0';
|
|
}
|
|
else if (TranslateResult == OutputSize)
|
|
{
|
|
// Output too small:
|
|
MultibyteArgV0[OutputSize - 1] = '\0';
|
|
}
|
|
|
|
const auto Result = MainFunction(1, MultibyteArgV, true);
|
|
const auto Return = (Result == EXIT_SUCCESS) ? S_OK : E_FAIL;
|
|
|
|
serviceSetState(0, SERVICE_STOPPED, Return);
|
|
}
|
|
|
|
static inline SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
|
|
static inline HANDLE g_ServiceThread = INVALID_HANDLE_VALUE;
|
|
static inline wchar_t g_ServiceName[] = L"Cuberite";
|
|
};
|
|
|
|
#else
|
|
|
|
struct cStartAsService
|
|
{
|
|
/** Make a UNIX daemon. */
|
|
template <auto>
|
|
static bool MakeIntoService()
|
|
{
|
|
pid_t pid = fork();
|
|
|
|
// fork() returns a negative value on error.
|
|
if (pid < 0)
|
|
{
|
|
throw std::system_error(errno, std::system_category());
|
|
}
|
|
|
|
// Check if we are the parent or child process. Parent stops here.
|
|
if (pid > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Child process now goes quiet, running in the background.
|
|
close(STDIN_FILENO);
|
|
close(STDOUT_FILENO);
|
|
close(STDERR_FILENO);
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
#endif
|