1 #include <unistd.h>
2 #include <signal.h>
3 #include <stdlib.h>
4 #include <limits.h>
5 #include <sys/time.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <sys/ipc.h>
12 #include <sys/shm.h>
13 #include <sys/stat.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <sys/wait.h>
17 #include <sys/mman.h>
18 #include <errno.h>
19 #include <execinfo.h>
20 #include <sys/utsname.h>
21 #define __USE_GNU 1
22 #define _GNU_SOURCE
23 #include <link.h>
24 #include <sys/ucontext.h>
25 #include <sys/resource.h>
26
27 //for old headers
28 #ifndef REG_EIP
29 #ifndef EIP
30 #define EIP 12 //aiee
31 #endif
32 #define REG_EIP EIP
33 #endif
34
35 //#include <fenv.h>
36 #include <dlfcn.h>
37
38 #include "../qcommon/qcommon.h"
39
40 #include "../linux/rw_linux.h"
41
42 cvar_t *nostdin;
43 cvar_t *nostdout;
44
45 extern cvar_t *sys_loopstyle;
46
47 unsigned sys_frame_time;
48
49 //uid_t saved_euid;
50 qboolean stdin_active = true;
51
52 // =======================================================================
53 // General routines
54 // =======================================================================
55 #ifndef NO_SERVER
Sys_ConsoleOutput(const char * string)56 void Sys_ConsoleOutput (const char *string)
57 {
58 char text[2048];
59 int i, j;
60
61 if (nostdout && nostdout->intvalue)
62 return;
63
64 i = 0;
65 j = 0;
66
67 //strip high bits
68 while (string[j])
69 {
70 text[i] = string[j] & 127;
71
72 //strip low bits
73 if (text[i] >= 32 || text[i] == '\n' || text[i] == '\t')
74 i++;
75
76 j++;
77
78 if (i == sizeof(text)-2)
79 {
80 text[i++] = '\n';
81 break;
82 }
83 }
84 text[i] = 0;
85
86 fputs(text, stdout);
87 }
88 #endif
89
Sys_FileLength(const char * path)90 int Sys_FileLength (const char *path)
91 {
92 struct stat st;
93
94 if (stat (path, &st) || (st.st_mode & S_IFDIR))
95 return -1;
96
97 return st.st_size;
98 }
99
Sys_getrusage_f(void)100 void Sys_getrusage_f (void)
101 {
102 struct rusage usage;
103
104 getrusage (RUSAGE_SELF, &usage);
105
106 //FIXME
107 Com_Printf ("user:", LOG_GENERAL);
108 }
109
Sys_Sleep(int msec)110 void Sys_Sleep (int msec)
111 {
112 usleep (msec*1000);
113 }
114
Sys_SetWindowText(char * dummy)115 void Sys_SetWindowText (char *dummy)
116 {
117 }
118
Sys_Printf(char * fmt,...)119 void Sys_Printf (char *fmt, ...)
120 {
121 va_list argptr;
122 char text[1024];
123 unsigned char *p;
124
125 if (nostdout && nostdout->intvalue)
126 return;
127
128 va_start (argptr,fmt);
129 vsprintf (text,fmt,argptr);
130 va_end (argptr);
131
132 if (strlen(text) > sizeof(text))
133 Sys_Error("memory overwrite in Sys_Printf");
134
135 for (p = (unsigned char *)text; *p; p++) {
136 *p &= 0x7f;
137 if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
138 printf("[%02x]", *p);
139 else
140 putc(*p, stdout);
141 }
142 }
143
Sys_Quit(void)144 void Sys_Quit (void)
145 {
146 #ifndef DEDICATED_ONLY
147 CL_Shutdown ();
148 #endif
149 Qcommon_Shutdown ();
150 fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
151 exit(0);
152 }
153
Sys_KillServer(int sig)154 void Sys_KillServer (int sig)
155 {
156 signal (SIGTERM, SIG_DFL);
157 signal (SIGINT, SIG_DFL);
158
159 Com_Printf ("Got sig %d, shutting down.\n", LOG_SERVER|LOG_NOTICE, sig);
160 Cmd_TokenizeString (va("Exiting on signal %d\n", sig), 0);
161 Com_Quit();
162 }
163
164 #if R1RELEASE == 3
dlcallback(struct dl_phdr_info * info,size_t size,void * data)165 static int dlcallback (struct dl_phdr_info *info, size_t size, void *data)
166 {
167 int j;
168 int end;
169
170 end = 0;
171
172 if (!info->dlpi_name || !info->dlpi_name[0])
173 return 0;
174
175 for (j = 0; j < info->dlpi_phnum; j++)
176 {
177 end += info->dlpi_phdr[j].p_memsz;
178 }
179
180 //this is terrible.
181 #if __WORDSIZE == 64
182 fprintf (stderr, "[0x%lux-0x%lux] %s\n", info->dlpi_addr, info->dlpi_addr + end, info->dlpi_name);
183 #else
184 fprintf (stderr, "[0x%ux-0x%ux] %s\n", info->dlpi_addr, info->dlpi_addr + end, info->dlpi_name);
185 #endif
186 return 0;
187 }
188 #endif
189
190 /* Obtain a backtrace and print it to stderr.
191 * Adapted from http://www.delorie.com/gnu/docs/glibc/libc_665.html
192 */
193 #ifndef __i386__
Sys_Backtrace(int sig)194 void Sys_Backtrace (int sig)
195 #else
196 void Sys_Backtrace (int sig, siginfo_t *siginfo, void *secret)
197 #endif
198 {
199 void *array[32];
200 struct utsname info;
201 size_t size;
202 size_t i;
203 char **strings;
204 #ifdef __i386__
205 ucontext_t *uc = (ucontext_t *)secret;
206 #endif
207
208 signal (SIGSEGV, SIG_DFL);
209
210 fprintf (stderr, "=====================================================\n"
211 "Segmentation Fault\n"
212 "=====================================================\n"
213 "A crash has occured within R1Q2 or the Game DLL (mod)\n"
214 "that you are running. This is most likely caused by\n"
215 "using the wrong server binary (eg, r1q2ded instead of\n"
216 "r1q2ded-old) for the mod you are running. The server\n"
217 "README on the R1Q2 forums has more information about\n"
218 "which binaries you should be using.\n"
219 "\n"
220 "If possible, try re-building R1Q2 and the mod you are\n"
221 "running from source code to ensure it isn't a compile\n"
222 "problem. If the crash still persists, please post the\n"
223 "following debug info on the R1Q2 forums with details\n"
224 "including the mod name, version, Linux distribution\n"
225 "and any other pertinent information.\n"
226 "\n");
227
228 size = backtrace (array, sizeof(array)/sizeof(void*));
229
230 #ifdef __i386__
231 #ifdef __FreeBSD__
232 array[1] = (void *) uc->uc_mcontext.mc_eip;
233 #else /* __linux__ */
234 array[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
235 #endif
236 #endif
237
238 strings = backtrace_symbols (array, size);
239
240 fprintf (stderr, "Stack dump (%zd frames):\n", size);
241
242 for (i = 0; i < size; i++)
243 fprintf (stderr, "%.2zd: %s\n", i, strings[i]);
244
245 fprintf (stderr, "\nVersion: " R1BINARY " " VERSION " (" BUILDSTRING " " CPUSTRING ") " RELEASESTRING "\n");
246
247 uname (&info);
248 fprintf (stderr, "OS Info: %s %s %s %s %s\n\n", info.sysname, info.nodename, info.release, info.version, info.machine);
249
250 #if R1RELEASE == 3
251 fprintf (stderr, "Loaded libraries:\n");
252 dl_iterate_phdr(dlcallback, NULL);
253 #endif
254
255 free (strings);
256
257 raise (SIGSEGV);
258 }
259
Sys_ProcessTimes_f(void)260 void Sys_ProcessTimes_f (void)
261 {
262 struct rusage usage;
263 int days, hours, minutes;
264 double seconds;
265
266 if (getrusage (RUSAGE_SELF, &usage))
267 {
268 Com_Printf ("getrusage(): %s\n", LOG_GENERAL, strerror(errno));
269 return;
270 }
271
272 //1 second = 1 000 000 microseconds
273 days = usage.ru_stime.tv_sec / 86400;
274 usage.ru_stime.tv_sec -= days * 86400;
275
276 hours = usage.ru_stime.tv_sec / 3600;
277 usage.ru_stime.tv_sec -= hours * 3600;
278
279 minutes = usage.ru_stime.tv_sec / 60;
280 usage.ru_stime.tv_sec -= minutes * 60;
281
282 seconds = usage.ru_stime.tv_sec + (usage.ru_stime.tv_usec / 1000000);
283
284 Com_Printf ("%dd %dh %dm %gs kernel\n", LOG_GENERAL, days, hours, minutes, seconds);
285
286 days = usage.ru_utime.tv_sec / 86400;
287 usage.ru_utime.tv_sec -= days * 86400;
288
289 hours = usage.ru_utime.tv_sec / 3600;
290 usage.ru_utime.tv_sec -= hours * 3600;
291
292 minutes = usage.ru_utime.tv_sec / 60;
293 usage.ru_utime.tv_sec -= minutes * 60;
294
295 seconds = usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec / 1000000);
296
297 Com_Printf ("%dd %dh %dm %gs user\n", LOG_GENERAL, days, hours, minutes, seconds);
298 }
299
300 static unsigned int goodspins, badspins;
301
Sys_Spinstats_f(void)302 void Sys_Spinstats_f (void)
303 {
304 Com_Printf ("%u fast spins, %u slow spins, %.2f%% slow.\n", LOG_GENERAL, goodspins, badspins, ((float)badspins / (float)(goodspins+badspins)) * 100.0f);
305 }
306
Sys_GetFPUStatus(void)307 unsigned short Sys_GetFPUStatus (void)
308 {
309 unsigned short fpuword;
310 __asm__ __volatile__ ("fnstcw %0" : "=m" (fpuword));
311 return fpuword;
312 }
313
314 /*
315 * Round to zero, 24 bit precision
316 */
Sys_SetFPU(void)317 void Sys_SetFPU (void)
318 {
319 unsigned short fpuword;
320 fpuword = Sys_GetFPUStatus ();
321 fpuword &= ~(3 << 8);
322 fpuword |= (0 << 8);
323 fpuword &= ~(3 << 10);
324 fpuword |= (0 << 10);
325 __asm__ __volatile__ ("fldcw %0" : : "m" (fpuword));
326 }
327
Sys_Init(void)328 void Sys_Init(void)
329 {
330 #if id386
331 // Sys_SetFPCW();
332 #endif
333 /* Install our signal handler */
334 #ifdef __i386__
335 struct sigaction sa;
336
337 if (sizeof(uint32) != 4)
338 Sys_Error ("uint32 != 32 bits");
339 else if (sizeof(uint64) != 8)
340 Sys_Error ("uint64 != 64 bits");
341 else if (sizeof(uint16) != 2)
342 Sys_Error ("uint16 != 16 bits");
343
344 // fesetround (FE_TOWARDZERO);
345
346 Sys_SetFPU ();
347 Sys_CheckFPUStatus ();
348
349 sa.sa_sigaction = (void *)Sys_Backtrace;
350 sigemptyset (&sa.sa_mask);
351 sa.sa_flags = SA_RESTART | SA_SIGINFO;
352
353 sigaction(SIGSEGV, &sa, NULL);
354 #else
355 signal (SIGSEGV, Sys_Backtrace);
356 #endif
357
358 signal (SIGTERM, Sys_KillServer);
359 signal (SIGINT, Sys_KillServer);
360
361 //initialize timer base
362 Sys_Milliseconds ();
363 }
364
Sys_Error(const char * error,...)365 void Sys_Error (const char *error, ...)
366 {
367 va_list argptr;
368 char string[1024];
369
370 // change stdin to non blocking
371 fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
372
373 #ifndef DEDICATED_ONLY
374 CL_Shutdown ();
375 #endif
376 Qcommon_Shutdown ();
377
378 va_start (argptr,error);
379 vsprintf (string,error,argptr);
380 va_end (argptr);
381 fprintf(stderr, "Error: %s\n", string);
382
383 exit (1);
384
385 }
386
Sys_Warn(char * warning,...)387 void Sys_Warn (char *warning, ...)
388 {
389 va_list argptr;
390 char string[1024];
391
392 va_start (argptr,warning);
393 vsprintf (string,warning,argptr);
394 va_end (argptr);
395 fprintf(stderr, "Warning: %s", string);
396 }
397
398 /*
399 ============
400 Sys_FileTime
401
402 returns -1 if not present
403 ============
404 */
Sys_FileTime(char * path)405 int Sys_FileTime (char *path)
406 {
407 struct stat buf;
408
409 if (stat (path,&buf) == -1)
410 return -1;
411
412 return buf.st_mtime;
413 }
414
floating_point_exception_handler(int whatever)415 void floating_point_exception_handler(int whatever)
416 {
417 // Sys_Warn("floating point exception\n");
418 signal(SIGFPE, floating_point_exception_handler);
419 }
420
Sys_ConsoleInput(void)421 char *Sys_ConsoleInput(void)
422 {
423 static char text[1024];
424 int len;
425 fd_set fdset;
426 struct timeval timeout;
427
428 if (!dedicated || !dedicated->intvalue)
429 return NULL;
430
431 if (!stdin_active || (nostdin && nostdin->intvalue))
432 return NULL;
433
434 FD_ZERO(&fdset);
435 FD_SET(0, &fdset); // stdin
436 timeout.tv_sec = 0;
437 timeout.tv_usec = 0;
438 if (select (1, &fdset, NULL, NULL, &timeout) < 1 || !FD_ISSET(0, &fdset))
439 return NULL;
440
441 len = read (0, text, sizeof(text));
442 if (len == 0)
443 { // eof!
444 //stdin_active = false;
445 return NULL;
446 }
447 else if (len == sizeof(text))
448 {
449 Com_Printf ("Sys_ConsoleInput: Line too long, discarded.\n", LOG_SERVER);
450 return NULL;
451 }
452
453 if (len < 1)
454 return NULL;
455
456 text[len-1] = 0; // rip off the /n and terminate
457
458 return text;
459 }
460
461 /*****************************************************************************/
462
463 static void *game_library;
464
465 /*
466 =================
467 Sys_UnloadGame
468 =================
469 */
Sys_UnloadGame(void)470 void Sys_UnloadGame (void)
471 {
472 if (game_library)
473 dlclose (game_library);
474 game_library = NULL;
475 }
476
477 /*
478 =================
479 Sys_GetGameAPI
480
481 Loads the game dll
482 =================
483 */
Sys_GetGameAPI(void * parms,int baseq2)484 void *Sys_GetGameAPI (void *parms, int baseq2)
485 {
486 void *(*GetGameAPI) (void *);
487
488 char name[MAX_OSPATH];
489 char *path;
490 const char *gamename = "game.so";
491
492 if (game_library)
493 Com_Error (ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame");
494
495 Com_Printf("------- Loading %s -------\n", LOG_SERVER|LOG_NOTICE, gamename);
496
497 if (baseq2)
498 {
499 Com_sprintf (name, sizeof(name), "%s/%s", BASEDIRNAME, gamename);
500 game_library = dlopen (name, RTLD_NOW );
501
502 if (game_library == NULL) {
503 Com_Printf ("dlopen(): %s\n", LOG_SERVER, dlerror());
504 Com_Printf ("Attempting to load with lazy symbols...", LOG_SERVER);
505 game_library = dlopen(name, RTLD_LAZY);
506 }
507 }
508 else
509 {
510 // now run through the search paths
511 path = NULL;
512 for (;;)
513 {
514 path = FS_NextPath (path);
515 if (!path)
516 return NULL; // couldn't find one anywhere
517 Com_sprintf (name, sizeof(name), "%s/%s", path, gamename);
518 game_library = dlopen (name, RTLD_NOW );
519 if (game_library)
520 {
521 Com_DPrintf ("LoadLibrary (%s)\n",name);
522 break;
523 }
524 else
525 {
526 Com_Printf ("dlopen(): %s\n", LOG_SERVER, dlerror());
527 Com_Printf ("Attempting to load with lazy symbols...", LOG_SERVER);
528 game_library = dlopen(name, RTLD_LAZY);
529 if (game_library)
530 {
531 Com_Printf ("ok\n", LOG_SERVER);
532 break;
533 }
534 else
535 {
536 Com_Printf ("dlopen(): %s\n", LOG_SERVER, dlerror());
537 }
538 }
539 }
540 }
541
542 if (!game_library)
543 return NULL;
544
545 GetGameAPI = (void *(*)(void *))dlsym (game_library, "GetGameAPI");
546 if (!GetGameAPI)
547 {
548 Com_Printf ("dlsym(): %s\n", LOG_SERVER, dlerror());
549 Sys_UnloadGame ();
550 return NULL;
551 }
552
553 return GetGameAPI (parms);
554 }
555
556 /*****************************************************************************/
557
Sys_AppActivate(void)558 void Sys_AppActivate (void)
559 {
560 }
561
Sys_SendKeyEvents(void)562 void Sys_SendKeyEvents (void)
563 {
564 #ifndef DEDICATED_ONLY
565 if (KBD_Update_fp)
566 KBD_Update_fp();
567 #endif
568
569 // grab frame time
570 sys_frame_time = Sys_Milliseconds();
571 }
572
573 /*****************************************************************************/
574
Sys_GetClipboardData(void)575 char *Sys_GetClipboardData(void)
576 {
577 return NULL;
578 }
579
main(int argc,char ** argv)580 int main (int argc, char **argv)
581 {
582 unsigned int time, oldtime, newtime, spins;
583
584 // go back to real user for config loads
585 //saved_euid = geteuid();
586 //seteuid(getuid());
587 //
588
589 if (getuid() == 0 || geteuid() == 0)
590 Sys_Error ("For security reasons, do not run Quake II as root.");
591
592 binary_name = argv[0];
593
594 Qcommon_Init(argc, argv);
595
596 fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
597
598 nostdin = Cvar_Get ("nostdin", "0", 0);
599 nostdout = Cvar_Get("nostdout", "0", 0);
600 if (!nostdout->intvalue) {
601 fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
602 // printf ("Linux Quake -- Version %0.3f\n", LINUX_VERSION);
603 }
604
605 oldtime = Sys_Milliseconds ();
606 while (1)
607 {
608 // find time spent rendering last frame
609 if (dedicated->intvalue && sys_loopstyle->intvalue)
610 {
611 newtime = Sys_Milliseconds ();
612 time = newtime - oldtime;
613 spins = 0;
614 }
615 else
616 {
617 spins = 0;
618 do
619 {
620 newtime = Sys_Milliseconds ();
621 time = newtime - oldtime;
622 spins++;
623 } while (time < 1);
624 }
625
626 if (spins > 500)
627 badspins++;
628 else
629 goodspins++;
630
631 Qcommon_Frame (time);
632 oldtime = newtime;
633 }
634
635 }
636
637 //r1 :redundant
Sys_CopyProtect(void)638 void Sys_CopyProtect(void)
639 {
640 }
641
642 #if 0
643 /*
644 ================
645 Sys_MakeCodeWriteable
646 ================
647 */
648 void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
649 {
650
651 int r;
652 unsigned long addr;
653 int psize = getpagesize();
654
655 addr = (startaddr & ~(psize-1)) - psize;
656
657 // fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr,
658 // addr, startaddr+length, length);
659
660 r = mprotect((char*)addr, length + startaddr - addr + psize, 7);
661
662 if (r < 0)
663 Sys_Error("Protection change failed\n");
664
665 }
666
667 #endif
668
Sys_CheckFPUStatus(void)669 qboolean Sys_CheckFPUStatus (void)
670 {
671 static unsigned short last_word = 0;
672 unsigned short fpu_control_word;
673
674 fpu_control_word = Sys_GetFPUStatus ();
675
676 Com_DPrintf ("Sys_CheckFPUStatus: rounding %d, precision %d\n", (fpu_control_word >> 10) & 3, (fpu_control_word >> 8) & 3);
677
678 //check rounding (10) and precision (8) are set properly
679 if (((fpu_control_word >> 10) & 3) != 0 ||
680 ((fpu_control_word >> 8) & 3) != 0)
681 {
682 if (fpu_control_word != last_word)
683 {
684 last_word = fpu_control_word;
685 return false;
686 }
687 }
688
689 last_word = fpu_control_word;
690 return true;
691 }
692
Sys_ShellExec(const char * cmd)693 void Sys_ShellExec (const char *cmd)
694 {
695 //FIXME
696 }
697
Sys_OpenURL(void)698 void Sys_OpenURL (void)
699 {
700 //FIXME
701 }
702
Sys_UpdateURLMenu(const char * s)703 void Sys_UpdateURLMenu (const char *s)
704 {
705 //FIXME
706 }
707