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