50a94f972d
Use the standard NDEBUG.
311 lines
7.8 KiB
C++
311 lines
7.8 KiB
C++
|
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
|
|
|
#include "main.h"
|
|
#include "BuildInfo.h"
|
|
#include "Logger.h"
|
|
#include "MemorySettingsRepository.h"
|
|
#include "OSSupport/NetworkSingleton.h"
|
|
#include "OSSupport/MiniDumpWriter.h"
|
|
#include "OSSupport/StartAsService.h"
|
|
#include "Root.h"
|
|
#include "tclap/CmdLine.h"
|
|
|
|
#include <csignal>
|
|
#include <cstdlib>
|
|
|
|
|
|
|
|
|
|
|
|
bool g_ShouldLogCommIn;
|
|
bool g_ShouldLogCommOut;
|
|
bool g_RunAsService;
|
|
|
|
/** Global that registers itself as a last chance exception handler to write a minidump on crash. */
|
|
static MiniDumpWriter g_MiniDumpWriter;
|
|
|
|
|
|
|
|
|
|
|
|
// Because SIG_DFL or SIG_IGN could be NULL instead of nullptr, we need to disable the Clang warning here
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
|
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
|
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
|
#endif // __clang__
|
|
|
|
static void NonCtrlHandler(int a_Signal)
|
|
{
|
|
LOGD("Terminate event raised from std::signal");
|
|
|
|
switch (a_Signal)
|
|
{
|
|
case SIGSEGV:
|
|
{
|
|
PrintStackTrace();
|
|
|
|
LOGERROR(
|
|
"Failure report: \n\n"
|
|
" :( | Cuberite has encountered an error and needs to close\n"
|
|
" | SIGSEGV: Segmentation fault\n"
|
|
" |\n"
|
|
#ifdef BUILD_ID
|
|
" | Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID ")\n"
|
|
" | from commit " BUILD_COMMIT_ID "\n"
|
|
#endif
|
|
);
|
|
|
|
std::signal(SIGSEGV, SIG_DFL);
|
|
return;
|
|
}
|
|
case SIGABRT:
|
|
#ifdef SIGABRT_COMPAT
|
|
case SIGABRT_COMPAT:
|
|
#endif
|
|
{
|
|
PrintStackTrace();
|
|
|
|
LOGERROR(
|
|
"Failure report: \n\n"
|
|
" :( | Cuberite has encountered an error and needs to close\n"
|
|
" | SIGABRT: Server self-terminated due to an internal fault\n"
|
|
" |\n"
|
|
#ifdef BUILD_ID
|
|
" | Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID ")\n"
|
|
" | from commit " BUILD_COMMIT_ID "\n"
|
|
#endif
|
|
);
|
|
|
|
std::signal(SIGSEGV, SIG_DFL);
|
|
return;
|
|
}
|
|
case SIGINT:
|
|
case SIGTERM:
|
|
{
|
|
// Server is shutting down, wait for it...
|
|
cRoot::Stop();
|
|
return;
|
|
}
|
|
#ifdef SIGPIPE
|
|
case SIGPIPE:
|
|
{
|
|
// Ignore (PR #2487)
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif // __clang__
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
// Handle CTRL events in windows, including console window close
|
|
static BOOL CtrlHandler(DWORD fdwCtrlType)
|
|
{
|
|
cRoot::Stop();
|
|
LOGD("Terminate event raised from the Windows CtrlHandler");
|
|
|
|
// Delay as much as possible to try to get the server to shut down cleanly - 10 seconds given by Windows
|
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
|
|
|
// Returning from main() automatically aborts this handler thread
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// ParseArguments - Read the startup arguments and store into a settings object
|
|
|
|
static void ParseArguments(int argc, char ** argv, cMemorySettingsRepository & Settings)
|
|
{
|
|
// Parse the comand line args:
|
|
TCLAP::CmdLine cmd("Cuberite");
|
|
TCLAP::ValueArg<int> slotsArg ("s", "max-players", "Maximum number of slots for the server to use, overrides setting in setting.ini", false, -1, "number", cmd);
|
|
TCLAP::ValueArg<AString> confArg ("c", "config-file", "Config file to use", false, "settings.ini", "string", cmd);
|
|
TCLAP::MultiArg<int> portsArg ("p", "port", "The port number the server should listen to", false, "port", cmd);
|
|
TCLAP::SwitchArg commLogArg ("", "log-comm", "Log server client communications to file", cmd);
|
|
TCLAP::SwitchArg commLogInArg ("", "log-comm-in", "Log inbound server client communications to file", cmd);
|
|
TCLAP::SwitchArg commLogOutArg ("", "log-comm-out", "Log outbound server client communications to file", cmd);
|
|
TCLAP::SwitchArg crashDumpFull ("", "crash-dump-full", "Crashdumps created by the server will contain full server memory", cmd);
|
|
TCLAP::SwitchArg crashDumpGlobals("", "crash-dump-globals", "Crashdumps created by the server will contain the global variables' values", cmd);
|
|
TCLAP::SwitchArg noBufArg ("", "no-output-buffering", "Disable output buffering", cmd);
|
|
TCLAP::SwitchArg noFileLogArg ("", "no-log-file", "Disable logging to file", cmd);
|
|
TCLAP::SwitchArg runAsServiceArg ("d", "service", "Run as a service on Windows, or daemon on UNIX like systems", cmd);
|
|
cmd.parse(argc, argv);
|
|
|
|
// Copy the parsed args' values into a settings repository:
|
|
if (confArg.isSet())
|
|
{
|
|
AString conf_file = confArg.getValue();
|
|
Settings.AddValue("Server", "ConfigFile", conf_file);
|
|
}
|
|
if (slotsArg.isSet())
|
|
{
|
|
int slots = slotsArg.getValue();
|
|
Settings.AddValue("Server", "MaxPlayers", static_cast<Int64>(slots));
|
|
}
|
|
if (portsArg.isSet())
|
|
{
|
|
for (auto port: portsArg.getValue())
|
|
{
|
|
Settings.AddValue("Server", "Ports", std::to_string(port));
|
|
}
|
|
}
|
|
if (noFileLogArg.getValue())
|
|
{
|
|
Settings.AddValue("Server", "DisableLogFile", true);
|
|
}
|
|
if (commLogArg.getValue())
|
|
{
|
|
g_ShouldLogCommIn = true;
|
|
g_ShouldLogCommOut = true;
|
|
}
|
|
else
|
|
{
|
|
g_ShouldLogCommIn = commLogInArg.getValue();
|
|
g_ShouldLogCommOut = commLogOutArg.getValue();
|
|
}
|
|
if (noBufArg.getValue())
|
|
{
|
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
|
}
|
|
Settings.SetReadOnly();
|
|
|
|
if (runAsServiceArg.getValue())
|
|
{
|
|
g_RunAsService = true;
|
|
}
|
|
|
|
// Apply the CrashDump flags for platforms that support them:
|
|
if (crashDumpGlobals.getValue())
|
|
{
|
|
g_MiniDumpWriter.AddDumpFlags(MiniDumpFlags::WithDataSegments);
|
|
}
|
|
if (crashDumpFull.getValue())
|
|
{
|
|
g_MiniDumpWriter.AddDumpFlags(MiniDumpFlags::WithFullMemory);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// UniversalMain - Main startup logic for both standard running and as a service
|
|
|
|
static int UniversalMain(int argc, char * argv[], bool RunningAsService)
|
|
{
|
|
// Initialize logging subsystem:
|
|
cLogger::InitiateMultithreading();
|
|
|
|
struct NetworkRAII
|
|
{
|
|
NetworkRAII()
|
|
{
|
|
// Initialize LibEvent:
|
|
cNetworkSingleton::Get().Initialise();
|
|
}
|
|
|
|
~NetworkRAII()
|
|
{
|
|
// Shutdown all of LibEvent:
|
|
cNetworkSingleton::Get().Terminate();
|
|
}
|
|
};
|
|
|
|
try
|
|
{
|
|
cMemorySettingsRepository Settings;
|
|
ParseArguments(argc, argv, Settings); // Make sure g_RunAsService is set correctly before checking it's value
|
|
|
|
// Attempt to run as a service:
|
|
if (!RunningAsService && g_RunAsService)
|
|
{
|
|
// This will either fork or call UniversalMain again:
|
|
if (cStartAsService::MakeIntoService<&UniversalMain>())
|
|
{
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
NetworkRAII LibEvent;
|
|
cRoot Root;
|
|
|
|
if (!Root.Run(Settings))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
catch (const fmt::format_error & Oops)
|
|
{
|
|
std::cerr << "Formatting exception: " << Oops.what() << '\n';
|
|
}
|
|
catch (const TCLAP::ArgException & Oops)
|
|
{
|
|
std::cerr << fmt::sprintf("Error reading command line {} for argument {}\n", Oops.error(), Oops.argId());
|
|
}
|
|
catch (const std::exception & Oops)
|
|
{
|
|
std::cerr << "Standard exception: " << Oops.what() << '\n';
|
|
}
|
|
catch (...)
|
|
{
|
|
std::cerr << "Unknown exception!\n";
|
|
}
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char ** argv)
|
|
{
|
|
#if !defined(NDEBUG) && defined(_MSC_VER)
|
|
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
|
|
// _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output)
|
|
// Only useful when the leak is in the same sequence all the time
|
|
// _CrtSetBreakAlloc(85950);
|
|
|
|
#endif // !NDEBUG && _MSC_VER
|
|
|
|
std::signal(SIGSEGV, NonCtrlHandler);
|
|
std::signal(SIGTERM, NonCtrlHandler);
|
|
std::signal(SIGINT, NonCtrlHandler);
|
|
std::signal(SIGABRT, NonCtrlHandler);
|
|
#ifdef SIGABRT_COMPAT
|
|
std::signal(SIGABRT_COMPAT, NonCtrlHandler);
|
|
#endif
|
|
#ifdef SIGPIPE
|
|
std::signal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
VERIFY(SetConsoleCtrlHandler(reinterpret_cast<PHANDLER_ROUTINE>(CtrlHandler), TRUE) == TRUE);
|
|
#endif
|
|
|
|
return UniversalMain(argc, argv, false);
|
|
}
|