2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 18:09:57 -04:00
# include "Root.h"
2012-06-14 09:06:06 -04:00
2014-07-17 13:13:23 -04:00
# include <exception>
# include <csignal>
# include <stdlib.h>
2012-06-14 09:06:06 -04:00
2013-05-25 10:59:41 -04:00
# ifdef _MSC_VER
2012-06-14 09:06:06 -04:00
# include <dbghelp.h>
2013-05-25 10:59:41 -04:00
# endif // _MSC_VER
2012-06-14 09:06:06 -04:00
2013-12-22 15:03:58 -05:00
// Here, we have some ALL CAPS variables, to give the impression that this is deeeep, gritty programming :P
2014-07-17 13:13:23 -04:00
bool g_TERMINATE_EVENT_RAISED = false ; // If something has told the server to stop; checked periodically in cRoot
bool g_SERVER_TERMINATED = false ; // Set to true when the server terminates, so our CTRL handler can then tell Windows to close the console
2013-12-22 15:03:58 -05:00
2012-08-15 17:24:11 -04:00
2012-06-14 09:06:06 -04:00
2014-01-26 11:54:18 -05:00
/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
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 ;
2014-01-24 17:03:48 -05:00
2012-06-14 09:06:06 -04:00
/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
2014-02-20 14:11:17 -05:00
// _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
2012-06-14 09:06:06 -04:00
# if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
# pragma warning(push)
# pragma warning(disable:4100)
# include "LeakFinder.h"
# pragma warning(pop)
# endif
2014-07-17 10:33:09 -04:00
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 " ) ;
g_TERMINATE_EVENT_RAISED = true ;
2012-06-14 09:06:06 -04:00
2013-12-22 15:03:58 -05:00
switch ( a_Signal )
{
case SIGSEGV :
{
std : : signal ( SIGSEGV , SIG_DFL ) ;
2014-01-16 17:30:57 -05:00
LOGERROR ( " D: | MCServer has encountered an error and needs to close " ) ;
LOGERROR ( " Details | SIGSEGV: Segmentation fault " ) ;
2014-06-01 12:23:02 -04:00
abort ( ) ;
2013-12-22 15:03:58 -05:00
}
2014-01-16 17:30:57 -05:00
case SIGABRT :
2014-01-17 05:13:35 -05:00
# ifdef SIGABRT_COMPAT
2014-01-16 17:30:57 -05:00
case SIGABRT_COMPAT :
2014-01-17 05:13:35 -05:00
# endif
2014-01-16 17:30:57 -05:00
{
std : : signal ( a_Signal , SIG_DFL ) ;
LOGERROR ( " D: | MCServer has encountered an error and needs to close " ) ;
LOGERROR ( " Details | SIGABRT: Server self-terminated due to an internal fault " ) ;
2014-06-01 12:23:02 -04:00
abort ( ) ;
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 :
{
2014-07-17 13:13:23 -04:00
std : : signal ( a_Signal , SIG_IGN ) ; // Server is shutting down, wait for it...
2014-01-07 16:23:26 -05:00
break ;
}
2013-12-22 15:03:58 -05:00
default : break ;
}
2012-06-14 09:06:06 -04:00
}
2013-05-25 10:59:41 -04:00
# if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 09:06:06 -04:00
// Windows 32-bit stuff: when the server crashes, create a "dump file" containing the callstack of each thread and some variables; let the user send us that crash file for analysis
typedef BOOL ( WINAPI * pMiniDumpWriteDump ) (
HANDLE hProcess ,
DWORD ProcessId ,
HANDLE hFile ,
MINIDUMP_TYPE DumpType ,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam ,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam ,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
) ;
pMiniDumpWriteDump g_WriteMiniDump ; // The function in dbghlp DLL that creates dump files
char g_DumpFileName [ MAX_PATH ] ; // Filename of the dump file; hes to be created before the dump handler kicks in
char g_ExceptionStack [ 128 * 1024 ] ; // Substitute stack, just in case the handler kicks in because of "insufficient stack space"
MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal ; // By default dump only the stack and some helpers
/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar.
Its purpose is to create the crashdump using the dbghlp DLLs
*/
LONG WINAPI LastChanceExceptionFilter ( __in struct _EXCEPTION_POINTERS * a_ExceptionInfo )
{
char * newStack = & g_ExceptionStack [ sizeof ( g_ExceptionStack ) ] ;
char * oldStack ;
// Use the substitute stack:
// This code is the reason why we don't support 64-bit (yet)
_asm
{
mov oldStack , esp
mov esp , newStack
}
MINIDUMP_EXCEPTION_INFORMATION ExcInformation ;
ExcInformation . ThreadId = GetCurrentThreadId ( ) ;
ExcInformation . ExceptionPointers = a_ExceptionInfo ;
ExcInformation . ClientPointers = 0 ;
// Write the dump file:
HANDLE dumpFile = CreateFile ( g_DumpFileName , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
g_WriteMiniDump ( GetCurrentProcess ( ) , GetCurrentProcessId ( ) , dumpFile , g_DumpFlags , ( a_ExceptionInfo ) ? & ExcInformation : NULL , NULL , NULL ) ;
CloseHandle ( dumpFile ) ;
// Revert to old stack:
_asm
{
mov esp , oldStack
}
return 0 ;
}
# endif // _WIN32 && !_WIN64
2013-12-22 15:03:58 -05:00
# ifdef _WIN32
// Handle CTRL events in windows, including console window close
BOOL CtrlHandler ( DWORD fdwCtrlType )
{
g_TERMINATE_EVENT_RAISED = true ;
LOGD ( " Terminate event raised from the Windows CtrlHandler " ) ;
2014-07-17 13:13:23 -04:00
if ( fdwCtrlType = = CTRL_CLOSE_EVENT ) // Console window closed via 'x' button, Windows will try to close immediately, therefore...
2013-12-22 15:03:58 -05:00
{
2014-07-17 13:13:23 -04:00
while ( ! g_SERVER_TERMINATED ) { cSleep : : MilliSleep ( 100 ) ; } // Delay as much as possible to try to get the server to shut down cleanly
2013-12-22 15:03:58 -05:00
}
return TRUE ;
}
# endif
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 09:06:06 -04:00
// main:
int main ( int argc , char * * argv )
{
2013-12-22 15:03:58 -05:00
UNUSED ( argc ) ;
UNUSED ( argv ) ;
2012-06-14 09:06:06 -04:00
# if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
InitLeakFinder ( ) ;
# endif
// Magic code to produce dump-files on Windows if the server crashes:
2013-05-25 10:59:41 -04:00
# if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
2012-06-14 09:06:06 -04:00
HINSTANCE hDbgHelp = LoadLibrary ( " DBGHELP.DLL " ) ;
g_WriteMiniDump = ( pMiniDumpWriteDump ) GetProcAddress ( hDbgHelp , " MiniDumpWriteDump " ) ;
if ( g_WriteMiniDump ! = NULL )
{
_snprintf_s ( g_DumpFileName , ARRAYCOUNT ( g_DumpFileName ) , _TRUNCATE , " crash_mcs_%x.dmp " , GetCurrentProcessId ( ) ) ;
SetUnhandledExceptionFilter ( LastChanceExceptionFilter ) ;
// Parse arguments for minidump flags:
for ( int i = 0 ; i < argc ; i + + )
{
2012-07-16 15:20:37 -04:00
if ( _stricmp ( argv [ i ] , " /cdg " ) = = 0 )
2012-06-14 09:06:06 -04:00
{
// Add globals to the dump
g_DumpFlags = ( MINIDUMP_TYPE ) ( g_DumpFlags | MiniDumpWithDataSegs ) ;
}
2012-07-16 15:20:37 -04:00
else if ( _stricmp ( argv [ i ] , " /cdf " ) = = 0 )
2012-06-14 09:06:06 -04:00
{
// Add full memory to the dump (HUUUGE file)
g_DumpFlags = ( MINIDUMP_TYPE ) ( g_DumpFlags | MiniDumpWithFullMemory ) ;
}
} // for i - argv[]
}
# endif // _WIN32 && !_WIN64
// End of dump-file magic
2013-12-22 15:03:58 -05:00
# ifdef _WIN32
if ( ! SetConsoleCtrlHandler ( ( PHANDLER_ROUTINE ) CtrlHandler , TRUE ) )
{
LOGERROR ( " Could not install the Windows CTRL handler! " ) ;
}
# endif
2012-06-14 09:06:06 -04:00
# if defined(_DEBUG) && 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 // _DEBUG && _MSC_VER
# ifndef _DEBUG
2013-12-22 15:03:58 -05:00
std : : signal ( SIGSEGV , NonCtrlHandler ) ;
std : : signal ( SIGTERM , NonCtrlHandler ) ;
2014-01-24 17:03:48 -05:00
std : : signal ( SIGINT , NonCtrlHandler ) ;
2014-01-26 11:15:05 -05:00
std : : signal ( SIGABRT , NonCtrlHandler ) ;
# ifdef SIGABRT_COMPAT
std : : signal ( SIGABRT_COMPAT , NonCtrlHandler ) ;
2014-07-17 13:13:23 -04:00
# endif // SIGABRT_COMPAT
2012-06-14 09:06:06 -04:00
# endif
// DEBUG: test the dumpfile creation:
// *((int *)0) = 0;
2014-01-24 17:03:48 -05:00
// Check if comm logging is to be enabled:
for ( int i = 0 ; i < argc ; i + + )
{
2014-02-27 09:17:42 -05:00
AString Arg ( argv [ i ] ) ;
2014-01-24 17:03:48 -05:00
if (
2014-02-27 09:17:42 -05:00
( NoCaseCompare ( Arg , " /commlog " ) = = 0 ) | |
( NoCaseCompare ( Arg , " /logcomm " ) = = 0 )
2014-01-24 17:03:48 -05:00
)
{
2014-01-26 11:54:18 -05:00
g_ShouldLogCommIn = true ;
g_ShouldLogCommOut = true ;
}
2014-02-27 09:17:42 -05:00
else if (
( NoCaseCompare ( Arg , " /commlogin " ) = = 0 ) | |
( NoCaseCompare ( Arg , " /comminlog " ) = = 0 ) | |
( NoCaseCompare ( Arg , " /logcommin " ) = = 0 )
2014-01-26 11:54:18 -05:00
)
{
g_ShouldLogCommIn = true ;
}
2014-02-27 09:17:42 -05:00
else if (
( NoCaseCompare ( Arg , " /commlogout " ) = = 0 ) | |
( NoCaseCompare ( Arg , " /commoutlog " ) = = 0 ) | |
( NoCaseCompare ( Arg , " /logcommout " ) = = 0 )
2014-01-26 11:54:18 -05:00
)
{
g_ShouldLogCommOut = true ;
2014-01-24 17:03:48 -05:00
}
2014-02-27 09:17:42 -05:00
else if ( NoCaseCompare ( Arg , " nooutbuf " ) = = 0 )
{
setvbuf ( stdout , NULL , _IONBF , 0 ) ;
}
} // for i - argv[]
2014-01-24 17:03:48 -05:00
2012-08-15 17:24:11 -04:00
# if !defined(ANDROID_NDK)
2012-06-14 09:06:06 -04:00
try
2012-08-15 17:24:11 -04:00
# endif
2012-06-14 09:06:06 -04:00
{
2014-07-17 10:33:09 -04:00
cRoot Root ;
2012-06-14 09:06:06 -04:00
Root . Start ( ) ;
}
2012-08-15 17:24:11 -04:00
# if !defined(ANDROID_NDK)
2012-06-14 09:06:06 -04:00
catch ( std : : exception & e )
{
LOGERROR ( " Standard exception: %s " , e . what ( ) ) ;
}
catch ( . . . )
{
LOGERROR ( " Unknown exception! " ) ;
}
2012-08-15 17:24:11 -04:00
# endif
2012-06-14 09:06:06 -04:00
# if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
DeinitLeakFinder ( ) ;
# endif
2013-12-22 15:03:58 -05:00
g_SERVER_TERMINATED = true ;
return EXIT_SUCCESS ;
2012-06-14 09:06:06 -04:00
}