2 # ifndef DONT_USE_SETDLLDIRECTORY
3 # define _WIN32_WINNT 0x0502
11 # include <mmsystem.h> // timeGetTime
12 # include <time.h> // localtime
13 # include <conio.h> // _kbhit, _getch, _putch
14 # include <io.h> // write; Include this BEFORE darkplaces.h because it uses strncpy which trips DP_STATIC_ASSERT
16 #pragma comment(lib, "winmm.lib")
20 # include <sys/sysctl.h>
23 # include <android/log.h>
27 # include <sys/time.h>
37 #include "taskqueue.h"
42 // Microsoft's compiler complains about portable code
43 #pragma warning(disable : 4996)
48 static char sys_timestring[128];
49 char *Sys_TimeString(const char *timeformat)
51 time_t mytime = time(NULL);
54 localtime_s(&mytm, &mytime);
55 strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
57 strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
59 return sys_timestring;
66 void Sys_AllowProfiling(qbool enable)
70 extern void monstartup(const char *libname);
71 extern void moncleanup(void);
73 monstartup("libmain.so");
77 #elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
78 extern int moncontrol(int);
85 ===============================================================================
89 ===============================================================================
92 static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
94 const dllfunction_t *func;
97 for (func = fcts; func && func->name != NULL; func++)
98 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
102 Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
104 Con_DPrintf("\nContinuing with");
111 for (func = fcts; func && func->name != NULL; func++)
112 *func->funcvariable = NULL;
117 qbool Sys_LoadSelf(dllhandle_t *handle)
119 dllhandle_t dllhandle = 0;
124 dllhandle = LoadLibrary (NULL);
126 dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
132 qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
135 const dllfunction_t *func;
136 dllhandle_t dllhandle = 0;
143 #ifdef PREFER_PRELOAD
144 dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
145 if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
147 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
152 Sys_FreeLibrary(&dllhandle);
158 for (func = fcts; func && func->name != NULL; func++)
159 *func->funcvariable = NULL;
161 // Try every possible name
162 Con_DPrintf ("Trying to load library...");
163 for (i = 0; dllnames[i] != NULL; i++)
165 Con_DPrintf (" \"%s\"", dllnames[i]);
167 # ifndef DONT_USE_SETDLLDIRECTORY
169 SetDllDirectory("bin64");
171 SetDllDirectory("bin32");
175 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
177 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
180 Sys_FreeLibrary (&dllhandle);
184 // see if the names can be loaded relative to the executable path
185 // (this is for Mac OSX which does not check next to the executable)
186 if (!dllhandle && strrchr(sys.argv[0], '/'))
188 char path[MAX_OSPATH];
189 dp_strlcpy(path, sys.argv[0], sizeof(path));
190 strrchr(path, '/')[1] = 0;
191 for (i = 0; dllnames[i] != NULL; i++)
193 char temp[MAX_OSPATH];
194 dp_strlcpy(temp, path, sizeof(temp));
195 dp_strlcat(temp, dllnames[i], sizeof(temp));
196 Con_DPrintf (" \"%s\"", temp);
198 if(Sys_LoadLibrary(temp, &dllhandle))
200 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
203 Sys_FreeLibrary (&dllhandle);
211 Con_DPrintf(" - failed.\n");
215 Con_DPrintf(" - loaded.\n");
216 Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
225 qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
227 dllhandle_t dllhandle = 0;
234 dllhandle = LoadLibrary (name);
236 dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
246 void Sys_FreeLibrary (dllhandle_t* handle)
249 if (handle == NULL || *handle == NULL)
253 FreeLibrary (*handle);
262 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
266 return (void *)GetProcAddress (handle, name);
268 return (void *)dlsym (handle, name);
276 # define HAVE_TIMEGETTIME 1
277 # define HAVE_QUERYPERFORMANCECOUNTER 1
278 # define HAVE_Sleep 1
282 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
283 # define HAVE_CLOCKGETTIME 1
285 // FIXME improve this check, manpage hints to DST_NONE
286 # define HAVE_GETTIMEOFDAY 1
290 # define HAVE_SELECT 1
294 // FIXME improve this check
295 # define HAVE_USLEEP 1
298 // these are referenced elsewhere
299 cvar_t sys_usenoclockbutbenchmark = {CF_SHARED, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
300 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
303 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
304 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
305 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
306 #if HAVE_QUERYPERFORMANCECOUNTER
307 static cvar_t sys_usequeryperformancecounter = {CF_SHARED | CF_ARCHIVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
309 #if HAVE_CLOCKGETTIME
310 static cvar_t sys_useclockgettime = {CF_SHARED | CF_ARCHIVE, "sys_useclockgettime", "1", "use POSIX clock_gettime function (not adjusted by NTP on some older Linux kernels) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
313 static cvar_t sys_stdout = {CF_SHARED, "sys_stdout", "1", "0: nothing is written to stdout (-nostdout cmdline option sets this), 1: normal messages are written to stdout, 2: normal messages are written to stderr (-stderr cmdline option sets this)"};
315 static cvar_t sys_stdout_blocks = {CF_SHARED, "sys_stdout_blocks", "0", "1: writes to stdout and stderr streams will block (causing a stutter or complete halt) if the buffer is full, ensuring no messages are lost at a price"};
318 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
324 Returns the position (1 to argc-1) in the program's argument list
325 where the given parameter apears, or 0 if not present
328 int Sys_CheckParm (const char *parm)
332 for (i=1 ; i<sys.argc ; i++)
335 continue; // NEXTSTEP sometimes clears appkit vars.
336 if (!strcmp (parm,sys.argv[i]))
343 static void Sys_UpdateOutFD_c(cvar_t *var)
345 switch (sys_stdout.integer)
347 case 0: sys.outfd = -1; break;
349 case 1: sys.outfd = fileno(stdout); break;
350 case 2: sys.outfd = fileno(stderr); break;
354 void Sys_Init_Commands (void)
356 Cvar_RegisterVariable(&sys_debugsleep);
357 Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
358 Cvar_RegisterVariable(&sys_libdir);
359 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
360 if(sys_supportsdlgetticks)
362 Cvar_RegisterVariable(&sys_usesdlgetticks);
363 Cvar_RegisterVariable(&sys_usesdldelay);
366 #if HAVE_QUERYPERFORMANCECOUNTER
367 Cvar_RegisterVariable(&sys_usequeryperformancecounter);
369 #if HAVE_CLOCKGETTIME
370 Cvar_RegisterVariable(&sys_useclockgettime);
372 Cvar_RegisterVariable(&sys_stdout);
373 Cvar_RegisterCallback(&sys_stdout, Sys_UpdateOutFD_c);
375 Cvar_RegisterVariable(&sys_stdout_blocks);
379 double Sys_DirtyTime(void)
381 // first all the OPTIONAL timers
383 // benchmark timer (fake clock)
384 if(sys_usenoclockbutbenchmark.integer)
386 double old_benchmark_time = benchmark_time;
388 if(benchmark_time == old_benchmark_time)
389 Sys_Abort("sys_usenoclockbutbenchmark cannot run any longer, sorry");
390 return benchmark_time * 0.000001;
392 #if HAVE_QUERYPERFORMANCECOUNTER
393 if (sys_usequeryperformancecounter.integer)
395 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
396 // QueryPerformanceCounter
398 // Windows 95/98/ME/NT/2000/XP
400 // very accurate (CPU cycles)
402 // does not necessarily match realtime too well (tends to get faster and faster in win98)
403 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
405 LARGE_INTEGER PerformanceFreq;
406 LARGE_INTEGER PerformanceCount;
408 if (QueryPerformanceFrequency (&PerformanceFreq))
410 QueryPerformanceCounter (&PerformanceCount);
412 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
413 return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
417 Con_Printf("No hardware timer available\n");
418 // fall back to other clock sources
419 Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
424 #if HAVE_CLOCKGETTIME
425 if (sys_useclockgettime.integer)
428 # ifdef CLOCK_MONOTONIC
430 clock_gettime(CLOCK_MONOTONIC, &ts);
433 clock_gettime(CLOCK_HIGHRES, &ts);
435 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
439 // now all the FALLBACK timers
440 if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
441 return (double) Sys_SDL_GetTicks() / 1000.0;
442 #if HAVE_GETTIMEOFDAY
445 gettimeofday(&tp, NULL);
446 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
448 #elif HAVE_TIMEGETTIME
450 static int firsttimegettime = true;
453 // Windows 95/98/ME/NT/2000/XP
455 // reasonable accuracy (millisecond)
457 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
459 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
460 if (firsttimegettime)
463 firsttimegettime = false;
466 return (double) timeGetTime() / 1000.0;
469 // fallback for using the SDL timer if no other timer is available
470 // this calls Sys_Abort() if not linking against SDL
471 return (double) Sys_SDL_GetTicks() / 1000.0;
475 extern cvar_t host_maxwait;
476 double Sys_Sleep(double time)
479 uint32_t microseconds;
481 // convert to microseconds
484 if(host_maxwait.value <= 0)
485 time = min(time, 1000000.0);
487 time = min(time, host_maxwait.value * 1000.0);
489 if (time < 1 || host.restless)
490 return 0; // not sleeping this frame
492 microseconds = time; // post-validation to prevent overflow
494 if(sys_usenoclockbutbenchmark.integer)
496 double old_benchmark_time = benchmark_time;
497 benchmark_time += microseconds;
498 if(benchmark_time == old_benchmark_time)
499 Sys_Abort("sys_usenoclockbutbenchmark cannot run any longer, sorry");
503 if(sys_debugsleep.integer)
504 Con_Printf("sys_debugsleep: requesting %u ", microseconds);
505 dt = Sys_DirtyTime();
507 // less important on newer libcurl so no need to disturb dedicated servers
508 if (cls.state != ca_dedicated && Curl_Select(microseconds))
510 // a transfer is ready or we finished sleeping
512 else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
513 Sys_SDL_Delay(microseconds / 1000);
523 if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
525 List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
527 if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
529 if (lastfd < s->inetsocket)
530 lastfd = s->inetsocket;
531 #if defined(WIN32) && !defined(_MSC_VER)
532 FD_SET((int)s->inetsocket, &fdreadset);
534 FD_SET((unsigned int)s->inetsocket, &fdreadset);
539 tv.tv_sec = microseconds / 1000000;
540 tv.tv_usec = microseconds % 1000000;
541 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
542 // (so much for POSIX...)
543 // bones_was_here: but a zeroed fd_set seems to be tolerated (tested on Win 7)
544 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
548 usleep(microseconds);
551 Sleep(microseconds / 1000);
554 Sys_SDL_Delay(microseconds / 1000);
557 dt = Sys_DirtyTime() - dt;
558 if(sys_debugsleep.integer)
559 Con_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - microseconds);
560 return (dt < 0 || dt >= 1800) ? 0 : dt;
565 ===============================================================================
569 ===============================================================================
572 // NOTE: use only POSIX async-signal-safe library functions here (see: man signal-safety)
573 void Sys_Print(const char *text, size_t textlen)
576 if (developer.integer > 0)
578 __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
584 // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
585 // this is because both go to /dev/tty by default!
587 int origflags = fcntl(sys.outfd, F_GETFL, 0);
588 if (sys_stdout_blocks.integer)
589 fcntl(sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
595 fs_offset_t written = (fs_offset_t)write(sys.outfd, text, textlen);
597 break; // sorry, I cannot do anything about this error - without an output
601 if (sys_stdout_blocks.integer)
602 fcntl(sys.outfd, F_SETFL, origflags);
605 //fprintf(stdout, "%s", text);
609 void Sys_Printf(const char *fmt, ...)
612 char msg[MAX_INPUTLINE];
615 va_start(argptr,fmt);
616 msglen = dpvsnprintf(msg, sizeof(msg), fmt, argptr);
620 Sys_Print(msg, msglen);
623 /// Reads a line from POSIX stdin or the Windows console
624 char *Sys_ConsoleInput(void)
626 static char text[MAX_INPUTLINE];
628 static unsigned int len = 0;
653 if (len < sizeof (text) - 1)
662 struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
665 FD_SET(fileno(stdin), &fdset);
666 if (select(1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(fileno(stdin), &fdset))
667 return fgets(text, sizeof(text), stdin);
674 ===============================================================================
678 ===============================================================================
681 void Sys_Abort (const char *error, ...)
684 char string[MAX_INPUTLINE];
687 // set output to blocking stderr
688 sys.outfd = fileno(stderr);
690 fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
693 va_start (argptr,error);
694 dpvsnprintf (string, sizeof (string), error, argptr);
697 Con_Printf(CON_ERROR "Engine Abort: %s\n^9%s\n", string, engineversion);
699 dp_strlcat(string, "\n\n", sizeof(string));
700 dp_strlcat(string, engineversion, sizeof(string));
702 // Most shutdown funcs can't be called here as they could error while we error.
704 // DP8 TODO: send a disconnect message indicating we aborted, see Host_Error() and Sys_HandleCrash()
706 if (cls.demorecording)
707 CL_Stop_f(cmd_local);
710 sv.active = false; // make SV_DropClient() skip the QC stuff to avoid recursive errors
711 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
712 if (host_client->active)
713 SV_DropClient(false, "Server abort!"); // closes demo file
715 // don't want a dead window left blocking the OS UI or the abort dialog
719 host.state = host_failed; // make Sys_HandleSignal() call exit()
720 Sys_SDL_Dialog("Engine Abort", string);
728 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
730 const char *p = PATH;
734 while((q = strchr(p, ':')))
736 dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
737 if(FS_SysFileExists(buf))
741 if(!q) // none found - try the last item
743 dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
744 if(FS_SysFileExists(buf))
752 static const char *Sys_FindExecutableName(void)
757 static char exenamebuf[MAX_OSPATH+1];
759 #if defined(__FreeBSD__)
760 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
761 size_t exenamebuflen = sizeof(exenamebuf)-1;
762 if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
766 #elif defined(__linux__)
767 n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
769 if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
774 if(strchr(sys.argv[0], '/'))
775 return sys.argv[0]; // possibly a relative path
777 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
781 void Sys_ProvideSelfFD(void)
785 sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
788 // for x86 cpus only... (x64 has SSE2_PRESENT)
789 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
790 // code from SDL, shortened as we can expect CPUID to work
791 static int CPUID_Features(void)
794 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
796 " movl %%ebx,%%edi\n"
797 " xorl %%eax,%%eax \n"
799 " cpuid # Get family/model/stepping/features\n"
801 " movl %%edi,%%ebx\n"
804 : "%eax", "%ecx", "%edx", "%edi"
806 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
810 cpuid ; Get family/model/stepping/features
814 # error SSE_POSSIBLE set but no CPUID implementation
821 qbool Sys_HaveSSE(void)
823 // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
824 if(Sys_CheckParm("-nosse"))
829 // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
830 if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
832 if(CPUID_Features() & (1 << 25))
838 qbool Sys_HaveSSE2(void)
840 // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
841 if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
846 // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
847 if(Sys_CheckParm("-forcesse2"))
849 if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
856 /// called to set process priority for dedicated servers
857 #if defined(__linux__)
858 #include <sys/resource.h>
861 void Sys_InitProcessNice (void)
864 sys.nicepossible = false;
865 if(Sys_CheckParm("-nonice"))
868 sys.nicelevel = getpriority(PRIO_PROCESS, 0);
871 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
874 if(getrlimit(RLIMIT_NICE, &lim))
876 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
879 if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
881 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
884 sys.nicepossible = true;
887 void Sys_MakeProcessNice (void)
889 if(!sys.nicepossible)
893 Con_DPrintf("Process is becoming 'nice'...\n");
894 if(setpriority(PRIO_PROCESS, 0, 19))
895 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
898 void Sys_MakeProcessMean (void)
900 if(!sys.nicepossible)
904 Con_DPrintf("Process is becoming 'mean'...\n");
905 if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
906 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
910 void Sys_InitProcessNice (void)
913 void Sys_MakeProcessNice (void)
916 void Sys_MakeProcessMean (void)
922 static const char *Sys_SigDesc(int sig)
926 // Windows only supports the C99 signals
927 case SIGINT: return "Interrupt";
928 case SIGILL: return "Illegal instruction";
929 case SIGABRT: return "Aborted";
930 case SIGFPE: return "Floating point exception";
931 case SIGSEGV: return "Segmentation fault";
932 case SIGTERM: return "Termination";
934 // POSIX has several others worth catching
935 case SIGHUP: return "Hangup";
936 case SIGQUIT: return "Quit";
937 case SIGBUS: return "Bus error (bad memory access)";
938 case SIGPIPE: return "Broken pipe";
940 default: return "Yo dawg, we bugged out while bugging out";
944 /** Halt and try not to catch fire.
945 * Writing to any file could corrupt it,
946 * any uneccessary code could crash while we crash.
947 * Try to use only POSIX async-signal-safe library functions here (see: man signal-safety).
949 static void Sys_HandleCrash(int sig)
951 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
952 // Before doing anything else grab the stack frame addresses
953 #include <execinfo.h>
954 void *stackframes[32];
955 int framecount = backtrace(stackframes, 32);
958 char dialogtext[3072];
959 const char *sigdesc = Sys_SigDesc(sig);
961 // set output to blocking stderr and print header, backtrace, version
962 sys.outfd = fileno(stderr); // not async-signal-safe :(
964 fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
965 Sys_Print("\n\n\e[1;37;41m Engine Crash: ", 30);
966 Sys_Print(sigdesc, strlen(sigdesc));
967 Sys_Print(" \e[m\n", 8);
968 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
969 // the first two addresses will be in this function and in signal() in libc
970 backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
972 Sys_Print("\e[1m", 4);
973 Sys_Print(engineversion, strlen(engineversion));
974 Sys_Print("\e[m\n", 4);
975 #else // Windows console doesn't support colours
976 Sys_Print("\n\nEngine Crash: ", 16);
977 Sys_Print(sigdesc, strlen(sigdesc));
979 Sys_Print(engineversion, strlen(engineversion));
983 // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Abort() and Host_Error()
985 // don't want a dead window left blocking the OS UI or the crash dialog
989 // prepare the dialogtext: signal, backtrace, version
990 // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
991 dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
992 dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
993 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
994 btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
996 for (int i = 0; i < framecount - 2; ++i)
998 dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
999 dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1002 dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1003 dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
1005 host.state = host_failed; // make Sys_HandleSignal() call _Exit()
1006 Sys_SDL_Dialog("Engine Crash", dialogtext);
1008 fflush(stderr); // not async-signal-safe :(
1012 static void Sys_HandleSignal(int sig)
1014 const char *sigdesc = Sys_SigDesc(sig);
1015 Sys_Print("\nReceived ", 10);
1016 Sys_Print(sigdesc, strlen(sigdesc));
1017 Sys_Print(" signal, exiting...\n", 20);
1018 if (host.state == host_failed)
1020 // user is trying to kill the process while the dialog is open
1021 fflush(stderr); // not async-signal-safe :(
1024 host.state = host_shutdown;
1027 /// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
1028 static void Sys_InitSignals(void)
1030 // Windows only supports the C99 signals
1031 signal(SIGINT, Sys_HandleSignal);
1032 signal(SIGILL, Sys_HandleCrash);
1033 signal(SIGABRT, Sys_HandleCrash);
1034 signal(SIGFPE, Sys_HandleCrash);
1035 signal(SIGSEGV, Sys_HandleCrash);
1036 signal(SIGTERM, Sys_HandleSignal);
1038 // POSIX has several others worth catching
1039 signal(SIGHUP, Sys_HandleSignal);
1040 signal(SIGQUIT, Sys_HandleSignal);
1041 signal(SIGBUS, Sys_HandleCrash);
1042 signal(SIGPIPE, Sys_HandleSignal);
1046 int main (int argc, char **argv)
1049 sys.argv = (const char **)argv;
1051 // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
1052 // COMMANDLINEOPTION: -noterminal disables console output on stdout
1053 if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
1054 sys_stdout.string = "0";
1055 // COMMANDLINEOPTION: -stderr moves console output to stderr
1056 else if(Sys_CheckParm("-stderr"))
1057 sys_stdout.string = "2";
1058 // too early for Cvar_SetQuick
1059 sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
1060 Sys_UpdateOutFD_c(&sys_stdout);
1062 fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
1063 // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
1064 fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
1065 fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
1069 Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
1072 Sys_AllowProfiling(true);
1080 Sys_AllowProfiling(false);
1084 fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) & ~O_NONBLOCK);
1085 fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) & ~O_NONBLOCK);