+
+/** Halt and try not to catch fire.
+ * Writing to any file could corrupt it,
+ * any uneccessary code could crash while we crash.
+ * No malloc() (libgcc should be loaded already) or Con_Printf() allowed here.
+ */
+static void Sys_HandleCrash(int sig)
+{
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ // Before doing anything else grab the stack frame addresses
+ #include <execinfo.h>
+ void *stackframes[32];
+ int framecount = backtrace(stackframes, 32);
+#endif
+
+ // Windows doesn't have strsignal()
+ const char *sigdesc;
+ switch (sig)
+ {
+#ifndef WIN32 // or SIGBUS
+ case SIGBUS: sigdesc = "Bus error"; break;
+#endif
+ case SIGILL: sigdesc = "Illegal instruction"; break;
+ case SIGABRT: sigdesc = "Aborted"; break;
+ case SIGFPE: sigdesc = "Floating point exception"; break;
+ case SIGSEGV: sigdesc = "Segmentation fault"; break;
+ default: sigdesc = "Yo dawg, we hit a bug while hitting a bug";
+ }
+
+ // set output to blocking stderr
+ sys.outfd = fileno(stderr);
+#ifndef WIN32
+ fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
+#endif
+
+ fprintf(stderr, "\n\n\e[1;37;41m Engine Crash: %s (%d) \e[m\n", sigdesc, sig);
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ // the first two addresses will be in this function and in signal() in libc
+ backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
+#endif
+ fprintf(stderr, "\e[1m%s\e[m\n", engineversion);
+
+ // DP8 TODO: send a disconnect message indicating we crashed, see CL_DisconnectEx()
+
+ // don't want a dead window left blocking the OS UI or the crash dialog
+ VID_Shutdown();
+ S_StopAllSounds();
+
+ Sys_SDL_Dialog("Engine Crash", sigdesc);
+
+ fflush(stderr);
+
+ exit (sig);
+}
+
+static void Sys_HandleSignal(int sig)
+{
+#ifdef WIN32
+ // Windows users will likely never see this so no point replicating strsignal()
+ Con_Printf("\nReceived signal %d, exiting...\n", sig);
+#else
+ Con_Printf("\nReceived %s signal (%d), exiting...\n", strsignal(sig), sig);
+#endif
+ host.state = host_shutdown;
+}
+
+/// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
+static void Sys_InitSignals(void)
+{
+// Windows docs say its signal() only accepts these ones
+ signal(SIGABRT, Sys_HandleCrash);
+ signal(SIGFPE, Sys_HandleCrash);
+ signal(SIGILL, Sys_HandleCrash);
+ signal(SIGINT, Sys_HandleSignal);
+ signal(SIGSEGV, Sys_HandleCrash);
+ signal(SIGTERM, Sys_HandleSignal);
+#ifndef WIN32
+ signal(SIGHUP, Sys_HandleSignal);
+ signal(SIGQUIT, Sys_HandleSignal);
+ signal(SIGBUS, Sys_HandleCrash);
+ signal(SIGPIPE, Sys_HandleSignal);
+#endif
+}
+
+int main (int argc, char **argv)
+{
+ sys.argc = argc;
+ sys.argv = (const char **)argv;
+
+ // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
+ // COMMANDLINEOPTION: -noterminal disables console output on stdout
+ if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
+ sys_stdout.string = "0";
+ // COMMANDLINEOPTION: -stderr moves console output to stderr
+ else if(Sys_CheckParm("-stderr"))
+ sys_stdout.string = "2";
+ // too early for Cvar_SetQuick
+ sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
+ Sys_UpdateOutFD_c(&sys_stdout);
+#ifndef WIN32
+ fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
+ // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
+ fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
+ fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
+#endif
+
+ sys.selffd = -1;
+ Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
+
+#ifdef __ANDROID__
+ Sys_AllowProfiling(true);
+#endif
+
+ Sys_InitSignals();
+
+ Host_Main();
+
+ Sys_Quit(0);
+
+ return 0;
+}