Cuberite
A lightweight, fast and extensible game server for Minecraft
StartAsService.h
Go to the documentation of this file.
1 
2 // StartAsService.h
3 
4 // Handles startup as a Windows Service or UNIX daemon.
5 
6 // This file MUST NOT be included from anywhere other than main.cpp.
7 
8 
9 
10 
11 
12 #ifdef _WIN32
13 
14 class StartAsService
15 {
16 public:
17 
19  template <auto UniversalMain>
20  static bool MakeIntoService()
21  {
22  SERVICE_TABLE_ENTRY ServiceTable[] =
23  {
24  { g_ServiceName, (LPSERVICE_MAIN_FUNCTION)serviceMain<UniversalMain> },
25  { nullptr, nullptr }
26  };
27 
28  if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
29  {
30  throw std::system_error(GetLastError(), std::system_category());
31  }
32 
33  return true;
34  }
35 
36 private:
37 
39  static void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode)
40  {
41  SERVICE_STATUS serviceStatus = {};
42  serviceStatus.dwCheckPoint = 0;
43  serviceStatus.dwControlsAccepted = acceptedControls;
44  serviceStatus.dwCurrentState = newState;
45  serviceStatus.dwServiceSpecificExitCode = 0;
46  serviceStatus.dwServiceType = SERVICE_WIN32;
47  serviceStatus.dwWaitHint = 0;
48  serviceStatus.dwWin32ExitCode = exitCode;
49 
50  if (SetServiceStatus(g_StatusHandle, &serviceStatus) == FALSE)
51  {
52  LOGERROR("SetServiceStatus() failed\n");
53  }
54  }
55 
57  static void WINAPI serviceCtrlHandler(DWORD CtrlCode)
58  {
59  if (CtrlCode == SERVICE_CONTROL_STOP)
60  {
61  std::raise(SIGINT);
62  serviceSetState(0, SERVICE_STOP_PENDING, 0);
63  }
64  }
65 
66  /* Startup logic for running as a service */
67  template <auto MainFunction>
68  static void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
69  {
70  wchar_t applicationFilename[MAX_PATH];
71  wchar_t applicationDirectory[MAX_PATH];
72 
73  // Get this binary's file path:
74  if (GetModuleFileName(nullptr, applicationFilename, std::size(applicationFilename)) == 0)
75  {
76  serviceSetState(0, SERVICE_STOPPED, GetLastError());
77  return;
78  }
79 
80  const auto LastComponent = std::wcsrchr(applicationFilename, L'\\');
81  if (LastComponent == nullptr)
82  {
83  serviceSetState(0, SERVICE_STOPPED, E_UNEXPECTED);
84  return;
85  }
86 
87  const auto LengthToLastComponent = LastComponent - applicationFilename;
88 
89  // Strip off the filename, keep only the path:
90  std::wcsncpy(applicationDirectory, applicationFilename, LengthToLastComponent);
91  applicationDirectory[LengthToLastComponent] = L'\0'; // Make sure new path is null terminated
92 
93  // Services are run by the SCM, and inherit its working directory - usually System32.
94  // Set the working directory to the same location as the binary.
95  if (SetCurrentDirectory(applicationDirectory) == FALSE)
96  {
97  serviceSetState(0, SERVICE_STOPPED, GetLastError());
98  return;
99  }
100 
101 
102  g_StatusHandle = RegisterServiceCtrlHandler(g_ServiceName, serviceCtrlHandler);
103  if (g_StatusHandle == nullptr)
104  {
105  OutputDebugStringA("RegisterServiceCtrlHandler() failed\n");
106  serviceSetState(0, SERVICE_STOPPED, GetLastError());
107  return;
108  }
109 
110  serviceSetState(SERVICE_ACCEPT_STOP, SERVICE_RUNNING, 0);
111 
112  char MultibyteArgV0[MAX_PATH];
113  char * MultibyteArgV[] = { MultibyteArgV0 };
114 
115  const auto OutputSize = std::size(MultibyteArgV0);
116  const auto TranslateResult = std::wcstombs(MultibyteArgV0, argv[0], OutputSize);
117 
118  if (TranslateResult == static_cast<size_t>(-1))
119  {
120  // Translation failed entirely (!):
121  MultibyteArgV0[0] = '\0';
122  }
123  else if (TranslateResult == OutputSize)
124  {
125  // Output too small:
126  MultibyteArgV0[OutputSize - 1] = '\0';
127  }
128 
129  const auto Result = MainFunction(1, MultibyteArgV, true);
130  const auto Return = (Result == EXIT_SUCCESS) ? S_OK : E_FAIL;
131 
132  serviceSetState(0, SERVICE_STOPPED, Return);
133  }
134 
135  static inline SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
136  static inline HANDLE g_ServiceThread = INVALID_HANDLE_VALUE;
137  static inline wchar_t g_ServiceName[] = L"Cuberite";
138 };
139 
140 #else
141 
143 {
145  template <auto>
146  static bool MakeIntoService()
147  {
148  pid_t pid = fork();
149 
150  // fork() returns a negative value on error.
151  if (pid < 0)
152  {
153  throw std::system_error(errno, std::system_category());
154  }
155 
156  // Check if we are the parent or child process. Parent stops here.
157  if (pid > 0)
158  {
159  return true;
160  }
161 
162  // Child process now goes quiet, running in the background.
163  close(STDIN_FILENO);
164  close(STDOUT_FILENO);
165  close(STDERR_FILENO);
166 
167  return false;
168  }
169 };
170 
171 #endif
void LOGERROR(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:73
static bool MakeIntoService()
Make a UNIX daemon.