]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
physics: fix and refactor unsticking
[xonotic/darkplaces.git] / sys_shared.c
1 #ifdef WIN32
2 # ifndef DONT_USE_SETDLLDIRECTORY
3 #  define _WIN32_WINNT 0x0502
4 # endif
5 #endif
6
7 #define SUPPORTDLL
8
9 #ifdef WIN32
10 # include <windows.h>
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
15 #ifdef _MSC_VER
16 #pragma comment(lib, "winmm.lib")
17 #endif
18 #else
19 # ifdef __FreeBSD__
20 #  include <sys/sysctl.h>
21 # endif
22 # ifdef __ANDROID__
23 #  include <android/log.h>
24 # endif
25 # include <unistd.h>
26 # include <fcntl.h>
27 # include <sys/time.h>
28 # include <time.h>
29 # ifdef SUPPORTDLL
30 #  include <dlfcn.h>
31 # endif
32 #endif
33
34 #include <signal.h>
35
36 #include "quakedef.h"
37 #include "taskqueue.h"
38 #include "thread.h"
39 #include "libcurl.h"
40
41
42 sys_t sys;
43
44 static char sys_timestring[128];
45 char *Sys_TimeString(const char *timeformat)
46 {
47         time_t mytime = time(NULL);
48 #if _MSC_VER >= 1400
49         struct tm mytm;
50         localtime_s(&mytm, &mytime);
51         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
52 #else
53         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
54 #endif
55         return sys_timestring;
56 }
57
58
59 #ifdef __cplusplus
60 extern "C"
61 #endif
62 void Sys_AllowProfiling(qbool enable)
63 {
64 #ifdef __ANDROID__
65 #ifdef USE_PROFILER
66         extern void monstartup(const char *libname);
67         extern void moncleanup(void);
68         if (enable)
69                 monstartup("libmain.so");
70         else
71                 moncleanup();
72 #endif
73 #elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
74         extern int moncontrol(int);
75         moncontrol(enable);
76 #endif
77 }
78
79
80 /*
81 ===============================================================================
82
83 DLL MANAGEMENT
84
85 ===============================================================================
86 */
87
88 static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
89 {
90         const dllfunction_t *func;
91         if(dllhandle)
92         {
93                 for (func = fcts; func && func->name != NULL; func++)
94                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
95                         {
96                                 if(complain)
97                                 {
98                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
99                                         if(has_next)
100                                                 Con_DPrintf("\nContinuing with");
101                                 }
102                                 goto notfound;
103                         }
104                 return true;
105
106         notfound:
107                 for (func = fcts; func && func->name != NULL; func++)
108                         *func->funcvariable = NULL;
109         }
110         return false;
111 }
112
113 qbool Sys_LoadSelf(dllhandle_t *handle)
114 {
115         dllhandle_t dllhandle = 0;
116
117         if (handle == NULL)
118                 return false;
119 #ifdef WIN32
120         dllhandle = LoadLibrary (NULL);
121 #else
122         dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
123 #endif
124         *handle = dllhandle;
125         return true;
126 }
127
128 qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
129 {
130 #ifdef SUPPORTDLL
131         const dllfunction_t *func;
132         dllhandle_t dllhandle = 0;
133         unsigned int i;
134
135         if (handle == NULL)
136                 return false;
137
138 #ifndef WIN32
139 #ifdef PREFER_PRELOAD
140         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
141         if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
142         {
143                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
144                 *handle = dllhandle;
145                 return true;
146         }
147         else
148                 Sys_FreeLibrary(&dllhandle);
149 notfound:
150 #endif
151 #endif
152
153         // Initializations
154         for (func = fcts; func && func->name != NULL; func++)
155                 *func->funcvariable = NULL;
156
157         // Try every possible name
158         Con_DPrintf ("Trying to load library...");
159         for (i = 0; dllnames[i] != NULL; i++)
160         {
161                 Con_DPrintf (" \"%s\"", dllnames[i]);
162 #ifdef WIN32
163 # ifndef DONT_USE_SETDLLDIRECTORY
164 #  ifdef _WIN64
165                 SetDllDirectory("bin64");
166 #  else
167                 SetDllDirectory("bin32");
168 #  endif
169 # endif
170 #endif
171                 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
172                 {
173                         if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
174                                 break;
175                         else
176                                 Sys_FreeLibrary (&dllhandle);
177                 }
178         }
179
180         // see if the names can be loaded relative to the executable path
181         // (this is for Mac OSX which does not check next to the executable)
182         if (!dllhandle && strrchr(sys.argv[0], '/'))
183         {
184                 char path[MAX_OSPATH];
185                 dp_strlcpy(path, sys.argv[0], sizeof(path));
186                 strrchr(path, '/')[1] = 0;
187                 for (i = 0; dllnames[i] != NULL; i++)
188                 {
189                         char temp[MAX_OSPATH];
190                         dp_strlcpy(temp, path, sizeof(temp));
191                         dp_strlcat(temp, dllnames[i], sizeof(temp));
192                         Con_DPrintf (" \"%s\"", temp);
193
194                         if(Sys_LoadLibrary(temp, &dllhandle))
195                         {
196                                 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
197                                         break;
198                                 else
199                                         Sys_FreeLibrary (&dllhandle);
200                         }
201                 }
202         }
203
204         // No DLL found
205         if (! dllhandle)
206         {
207                 Con_DPrintf(" - failed.\n");
208                 return false;
209         }
210
211         Con_DPrintf(" - loaded.\n");
212         Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
213
214         *handle = dllhandle;
215         return true;
216 #else
217         return false;
218 #endif
219 }
220
221 qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
222 {
223         dllhandle_t dllhandle = 0;
224
225         if(handle == NULL)
226                 return false;
227
228 #ifdef SUPPORTDLL
229 # ifdef WIN32
230         dllhandle = LoadLibrary (name);
231 # else
232         dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
233 # endif
234 #endif
235         if(!dllhandle)
236                 return false;
237
238         *handle = dllhandle;
239         return true;
240 }
241
242 void Sys_FreeLibrary (dllhandle_t* handle)
243 {
244 #ifdef SUPPORTDLL
245         if (handle == NULL || *handle == NULL)
246                 return;
247
248 #ifdef WIN32
249         FreeLibrary (*handle);
250 #else
251         dlclose (*handle);
252 #endif
253
254         *handle = NULL;
255 #endif
256 }
257
258 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
259 {
260 #ifdef SUPPORTDLL
261 #ifdef WIN32
262         return (void *)GetProcAddress (handle, name);
263 #else
264         return (void *)dlsym (handle, name);
265 #endif
266 #else
267         return NULL;
268 #endif
269 }
270
271 #ifdef WIN32
272 # define HAVE_TIMEGETTIME 1
273 # define HAVE_QUERYPERFORMANCECOUNTER 1
274 # define HAVE_Sleep 1
275 #endif
276
277 #ifndef WIN32
278 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
279 # define HAVE_CLOCKGETTIME 1
280 #endif
281 // FIXME improve this check, manpage hints to DST_NONE
282 # define HAVE_GETTIMEOFDAY 1
283 #endif
284
285 #ifdef FD_SET
286 # define HAVE_SELECT 1
287 #endif
288
289 #ifndef WIN32
290 // FIXME improve this check
291 # define HAVE_USLEEP 1
292 #endif
293
294 // these are referenced elsewhere
295 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."};
296 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
297
298 // these are not
299 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
300 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
301 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
302 #if HAVE_QUERYPERFORMANCECOUNTER
303 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)"};
304 #endif
305 #if HAVE_CLOCKGETTIME
306 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)"};
307 #endif
308
309 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)"};
310 #ifndef WIN32
311 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"};
312 #endif
313
314 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
315
316 /*
317 ================
318 Sys_CheckParm
319
320 Returns the position (1 to argc-1) in the program's argument list
321 where the given parameter apears, or 0 if not present
322 ================
323 */
324 int Sys_CheckParm (const char *parm)
325 {
326         int i;
327
328         for (i=1 ; i<sys.argc ; i++)
329         {
330                 if (!sys.argv[i])
331                         continue;               // NEXTSTEP sometimes clears appkit vars.
332                 if (!strcmp (parm,sys.argv[i]))
333                         return i;
334         }
335
336         return 0;
337 }
338
339 static void Sys_UpdateOutFD_c(cvar_t *var)
340 {
341         switch (sys_stdout.integer)
342         {
343                 case 0: sys.outfd = -1; break;
344                 default:
345                 case 1: sys.outfd = fileno(stdout); break;
346                 case 2: sys.outfd = fileno(stderr); break;
347         }
348 }
349
350 void Sys_Init_Commands (void)
351 {
352         Cvar_RegisterVariable(&sys_debugsleep);
353         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
354         Cvar_RegisterVariable(&sys_libdir);
355 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
356         if(sys_supportsdlgetticks)
357         {
358                 Cvar_RegisterVariable(&sys_usesdlgetticks);
359                 Cvar_RegisterVariable(&sys_usesdldelay);
360         }
361 #endif
362 #if HAVE_QUERYPERFORMANCECOUNTER
363         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
364 #endif
365 #if HAVE_CLOCKGETTIME
366         Cvar_RegisterVariable(&sys_useclockgettime);
367 #endif
368         Cvar_RegisterVariable(&sys_stdout);
369         Cvar_RegisterCallback(&sys_stdout, Sys_UpdateOutFD_c);
370 #ifndef WIN32
371         Cvar_RegisterVariable(&sys_stdout_blocks);
372 #endif
373 }
374
375 double Sys_DirtyTime(void)
376 {
377         // first all the OPTIONAL timers
378
379         // benchmark timer (fake clock)
380         if(sys_usenoclockbutbenchmark.integer)
381         {
382                 double old_benchmark_time = benchmark_time;
383                 benchmark_time += 1;
384                 if(benchmark_time == old_benchmark_time)
385                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
386                 return benchmark_time * 0.000001;
387         }
388 #if HAVE_QUERYPERFORMANCECOUNTER
389         if (sys_usequeryperformancecounter.integer)
390         {
391                 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
392                 // QueryPerformanceCounter
393                 // platform:
394                 // Windows 95/98/ME/NT/2000/XP
395                 // features:
396                 // very accurate (CPU cycles)
397                 // known issues:
398                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
399                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
400                 double timescale;
401                 LARGE_INTEGER PerformanceFreq;
402                 LARGE_INTEGER PerformanceCount;
403
404                 if (QueryPerformanceFrequency (&PerformanceFreq))
405                 {
406                         QueryPerformanceCounter (&PerformanceCount);
407         
408                         timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
409                         return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
410                 }
411                 else
412                 {
413                         Con_Printf("No hardware timer available\n");
414                         // fall back to other clock sources
415                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
416                 }
417         }
418 #endif
419
420 #if HAVE_CLOCKGETTIME
421         if (sys_useclockgettime.integer)
422         {
423                 struct timespec ts;
424 #  ifdef CLOCK_MONOTONIC
425                 // linux
426                 clock_gettime(CLOCK_MONOTONIC, &ts);
427 #  else
428                 // sunos
429                 clock_gettime(CLOCK_HIGHRES, &ts);
430 #  endif
431                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
432         }
433 #endif
434
435         // now all the FALLBACK timers
436         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
437                 return (double) Sys_SDL_GetTicks() / 1000.0;
438 #if HAVE_GETTIMEOFDAY
439         {
440                 struct timeval tp;
441                 gettimeofday(&tp, NULL);
442                 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
443         }
444 #elif HAVE_TIMEGETTIME
445         {
446                 static int firsttimegettime = true;
447                 // timeGetTime
448                 // platform:
449                 // Windows 95/98/ME/NT/2000/XP
450                 // features:
451                 // reasonable accuracy (millisecond)
452                 // issues:
453                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
454
455                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
456                 if (firsttimegettime)
457                 {
458                         timeBeginPeriod(1);
459                         firsttimegettime = false;
460                 }
461
462                 return (double) timeGetTime() / 1000.0;
463         }
464 #else
465         // fallback for using the SDL timer if no other timer is available
466         // this calls Sys_Error() if not linking against SDL
467         return (double) Sys_SDL_GetTicks() / 1000.0;
468 #endif
469 }
470
471 extern cvar_t host_maxwait;
472 double Sys_Sleep(double time)
473 {
474         double dt;
475         uint32_t microseconds;
476
477         // convert to microseconds
478         time *= 1000000.0;
479
480         if(host_maxwait.value <= 0)
481                 time = min(time, 1000000.0);
482         else
483                 time = min(time, host_maxwait.value * 1000.0);
484
485         if (time < 1 || host.restless)
486                 return 0; // not sleeping this frame
487
488         microseconds = time; // post-validation to prevent overflow
489
490         if(sys_usenoclockbutbenchmark.integer)
491         {
492                 double old_benchmark_time = benchmark_time;
493                 benchmark_time += microseconds;
494                 if(benchmark_time == old_benchmark_time)
495                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
496                 return 0;
497         }
498
499         if(sys_debugsleep.integer)
500                 Con_Printf("sys_debugsleep: requesting %u ", microseconds);
501         dt = Sys_DirtyTime();
502
503         // less important on newer libcurl so no need to disturb dedicated servers
504         if (cls.state != ca_dedicated && Curl_Select(microseconds))
505         {
506                 // a transfer is ready or we finished sleeping
507         }
508         else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
509                 Sys_SDL_Delay(microseconds / 1000);
510 #if HAVE_SELECT
511         else
512         {
513                 struct timeval tv;
514                 lhnetsocket_t *s;
515                 fd_set fdreadset;
516                 int lastfd = -1;
517
518                 FD_ZERO(&fdreadset);
519                 if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
520                 {
521                         List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
522                         {
523                                 if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
524                                 {
525                                         if (lastfd < s->inetsocket)
526                                                 lastfd = s->inetsocket;
527         #if defined(WIN32) && !defined(_MSC_VER)
528                                         FD_SET((int)s->inetsocket, &fdreadset);
529         #else
530                                         FD_SET((unsigned int)s->inetsocket, &fdreadset);
531         #endif
532                                 }
533                         }
534                 }
535                 tv.tv_sec = microseconds / 1000000;
536                 tv.tv_usec = microseconds % 1000000;
537                 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
538                 // (so much for POSIX...)
539                 // bones_was_here: but a zeroed fd_set seems to be tolerated (tested on Win 7)
540                 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
541         }
542 #elif HAVE_USLEEP
543         else
544                 usleep(microseconds);
545 #elif HAVE_Sleep
546         else
547                 Sleep(microseconds / 1000);
548 #else
549         else
550                 Sys_SDL_Delay(microseconds / 1000);
551 #endif
552
553         dt = Sys_DirtyTime() - dt;
554         if(sys_debugsleep.integer)
555                 Con_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - microseconds);
556         return (dt < 0 || dt >= 1800) ? 0 : dt;
557 }
558
559
560 /*
561 ===============================================================================
562
563 STDIO
564
565 ===============================================================================
566 */
567
568 // NOTE: use only POSIX async-signal-safe library functions here (see: man signal-safety)
569 void Sys_Print(const char *text, size_t textlen)
570 {
571 #ifdef __ANDROID__
572         if (developer.integer > 0)
573         {
574                 __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
575         }
576 #else
577         if(sys.outfd < 0)
578                 return;
579   #ifndef WIN32
580         // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
581         // this is because both go to /dev/tty by default!
582         {
583                 int origflags = fcntl(sys.outfd, F_GETFL, 0);
584                 if (sys_stdout_blocks.integer)
585                         fcntl(sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
586   #else
587     #define write _write
588   #endif
589                 while(*text)
590                 {
591                         fs_offset_t written = (fs_offset_t)write(sys.outfd, text, textlen);
592                         if(written <= 0)
593                                 break; // sorry, I cannot do anything about this error - without an output
594                         text += written;
595                 }
596   #ifndef WIN32
597                 if (sys_stdout_blocks.integer)
598                         fcntl(sys.outfd, F_SETFL, origflags);
599         }
600   #endif
601         //fprintf(stdout, "%s", text);
602 #endif
603 }
604
605 void Sys_Printf(const char *fmt, ...)
606 {
607         va_list argptr;
608         char msg[MAX_INPUTLINE];
609         int msglen;
610
611         va_start(argptr,fmt);
612         msglen = dpvsnprintf(msg, sizeof(msg), fmt, argptr);
613         va_end(argptr);
614
615         if (msglen >= 0)
616                 Sys_Print(msg, msglen);
617 }
618
619 /// Reads a line from POSIX stdin or the Windows console
620 char *Sys_ConsoleInput(void)
621 {
622         static char text[MAX_INPUTLINE];
623 #ifdef WIN32
624         static unsigned int len = 0;
625         int c;
626
627         // read a line out
628         while (_kbhit ())
629         {
630                 c = _getch ();
631                 if (c == '\r')
632                 {
633                         text[len] = '\0';
634                         _putch ('\n');
635                         len = 0;
636                         return text;
637                 }
638                 if (c == '\b')
639                 {
640                         if (len)
641                         {
642                                 _putch (c);
643                                 _putch (' ');
644                                 _putch (c);
645                                 len--;
646                         }
647                         continue;
648                 }
649                 if (len < sizeof (text) - 1)
650                 {
651                         _putch (c);
652                         text[len] = c;
653                         len++;
654                 }
655         }
656 #else
657         fd_set fdset;
658         struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
659
660         FD_ZERO(&fdset);
661         FD_SET(fileno(stdin), &fdset);
662         if (select(1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(fileno(stdin), &fdset))
663                 return fgets(text, sizeof(text), stdin);
664 #endif
665         return NULL;
666 }
667
668
669 /*
670 ===============================================================================
671
672 Startup and Shutdown
673
674 ===============================================================================
675 */
676
677 void Sys_Error (const char *error, ...)
678 {
679         va_list argptr;
680         char string[MAX_INPUTLINE];
681         int i;
682
683         // Disable Sys_HandleSignal() but not Sys_HandleCrash()
684         host.state = host_shutdown;
685
686         // set output to blocking stderr
687         sys.outfd = fileno(stderr);
688 #ifndef WIN32
689         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
690 #endif
691
692         va_start (argptr,error);
693         dpvsnprintf (string, sizeof (string), error, argptr);
694         va_end (argptr);
695
696         Con_Printf(CON_ERROR "Engine Aborted: %s\n^9%s\n", string, engineversion);
697
698         dp_strlcat(string, "\n\n", sizeof(string));
699         dp_strlcat(string, engineversion, sizeof(string));
700
701         // Most shutdown funcs can't be called here as they could error while we error.
702
703         // DP8 TODO: send a disconnect message indicating we aborted, see Host_Error() and Sys_HandleCrash()
704
705         if (cls.demorecording)
706                 CL_Stop_f(cmd_local);
707         if (sv.active)
708         {
709                 sv.active = false; // make SV_DropClient() skip the QC stuff to avoid recursive errors
710                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
711                         if (host_client->active)
712                                 SV_DropClient(false, "Server aborted!"); // closes demo file
713         }
714         // don't want a dead window left blocking the OS UI or the abort dialog
715         VID_Shutdown();
716         S_StopAllSounds();
717
718         host.state = host_failed; // make Sys_HandleSignal() call _Exit()
719         Sys_SDL_Dialog("Engine Aborted", string);
720
721         fflush(stderr);
722         exit (1);
723 }
724
725 #ifndef WIN32
726 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
727 {
728         const char *p = PATH;
729         const char *q;
730         if(p && name)
731         {
732                 while((q = strchr(p, ':')))
733                 {
734                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
735                         if(FS_SysFileExists(buf))
736                                 return buf;
737                         p = q + 1;
738                 }
739                 if(!q) // none found - try the last item
740                 {
741                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
742                         if(FS_SysFileExists(buf))
743                                 return buf;
744                 }
745         }
746         return name;
747 }
748 #endif
749
750 static const char *Sys_FindExecutableName(void)
751 {
752 #if defined(WIN32)
753         return sys.argv[0];
754 #else
755         static char exenamebuf[MAX_OSPATH+1];
756         ssize_t n = -1;
757 #if defined(__FreeBSD__)
758         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
759         size_t exenamebuflen = sizeof(exenamebuf)-1;
760         if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
761         {
762                 n = exenamebuflen;
763         }
764 #elif defined(__linux__)
765         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
766 #endif
767         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
768         {
769                 exenamebuf[n] = 0;
770                 return exenamebuf;
771         }
772         if(strchr(sys.argv[0], '/'))
773                 return sys.argv[0]; // possibly a relative path
774         else
775                 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
776 #endif
777 }
778
779 void Sys_ProvideSelfFD(void)
780 {
781         if(sys.selffd != -1)
782                 return;
783         sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
784 }
785
786 // for x86 cpus only...  (x64 has SSE2_PRESENT)
787 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
788 // code from SDL, shortened as we can expect CPUID to work
789 static int CPUID_Features(void)
790 {
791         int features = 0;
792 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
793         __asm__ (
794 "        movl    %%ebx,%%edi\n"
795 "        xorl    %%eax,%%eax                                           \n"
796 "        incl    %%eax                                                 \n"
797 "        cpuid                       # Get family/model/stepping/features\n"
798 "        movl    %%edx,%0                                              \n"
799 "        movl    %%edi,%%ebx\n"
800         : "=m" (features)
801         :
802         : "%eax", "%ecx", "%edx", "%edi"
803         );
804 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
805         __asm {
806         xor     eax, eax
807         inc     eax
808         cpuid                       ; Get family/model/stepping/features
809         mov     features, edx
810         }
811 # else
812 #  error SSE_POSSIBLE set but no CPUID implementation
813 # endif
814         return features;
815 }
816 #endif
817
818 #ifdef SSE_POSSIBLE
819 qbool Sys_HaveSSE(void)
820 {
821         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
822         if(Sys_CheckParm("-nosse"))
823                 return false;
824 #ifdef SSE_PRESENT
825         return true;
826 #else
827         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
828         if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
829                 return true;
830         if(CPUID_Features() & (1 << 25))
831                 return true;
832         return false;
833 #endif
834 }
835
836 qbool Sys_HaveSSE2(void)
837 {
838         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
839         if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
840                 return false;
841 #ifdef SSE2_PRESENT
842         return true;
843 #else
844         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
845         if(Sys_CheckParm("-forcesse2"))
846                 return true;
847         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
848                 return true;
849         return false;
850 #endif
851 }
852 #endif
853
854 /// called to set process priority for dedicated servers
855 #if defined(__linux__)
856 #include <sys/resource.h>
857 #include <errno.h>
858
859 void Sys_InitProcessNice (void)
860 {
861         struct rlimit lim;
862         sys.nicepossible = false;
863         if(Sys_CheckParm("-nonice"))
864                 return;
865         errno = 0;
866         sys.nicelevel = getpriority(PRIO_PROCESS, 0);
867         if(errno)
868         {
869                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
870                 return;
871         }
872         if(getrlimit(RLIMIT_NICE, &lim))
873         {
874                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
875                 return;
876         }
877         if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
878         {
879                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
880                 return;
881         }
882         sys.nicepossible = true;
883         sys.isnice = false;
884 }
885 void Sys_MakeProcessNice (void)
886 {
887         if(!sys.nicepossible)
888                 return;
889         if(sys.isnice)
890                 return;
891         Con_DPrintf("Process is becoming 'nice'...\n");
892         if(setpriority(PRIO_PROCESS, 0, 19))
893                 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
894         sys.isnice = true;
895 }
896 void Sys_MakeProcessMean (void)
897 {
898         if(!sys.nicepossible)
899                 return;
900         if(!sys.isnice)
901                 return;
902         Con_DPrintf("Process is becoming 'mean'...\n");
903         if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
904                 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
905         sys.isnice = false;
906 }
907 #else
908 void Sys_InitProcessNice (void)
909 {
910 }
911 void Sys_MakeProcessNice (void)
912 {
913 }
914 void Sys_MakeProcessMean (void)
915 {
916 }
917 #endif
918
919
920 static const char *Sys_SigDesc(int sig)
921 {
922         switch (sig)
923         {
924                 // Windows only supports the C99 signals
925                 case SIGINT:  return "Interrupt";
926                 case SIGILL:  return "Illegal instruction";
927                 case SIGABRT: return "Aborted";
928                 case SIGFPE:  return "Floating point exception";
929                 case SIGSEGV: return "Segmentation fault";
930                 case SIGTERM: return "Termination";
931 #ifndef WIN32
932                 // POSIX has several others worth catching
933                 case SIGHUP:  return "Hangup";
934                 case SIGQUIT: return "Quit";
935                 case SIGBUS:  return "Bus error (bad memory access)";
936                 case SIGPIPE: return "Broken pipe";
937 #endif
938                 default:      return "Yo dawg, we bugged out while bugging out";
939         }
940 }
941
942 /** Halt and try not to catch fire.
943  * Writing to any file could corrupt it,
944  * any uneccessary code could crash while we crash.
945  * Try to use only POSIX async-signal-safe library functions here (see: man signal-safety).
946  */
947 static void Sys_HandleCrash(int sig)
948 {
949 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
950         // Before doing anything else grab the stack frame addresses
951         #include <execinfo.h>
952         void *stackframes[32];
953         int framecount = backtrace(stackframes, 32);
954         char **btstrings;
955 #endif
956         char dialogtext[3072];
957         const char *sigdesc;
958
959         // Break any loop and disable Sys_HandleSignal()
960         if (host.state == host_failing || host.state == host_failed)
961                 return;
962         host.state = host_failing;
963
964         sigdesc = Sys_SigDesc(sig);
965
966         // set output to blocking stderr and print header, backtrace, version
967         sys.outfd = fileno(stderr); // not async-signal-safe :(
968 #ifndef WIN32
969         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
970         Sys_Print("\n\n\e[1;37;41m    Engine Crash: ", 30);
971         Sys_Print(sigdesc, strlen(sigdesc));
972         Sys_Print("    \e[m\n", 8);
973   #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
974         // the first two addresses will be in this function and in signal() in libc
975         backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
976   #endif
977         Sys_Print("\e[1m", 4);
978         Sys_Print(engineversion, strlen(engineversion));
979         Sys_Print("\e[m\n", 4);
980 #else // Windows console doesn't support colours
981         Sys_Print("\n\nEngine Crash: ", 16);
982         Sys_Print(sigdesc, strlen(sigdesc));
983         Sys_Print("\n", 1);
984         Sys_Print(engineversion, strlen(engineversion));
985         Sys_Print("\n", 1);
986 #endif
987
988         // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Error() and Host_Error()
989
990         // don't want a dead window left blocking the OS UI or the crash dialog
991         VID_Shutdown();
992         S_StopAllSounds();
993
994         // prepare the dialogtext: signal, backtrace, version
995         // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
996         dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
997         dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
998 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
999         btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
1000         if (btstrings)
1001                 for (int i = 0; i < framecount - 2; ++i)
1002                 {
1003                         dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
1004                         dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1005                 }
1006 #endif
1007         dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1008         dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
1009
1010         host.state = host_failed; // make Sys_HandleSignal() call _Exit()
1011         Sys_SDL_Dialog("Engine Crash", dialogtext);
1012
1013         fflush(stderr); // not async-signal-safe :(
1014
1015         // Continue execution with default signal handling.
1016         // A real crash will be re-triggered so the platform can handle it,
1017         // a fake crash (kill -SEGV) will cause a graceful shutdown.
1018         signal(sig, SIG_DFL);
1019 }
1020
1021 static void Sys_HandleSignal(int sig)
1022 {
1023         const char *sigdesc;
1024
1025         // Break any loop, eg if each Sys_Print triggers a SIGPIPE
1026         if (host.state == host_shutdown || host.state == host_failing)
1027                 return;
1028
1029         sigdesc = Sys_SigDesc(sig);
1030         Sys_Print("\nReceived ", 10);
1031         Sys_Print(sigdesc, strlen(sigdesc));
1032         Sys_Print(" signal, exiting...\n", 20);
1033         if (host.state == host_failed)
1034         {
1035                 // user is trying to kill the process while the SDL dialog is open
1036                 fflush(stderr); // not async-signal-safe :(
1037                 _Exit(sig);
1038         }
1039         host.state = host_shutdown;
1040 }
1041
1042 /// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
1043 static void Sys_InitSignals(void)
1044 {
1045         // Windows only supports the C99 signals
1046         signal(SIGINT,  Sys_HandleSignal);
1047         signal(SIGILL,  Sys_HandleCrash);
1048         signal(SIGABRT, Sys_HandleCrash);
1049         signal(SIGFPE,  Sys_HandleCrash);
1050         signal(SIGSEGV, Sys_HandleCrash);
1051         signal(SIGTERM, Sys_HandleSignal);
1052 #ifndef WIN32
1053         // POSIX has several others worth catching
1054         signal(SIGHUP,  Sys_HandleSignal);
1055         signal(SIGQUIT, Sys_HandleSignal);
1056         signal(SIGBUS,  Sys_HandleCrash);
1057         signal(SIGPIPE, Sys_HandleSignal);
1058 #endif
1059 }
1060
1061 int main (int argc, char **argv)
1062 {
1063         sys.argc = argc;
1064         sys.argv = (const char **)argv;
1065
1066         // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
1067         // COMMANDLINEOPTION: -noterminal disables console output on stdout
1068         if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
1069                 sys_stdout.string = "0";
1070         // COMMANDLINEOPTION: -stderr moves console output to stderr
1071         else if(Sys_CheckParm("-stderr"))
1072                 sys_stdout.string = "2";
1073         // too early for Cvar_SetQuick
1074         sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
1075         Sys_UpdateOutFD_c(&sys_stdout);
1076 #ifndef WIN32
1077         fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
1078         // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
1079         fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
1080         fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
1081 #endif
1082
1083         sys.selffd = -1;
1084         Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
1085
1086 #ifdef __ANDROID__
1087         Sys_AllowProfiling(true);
1088 #endif
1089
1090         Sys_InitSignals();
1091
1092         Host_Main();
1093
1094 #ifdef __ANDROID__
1095         Sys_AllowProfiling(false);
1096 #endif
1097
1098 #ifndef WIN32
1099         fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) & ~O_NONBLOCK);
1100         fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) & ~O_NONBLOCK);
1101 #endif
1102         fflush(stdout);
1103         fflush(stderr);
1104
1105         return 0;
1106 }