diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 233 |
1 files changed, 199 insertions, 34 deletions
diff --git a/src/main.cpp b/src/main.cpp index 428e89e93..2e7e107f7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,11 +29,24 @@ bool g_ShouldLogCommIn; /** If set to true, the protocols will log each player's outgoing (S->C) communication to a per-connection logfile */ bool g_ShouldLogCommOut; +/** If set to true, binary will attempt to run as a service on Windows */ +bool cRoot::m_RunAsService = false; -/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window + +#if defined(_WIN32) + SERVICE_STATUS_HANDLE g_StatusHandle = NULL; + HANDLE g_ServiceThread = INVALID_HANDLE_VALUE; + #define SERVICE_NAME "MCServerService" +#endif + + + + + +/** If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window */ // _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013 // and we haven't had a memory leak for over a year anyway. // #define ENABLE_LEAK_FINDER @@ -160,6 +173,7 @@ LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_Except + #ifdef _WIN32 // Handle CTRL events in windows, including console window close BOOL CtrlHandler(DWORD fdwCtrlType) @@ -181,6 +195,167 @@ BOOL CtrlHandler(DWORD fdwCtrlType) //////////////////////////////////////////////////////////////////////////////// +// universalMain - Main startup logic for both standard running and as a service + +void universalMain() +{ + #ifdef _WIN32 + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE)) + { + LOGERROR("Could not install the Windows CTRL handler!"); + } + #endif + + // Initialize logging subsystem: + cLogger::InitiateMultithreading(); + + // Initialize LibEvent: + cNetworkSingleton::Get(); + + #if !defined(ANDROID_NDK) + try + #endif + { + cRoot Root; + Root.Start(); + } + #if !defined(ANDROID_NDK) + catch (std::exception & e) + { + LOGERROR("Standard exception: %s", e.what()); + } + catch (...) + { + LOGERROR("Unknown exception!"); + } + #endif + + g_ServerTerminated = true; + + // Shutdown all of LibEvent: + cNetworkSingleton::Get().Terminate(); +} + + + + +#if defined(_WIN32) +//////////////////////////////////////////////////////////////////////////////// +// serviceWorkerThread: Keep the service alive + +DWORD WINAPI serviceWorkerThread(LPVOID lpParam) +{ + UNREFERENCED_PARAMETER(lpParam); + + // Do the normal startup + universalMain(); + + return ERROR_SUCCESS; +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// serviceSetState: Set the internal status of the service + +void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode) +{ + SERVICE_STATUS serviceStatus; + ZeroMemory(&serviceStatus, sizeof(SERVICE_STATUS)); + 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"); + } +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// serviceCtrlHandler: Handle stop events from the Service Control Manager + +void WINAPI serviceCtrlHandler(DWORD CtrlCode) +{ + switch (CtrlCode) + { + case SERVICE_CONTROL_STOP: + { + cRoot::m_ShouldStop = true; + serviceSetState(0, SERVICE_STOP_PENDING, 0); + break; + } + + default: + { + break; + } + } +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// serviceMain: Startup logic for running as a service + +void WINAPI serviceMain(DWORD argc, TCHAR *argv[]) +{ + #if defined(_DEBUG) && defined(DEBUG_SERVICE_STARTUP) + Sleep(10000); + #endif + + char applicationFilename[MAX_PATH]; + char applicationDirectory[MAX_PATH]; + + GetModuleFileName(NULL, applicationFilename, sizeof(applicationFilename)); // This binary's file path. + + // Strip off the filename, keep only the path: + strncpy_s(applicationDirectory, sizeof(applicationDirectory), applicationFilename, (strrchr(applicationFilename, '\\') - applicationFilename)); + applicationDirectory[strlen(applicationDirectory)] = '\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. + SetCurrentDirectory(applicationDirectory); + + g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, serviceCtrlHandler); + + if (g_StatusHandle == NULL) + { + OutputDebugStringA("RegisterServiceCtrlHandler() failed\n"); + serviceSetState(0, SERVICE_STOPPED, GetLastError()); + return; + } + + serviceSetState(SERVICE_ACCEPT_STOP, SERVICE_RUNNING, 0); + + g_ServiceThread = CreateThread(NULL, 0, serviceWorkerThread, NULL, 0, NULL); + if (g_ServiceThread == NULL) + { + OutputDebugStringA("CreateThread() failed\n"); + serviceSetState(0, SERVICE_STOPPED, GetLastError()); + return; + } + WaitForSingleObject(g_ServiceThread, INFINITE); // Wait here for a stop signal. + + CloseHandle(g_ServiceThread); + + serviceSetState(0, SERVICE_STOPPED, 0); +} +#endif + + + + +//////////////////////////////////////////////////////////////////////////////// // main: int main( int argc, char **argv) @@ -219,13 +394,6 @@ int main( int argc, char **argv) #endif // _WIN32 && !_WIN64 // End of dump-file magic - #ifdef _WIN32 - if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE)) - { - LOGERROR("Could not install the Windows CTRL handler!"); - } - #endif - #if defined(_DEBUG) && defined(_MSC_VER) _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); @@ -280,42 +448,39 @@ int main( int argc, char **argv) { setvbuf(stdout, nullptr, _IONBF, 0); } + else if (NoCaseCompare(Arg, "/service") == 0) + { + cRoot::m_RunAsService = true; + } } // for i - argv[] - - // Initialize logging subsystem: - cLogger::InitiateMultithreading(); - - // Initialize LibEvent: - cNetworkSingleton::Get(); - - #if !defined(ANDROID_NDK) - try - #endif - { - cRoot Root; - Root.Start(); - } - #if !defined(ANDROID_NDK) - catch (std::exception & e) + + #if defined(_WIN32) + // Attempt to run as a service + if (cRoot::m_RunAsService) { - LOGERROR("Standard exception: %s", e.what()); + SERVICE_TABLE_ENTRY ServiceTable[] = + { + { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)serviceMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcher(ServiceTable) == FALSE) + { + LOGERROR("Attempted, but failed, service startup."); + return GetLastError(); + } } - catch (...) + else + #endif { - LOGERROR("Unknown exception!"); + // Not running as a service, do normal startup + universalMain(); } - #endif - #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) DeinitLeakFinder(); #endif - g_ServerTerminated = true; - - // Shutdown all of LibEvent: - cNetworkSingleton::Get().Terminate(); - return EXIT_SUCCESS; } |