1
0

Fixed and improved restarting

Restarts are now an actual, close-as-possible to application
exit+reopen.
This commit is contained in:
Tiger Wang 2015-06-17 15:38:00 +01:00
parent b5ed23d2a6
commit 4315a11393
7 changed files with 253 additions and 211 deletions

View File

@ -17,6 +17,7 @@ echo "Building..."
make -j 2; make -j 2;
make -j 2 test ARGS="-V"; make -j 2 test ARGS="-V";
cd MCServer/; cd MCServer/;
if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ] if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ]; then
then echo stop | $MCSERVER_PATH; echo restart | $MCSERVER_PATH;
echo stop | $MCSERVER_PATH;
fi fi

View File

@ -18,8 +18,36 @@
cNetworkSingleton::cNetworkSingleton(void): cNetworkSingleton::cNetworkSingleton() :
m_HasTerminated(false) m_HasTerminated(true)
{
}
cNetworkSingleton::~cNetworkSingleton()
{
// Check that Terminate has been called already:
ASSERT(m_HasTerminated);
}
cNetworkSingleton & cNetworkSingleton::Get(void)
{
static cNetworkSingleton Instance;
return Instance;
}
void cNetworkSingleton::Initialise(void)
{ {
// Windows: initialize networking: // Windows: initialize networking:
#ifdef _WIN32 #ifdef _WIN32
@ -64,26 +92,7 @@ cNetworkSingleton::cNetworkSingleton(void):
// Create the event loop thread: // Create the event loop thread:
m_EventLoopThread = std::thread(RunEventLoop, this); m_EventLoopThread = std::thread(RunEventLoop, this);
} m_HasTerminated = false;
cNetworkSingleton::~cNetworkSingleton()
{
// Check that Terminate has been called already:
ASSERT(m_HasTerminated);
}
cNetworkSingleton & cNetworkSingleton::Get(void)
{
static cNetworkSingleton Instance;
return Instance;
} }

View File

@ -44,13 +44,18 @@ typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
class cNetworkSingleton class cNetworkSingleton
{ {
public: public:
cNetworkSingleton();
~cNetworkSingleton(); ~cNetworkSingleton();
/** Returns the singleton instance of this class */ /** Returns the singleton instance of this class */
static cNetworkSingleton & Get(void); static cNetworkSingleton & Get(void);
/** Initialises all network-related threads.
To be called on first run or after app restart. */
void Initialise(void);
/** Terminates all network-related threads. /** Terminates all network-related threads.
To be used only on app shutdown. To be used only on app shutdown or restart.
MSVC runtime requires that the LibEvent networking be shut down before the main() function is exitted; this is the way to do it. */ MSVC runtime requires that the LibEvent networking be shut down before the main() function is exitted; this is the way to do it. */
void Terminate(void); void Terminate(void);
@ -122,10 +127,6 @@ protected:
/** The thread in which the main LibEvent loop runs. */ /** The thread in which the main LibEvent loop runs. */
std::thread m_EventLoopThread; std::thread m_EventLoopThread;
/** Initializes the LibEvent internals. */
cNetworkSingleton(void);
/** Converts LibEvent-generated log events into log messages in MCS log. */ /** Converts LibEvent-generated log events into log messages in MCS log. */
static void LogCallback(int a_Severity, const char * a_Msg); static void LogCallback(int a_Severity, const char * a_Msg);

View File

@ -26,10 +26,10 @@
#include <iostream> #include <iostream>
#ifdef _WIN32 #ifdef _WIN32
#include <conio.h>
#include <psapi.h> #include <psapi.h>
#elif defined(__linux__) #elif defined(__linux__)
#include <fstream> #include <fstream>
#include <signal.h>
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <mach/mach.h> #include <mach/mach.h>
#endif #endif
@ -39,7 +39,6 @@
cRoot * cRoot::s_Root = nullptr; cRoot * cRoot::s_Root = nullptr;
bool cRoot::m_ShouldStop = false;
@ -53,10 +52,10 @@ cRoot::cRoot(void) :
m_FurnaceRecipe(nullptr), m_FurnaceRecipe(nullptr),
m_WebAdmin(nullptr), m_WebAdmin(nullptr),
m_PluginManager(nullptr), m_PluginManager(nullptr),
m_MojangAPI(nullptr), m_MojangAPI(nullptr)
m_bRestart(false)
{ {
s_Root = this; s_Root = this;
m_InputThreadRunFlag.clear();
} }
@ -76,24 +75,22 @@ void cRoot::InputThread(cRoot & a_Params)
{ {
cLogCommandOutputCallback Output; cLogCommandOutputCallback Output;
while (!cRoot::m_ShouldStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good()) while (a_Params.m_InputThreadRunFlag.test_and_set() && std::cin.good())
{ {
AString Command; AString Command;
std::getline(std::cin, Command); std::getline(std::cin, Command);
if (!Command.empty()) if (!Command.empty())
{ {
// Execute and clear command string when submitted
a_Params.ExecuteConsoleCommand(TrimString(Command), Output); a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
} }
} }
if (m_TerminateEventRaised || !std::cin.good()) // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running
if (!std::cin.good())
{ {
// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running
// Stop the server: // Stop the server:
if (!m_RunAsService) // Dont kill if running as a service a_Params.QueueExecuteConsoleCommand("stop");
{
a_Params.m_ShouldStop = true;
}
} }
} }
@ -101,12 +98,12 @@ void cRoot::InputThread(cRoot & a_Params)
void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo) void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
{ {
#ifdef _WIN32 #ifdef _WIN32
HWND hwnd = GetConsoleWindow(); HWND hwnd = GetConsoleWindow();
HMENU hmenu = GetSystemMenu(hwnd, FALSE); HMENU hmenu = GetSystemMenu(hwnd, FALSE);
EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling
#endif #endif
cLogger::cListener * consoleLogListener = MakeConsoleListener(m_RunAsService); cLogger::cListener * consoleLogListener = MakeConsoleListener(m_RunAsService);
@ -127,156 +124,197 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
#endif #endif
cDeadlockDetect dd; cDeadlockDetect dd;
auto BeginTime = std::chrono::steady_clock::now();
m_ShouldStop = false; LoadGlobalSettings();
while (!m_ShouldStop)
LOG("Creating new server instance...");
m_Server = new cServer();
LOG("Reading server config...");
auto IniFile = cpp14::make_unique<cIniFile>();
if (!IniFile->ReadFile("settings.ini"))
{ {
auto BeginTime = std::chrono::steady_clock::now(); LOGWARN("Regenerating settings.ini, all settings will be reset");
m_bRestart = false; IniFile->AddHeaderComment(" This is the main server configuration");
IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
LoadGlobalSettings(); IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
}
LOG("Creating new server instance..."); auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(a_OverridesRepo));
m_Server = new cServer();
LOG("Reading server config...");
auto IniFile = cpp14::make_unique<cIniFile>();
if (!IniFile->ReadFile("settings.ini"))
{
LOGWARN("Regenerating settings.ini, all settings will be reset");
IniFile->AddHeaderComment(" This is the main server configuration");
IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
}
auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(overridesRepo));
LOG("Starting server...");
m_MojangAPI = new cMojangAPI;
bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true);
m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate))
{
settingsRepo->Flush();
LOGERROR("Failure starting server, aborting...");
return;
}
m_WebAdmin = new cWebAdmin();
m_WebAdmin->Init();
LOGD("Loading settings...");
m_RankManager.reset(new cRankManager());
m_RankManager->Initialize(*m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
LOGD("Loading worlds...");
LoadWorlds(*settingsRepo);
LOGD("Loading plugin manager...");
m_PluginManager = new cPluginManager();
m_PluginManager->ReloadPluginsNow(*settingsRepo);
LOGD("Loading MonsterConfig...");
m_MonsterConfig = new cMonsterConfig;
// This sets stuff in motion
LOGD("Starting Authenticator...");
m_Authenticator.Start(*settingsRepo);
LOGD("Starting worlds...");
StartWorlds();
if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true))
{
LOGD("Starting deadlock detector...");
dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20));
}
LOG("Starting server...");
m_MojangAPI = new cMojangAPI;
bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true);
m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate))
{
settingsRepo->Flush(); settingsRepo->Flush();
LOGERROR("Failure starting server, aborting...");
return;
}
LOGD("Finalising startup..."); m_WebAdmin = new cWebAdmin();
if (m_Server->Start()) m_WebAdmin->Init();
{
m_WebAdmin->Start();
#if !defined(ANDROID_NDK) LOGD("Loading settings...");
m_RankManager.reset(new cRankManager());
m_RankManager->Initialize(*m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes();
m_FurnaceRecipe = new cFurnaceRecipe();
LOGD("Loading worlds...");
LoadWorlds(*settingsRepo);
LOGD("Loading plugin manager...");
m_PluginManager = new cPluginManager();
m_PluginManager->ReloadPluginsNow(*settingsRepo);
LOGD("Loading MonsterConfig...");
m_MonsterConfig = new cMonsterConfig;
// This sets stuff in motion
LOGD("Starting Authenticator...");
m_Authenticator.Start(*settingsRepo);
LOGD("Starting worlds...");
StartWorlds();
if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true))
{
LOGD("Starting deadlock detector...");
dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20));
}
settingsRepo->Flush();
LOGD("Finalising startup...");
if (m_Server->Start())
{
m_WebAdmin->Start();
#if !defined(ANDROID_NDK)
LOGD("Starting InputThread..."); LOGD("Starting InputThread...");
try try
{ {
m_InputThreadRunFlag.test_and_set();
m_InputThread = std::thread(InputThread, std::ref(*this)); m_InputThread = std::thread(InputThread, std::ref(*this));
m_InputThread.detach();
} }
catch (std::system_error & a_Exception) catch (std::system_error & a_Exception)
{ {
LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what()); LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what());
} }
#endif #endif
LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
// Save the current time // Save the current time
m_StartTime = std::chrono::steady_clock::now(); m_StartTime = std::chrono::steady_clock::now();
#ifdef _WIN32 #ifdef _WIN32
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
#endif #endif
while (!m_ShouldStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads for (;;)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (m_TerminateEventRaised)
{
m_ShouldStop = true;
}
// Stop the server:
m_WebAdmin->Stop();
LOG("Shutting down server...");
m_Server->Shutdown();
} // if (m_Server->Start())
else
{ {
m_ShouldStop = true; m_StopEvent.Wait();
if (m_TerminateEventRaised && m_RunAsService)
{
// Dont kill if running as a service
m_TerminateEventRaised = false;
}
else
{
break;
}
} }
delete m_MojangAPI; m_MojangAPI = nullptr; // Stop the server:
m_WebAdmin->Stop();
LOGD("Shutting down deadlock detector..."); LOG("Shutting down server...");
dd.Stop(); m_Server->Shutdown();
} // if (m_Server->Start()
LOGD("Stopping world threads..."); delete m_MojangAPI; m_MojangAPI = nullptr;
StopWorlds();
LOGD("Stopping authenticator..."); LOGD("Shutting down deadlock detector...");
m_Authenticator.Stop(); dd.Stop();
LOGD("Freeing MonsterConfig..."); LOGD("Stopping world threads...");
delete m_MonsterConfig; m_MonsterConfig = nullptr; StopWorlds();
delete m_WebAdmin; m_WebAdmin = nullptr;
LOGD("Unloading recipes..."); LOGD("Stopping authenticator...");
delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr; m_Authenticator.Stop();
delete m_CraftingRecipes; m_CraftingRecipes = nullptr;
LOG("Unloading worlds..."); LOGD("Freeing MonsterConfig...");
UnloadWorlds(); delete m_MonsterConfig; m_MonsterConfig = nullptr;
delete m_WebAdmin; m_WebAdmin = nullptr;
LOGD("Stopping plugin manager..."); LOGD("Unloading recipes...");
delete m_PluginManager; m_PluginManager = nullptr; delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr;
delete m_CraftingRecipes; m_CraftingRecipes = nullptr;
cItemHandler::Deinit(); LOG("Unloading worlds...");
UnloadWorlds();
LOG("Cleaning up..."); LOGD("Stopping plugin manager...");
delete m_Server; m_Server = nullptr; delete m_PluginManager; m_PluginManager = nullptr;
cItemHandler::Deinit();
LOG("Cleaning up...");
delete m_Server; m_Server = nullptr;
m_InputThreadRunFlag.clear();
#ifdef _WIN32
DWORD Length;
INPUT_RECORD Record
{
static_cast<WORD>(KEY_EVENT),
{
{
TRUE,
1,
VK_RETURN,
MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC),
{ { VK_RETURN } },
0
}
}
};
// Can't kill the input thread since it breaks cin (getline doesn't block / receive input on restart)
// Apparently no way to unblock getline
// Only thing I can think of for now
if (WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &Record, 1, &Length) == 0)
{
LOGWARN("Couldn't notify the input thread; the server will hang before shutdown!");
m_TerminateEventRaised = true;
m_InputThread.detach();
}
else
{
m_InputThread.join();
}
#else
if (pthread_kill(m_InputThread.native_handle(), SIGKILL) != 0)
{
LOGWARN("Couldn't notify the input thread; the server will hang before shutdown!");
m_TerminateEventRaised = true;
m_InputThread.detach();
}
#endif
if (m_TerminateEventRaised)
{
LOG("Shutdown successful!"); LOG("Shutdown successful!");
} }
else
{
LOG("Shutdown successful - restarting...");
}
LOG("--- Stopped Log ---"); LOG("--- Stopped Log ---");
cLogger::GetInstance().DetachListener(consoleLogListener); cLogger::GetInstance().DetachListener(consoleLogListener);
@ -475,19 +513,9 @@ void cRoot::TickCommands(void)
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{ {
// Some commands are built-in:
if (a_Cmd == "stop")
{
m_ShouldStop = true;
}
else if (a_Cmd == "restart")
{
m_bRestart = true;
}
// Put the command into a queue (Alleviates FS #363): // Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands); cCSLock Lock(m_CSPendingCommands);
m_PendingCommands.push_back(cCommand(a_Cmd, &a_Output)); m_PendingCommands.emplace_back(a_Cmd, &a_Output);
} }
@ -507,15 +535,18 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{ {
// cRoot handles stopping and restarting due to our access to controlling variables // Some commands are built-in:
if (a_Cmd == "stop") if (a_Cmd == "stop")
{ {
m_ShouldStop = true; m_TerminateEventRaised = true;
m_StopEvent.Set();
m_InputThreadRunFlag.clear();
return; return;
} }
else if (a_Cmd == "restart") else if (a_Cmd == "restart")
{ {
m_bRestart = true; m_StopEvent.Set();
m_InputThreadRunFlag.clear();
return; return;
} }

View File

@ -7,6 +7,7 @@
#include "Defines.h" #include "Defines.h"
#include "RankManager.h" #include "RankManager.h"
#include <thread> #include <thread>
#include <atomic>
@ -48,13 +49,12 @@ public:
static bool m_TerminateEventRaised; static bool m_TerminateEventRaised;
static bool m_RunAsService; static bool m_RunAsService;
static bool m_ShouldStop;
cRoot(void); cRoot(void);
~cRoot(); ~cRoot();
void Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo); void Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo);
// tolua_begin // tolua_begin
cServer * GetServer(void) { return m_Server; } cServer * GetServer(void) { return m_Server; }
@ -200,6 +200,8 @@ private:
cCommandQueue m_PendingCommands; cCommandQueue m_PendingCommands;
std::thread m_InputThread; std::thread m_InputThread;
cEvent m_StopEvent;
std::atomic_flag m_InputThreadRunFlag;
cServer * m_Server; cServer * m_Server;
cMonsterConfig * m_MonsterConfig; cMonsterConfig * m_MonsterConfig;
@ -213,9 +215,7 @@ private:
std::unique_ptr<cRankManager> m_RankManager; std::unique_ptr<cRankManager> m_RankManager;
cHTTPServer m_HTTPServer; cHTTPServer m_HTTPServer;
bool m_bRestart;
void LoadGlobalSettings(); void LoadGlobalSettings();

View File

@ -22,9 +22,6 @@
/** If something has told the server to stop; checked periodically in cRoot */ /** If something has told the server to stop; checked periodically in cRoot */
bool cRoot::m_TerminateEventRaised = false; bool cRoot::m_TerminateEventRaised = false;
/** Set to true when the server terminates, so our CTRL handler can then tell the OS to close the console. */
static bool g_ServerTerminated = false;
/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */ /** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
bool g_ShouldLogCommIn; bool g_ShouldLogCommIn;
@ -72,7 +69,7 @@ Synchronize this with Server.cpp to enable the "dumpmem" console command. */
void NonCtrlHandler(int a_Signal) void NonCtrlHandler(int a_Signal)
{ {
LOGD("Terminate event raised from std::signal"); LOGD("Terminate event raised from std::signal");
cRoot::m_TerminateEventRaised = true; cRoot::Get()->QueueExecuteConsoleCommand("stop");
switch (a_Signal) switch (a_Signal)
{ {
@ -189,13 +186,11 @@ LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_Except
// Handle CTRL events in windows, including console window close // Handle CTRL events in windows, including console window close
BOOL CtrlHandler(DWORD fdwCtrlType) BOOL CtrlHandler(DWORD fdwCtrlType)
{ {
cRoot::m_TerminateEventRaised = true; cRoot::Get()->QueueExecuteConsoleCommand("stop");
LOGD("Terminate event raised from the Windows CtrlHandler"); LOGD("Terminate event raised from the Windows CtrlHandler");
while (!g_ServerTerminated) std::this_thread::sleep_for(std::chrono::seconds(10)); // Delay as much as possible to try to get the server to shut down cleanly - 10 seconds given by Windows
{ // Returning from main() automatically aborts this handler thread
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Delay as much as possible to try to get the server to shut down cleanly
}
return TRUE; return TRUE;
} }
@ -206,29 +201,22 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// universalMain - Main startup logic for both standard running and as a service // UniversalMain - Main startup logic for both standard running and as a service
void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo) void UniversalMain(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
{ {
#ifdef _WIN32
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
{
LOGERROR("Could not install the Windows CTRL handler!");
}
#endif
// Initialize logging subsystem: // Initialize logging subsystem:
cLogger::InitiateMultithreading(); cLogger::InitiateMultithreading();
// Initialize LibEvent: // Initialize LibEvent:
cNetworkSingleton::Get(); cNetworkSingleton::Get().Initialise();
#if !defined(ANDROID_NDK) #if !defined(ANDROID_NDK)
try try
#endif #endif
{ {
cRoot Root; cRoot Root;
Root.Start(std::move(overridesRepo)); Root.Start(std::move(a_OverridesRepo));
} }
#if !defined(ANDROID_NDK) #if !defined(ANDROID_NDK)
catch (std::exception & e) catch (std::exception & e)
@ -241,8 +229,6 @@ void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
} }
#endif #endif
g_ServerTerminated = true;
// Shutdown all of LibEvent: // Shutdown all of LibEvent:
cNetworkSingleton::Get().Terminate(); cNetworkSingleton::Get().Terminate();
} }
@ -259,8 +245,11 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
{ {
UNREFERENCED_PARAMETER(lpParam); UNREFERENCED_PARAMETER(lpParam);
// Do the normal startup while (!cRoot::m_TerminateEventRaised)
universalMain(cpp14::make_unique<cMemorySettingsRepository>()); {
// Do the normal startup
UniversalMain(cpp14::make_unique<cMemorySettingsRepository>());
}
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }
@ -274,8 +263,7 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode) void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode)
{ {
SERVICE_STATUS serviceStatus; SERVICE_STATUS serviceStatus = {};
ZeroMemory(&serviceStatus, sizeof(SERVICE_STATUS));
serviceStatus.dwCheckPoint = 0; serviceStatus.dwCheckPoint = 0;
serviceStatus.dwControlsAccepted = acceptedControls; serviceStatus.dwControlsAccepted = acceptedControls;
serviceStatus.dwCurrentState = newState; serviceStatus.dwCurrentState = newState;
@ -302,11 +290,10 @@ void WINAPI serviceCtrlHandler(DWORD CtrlCode)
{ {
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_STOP:
{ {
cRoot::m_ShouldStop = true; cRoot::Get()->QueueExecuteConsoleCommand("stop");
serviceSetState(0, SERVICE_STOP_PENDING, 0); serviceSetState(0, SERVICE_STOP_PENDING, 0);
break; break;
} }
default: default:
{ {
break; break;
@ -365,7 +352,7 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv) std::unique_ptr<cMemorySettingsRepository> ParseArguments(int argc, char **argv)
{ {
try try
{ {
@ -484,7 +471,13 @@ int main(int argc, char **argv)
#endif // SIGABRT_COMPAT #endif // SIGABRT_COMPAT
#endif #endif
auto argsRepo = parseArguments(argc, argv);
#ifdef _WIN32
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
{
LOGERROR("Could not install the Windows CTRL handler!");
}
#endif
// Attempt to run as a service // Attempt to run as a service
if (cRoot::m_RunAsService) if (cRoot::m_RunAsService)
@ -522,13 +515,19 @@ int main(int argc, char **argv)
close(STDOUT_FILENO); close(STDOUT_FILENO);
close(STDERR_FILENO); close(STDERR_FILENO);
universalMain(std::move(argsRepo)); while (!cRoot::m_TerminateEventRaised)
{
UniversalMain(std::move(ParseArguments(argc, argv)));
}
#endif #endif
} }
else else
{ {
// Not running as a service, do normal startup while (!cRoot::m_TerminateEventRaised)
universalMain(std::move(argsRepo)); {
// Not running as a service, do normal startup
UniversalMain(std::move(ParseArguments(argc, argv)));
}
} }
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)

View File

@ -118,6 +118,7 @@ static void DoTest(void)
int main() int main()
{ {
cNetworkSingleton::Get().Initialise();
DoTest(); DoTest();
cNetworkSingleton::Get().Terminate(); cNetworkSingleton::Get().Terminate();