]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
5d19784daf45354765868ebdf13581026b75caf3
[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 static char sys_timestring[128];
42 char *Sys_TimeString(const char *timeformat)
43 {
44         time_t mytime = time(NULL);
45 #if _MSC_VER >= 1400
46         struct tm mytm;
47         localtime_s(&mytm, &mytime);
48         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
49 #else
50         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
51 #endif
52         return sys_timestring;
53 }
54
55
56 void Sys_Quit (int returnvalue)
57 {
58         // Unlock mutexes because the quit command may jump directly here, causing a deadlock
59         if ((cmd_local)->cbuf->lock)
60                 Cbuf_Unlock((cmd_local)->cbuf);
61         SV_UnlockThreadMutex();
62         TaskQueue_Frame(true);
63
64         if (Sys_CheckParm("-profilegameonly"))
65                 Sys_AllowProfiling(false);
66         host.state = host_shutdown;
67         Host_Shutdown();
68
69 #ifdef __ANDROID__
70         Sys_AllowProfiling(false);
71 #endif
72 #ifndef WIN32
73         fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK);
74 #endif
75         fflush(stdout);
76
77         exit(returnvalue);
78 }
79
80 #ifdef __cplusplus
81 extern "C"
82 #endif
83 void Sys_AllowProfiling(qbool enable)
84 {
85 #ifdef __ANDROID__
86 #ifdef USE_PROFILER
87         extern void monstartup(const char *libname);
88         extern void moncleanup(void);
89         if (enable)
90                 monstartup("libmain.so");
91         else
92                 moncleanup();
93 #endif
94 #elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
95         extern int moncontrol(int);
96         moncontrol(enable);
97 #endif
98 }
99
100
101 /*
102 ===============================================================================
103
104 DLL MANAGEMENT
105
106 ===============================================================================
107 */
108
109 static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
110 {
111         const dllfunction_t *func;
112         if(dllhandle)
113         {
114                 for (func = fcts; func && func->name != NULL; func++)
115                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
116                         {
117                                 if(complain)
118                                 {
119                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
120                                         if(has_next)
121                                                 Con_DPrintf("\nContinuing with");
122                                 }
123                                 goto notfound;
124                         }
125                 return true;
126
127         notfound:
128                 for (func = fcts; func && func->name != NULL; func++)
129                         *func->funcvariable = NULL;
130         }
131         return false;
132 }
133
134 qbool Sys_LoadSelf(dllhandle_t *handle)
135 {
136         dllhandle_t dllhandle = 0;
137
138         if (handle == NULL)
139                 return false;
140 #ifdef WIN32
141         dllhandle = LoadLibrary (NULL);
142 #else
143         dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
144 #endif
145         *handle = dllhandle;
146         return true;
147 }
148
149 qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
150 {
151 #ifdef SUPPORTDLL
152         const dllfunction_t *func;
153         dllhandle_t dllhandle = 0;
154         unsigned int i;
155
156         if (handle == NULL)
157                 return false;
158
159 #ifndef WIN32
160 #ifdef PREFER_PRELOAD
161         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
162         if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
163         {
164                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
165                 *handle = dllhandle;
166                 return true;
167         }
168         else
169                 Sys_FreeLibrary(&dllhandle);
170 notfound:
171 #endif
172 #endif
173
174         // Initializations
175         for (func = fcts; func && func->name != NULL; func++)
176                 *func->funcvariable = NULL;
177
178         // Try every possible name
179         Con_DPrintf ("Trying to load library...");
180         for (i = 0; dllnames[i] != NULL; i++)
181         {
182                 Con_DPrintf (" \"%s\"", dllnames[i]);
183 #ifdef WIN32
184 # ifndef DONT_USE_SETDLLDIRECTORY
185 #  ifdef _WIN64
186                 SetDllDirectory("bin64");
187 #  else
188                 SetDllDirectory("bin32");
189 #  endif
190 # endif
191 #endif
192                 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
193                 {
194                         if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
195                                 break;
196                         else
197                                 Sys_FreeLibrary (&dllhandle);
198                 }
199         }
200
201         // see if the names can be loaded relative to the executable path
202         // (this is for Mac OSX which does not check next to the executable)
203         if (!dllhandle && strrchr(sys.argv[0], '/'))
204         {
205                 char path[MAX_OSPATH];
206                 strlcpy(path, sys.argv[0], sizeof(path));
207                 strrchr(path, '/')[1] = 0;
208                 for (i = 0; dllnames[i] != NULL; i++)
209                 {
210                         char temp[MAX_OSPATH];
211                         strlcpy(temp, path, sizeof(temp));
212                         strlcat(temp, dllnames[i], sizeof(temp));
213                         Con_DPrintf (" \"%s\"", temp);
214
215                         if(Sys_LoadLibrary(temp, &dllhandle))
216                         {
217                                 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
218                                         break;
219                                 else
220                                         Sys_FreeLibrary (&dllhandle);
221                         }
222                 }
223         }
224
225         // No DLL found
226         if (! dllhandle)
227         {
228                 Con_DPrintf(" - failed.\n");
229                 return false;
230         }
231
232         Con_DPrintf(" - loaded.\n");
233         Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
234
235         *handle = dllhandle;
236         return true;
237 #else
238         return false;
239 #endif
240 }
241
242 qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
243 {
244         dllhandle_t dllhandle = 0;
245
246         if(handle == NULL)
247                 return false;
248
249 #ifdef SUPPORTDLL
250 # ifdef WIN32
251         dllhandle = LoadLibrary (name);
252 # else
253         dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
254 # endif
255 #endif
256         if(!dllhandle)
257                 return false;
258
259         *handle = dllhandle;
260         return true;
261 }
262
263 void Sys_FreeLibrary (dllhandle_t* handle)
264 {
265 #ifdef SUPPORTDLL
266         if (handle == NULL || *handle == NULL)
267                 return;
268
269 #ifdef WIN32
270         FreeLibrary (*handle);
271 #else
272         dlclose (*handle);
273 #endif
274
275         *handle = NULL;
276 #endif
277 }
278
279 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
280 {
281 #ifdef SUPPORTDLL
282 #ifdef WIN32
283         return (void *)GetProcAddress (handle, name);
284 #else
285         return (void *)dlsym (handle, name);
286 #endif
287 #else
288         return NULL;
289 #endif
290 }
291
292 #ifdef WIN32
293 # define HAVE_TIMEGETTIME 1
294 # define HAVE_QUERYPERFORMANCECOUNTER 1
295 # define HAVE_Sleep 1
296 #endif
297
298 #ifndef WIN32
299 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
300 # define HAVE_CLOCKGETTIME 1
301 #endif
302 // FIXME improve this check, manpage hints to DST_NONE
303 # define HAVE_GETTIMEOFDAY 1
304 #endif
305
306 #ifdef FD_SET
307 # define HAVE_SELECT 1
308 #endif
309
310 #ifndef WIN32
311 // FIXME improve this check
312 # define HAVE_USLEEP 1
313 #endif
314
315 // these are referenced elsewhere
316 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."};
317 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
318
319 // these are not
320 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
321 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
322 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
323 #if HAVE_QUERYPERFORMANCECOUNTER
324 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)"};
325 #endif
326 #if HAVE_CLOCKGETTIME
327 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)"};
328 #endif
329
330 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
331
332 /*
333 ================
334 Sys_CheckParm
335
336 Returns the position (1 to argc-1) in the program's argument list
337 where the given parameter apears, or 0 if not present
338 ================
339 */
340 int Sys_CheckParm (const char *parm)
341 {
342         int i;
343
344         for (i=1 ; i<sys.argc ; i++)
345         {
346                 if (!sys.argv[i])
347                         continue;               // NEXTSTEP sometimes clears appkit vars.
348                 if (!strcmp (parm,sys.argv[i]))
349                         return i;
350         }
351
352         return 0;
353 }
354
355 void Sys_Init_Commands (void)
356 {
357         Cvar_RegisterVariable(&sys_debugsleep);
358         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
359         Cvar_RegisterVariable(&sys_libdir);
360 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
361         if(sys_supportsdlgetticks)
362         {
363                 Cvar_RegisterVariable(&sys_usesdlgetticks);
364                 Cvar_RegisterVariable(&sys_usesdldelay);
365         }
366 #endif
367 #if HAVE_QUERYPERFORMANCECOUNTER
368         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
369 #endif
370 #if HAVE_CLOCKGETTIME
371         Cvar_RegisterVariable(&sys_useclockgettime);
372 #endif
373 }
374
375 double Sys_DirtyTime(void)
376 {
377         // first all the OPTIONAL timers
378
379         // benchmark timer (fake clock)
380         if(sys_usenoclockbutbenchmark.integer)
381         {
382                 double old_benchmark_time = benchmark_time;
383                 benchmark_time += 1;
384                 if(benchmark_time == old_benchmark_time)
385                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
386                 return benchmark_time * 0.000001;
387         }
388 #if HAVE_QUERYPERFORMANCECOUNTER
389         if (sys_usequeryperformancecounter.integer)
390         {
391                 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
392                 // QueryPerformanceCounter
393                 // platform:
394                 // Windows 95/98/ME/NT/2000/XP
395                 // features:
396                 // very accurate (CPU cycles)
397                 // known issues:
398                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
399                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
400                 double timescale;
401                 LARGE_INTEGER PerformanceFreq;
402                 LARGE_INTEGER PerformanceCount;
403
404                 if (QueryPerformanceFrequency (&PerformanceFreq))
405                 {
406                         QueryPerformanceCounter (&PerformanceCount);
407         
408                         timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
409                         return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
410                 }
411                 else
412                 {
413                         Con_Printf("No hardware timer available\n");
414                         // fall back to other clock sources
415                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
416                 }
417         }
418 #endif
419
420 #if HAVE_CLOCKGETTIME
421         if (sys_useclockgettime.integer)
422         {
423                 struct timespec ts;
424 #  ifdef CLOCK_MONOTONIC
425                 // linux
426                 clock_gettime(CLOCK_MONOTONIC, &ts);
427 #  else
428                 // sunos
429                 clock_gettime(CLOCK_HIGHRES, &ts);
430 #  endif
431                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
432         }
433 #endif
434
435         // now all the FALLBACK timers
436         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
437                 return (double) Sys_SDL_GetTicks() / 1000.0;
438 #if HAVE_GETTIMEOFDAY
439         {
440                 struct timeval tp;
441                 gettimeofday(&tp, NULL);
442                 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
443         }
444 #elif HAVE_TIMEGETTIME
445         {
446                 static int firsttimegettime = true;
447                 // timeGetTime
448                 // platform:
449                 // Windows 95/98/ME/NT/2000/XP
450                 // features:
451                 // reasonable accuracy (millisecond)
452                 // issues:
453                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
454
455                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
456                 if (firsttimegettime)
457                 {
458                         timeBeginPeriod(1);
459                         firsttimegettime = false;
460                 }
461
462                 return (double) timeGetTime() / 1000.0;
463         }
464 #else
465         // fallback for using the SDL timer if no other timer is available
466         // this calls Sys_Error() if not linking against SDL
467         return (double) Sys_SDL_GetTicks() / 1000.0;
468 #endif
469 }
470
471 extern cvar_t host_maxwait;
472 double Sys_Sleep(double time)
473 {
474         double dt;
475         uint32_t microseconds;
476
477         // convert to microseconds
478         time *= 1000000.0;
479
480         if(host_maxwait.value <= 0)
481                 time = min(time, 1000000.0);
482         else
483                 time = min(time, host_maxwait.value * 1000.0);
484
485         if (time < 1 || host.restless)
486                 return 0; // not sleeping this frame
487
488         microseconds = time; // post-validation to prevent overflow
489
490         if(sys_usenoclockbutbenchmark.integer)
491         {
492                 double old_benchmark_time = benchmark_time;
493                 benchmark_time += microseconds;
494                 if(benchmark_time == old_benchmark_time)
495                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
496                 return 0;
497         }
498
499         if(sys_debugsleep.integer)
500                 Sys_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                 Sys_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 void Sys_Print(const char *text)
560 {
561 #ifdef __ANDROID__
562         if (developer.integer > 0)
563         {
564                 __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
565         }
566 #else
567         if(sys.outfd < 0)
568                 return;
569   #ifndef WIN32
570         // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
571         // this is because both go to /dev/tty by default!
572         {
573                 int origflags = fcntl (sys.outfd, F_GETFL, 0);
574                 fcntl (sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
575   #else
576     #define write _write
577   #endif
578                 while(*text)
579                 {
580                         fs_offset_t written = (fs_offset_t)write(sys.outfd, text, (int)strlen(text));
581                         if(written <= 0)
582                                 break; // sorry, I cannot do anything about this error - without an output
583                         text += written;
584                 }
585   #ifndef WIN32
586                 fcntl (sys.outfd, F_SETFL, origflags);
587         }
588   #endif
589         //fprintf(stdout, "%s", text);
590 #endif
591 }
592
593 void Sys_Printf(const char *fmt, ...)
594 {
595         va_list argptr;
596         char msg[MAX_INPUTLINE];
597
598         va_start(argptr,fmt);
599         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
600         va_end(argptr);
601
602         Sys_Print(msg);
603 }
604
605 char *Sys_ConsoleInput(void)
606 {
607         static char text[MAX_INPUTLINE];
608         static unsigned int len = 0;
609 #ifdef WIN32
610         int c;
611
612         // read a line out
613         while (_kbhit ())
614         {
615                 c = _getch ();
616                 if (c == '\r')
617                 {
618                         text[len] = '\0';
619                         _putch ('\n');
620                         len = 0;
621                         return text;
622                 }
623                 if (c == '\b')
624                 {
625                         if (len)
626                         {
627                                 _putch (c);
628                                 _putch (' ');
629                                 _putch (c);
630                                 len--;
631                         }
632                         continue;
633                 }
634                 if (len < sizeof (text) - 1)
635                 {
636                         _putch (c);
637                         text[len] = c;
638                         len++;
639                 }
640         }
641 #else
642         fd_set fdset;
643         struct timeval timeout;
644         FD_ZERO(&fdset);
645         FD_SET(0, &fdset); // stdin
646         timeout.tv_sec = 0;
647         timeout.tv_usec = 0;
648         if (select (1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(0, &fdset))
649         {
650                 len = read (0, text, sizeof(text) - 1);
651                 if (len >= 1)
652                 {
653                         // rip off the \n and terminate
654                         // div0: WHY? console code can deal with \n just fine
655                         // this caused problems with pasting stuff into a terminal window
656                         // so, not ripping off the \n, but STILL keeping a NUL terminator
657                         text[len] = 0;
658                         return text;
659                 }
660         }
661 #endif
662         return NULL;
663 }
664
665
666 void Sys_Error (const char *error, ...)
667 {
668         va_list argptr;
669         char string[MAX_INPUTLINE];
670
671 // change stdin to non blocking
672 #ifndef WIN32
673         fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK);
674 #endif
675
676         va_start (argptr,error);
677         dpvsnprintf (string, sizeof (string), error, argptr);
678         va_end (argptr);
679
680         Con_Printf(CON_ERROR "Engine Error: %s\n", string);
681
682         // don't want a dead window left blocking the OS UI or the crash dialog
683         Host_Shutdown();
684
685         Sys_SDL_Dialog("Engine Error", string);
686
687         exit (1);
688 }
689
690 #ifndef WIN32
691 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
692 {
693         const char *p = PATH;
694         const char *q;
695         if(p && name)
696         {
697                 while((q = strchr(p, ':')))
698                 {
699                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
700                         if(FS_SysFileExists(buf))
701                                 return buf;
702                         p = q + 1;
703                 }
704                 if(!q) // none found - try the last item
705                 {
706                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
707                         if(FS_SysFileExists(buf))
708                                 return buf;
709                 }
710         }
711         return name;
712 }
713 #endif
714
715 static const char *Sys_FindExecutableName(void)
716 {
717 #if defined(WIN32)
718         return sys.argv[0];
719 #else
720         static char exenamebuf[MAX_OSPATH+1];
721         ssize_t n = -1;
722 #if defined(__FreeBSD__)
723         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
724         size_t exenamebuflen = sizeof(exenamebuf)-1;
725         if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
726         {
727                 n = exenamebuflen;
728         }
729 #elif defined(__linux__)
730         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
731 #endif
732         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
733         {
734                 exenamebuf[n] = 0;
735                 return exenamebuf;
736         }
737         if(strchr(sys.argv[0], '/'))
738                 return sys.argv[0]; // possibly a relative path
739         else
740                 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
741 #endif
742 }
743
744 void Sys_ProvideSelfFD(void)
745 {
746         if(sys.selffd != -1)
747                 return;
748         sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
749 }
750
751 // for x86 cpus only...  (x64 has SSE2_PRESENT)
752 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
753 // code from SDL, shortened as we can expect CPUID to work
754 static int CPUID_Features(void)
755 {
756         int features = 0;
757 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
758         __asm__ (
759 "        movl    %%ebx,%%edi\n"
760 "        xorl    %%eax,%%eax                                           \n"
761 "        incl    %%eax                                                 \n"
762 "        cpuid                       # Get family/model/stepping/features\n"
763 "        movl    %%edx,%0                                              \n"
764 "        movl    %%edi,%%ebx\n"
765         : "=m" (features)
766         :
767         : "%eax", "%ecx", "%edx", "%edi"
768         );
769 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
770         __asm {
771         xor     eax, eax
772         inc     eax
773         cpuid                       ; Get family/model/stepping/features
774         mov     features, edx
775         }
776 # else
777 #  error SSE_POSSIBLE set but no CPUID implementation
778 # endif
779         return features;
780 }
781 #endif
782
783 #ifdef SSE_POSSIBLE
784 qbool Sys_HaveSSE(void)
785 {
786         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
787         if(Sys_CheckParm("-nosse"))
788                 return false;
789 #ifdef SSE_PRESENT
790         return true;
791 #else
792         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
793         if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
794                 return true;
795         if(CPUID_Features() & (1 << 25))
796                 return true;
797         return false;
798 #endif
799 }
800
801 qbool Sys_HaveSSE2(void)
802 {
803         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
804         if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
805                 return false;
806 #ifdef SSE2_PRESENT
807         return true;
808 #else
809         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
810         if(Sys_CheckParm("-forcesse2"))
811                 return true;
812         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
813                 return true;
814         return false;
815 #endif
816 }
817 #endif
818
819 /// called to set process priority for dedicated servers
820 #if defined(__linux__)
821 #include <sys/resource.h>
822 #include <errno.h>
823
824 void Sys_InitProcessNice (void)
825 {
826         struct rlimit lim;
827         sys.nicepossible = false;
828         if(Sys_CheckParm("-nonice"))
829                 return;
830         errno = 0;
831         sys.nicelevel = getpriority(PRIO_PROCESS, 0);
832         if(errno)
833         {
834                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
835                 return;
836         }
837         if(getrlimit(RLIMIT_NICE, &lim))
838         {
839                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
840                 return;
841         }
842         if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
843         {
844                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
845                 return;
846         }
847         sys.nicepossible = true;
848         sys.isnice = false;
849 }
850 void Sys_MakeProcessNice (void)
851 {
852         if(!sys.nicepossible)
853                 return;
854         if(sys.isnice)
855                 return;
856         Con_DPrintf("Process is becoming 'nice'...\n");
857         if(setpriority(PRIO_PROCESS, 0, 19))
858                 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
859         sys.isnice = true;
860 }
861 void Sys_MakeProcessMean (void)
862 {
863         if(!sys.nicepossible)
864                 return;
865         if(!sys.isnice)
866                 return;
867         Con_DPrintf("Process is becoming 'mean'...\n");
868         if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
869                 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
870         sys.isnice = false;
871 }
872 #else
873 void Sys_InitProcessNice (void)
874 {
875 }
876 void Sys_MakeProcessNice (void)
877 {
878 }
879 void Sys_MakeProcessMean (void)
880 {
881 }
882 #endif
883
884 int main (int argc, char **argv)
885 {
886         signal(SIGFPE, SIG_IGN);
887
888         sys.argc = argc;
889         sys.argv = (const char **)argv;
890
891         // COMMANDLINEOPTION: -noterminal disables console output on stdout
892         if(Sys_CheckParm("-noterminal"))
893                 sys.outfd = -1;
894         // COMMANDLINEOPTION: -stderr moves console output to stderr
895         else if(Sys_CheckParm("-stderr"))
896                 sys.outfd = 2;
897         else
898                 sys.outfd = 1;
899
900         sys.selffd = -1;
901         Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
902
903 #ifndef WIN32
904         fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK);
905 #endif
906
907 #ifdef __ANDROID__
908         Sys_AllowProfiling(true);
909 #endif
910
911         Host_Main();
912
913         Sys_Quit(0);
914
915         return 0;
916 }