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