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