Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // Based on https://gist.github.com/jvranish/4441299
  2.  
  3. #if defined(__linux__) || defined(__FreeBSD__) // Pierre-Marie Baty -- added compile guard
  4.  
  5. #define _GNU_SOURCE
  6. #include "harness/os.h"
  7. #include <assert.h>
  8. #include <ctype.h>
  9. #include <dirent.h>
  10. #include <err.h>
  11. #include <errno.h>
  12. #include <execinfo.h>
  13. #include <fcntl.h>
  14. #include <libgen.h>
  15. #include <limits.h>
  16. #include <link.h>
  17. #include <signal.h>
  18. #include <stdbool.h>
  19. #include <stdint.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <sys/stat.h>
  24. #include <sys/types.h>
  25. #include <termios.h>
  26. #include <time.h>
  27. #include <unistd.h>
  28.  
  29. #define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0]))
  30. #define MAX_STACK_FRAMES 64
  31. #define TRACER_PID_STRING "TracerPid:"
  32.  
  33. static int stack_nbr = 0;
  34. static char _program_name[1024];
  35.  
  36. static void* stack_traces[MAX_STACK_FRAMES];
  37. static char name_buf[4096];
  38.  
  39. struct dl_iterate_callback_data {
  40.     int initialized;
  41.     intptr_t start;
  42. } dethrace_dl_data;
  43.  
  44. static int dl_iterate_callback(struct dl_phdr_info* info, size_t size, void* data) {
  45.     struct dl_iterate_callback_data* callback_data = data;
  46.  
  47.     if (strcmp(info->dlpi_name, "") == 0) {
  48.         callback_data->start = info->dlpi_addr;
  49.     }
  50.     return 0;
  51. }
  52.  
  53. static intptr_t get_dethrace_offset(void) {
  54.     if (!dethrace_dl_data.initialized) {
  55.         dethrace_dl_data.initialized = 1;
  56.         dl_iterate_phdr(dl_iterate_callback, &dethrace_dl_data);
  57.     }
  58.     return dethrace_dl_data.start;
  59. }
  60.  
  61. // Resolve symbol name and source location given the path to the executable and an address
  62. int addr2line(char const* const program_name, void const* const addr) {
  63.     static char addr2line_cmd[MAXPATHLEN]; // Pierre-Marie Baty -- use the heap instead
  64.  
  65.     /* have addr2line map the address to the related line in the code */
  66.     sprintf(addr2line_cmd, "addr2line -f -p -e \"%s\" %p", program_name, addr - get_dethrace_offset()); // Pierre-Marie Baty -- enclosed program_name within quotes to support pathnames with spaces
  67.  
  68.     fprintf(stderr, "%d: ", stack_nbr++);
  69.     return system(addr2line_cmd);
  70. }
  71.  
  72. static void print_stack_trace(void) {
  73.     int i, trace_size = 0;
  74.     char** messages = (char**)NULL;
  75.  
  76.     fputs("\nStack trace:\n", stderr);
  77.  
  78.     trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
  79.     messages = backtrace_symbols(stack_traces, trace_size);
  80.  
  81.     /* skip the first couple stack frames (as they are this function and
  82.      our handler) and also skip the last frame as it's (always?) junk. */
  83.     for (i = 3; i < (trace_size - 1); ++i) {
  84.         if (addr2line(_program_name, stack_traces[i]) != 0) {
  85.             printf("  error determining line # for: %s\n", messages[i]);
  86.         }
  87.     }
  88.     if (messages) {
  89.         free(messages);
  90.     }
  91. }
  92.  
  93. static void signal_handler(int sig, siginfo_t* siginfo, void* context) {
  94.     (void)context;
  95.     fputs("\n******************\n", stderr);
  96.  
  97.     switch (sig) {
  98.     case SIGSEGV:
  99.         fputs("Caught SIGSEGV\n", stderr);
  100.         break;
  101.     case SIGINT:
  102.         fputs("Caught SIGINT\n", stderr);
  103.         break;
  104.     case SIGFPE:
  105.         switch (siginfo->si_code) {
  106.         case FPE_INTDIV:
  107.             fputs("Caught SIGFPE: FPE_INTDIV\n", stderr);
  108.             break;
  109.         case FPE_INTOVF:
  110.             fputs("Caught SIGFPE: FPE_INTOVF\n", stderr);
  111.             break;
  112.         case FPE_FLTDIV:
  113.             fputs("Caught SIGFPE: FPE_FLTDIV\n", stderr);
  114.             break;
  115.         case FPE_FLTOVF:
  116.             fputs("Caught SIGFPE: FPE_FLTOVF\n", stderr);
  117.             break;
  118.         case FPE_FLTUND:
  119.             fputs("Caught SIGFPE: FPE_FLTUND\n", stderr);
  120.             break;
  121.         case FPE_FLTRES:
  122.             fputs("Caught SIGFPE: FPE_FLTRES\n", stderr);
  123.             break;
  124.         case FPE_FLTINV:
  125.             fputs("Caught SIGFPE: FPE_FLTINV\n", stderr);
  126.             break;
  127.         case FPE_FLTSUB:
  128.             fputs("Caught SIGFPE: FPE_FLTSUB\n", stderr);
  129.             break;
  130.         default:
  131.             fputs("Caught SIGFPE: Arithmetic Exception\n", stderr);
  132.             break;
  133.         }
  134.         break;
  135.     case SIGILL:
  136.         switch (siginfo->si_code) {
  137.         case ILL_ILLOPC:
  138.             fputs("Caught SIGILL: ILL_ILLOPC\n", stderr);
  139.             break;
  140.         case ILL_ILLOPN:
  141.             fputs("Caught SIGILL: ILL_ILLOPN\n", stderr);
  142.             break;
  143.         case ILL_ILLADR:
  144.             fputs("Caught SIGILL: ILL_ILLADR\n", stderr);
  145.             break;
  146.         case ILL_ILLTRP:
  147.             fputs("Caught SIGILL: ILL_ILLTRP\n", stderr);
  148.             break;
  149.         case ILL_PRVOPC:
  150.             fputs("Caught SIGILL: ILL_PRVOPC\n", stderr);
  151.             break;
  152.         case ILL_PRVREG:
  153.             fputs("Caught SIGILL: ILL_PRVREG\n", stderr);
  154.             break;
  155.         case ILL_COPROC:
  156.             fputs("Caught SIGILL: ILL_COPROC\n", stderr);
  157.             break;
  158.         case ILL_BADSTK:
  159.             fputs("Caught SIGILL: ILL_BADSTK\n", stderr);
  160.             break;
  161.         default:
  162.             fputs("Caught SIGILL: Illegal Instruction\n", stderr);
  163.             break;
  164.         }
  165.         break;
  166.     case SIGTERM:
  167.         fputs("Caught SIGTERM\n", stderr);
  168.         break;
  169.     case SIGABRT:
  170.         fputs("Caught SIGABRT\n", stderr);
  171.         break;
  172.     default:
  173.         break;
  174.     }
  175.     fputs("******************\n", stderr);
  176.     print_stack_trace();
  177.     exit(1);
  178. }
  179.  
  180. void resolve_full_path(char* path, const char* argv0) {
  181.     if (argv0[0] == '/') { // run with absolute path
  182.         strcpy(path, argv0);
  183.     } else {               // run with relative path
  184.         if (NULL == getcwd(path, PATH_MAX)) {
  185.             perror("getcwd error");
  186.             return;
  187.         }
  188.         strcat(path, "/");
  189.         strcat(path, argv0);
  190.     }
  191. }
  192.  
  193. void OS_InstallSignalHandler(char* program_name) {
  194.     resolve_full_path(_program_name, program_name);
  195.  
  196.     /* setup alternate stack */
  197.     {
  198.         stack_t ss = {};
  199.         ss.ss_sp = malloc(2 * MINSIGSTKSZ);
  200.         if (ss.ss_sp == NULL) {
  201.             err(1, "malloc");
  202.         }
  203.         ss.ss_size = 2 * MINSIGSTKSZ;
  204.         ss.ss_flags = 0;
  205.  
  206.         if (sigaltstack(&ss, NULL) != 0) {
  207.             err(1, "sigaltstack");
  208.         }
  209.     }
  210.  
  211.     /* register our signal handlers */
  212.     {
  213.         struct sigaction sig_action = {};
  214.         sig_action.sa_sigaction = signal_handler;
  215.         sigemptyset(&sig_action.sa_mask);
  216.  
  217.         sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
  218.  
  219.         if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
  220.             err(1, "sigaction");
  221.         }
  222.         if (sigaction(SIGFPE, &sig_action, NULL) != 0) {
  223.             err(1, "sigaction");
  224.         }
  225.         if (sigaction(SIGINT, &sig_action, NULL) != 0) {
  226.             err(1, "sigaction");
  227.         }
  228.         if (sigaction(SIGILL, &sig_action, NULL) != 0) {
  229.             err(1, "sigaction");
  230.         }
  231.         if (sigaction(SIGTERM, &sig_action, NULL) != 0) {
  232.             err(1, "sigaction");
  233.         }
  234.         if (sigaction(SIGABRT, &sig_action, NULL) != 0) {
  235.             err(1, "sigaction");
  236.         }
  237.     }
  238. }
  239.  
  240. FILE* OS_fopen(const char* pathname, const char* mode) {
  241.     FILE* f = fopen(pathname, mode);
  242.     if (f != NULL) {
  243.         return f;
  244.     }
  245.     char buffer[512];
  246.     char buffer2[512];
  247.     strcpy(buffer, pathname);
  248.     strcpy(buffer2, pathname);
  249.     char* pDirName = dirname(buffer);
  250.     char* pBaseName = basename(buffer2);
  251.     DIR* pDir = opendir(pDirName);
  252.     if (pDir == NULL) {
  253.         return NULL;
  254.     }
  255.     for (struct dirent* pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir)) {
  256.         if (strcasecmp(pBaseName, pDirent->d_name) == 0) {
  257.             strcat(pDirName, "/");
  258.             strcat(pDirName, pDirent->d_name);
  259.             f = fopen(pDirName, mode);
  260.             break;
  261.         }
  262.     }
  263.     closedir(pDir);
  264.     if (f == NULL) {
  265.         fprintf(stderr, "Failed to open \"%s\" (%s)\n", pathname, strerror(errno));
  266.     }
  267.     return f;
  268. }
  269.  
  270. size_t OS_ConsoleReadPassword(char* pBuffer, size_t pBufferLen) {
  271.     struct termios old, new;
  272.     char c;
  273.     size_t len;
  274.  
  275.     tcgetattr(STDIN_FILENO, &old);
  276.     new = old;
  277.     new.c_lflag &= ~(ICANON | ECHO);
  278.     tcsetattr(STDIN_FILENO, TCSAFLUSH, &new);
  279.  
  280.     len = 0;
  281.     pBuffer[len] = '\0';
  282.     while (1) {
  283.         if (read(STDIN_FILENO, &c, 1) == 1) {
  284.             if (c == 0x7f) {
  285.                 if (len > 0) {
  286.                     len--;
  287.                     pBuffer[len] = '\0';
  288.                     printf("\033[1D \033[1D");
  289.                     fflush(stdout);
  290.                     continue;
  291.                 }
  292.             } else if (c == '\r' || c == '\n') {
  293.                 printf("\n");
  294.                 fflush(stdout);
  295.                 break;
  296.             } else if (len < pBufferLen - 1) {
  297.                 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
  298.                     pBuffer[len] = c;
  299.                     printf("*");
  300.                     fflush(stdout);
  301.                     len++;
  302.                     pBuffer[len] = '\0';
  303.                 }
  304.             }
  305.         }
  306.     }
  307.  
  308.     tcsetattr(STDIN_FILENO, TCSANOW, &old);
  309.     return len;
  310. }
  311.  
  312. char* OS_Dirname(const char* path) {
  313.     strcpy(name_buf, path);
  314.     return dirname(name_buf);
  315. }
  316.  
  317. char* OS_Basename(const char* path) {
  318.     strcpy(name_buf, path);
  319.     return basename(name_buf);
  320. }
  321.  
  322. #endif // __linux__
  323.