]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
46cec64247666a080dd3ea44f17c09ea6acd5e23
[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
272
273 #ifdef WIN32
274 # define HAVE_TIMEGETTIME 1
275 # define HAVE_QUERYPERFORMANCECOUNTER 1
276 # define HAVE_WIN32_USLEEP 1
277 # define HAVE_Sleep 1
278 #else
279 # if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
280 #  define HAVE_CLOCKGETTIME 1
281 # endif
282 # if _POSIX_VERSION >= 200112L
283 // MacOS advertises POSIX support but doesn't implement clock_nanosleep().
284 // POSIX deprecated and removed usleep() so select() seems like a safer choice.
285 #  if defined(MACOSX)
286 #   define HAVE_SELECT_POSIX 1
287 #  else
288 #   define HAVE_CLOCK_NANOSLEEP 1
289 #  endif
290 # endif
291 #endif
292
293 #ifdef FD_SET
294 # define HAVE_SELECT 1
295 #endif
296
297 // these are referenced elsewhere
298 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."};
299 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
300
301 // these are not
302 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
303 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (low precision, for debugging)"};
304 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (low precision, for debugging)"};
305 #if HAVE_QUERYPERFORMANCECOUNTER
306 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)"};
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
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
366         Cvar_RegisterVariable(&sys_stdout);
367         Cvar_RegisterCallback(&sys_stdout, Sys_UpdateOutFD_c);
368 #ifndef WIN32
369         Cvar_RegisterVariable(&sys_stdout_blocks);
370 #endif
371 }
372
373 #ifdef WIN32
374 static LARGE_INTEGER PerformanceFreq;
375 /// Windows default timer resolution is only 15.625ms,
376 /// this affects (at least) timeGetTime() and all forms of sleeping.
377 static void Sys_SetTimerResolution(void)
378 {
379         NTSTATUS(NTAPI *qNtQueryTimerResolution)(OUT PULONG MinRes, OUT PULONG MaxRes, OUT PULONG CurrentRes);
380         NTSTATUS(NTAPI *qNtSetTimerResolution)(IN ULONG DesiredRes, IN BOOLEAN SetRes, OUT PULONG ActualRes);
381         const char* ntdll_names [] =
382         {
383                 "ntdll.dll",
384                 NULL
385         };
386         dllfunction_t ntdll_funcs[] =
387         {
388                 {"NtQueryTimerResolution", (void **) &qNtQueryTimerResolution},
389                 {"NtSetTimerResolution",   (void **) &qNtSetTimerResolution},
390                 {NULL, NULL}
391         };
392         dllhandle_t ntdll;
393         unsigned long WorstRes, BestRes, CurrentRes;
394
395         timeBeginPeriod(1); // 1ms, documented
396
397         // the best Windows can manage (typically 0.5ms)
398         // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html
399         if (Sys_LoadDependency(ntdll_names, &ntdll, ntdll_funcs))
400         {
401                 qNtQueryTimerResolution(&WorstRes, &BestRes, &CurrentRes); // no pointers may be NULL
402                 if (CurrentRes > BestRes)
403                         qNtSetTimerResolution(BestRes, true, &CurrentRes);
404
405                 Sys_FreeLibrary(&ntdll);
406         }
407
408         // Microsoft says the freq is fixed at boot and consistent across all processors
409         // and that it need only be queried once and cached.
410         QueryPerformanceFrequency (&PerformanceFreq);
411 }
412 #endif // WIN32
413
414 double Sys_DirtyTime(void)
415 {
416         // first all the OPTIONAL timers
417
418         // benchmark timer (fake clock)
419         if(sys_usenoclockbutbenchmark.integer)
420         {
421                 double old_benchmark_time = benchmark_time;
422                 benchmark_time += 1;
423                 if(benchmark_time == old_benchmark_time)
424                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
425                 return benchmark_time * 0.000001;
426         }
427
428         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
429                 return (double) Sys_SDL_GetTicks() / 1000.0;
430
431 #if HAVE_QUERYPERFORMANCECOUNTER
432         if (sys_usequeryperformancecounter.integer)
433         {
434                 // QueryPerformanceCounter
435                 // platform:
436                 // Windows 95/98/ME/NT/2000/XP
437                 // features:
438                 // + very accurate (constant-rate TSCs on modern systems)
439                 // known issues:
440                 // - does not necessarily match realtime too well (tends to get faster and faster in win98)
441                 // - wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
442                 // - higher access latency on Vista
443                 // Microsoft says on Win 7 or later, latency and overhead are very low, synchronisation is excellent.
444                 LARGE_INTEGER PerformanceCount;
445
446                 if (PerformanceFreq.QuadPart)
447                 {
448                         QueryPerformanceCounter (&PerformanceCount);
449                         return (double)PerformanceCount.QuadPart * (1.0 / (double)PerformanceFreq.QuadPart);
450                 }
451                 else
452                 {
453                         Con_Printf("No hardware timer available\n");
454                         // fall back to other clock sources
455                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
456                 }
457         }
458 #endif
459
460 #if HAVE_CLOCKGETTIME
461         {
462                 struct timespec ts;
463 #  ifdef CLOCK_MONOTONIC_RAW
464                 // Linux-specific, SDL_GetPerformanceCounter() uses it
465                 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
466 #  elif defined(CLOCK_MONOTONIC)
467                 // POSIX
468                 clock_gettime(CLOCK_MONOTONIC, &ts);
469 #  else
470                 // sunos
471                 clock_gettime(CLOCK_HIGHRES, &ts);
472 #  endif
473                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
474         }
475 #endif
476
477         // now all the FALLBACK timers
478 #if HAVE_TIMEGETTIME
479         {
480                 // timeGetTime
481                 // platform:
482                 // Windows 95/98/ME/NT/2000/XP
483                 // features:
484                 // reasonable accuracy (millisecond)
485                 // issues:
486                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
487                 // requires Sys_SetTimerResolution()
488                 return (double) timeGetTime() / 1000.0;
489         }
490 #else
491         // fallback for using the SDL timer if no other timer is available
492         // this calls Sys_Error() if not linking against SDL
493         return (double) Sys_SDL_GetTicks() / 1000.0;
494 #endif
495 }
496
497 double Sys_Sleep(double time)
498 {
499         double dt;
500         uint32_t msec, usec, nsec;
501
502         if (time < 1.0/1000000.0 || host.restless)
503                 return 0; // not sleeping this frame
504         if (time >= 1)
505                 time = 0.999999; // simpler, also ensures values are in range for all platform APIs
506         msec = time * 1000;
507         usec = time * 1000000;
508         nsec = time * 1000000000;
509
510         if(sys_usenoclockbutbenchmark.integer)
511         {
512                 double old_benchmark_time = benchmark_time;
513                 benchmark_time += usec;
514                 if(benchmark_time == old_benchmark_time)
515                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
516                 return 0;
517         }
518
519         if(sys_debugsleep.integer)
520                 Con_Printf("sys_debugsleep: requesting %u ", usec);
521         dt = Sys_DirtyTime();
522
523         // less important on newer libcurl so no need to disturb dedicated servers
524         if (cls.state != ca_dedicated && Curl_Select(msec))
525         {
526                 // a transfer is ready or we finished sleeping
527         }
528         else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
529                 Sys_SDL_Delay(msec);
530 #if HAVE_SELECT
531         else if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
532         {
533                 struct timeval tv;
534                 lhnetsocket_t *s;
535                 fd_set fdreadset;
536                 int lastfd = -1;
537
538                 FD_ZERO(&fdreadset);
539                 List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
540                 {
541                         if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
542                         {
543                                 if (lastfd < s->inetsocket)
544                                         lastfd = s->inetsocket;
545         #if defined(WIN32) && !defined(_MSC_VER)
546                                 FD_SET((int)s->inetsocket, &fdreadset);
547         #else
548                                 FD_SET((unsigned int)s->inetsocket, &fdreadset);
549         #endif
550                         }
551                 }
552                 tv.tv_sec = 0;
553                 tv.tv_usec = usec;
554                 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
555                 // (so much for POSIX...), not with an empty fd_set either.
556                 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
557         }
558 #endif
559 #if HAVE_CLOCK_NANOSLEEP
560         else
561         {
562                 struct timespec ts;
563                 ts.tv_sec = 0;
564                 ts.tv_nsec = nsec;
565                 clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
566         }
567 #elif HAVE_SELECT_POSIX
568         else
569         {
570                 struct timeval tv;
571                 tv.tv_sec = 0;
572                 tv.tv_usec = usec;
573                 select(0, NULL, NULL, NULL, &tv);
574         }
575 #elif HAVE_WIN32_USLEEP // Windows XP/2003 minimum
576         else
577         {
578                 HANDLE timer;
579                 LARGE_INTEGER sleeptime;
580
581                 // takes 100ns units, negative indicates relative time
582                 sleeptime.QuadPart = -((int64_t)nsec / 100);
583                 timer = CreateWaitableTimer(NULL, true, NULL);
584                 SetWaitableTimer(timer, &sleeptime, 0, NULL, NULL, 0);
585                 WaitForSingleObject(timer, INFINITE);
586                 CloseHandle(timer);
587         }
588 #elif HAVE_Sleep
589         else
590                 Sleep(msec);
591 #else
592         else
593                 Sys_SDL_Delay(msec);
594 #endif
595
596         dt = Sys_DirtyTime() - dt;
597         if(sys_debugsleep.integer)
598                 Con_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - usec);
599         return (dt < 0 || dt >= 1800) ? 0 : dt;
600 }
601
602
603 /*
604 ===============================================================================
605
606 STDIO
607
608 ===============================================================================
609 */
610
611 // NOTE: use only POSIX async-signal-safe library functions here (see: man signal-safety)
612 void Sys_Print(const char *text, size_t textlen)
613 {
614 #ifdef __ANDROID__
615         if (developer.integer > 0)
616         {
617                 __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
618         }
619 #else
620         if(sys.outfd < 0)
621                 return;
622   #ifndef WIN32
623         // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
624         // this is because both go to /dev/tty by default!
625         {
626                 int origflags = fcntl(sys.outfd, F_GETFL, 0);
627                 if (sys_stdout_blocks.integer)
628                         fcntl(sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
629   #else
630     #define write _write
631   #endif
632                 while(*text)
633                 {
634                         fs_offset_t written = (fs_offset_t)write(sys.outfd, text, textlen);
635                         if(written <= 0)
636                                 break; // sorry, I cannot do anything about this error - without an output
637                         text += written;
638                 }
639   #ifndef WIN32
640                 if (sys_stdout_blocks.integer)
641                         fcntl(sys.outfd, F_SETFL, origflags);
642         }
643   #endif
644         //fprintf(stdout, "%s", text);
645 #endif
646 }
647
648 void Sys_Printf(const char *fmt, ...)
649 {
650         va_list argptr;
651         char msg[MAX_INPUTLINE];
652         int msglen;
653
654         va_start(argptr,fmt);
655         msglen = dpvsnprintf(msg, sizeof(msg), fmt, argptr);
656         va_end(argptr);
657
658         if (msglen >= 0)
659                 Sys_Print(msg, msglen);
660 }
661
662 /// Reads a line from POSIX stdin or the Windows console
663 char *Sys_ConsoleInput(void)
664 {
665         static char text[MAX_INPUTLINE];
666 #ifdef WIN32
667         static unsigned int len = 0;
668         int c;
669
670         // read a line out
671         while (_kbhit ())
672         {
673                 c = _getch ();
674                 if (c == '\r')
675                 {
676                         text[len] = '\0';
677                         _putch ('\n');
678                         len = 0;
679                         return text;
680                 }
681                 if (c == '\b')
682                 {
683                         if (len)
684                         {
685                                 _putch (c);
686                                 _putch (' ');
687                                 _putch (c);
688                                 len--;
689                         }
690                         continue;
691                 }
692                 if (len < sizeof (text) - 1)
693                 {
694                         _putch (c);
695                         text[len] = c;
696                         len++;
697                 }
698         }
699 #else
700         fd_set fdset;
701         struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
702
703         FD_ZERO(&fdset);
704         FD_SET(fileno(stdin), &fdset);
705         if (select(1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(fileno(stdin), &fdset))
706                 return fgets(text, sizeof(text), stdin);
707 #endif
708         return NULL;
709 }
710
711
712 /*
713 ===============================================================================
714
715 Startup and Shutdown
716
717 ===============================================================================
718 */
719
720 void Sys_Error (const char *error, ...)
721 {
722         va_list argptr;
723         char string[MAX_INPUTLINE];
724         int i;
725
726         // Disable Sys_HandleSignal() but not Sys_HandleCrash()
727         host.state = host_shutdown;
728
729         // set output to blocking stderr
730         sys.outfd = fileno(stderr);
731 #ifndef WIN32
732         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
733 #endif
734
735         va_start (argptr,error);
736         dpvsnprintf (string, sizeof (string), error, argptr);
737         va_end (argptr);
738
739         Con_Printf(CON_ERROR "Engine Aborted: %s\n^9%s\n", string, engineversion);
740
741         dp_strlcat(string, "\n\n", sizeof(string));
742         dp_strlcat(string, engineversion, sizeof(string));
743
744         // Most shutdown funcs can't be called here as they could error while we error.
745
746         // DP8 TODO: send a disconnect message indicating we aborted, see Host_Error() and Sys_HandleCrash()
747
748         if (cls.demorecording)
749                 CL_Stop_f(cmd_local);
750         if (sv.active)
751         {
752                 sv.active = false; // make SV_DropClient() skip the QC stuff to avoid recursive errors
753                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
754                         if (host_client->active)
755                                 SV_DropClient(false, "Server aborted!"); // closes demo file
756         }
757         // don't want a dead window left blocking the OS UI or the abort dialog
758         VID_Shutdown();
759         S_StopAllSounds();
760
761         host.state = host_failed; // make Sys_HandleSignal() call _Exit()
762         Sys_SDL_Dialog("Engine Aborted", string);
763
764         fflush(stderr);
765         exit (1);
766 }
767
768 #ifndef WIN32
769 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
770 {
771         const char *p = PATH;
772         const char *q;
773         if(p && name)
774         {
775                 while((q = strchr(p, ':')))
776                 {
777                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
778                         if(FS_SysFileExists(buf))
779                                 return buf;
780                         p = q + 1;
781                 }
782                 if(!q) // none found - try the last item
783                 {
784                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
785                         if(FS_SysFileExists(buf))
786                                 return buf;
787                 }
788         }
789         return name;
790 }
791 #endif
792
793 static const char *Sys_FindExecutableName(void)
794 {
795 #if defined(WIN32)
796         return sys.argv[0];
797 #else
798         static char exenamebuf[MAX_OSPATH+1];
799         ssize_t n = -1;
800 #if defined(__FreeBSD__)
801         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
802         size_t exenamebuflen = sizeof(exenamebuf)-1;
803         if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
804         {
805                 n = exenamebuflen;
806         }
807 #elif defined(__linux__)
808         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
809 #endif
810         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
811         {
812                 exenamebuf[n] = 0;
813                 return exenamebuf;
814         }
815         if(strchr(sys.argv[0], '/'))
816                 return sys.argv[0]; // possibly a relative path
817         else
818                 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
819 #endif
820 }
821
822 void Sys_ProvideSelfFD(void)
823 {
824         if(sys.selffd != -1)
825                 return;
826         sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
827 }
828
829 // for x86 cpus only...  (x64 has SSE2_PRESENT)
830 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
831 // code from SDL, shortened as we can expect CPUID to work
832 static int CPUID_Features(void)
833 {
834         int features = 0;
835 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
836         __asm__ (
837 "        movl    %%ebx,%%edi\n"
838 "        xorl    %%eax,%%eax                                           \n"
839 "        incl    %%eax                                                 \n"
840 "        cpuid                       # Get family/model/stepping/features\n"
841 "        movl    %%edx,%0                                              \n"
842 "        movl    %%edi,%%ebx\n"
843         : "=m" (features)
844         :
845         : "%eax", "%ecx", "%edx", "%edi"
846         );
847 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
848         __asm {
849         xor     eax, eax
850         inc     eax
851         cpuid                       ; Get family/model/stepping/features
852         mov     features, edx
853         }
854 # else
855 #  error SSE_POSSIBLE set but no CPUID implementation
856 # endif
857         return features;
858 }
859 #endif
860
861 #ifdef SSE_POSSIBLE
862 qbool Sys_HaveSSE(void)
863 {
864         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
865         if(Sys_CheckParm("-nosse"))
866                 return false;
867 #ifdef SSE_PRESENT
868         return true;
869 #else
870         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
871         if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
872                 return true;
873         if(CPUID_Features() & (1 << 25))
874                 return true;
875         return false;
876 #endif
877 }
878
879 qbool Sys_HaveSSE2(void)
880 {
881         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
882         if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
883                 return false;
884 #ifdef SSE2_PRESENT
885         return true;
886 #else
887         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
888         if(Sys_CheckParm("-forcesse2"))
889                 return true;
890         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
891                 return true;
892         return false;
893 #endif
894 }
895 #endif
896
897 /// called to set process priority for dedicated servers
898 #if defined(__linux__)
899 #include <sys/resource.h>
900 #include <errno.h>
901
902 void Sys_InitProcessNice (void)
903 {
904         struct rlimit lim;
905         sys.nicepossible = false;
906         if(Sys_CheckParm("-nonice"))
907                 return;
908         errno = 0;
909         sys.nicelevel = getpriority(PRIO_PROCESS, 0);
910         if(errno)
911         {
912                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
913                 return;
914         }
915         if(getrlimit(RLIMIT_NICE, &lim))
916         {
917                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
918                 return;
919         }
920         if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
921         {
922                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
923                 return;
924         }
925         sys.nicepossible = true;
926         sys.isnice = false;
927 }
928 void Sys_MakeProcessNice (void)
929 {
930         if(!sys.nicepossible)
931                 return;
932         if(sys.isnice)
933                 return;
934         Con_DPrintf("Process is becoming 'nice'...\n");
935         if(setpriority(PRIO_PROCESS, 0, 19))
936                 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
937         sys.isnice = true;
938 }
939 void Sys_MakeProcessMean (void)
940 {
941         if(!sys.nicepossible)
942                 return;
943         if(!sys.isnice)
944                 return;
945         Con_DPrintf("Process is becoming 'mean'...\n");
946         if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
947                 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
948         sys.isnice = false;
949 }
950 #else
951 void Sys_InitProcessNice (void)
952 {
953 }
954 void Sys_MakeProcessNice (void)
955 {
956 }
957 void Sys_MakeProcessMean (void)
958 {
959 }
960 #endif
961
962
963 static const char *Sys_SigDesc(int sig)
964 {
965         switch (sig)
966         {
967                 // Windows only supports the C99 signals
968                 case SIGINT:  return "Interrupt";
969                 case SIGILL:  return "Illegal instruction";
970                 case SIGABRT: return "Aborted";
971                 case SIGFPE:  return "Floating point exception";
972                 case SIGSEGV: return "Segmentation fault";
973                 case SIGTERM: return "Termination";
974 #ifndef WIN32
975                 // POSIX has several others worth catching
976                 case SIGHUP:  return "Hangup";
977                 case SIGQUIT: return "Quit";
978                 case SIGBUS:  return "Bus error (bad memory access)";
979                 case SIGPIPE: return "Broken pipe";
980 #endif
981                 default:      return "Yo dawg, we bugged out while bugging out";
982         }
983 }
984
985 /** Halt and try not to catch fire.
986  * Writing to any file could corrupt it,
987  * any uneccessary code could crash while we crash.
988  * Try to use only POSIX async-signal-safe library functions here (see: man signal-safety).
989  */
990 static void Sys_HandleCrash(int sig)
991 {
992 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
993         // Before doing anything else grab the stack frame addresses
994         #include <execinfo.h>
995         void *stackframes[32];
996         int framecount = backtrace(stackframes, 32);
997         char **btstrings;
998 #endif
999         char dialogtext[3072];
1000         const char *sigdesc;
1001
1002         // Break any loop and disable Sys_HandleSignal()
1003         if (host.state == host_failing || host.state == host_failed)
1004                 return;
1005         host.state = host_failing;
1006
1007         sigdesc = Sys_SigDesc(sig);
1008
1009         // set output to blocking stderr and print header, backtrace, version
1010         sys.outfd = fileno(stderr); // not async-signal-safe :(
1011 #ifndef WIN32
1012         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
1013         Sys_Print("\n\n\e[1;37;41m    Engine Crash: ", 30);
1014         Sys_Print(sigdesc, strlen(sigdesc));
1015         Sys_Print("    \e[m\n", 8);
1016   #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
1017         // the first two addresses will be in this function and in signal() in libc
1018         backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
1019   #endif
1020         Sys_Print("\e[1m", 4);
1021         Sys_Print(engineversion, strlen(engineversion));
1022         Sys_Print("\e[m\n", 4);
1023 #else // Windows console doesn't support colours
1024         Sys_Print("\n\nEngine Crash: ", 16);
1025         Sys_Print(sigdesc, strlen(sigdesc));
1026         Sys_Print("\n", 1);
1027         Sys_Print(engineversion, strlen(engineversion));
1028         Sys_Print("\n", 1);
1029 #endif
1030
1031         // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Error() and Host_Error()
1032
1033         // don't want a dead window left blocking the OS UI or the crash dialog
1034         VID_Shutdown();
1035         S_StopAllSounds();
1036
1037         // prepare the dialogtext: signal, backtrace, version
1038         // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
1039         dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
1040         dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
1041 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
1042         btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
1043         if (btstrings)
1044                 for (int i = 0; i < framecount - 2; ++i)
1045                 {
1046                         dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
1047                         dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1048                 }
1049 #endif
1050         dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1051         dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
1052
1053         host.state = host_failed; // make Sys_HandleSignal() call _Exit()
1054         Sys_SDL_Dialog("Engine Crash", dialogtext);
1055
1056         fflush(stderr); // not async-signal-safe :(
1057
1058         // Continue execution with default signal handling.
1059         // A real crash will be re-triggered so the platform can handle it,
1060         // a fake crash (kill -SEGV) will cause a graceful shutdown.
1061         signal(sig, SIG_DFL);
1062 }
1063
1064 static void Sys_HandleSignal(int sig)
1065 {
1066         const char *sigdesc;
1067
1068         // Break any loop, eg if each Sys_Print triggers a SIGPIPE
1069         if (host.state == host_shutdown || host.state == host_failing)
1070                 return;
1071
1072         sigdesc = Sys_SigDesc(sig);
1073         Sys_Print("\nReceived ", 10);
1074         Sys_Print(sigdesc, strlen(sigdesc));
1075         Sys_Print(" signal, exiting...\n", 20);
1076         if (host.state == host_failed)
1077         {
1078                 // user is trying to kill the process while the SDL dialog is open
1079                 fflush(stderr); // not async-signal-safe :(
1080                 _Exit(sig);
1081         }
1082         host.state = host_shutdown;
1083 }
1084
1085 /// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
1086 static void Sys_InitSignals(void)
1087 {
1088         // Windows only supports the C99 signals
1089         signal(SIGINT,  Sys_HandleSignal);
1090         signal(SIGILL,  Sys_HandleCrash);
1091         signal(SIGABRT, Sys_HandleCrash);
1092         signal(SIGFPE,  Sys_HandleCrash);
1093         signal(SIGSEGV, Sys_HandleCrash);
1094         signal(SIGTERM, Sys_HandleSignal);
1095 #ifndef WIN32
1096         // POSIX has several others worth catching
1097         signal(SIGHUP,  Sys_HandleSignal);
1098         signal(SIGQUIT, Sys_HandleSignal);
1099         signal(SIGBUS,  Sys_HandleCrash);
1100         signal(SIGPIPE, Sys_HandleSignal);
1101 #endif
1102 }
1103
1104 /** main() but renamed so we can wrap it in sys_sdl.c and sys_null.c
1105  * to avoid needing to include SDL.h in this file (would make the dedicated server require SDL).
1106  * SDL builds need SDL.h in the file where main() is defined because SDL renames and wraps main().
1107  */
1108 int Sys_Main(int argc, char *argv[])
1109 {
1110         sys.argc = argc;
1111         sys.argv = (const char **)argv;
1112
1113         // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
1114         // COMMANDLINEOPTION: -noterminal disables console output on stdout
1115         if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
1116                 sys_stdout.string = "0";
1117         // COMMANDLINEOPTION: -stderr moves console output to stderr
1118         else if(Sys_CheckParm("-stderr"))
1119                 sys_stdout.string = "2";
1120         // too early for Cvar_SetQuick
1121         sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
1122         Sys_UpdateOutFD_c(&sys_stdout);
1123 #ifndef WIN32
1124         fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
1125         // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
1126         fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
1127         fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
1128 #endif
1129
1130         sys.selffd = -1;
1131         Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
1132
1133 #ifdef __ANDROID__
1134         Sys_AllowProfiling(true);
1135 #endif
1136
1137         Sys_InitSignals();
1138
1139 #ifdef WIN32
1140         Sys_SetTimerResolution();
1141 #endif
1142
1143         Host_Main();
1144
1145 #ifdef __ANDROID__
1146         Sys_AllowProfiling(false);
1147 #endif
1148
1149 #ifndef WIN32
1150         fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) & ~O_NONBLOCK);
1151         fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) & ~O_NONBLOCK);
1152 #endif
1153         fflush(stdout);
1154         fflush(stderr);
1155
1156         return 0;
1157 }