2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2020-10-05 06:27:14 -04:00
# include "main.h"
2015-05-04 04:07:03 -04:00
# include "BuildInfo.h"
2015-08-30 17:57:43 -04:00
# include "Logger.h"
2015-05-14 10:47:51 -04:00
# include "MemorySettingsRepository.h"
2020-07-22 19:34:43 -04:00
# include "OSSupport/NetworkSingleton.h"
# include "OSSupport/MiniDumpWriter.h"
# include "OSSupport/StartAsService.h"
# include "Root.h"
# include "tclap/CmdLine.h"
2012-08-15 17:24:11 -04:00
2020-07-22 19:34:43 -04:00
# include <csignal>
# include <cstdlib>
2016-01-06 10:20:12 -05:00
2015-01-26 08:46:20 -05:00
2014-01-26 11:54:18 -05:00
bool g_ShouldLogCommIn ;
bool g_ShouldLogCommOut ;
2020-07-22 19:34:43 -04:00
bool g_RunAsService ;
2014-01-24 17:03:48 -05:00
2020-07-22 19:34:43 -04:00
/** Global that registers itself as a last chance exception handler to write a minidump on crash. */
2020-10-05 06:27:14 -04:00
static MiniDumpWriter g_MiniDumpWriter ;
2014-01-24 17:03:48 -05:00
2015-04-05 11:07:10 -04:00
2017-12-21 06:36:58 -05:00
// Because SIG_DFL or SIG_IGN could be NULL instead of nullptr, we need to disable the Clang warning here
2017-12-22 13:25:46 -05:00
# ifdef __clang__
2017-12-21 06:36:58 -05:00
# pragma clang diagnostic push
2017-12-22 13:25:46 -05:00
# pragma clang diagnostic ignored "-Wunknown-warning-option"
# pragma clang diagnostic ignored "-Wunknown-pragmas"
2017-12-21 06:36:58 -05:00
# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
2017-12-22 13:25:46 -05:00
# endif // __clang__
2017-12-21 06:36:58 -05:00
2016-01-06 10:20:12 -05:00
static void NonCtrlHandler ( int a_Signal )
2012-06-14 09:06:06 -04:00
{
2013-12-22 15:03:58 -05:00
LOGD ( " Terminate event raised from std::signal " ) ;
2012-06-14 09:06:06 -04:00
2013-12-22 15:03:58 -05:00
switch ( a_Signal )
{
case SIGSEGV :
{
2014-11-29 17:06:10 -05:00
PrintStackTrace ( ) ;
2020-07-22 19:34:43 -04:00
2020-09-05 08:41:42 -04:00
LOGERROR (
" Failure report: \n \n "
" :( | Cuberite has encountered an error and needs to close \n "
" | SIGSEGV: Segmentation fault \n "
" | \n "
2020-07-22 19:34:43 -04:00
# ifdef BUILD_ID
2020-09-05 08:41:42 -04:00
" | Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID " ) \n "
" | from commit " BUILD_COMMIT_ID " \n "
2020-07-22 19:34:43 -04:00
# endif
2020-09-05 08:41:42 -04:00
) ;
2020-07-22 19:34:43 -04:00
std : : signal ( SIGSEGV , SIG_DFL ) ;
return ;
2013-12-22 15:03:58 -05:00
}
2014-01-16 17:30:57 -05:00
case SIGABRT :
2020-07-22 19:34:43 -04:00
# ifdef SIGABRT_COMPAT
2014-01-16 17:30:57 -05:00
case SIGABRT_COMPAT :
2020-07-22 19:34:43 -04:00
# endif
2014-01-16 17:30:57 -05:00
{
2014-11-29 17:06:10 -05:00
PrintStackTrace ( ) ;
2020-07-22 19:34:43 -04:00
2020-09-05 08:41:42 -04:00
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 "
2020-07-22 19:34:43 -04:00
# ifdef BUILD_ID
2020-09-05 08:41:42 -04:00
" | Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID " ) \n "
" | from commit " BUILD_COMMIT_ID " \n "
2020-07-22 19:34:43 -04:00
# endif
2020-09-05 08:41:42 -04:00
) ;
2020-07-22 19:34:43 -04:00
std : : signal ( SIGSEGV , SIG_DFL ) ;
return ;
2014-01-16 17:30:57 -05:00
}
2014-01-26 11:15:05 -05:00
case SIGINT :
2014-01-07 16:23:26 -05:00
case SIGTERM :
{
2020-07-22 19:34:43 -04:00
// Server is shutting down, wait for it...
2020-10-05 06:27:14 -04:00
cRoot : : Get ( ) - > Stop ( ) ;
2020-07-22 19:34:43 -04:00
return ;
2014-01-07 16:23:26 -05:00
}
2020-07-22 19:34:43 -04:00
# ifdef SIGPIPE
case SIGPIPE :
{
// Ignore (PR #2487)
return ;
}
# endif
2013-12-22 15:03:58 -05:00
}
2012-06-14 09:06:06 -04:00
}
2017-12-21 06:36:58 -05:00
2017-12-22 13:25:46 -05:00
# ifdef __clang__
2017-12-21 06:36:58 -05:00
# pragma clang diagnostic pop
2017-12-22 13:25:46 -05:00
# endif // __clang__
2012-06-14 09:06:06 -04:00
2015-04-05 11:07:10 -04:00
2013-12-22 15:03:58 -05:00
# ifdef _WIN32
// Handle CTRL events in windows, including console window close
2016-01-06 10:20:12 -05:00
static BOOL CtrlHandler ( DWORD fdwCtrlType )
2013-12-22 15:03:58 -05:00
{
2020-10-05 06:27:14 -04:00
cRoot : : Get ( ) - > Stop ( ) ;
2013-12-22 15:03:58 -05:00
LOGD ( " Terminate event raised from the Windows CtrlHandler " ) ;
2020-07-22 19:34:43 -04:00
// 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 ) ) ;
2015-06-17 10:38:00 -04:00
// Returning from main() automatically aborts this handler thread
2013-12-22 15:03:58 -05:00
return TRUE ;
}
# endif
2015-04-05 11:07:10 -04:00
2015-03-31 09:50:03 -04:00
////////////////////////////////////////////////////////////////////////////////
2020-07-22 19:34:43 -04:00
// ParseArguments - Read the startup arguments and store into a settings object
2015-03-31 09:50:03 -04:00
2020-10-05 06:27:14 -04:00
static void ParseArguments ( int argc , char * * argv , cMemorySettingsRepository & repo )
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
// 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 ( ) )
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
AString conf_file = confArg . getValue ( ) ;
repo . AddValue ( " Server " , " ConfigFile " , conf_file ) ;
2015-03-31 09:50:03 -04:00
}
2020-07-22 19:34:43 -04:00
if ( slotsArg . isSet ( ) )
2018-01-03 12:41:16 -05:00
{
2020-07-22 19:34:43 -04:00
int slots = slotsArg . getValue ( ) ;
repo . AddValue ( " Server " , " MaxPlayers " , static_cast < Int64 > ( slots ) ) ;
2018-01-03 12:41:16 -05:00
}
2020-07-22 19:34:43 -04:00
if ( portsArg . isSet ( ) )
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
for ( auto port : portsArg . getValue ( ) )
{
repo . AddValue ( " Server " , " Ports " , std : : to_string ( port ) ) ;
}
2015-03-31 09:50:03 -04:00
}
2020-07-22 19:34:43 -04:00
if ( noFileLogArg . getValue ( ) )
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
repo . AddValue ( " Server " , " DisableLogFile " , true ) ;
2015-03-31 09:50:03 -04:00
}
2020-07-22 19:34:43 -04:00
if ( commLogArg . getValue ( ) )
2015-06-17 10:38:00 -04:00
{
2020-07-22 19:34:43 -04:00
g_ShouldLogCommIn = true ;
g_ShouldLogCommOut = true ;
2015-06-17 10:38:00 -04:00
}
2020-07-22 19:34:43 -04:00
else
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
g_ShouldLogCommIn = commLogInArg . getValue ( ) ;
g_ShouldLogCommOut = commLogOutArg . getValue ( ) ;
2015-03-31 09:50:03 -04:00
}
2020-07-22 19:34:43 -04:00
if ( noBufArg . getValue ( ) )
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
setvbuf ( stdout , nullptr , _IONBF , 0 ) ;
2015-03-31 09:50:03 -04:00
}
2020-07-22 19:34:43 -04:00
repo . SetReadOnly ( ) ;
2015-03-31 09:50:03 -04:00
2020-07-22 19:34:43 -04:00
if ( runAsServiceArg . getValue ( ) )
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
g_RunAsService = true ;
2015-03-31 09:50:03 -04:00
}
2016-02-05 16:45:45 -05:00
2020-07-22 19:34:43 -04:00
// Apply the CrashDump flags for platforms that support them:
if ( crashDumpGlobals . getValue ( ) )
2015-03-31 09:50:03 -04:00
{
2020-07-22 19:34:43 -04:00
g_MiniDumpWriter . AddDumpFlags ( MiniDumpFlags : : WithDataSegments ) ;
}
if ( crashDumpFull . getValue ( ) )
{
g_MiniDumpWriter . AddDumpFlags ( MiniDumpFlags : : WithFullMemory ) ;
2015-03-31 09:50:03 -04:00
}
}
2015-06-02 06:58:19 -04:00
2020-07-22 19:34:43 -04:00
////////////////////////////////////////////////////////////////////////////////
// UniversalMain - Main startup logic for both standard running and as a service
static int UniversalMain ( int argc , char * argv [ ] , bool RunningAsService )
2015-05-14 10:47:51 -04:00
{
2020-07-22 19:34:43 -04:00
// Initialize logging subsystem:
cLogger : : InitiateMultithreading ( ) ;
struct NetworkRAII
2015-05-14 10:47:51 -04:00
{
2020-07-22 19:34:43 -04:00
NetworkRAII ( )
2017-02-22 08:10:32 -05:00
{
2020-07-22 19:34:43 -04:00
// Initialize LibEvent:
cNetworkSingleton : : Get ( ) . Initialise ( ) ;
2017-02-22 08:10:32 -05:00
}
2020-07-22 19:34:43 -04:00
~ NetworkRAII ( )
2015-05-18 10:43:26 -04:00
{
2020-07-22 19:34:43 -04:00
// Shutdown all of LibEvent:
cNetworkSingleton : : Get ( ) . Terminate ( ) ;
2015-05-18 11:04:27 -04:00
}
2020-07-22 19:34:43 -04:00
} ;
try
{
// Make sure g_RunAsService is set correctly before checking it's value
2020-10-05 06:27:14 -04:00
cMemorySettingsRepository Settings ;
ParseArguments ( argc , argv , Settings ) ;
2020-07-22 19:34:43 -04:00
// Attempt to run as a service
if ( ! RunningAsService & & g_RunAsService )
2015-05-18 11:04:27 -04:00
{
2020-07-22 19:34:43 -04:00
// This will either fork or call UniversalMain again:
if ( cStartAsService : : MakeIntoService < & UniversalMain > ( ) )
2015-05-18 11:04:27 -04:00
{
2020-07-22 19:34:43 -04:00
return EXIT_SUCCESS ;
2015-05-18 11:04:27 -04:00
}
2015-05-18 10:43:26 -04:00
}
2015-05-18 13:57:16 -04:00
2020-07-22 19:34:43 -04:00
while ( true )
2015-06-01 09:41:06 -04:00
{
2020-07-22 19:34:43 -04:00
NetworkRAII LibEvent ;
cRoot Root ;
2015-06-01 09:41:06 -04:00
2020-07-22 19:34:43 -04:00
if ( ! Root . Run ( Settings ) )
2015-06-02 06:58:19 -04:00
{
2020-07-22 19:34:43 -04:00
break ;
2015-06-02 06:58:19 -04:00
}
2020-07-22 19:34:43 -04:00
}
2015-05-14 10:47:51 -04:00
2020-07-22 19:34:43 -04:00
return EXIT_SUCCESS ;
2015-05-14 10:47:51 -04:00
}
2020-07-22 19:34:43 -04:00
catch ( const fmt : : format_error & Oops )
2015-05-14 10:47:51 -04:00
{
2020-07-22 19:34:43 -04:00
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 " ;
2015-05-14 10:47:51 -04:00
}
2020-07-22 19:34:43 -04:00
return EXIT_FAILURE ;
}
2013-12-22 15:03:58 -05:00
2015-06-02 06:58:19 -04:00
2012-06-14 09:06:06 -04:00
2016-01-06 10:20:12 -05:00
int main ( int argc , char * * argv )
2012-06-14 09:06:06 -04:00
{
2020-07-22 19:34:43 -04:00
# if !defined(NDEBUG) && defined(_MSC_VER)
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ) ;
2015-03-31 09:50:03 -04:00
2020-07-22 19:34:43 -04:00
// _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);
2015-06-04 09:13:07 -04:00
2020-07-22 19:34:43 -04:00
# endif // _DEBUG && _MSC_VER
2015-06-04 09:13:07 -04:00
2020-07-22 19:34:43 -04:00
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
2015-06-04 09:13:07 -04:00
2020-07-22 19:34:43 -04:00
# ifdef _WIN32
VERIFY ( SetConsoleCtrlHandler ( reinterpret_cast < PHANDLER_ROUTINE > ( CtrlHandler ) , TRUE ) = = TRUE ) ;
# endif
2015-06-04 09:13:07 -04:00
2020-07-22 19:34:43 -04:00
return UniversalMain ( argc , argv , false ) ;
2012-06-14 09:06:06 -04:00
}