#include "thread.h"
#include "libcurl.h"
-#ifdef WIN32
- // Microsoft's compiler complains about portable code
- #pragma warning(disable : 4996)
-#endif
sys_t sys;
}
-void Sys_Quit (int returnvalue)
-{
- // Unlock mutexes because the quit command may jump directly here, causing a deadlock
- if ((cmd_local)->cbuf->lock)
- Cbuf_Unlock((cmd_local)->cbuf);
- SV_UnlockThreadMutex();
- TaskQueue_Frame(true);
-
- if (Sys_CheckParm("-profilegameonly"))
- Sys_AllowProfiling(false);
- host.state = host_shutdown;
- Host_Shutdown();
-
-#ifdef __ANDROID__
- Sys_AllowProfiling(false);
-#endif
-
-#ifndef WIN32
- 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
- fflush(stdout);
- fflush(stderr);
-
- exit(returnvalue);
-}
-
#ifdef __cplusplus
extern "C"
#endif
===============================================================================
*/
+// NOTE: use only POSIX async-signal-safe library functions here (see: man signal-safety)
void Sys_Print(const char *text, size_t textlen)
{
#ifdef __ANDROID__
{
va_list argptr;
char string[MAX_INPUTLINE];
+ int i;
+
+ // Disable Sys_HandleSignal() but not Sys_HandleCrash()
+ host.state = host_shutdown;
// set output to blocking stderr
sys.outfd = fileno(stderr);
dpvsnprintf (string, sizeof (string), error, argptr);
va_end (argptr);
- Con_Printf(CON_ERROR "Engine Error: %s\n", string);
+ Con_Printf(CON_ERROR "Engine Aborted: %s\n^9%s\n", string, engineversion);
- // don't want a dead window left blocking the OS UI or the crash dialog
- Host_Shutdown();
+ dp_strlcat(string, "\n\n", sizeof(string));
+ dp_strlcat(string, engineversion, sizeof(string));
- Sys_SDL_Dialog("Engine Error", string);
+ // Most shutdown funcs can't be called here as they could error while we error.
- fflush(stderr);
+ // DP8 TODO: send a disconnect message indicating we aborted, see Host_Error() and Sys_HandleCrash()
+
+ if (cls.demorecording)
+ CL_Stop_f(cmd_local);
+ if (sv.active)
+ {
+ sv.active = false; // make SV_DropClient() skip the QC stuff to avoid recursive errors
+ for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ if (host_client->active)
+ SV_DropClient(false, "Server aborted!"); // closes demo file
+ }
+ // don't want a dead window left blocking the OS UI or the abort dialog
+ VID_Shutdown();
+ S_StopAllSounds();
+
+ host.state = host_failed; // make Sys_HandleSignal() call _Exit()
+ Sys_SDL_Dialog("Engine Aborted", string);
+ fflush(stderr);
exit (1);
}
}
#endif
+
+static const char *Sys_SigDesc(int sig)
+{
+ switch (sig)
+ {
+ // Windows only supports the C99 signals
+ case SIGINT: return "Interrupt";
+ case SIGILL: return "Illegal instruction";
+ case SIGABRT: return "Aborted";
+ case SIGFPE: return "Floating point exception";
+ case SIGSEGV: return "Segmentation fault";
+ case SIGTERM: return "Termination";
+#ifndef WIN32
+ // POSIX has several others worth catching
+ case SIGHUP: return "Hangup";
+ case SIGQUIT: return "Quit";
+ case SIGBUS: return "Bus error (bad memory access)";
+ case SIGPIPE: return "Broken pipe";
+#endif
+ default: return "Yo dawg, we bugged out while bugging out";
+ }
+}
+
/** 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.
+ * Try to use only POSIX async-signal-safe library functions here (see: man signal-safety).
*/
static void Sys_HandleCrash(int sig)
{
#include <execinfo.h>
void *stackframes[32];
int framecount = backtrace(stackframes, 32);
+ char **btstrings;
#endif
-
- // Windows doesn't have strsignal()
+ char dialogtext[3072];
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);
+ // Break any loop and disable Sys_HandleSignal()
+ if (host.state == host_failing || host.state == host_failed)
+ return;
+ host.state = host_failing;
+
+ sigdesc = Sys_SigDesc(sig);
+
+ // set output to blocking stderr and print header, backtrace, version
+ sys.outfd = fileno(stderr); // not async-signal-safe :(
#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
+ Sys_Print("\n\n\e[1;37;41m Engine Crash: ", 30);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print(" \e[m\n", 8);
+ #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
+ Sys_Print("\e[1m", 4);
+ Sys_Print(engineversion, strlen(engineversion));
+ Sys_Print("\e[m\n", 4);
+#else // Windows console doesn't support colours
+ Sys_Print("\n\nEngine Crash: ", 16);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print("\n", 1);
+ Sys_Print(engineversion, strlen(engineversion));
+ Sys_Print("\n", 1);
#endif
- fprintf(stderr, "\e[1m%s\e[m\n", engineversion);
- // DP8 TODO: send a disconnect message indicating we crashed, see CL_DisconnectEx()
+ // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Error() and Host_Error()
// 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);
+ // prepare the dialogtext: signal, backtrace, version
+ // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
+ dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
+ dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
+ if (btstrings)
+ for (int i = 0; i < framecount - 2; ++i)
+ {
+ dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
+ dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+ }
+#endif
+ dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+ dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
+
+ host.state = host_failed; // make Sys_HandleSignal() call _Exit()
+ Sys_SDL_Dialog("Engine Crash", dialogtext);
- fflush(stderr);
+ fflush(stderr); // not async-signal-safe :(
- exit (sig);
+ // Continue execution with default signal handling.
+ // A real crash will be re-triggered so the platform can handle it,
+ // a fake crash (kill -SEGV) will cause a graceful shutdown.
+ signal(sig, SIG_DFL);
}
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
+ const char *sigdesc;
+
+ // Break any loop, eg if each Sys_Print triggers a SIGPIPE
+ if (host.state == host_shutdown || host.state == host_failing)
+ return;
+
+ sigdesc = Sys_SigDesc(sig);
+ Sys_Print("\nReceived ", 10);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print(" signal, exiting...\n", 20);
+ if (host.state == host_failed)
+ {
+ // user is trying to kill the process while the SDL dialog is open
+ fflush(stderr); // not async-signal-safe :(
+ _Exit(sig);
+ }
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
+ // Windows only supports the C99 signals
+ signal(SIGINT, Sys_HandleSignal);
+ signal(SIGILL, Sys_HandleCrash);
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
+ // POSIX has several others worth catching
signal(SIGHUP, Sys_HandleSignal);
signal(SIGQUIT, Sys_HandleSignal);
signal(SIGBUS, Sys_HandleCrash);
Host_Main();
- Sys_Quit(0);
+#ifdef __ANDROID__
+ Sys_AllowProfiling(false);
+#endif
+
+#ifndef WIN32
+ 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
+ fflush(stdout);
+ fflush(stderr);
return 0;
}