udphole

Basic UDP wormhole proxy
git clone git://git.finwo.net/app/udphole
Log | Files | Refs | README | LICENSE

main.c (7298B)


      1 #ifdef __cplusplus
      2 extern "C" {
      3 #endif
      4 
      5 #include <signal.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <strings.h>
     10 
     11 #include "cofyc/argparse.h"
     12 #include "domain/cluster/cluster.h"
     13 #include "infrastructure/config.h"
     14 #include "interface/cli/command/cluster.h"
     15 #include "interface/cli/command/daemon.h"
     16 #include "interface/cli/command/list_commands.h"
     17 #include "interface/cli/common.h"
     18 #include "rxi/log.h"
     19 
     20 #ifdef __cplusplus
     21 }
     22 #endif
     23 
     24 #define INCBIN_SILENCE_BITCODE_WARNING
     25 #include "graphitemaster/incbin.h"
     26 INCTXT(License, "LICENSE.md");
     27 
     28 static const char *const usages[] = {
     29     "udphole [global] command [local]",
     30     "udphole list-commands",
     31     "udphole --license",
     32     NULL,
     33 };
     34 
     35 static FILE                 *log_file;
     36 static char                 *log_path;
     37 static volatile sig_atomic_t sighup_received;
     38 
     39 static void logfile_callback(log_Event *ev) {
     40   if (sighup_received) {
     41     sighup_received = 0;
     42     if (log_path && log_file) {
     43       fclose(log_file);
     44       log_file = fopen(log_path, "a");
     45     }
     46     config_reload();
     47     cluster_reload();
     48   }
     49   if (log_file) {
     50     char buf[64];
     51     buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
     52     fprintf(log_file, "%s %-5s %s:%d: ", buf, log_level_string(ev->level), ev->file, ev->line);
     53     vfprintf(log_file, ev->fmt, ev->ap);
     54     fprintf(log_file, "\n");
     55     fflush(log_file);
     56   }
     57 }
     58 
     59 static void sighup_handler(int sig) {
     60   (void)sig;
     61   sighup_received = 1;
     62 }
     63 
     64 #define MARKER_PARAGRAPH  "<!-- paragraph -->"
     65 #define MARKER_LIST_START "<!-- list:start -->"
     66 #define MARKER_LIST_END   "<!-- list:end -->"
     67 
     68 static void skip_whitespace(const char **p) {
     69   while (**p == ' ' || **p == '\t' || **p == '\n' || **p == '\r') (*p)++;
     70 }
     71 
     72 static void print_license_paragraph(const char *start, const char *end, int width) {
     73   if (start >= end) return;
     74   while (start < end && (*start == ' ' || *start == '\n' || *start == '\r')) start++;
     75   while (end > start && (end[-1] == ' ' || end[-1] == '\n' || end[-1] == '\r')) end--;
     76   if (start >= end) return;
     77   static char buf[4096];
     78   size_t      n = (size_t)(end - start);
     79   if (n >= sizeof(buf)) n = sizeof(buf) - 1;
     80   memcpy(buf, start, n);
     81   buf[n] = '\0';
     82   for (size_t i = 0; i < n; i++)
     83     if (buf[i] == '\n') buf[i] = ' ';
     84   cli_print_wrapped(stdout, buf, width, 0);
     85   fputc('\n', stdout);
     86   fputc('\n', stdout);
     87 }
     88 
     89 static void print_license_list(const char *start, const char *end, int width) {
     90   const int   left_col = 5;
     91   const char *p        = start;
     92   while (p < end) {
     93     skip_whitespace(&p);
     94     if (p >= end) break;
     95     if (*p < '0' || *p > '9') {
     96       p++;
     97       continue;
     98     }
     99     const char *num_start = p;
    100     while (p < end && *p >= '0' && *p <= '9') p++;
    101     if (p >= end || *p != '.') continue;
    102     p++;
    103     skip_whitespace(&p);
    104     const char *text_start = p;
    105     const char *item_end   = p;
    106     while (item_end < end) {
    107       const char *next = item_end;
    108       while (next < end && *next != '\n') next++;
    109       if (next < end) next++;
    110       if (next >= end) {
    111         item_end = end;
    112         break;
    113       }
    114       skip_whitespace(&next);
    115       if (next < end && *next >= '0' && *next <= '9') {
    116         const char *q = next;
    117         while (q < end && *q >= '0' && *q <= '9') q++;
    118         if (q < end && *q == '.') break;
    119       }
    120       item_end = next;
    121     }
    122     while (item_end > text_start && (item_end[-1] == ' ' || item_end[-1] == '\n' || item_end[-1] == '\r')) item_end--;
    123     int num = atoi(num_start);
    124     fprintf(stdout, " %2d. ", num);
    125     static char buf[1024];
    126     size_t      n = (size_t)(item_end - text_start);
    127     if (n >= sizeof(buf)) n = sizeof(buf) - 1;
    128     memcpy(buf, text_start, n);
    129     buf[n] = '\0';
    130     for (size_t i = 0; i < n; i++)
    131       if (buf[i] == '\n' || buf[i] == '\r') buf[i] = ' ';
    132     cli_print_wrapped(stdout, buf, width, left_col);
    133     fputc('\n', stdout);
    134     p = item_end;
    135   }
    136   fputc('\n', stdout);
    137 }
    138 
    139 static void cli_print_license(FILE *out, const char *text, int width) {
    140   const char *p = text;
    141   (void)out;
    142   while (*p) {
    143     const char *q = strstr(p, "<!-- ");
    144     if (!q) {
    145       print_license_paragraph(p, p + strlen(p), width);
    146       break;
    147     }
    148     if (q > p) print_license_paragraph(p, q, width);
    149     q += 5;
    150     if (strncmp(q, "paragraph -->", 13) == 0) {
    151       q += 13;
    152       skip_whitespace(&q);
    153       const char *r = strstr(q, "<!-- ");
    154       if (!r) r = q + strlen(q);
    155       print_license_paragraph(q, r, width);
    156       p = r;
    157       continue;
    158     }
    159     if (strncmp(q, "list:start -->", 14) == 0) {
    160       q += 14;
    161       skip_whitespace(&q);
    162       const char *r = strstr(q, "<!-- list:end -->");
    163       if (r) print_license_list(q, r, width);
    164       p = r ? r + 17 : q + strlen(q);
    165       continue;
    166     }
    167     p = q;
    168   }
    169 }
    170 
    171 int main(int argc, const char **argv) {
    172   const char *loglevel     = "info";
    173   const char *logfile_path = NULL;
    174   const char *config_path  = NULL;
    175   static int  license_flag = 0;
    176 
    177   cli_register_command("list-commands", "Displays known commands and their descriptions", cli_cmd_list_commands);
    178 
    179   cli_register_command("daemon", "Run the udphole daemon", cli_cmd_daemon);
    180 
    181   cli_register_command("cluster", "Run the udphole cluster", cli_cmd_cluster);
    182 
    183   struct argparse        argparse;
    184   struct argparse_option options[] = {
    185       OPT_HELP(),
    186       OPT_STRING('f', "config", &config_path, "config file path (default: auto-detect)", NULL, 0, 0),
    187       OPT_STRING('v', "verbosity", &loglevel, "log verbosity: fatal,error,warn,info,debug,trace (default: info)", NULL,
    188                  0, 0),
    189       OPT_STRING(0, "log", &logfile_path, "also write log to file (SIGHUP reopens for logrotate)", NULL, 0, 0),
    190       OPT_BOOLEAN(0, "license", &license_flag, "print license and exit", NULL, 0, 0),
    191       OPT_END(),
    192   };
    193   argparse_init(&argparse, options, usages, ARGPARSE_STOP_AT_NON_OPTION);
    194   argc = argparse_parse(&argparse, argc, argv);
    195 
    196   if (license_flag) {
    197     cli_print_license(stdout, (const char *)gLicenseData, cli_get_output_width(120));
    198     return 0;
    199   }
    200 
    201   if (argc < 1) {
    202     argparse_usage(&argparse);
    203     return 1;
    204   }
    205 
    206   if (!config_path || !config_path[0]) config_path = cli_resolve_default_config();
    207   config_set_path(config_path);
    208   config_init();
    209 
    210   int level = LOG_INFO;
    211   if (0) {
    212     (void)0;
    213   } else if (!strcasecmp(loglevel, "trace")) {
    214     level = LOG_TRACE;
    215   } else if (!strcasecmp(loglevel, "debug")) {
    216     level = LOG_DEBUG;
    217   } else if (!strcasecmp(loglevel, "info")) {
    218     level = LOG_INFO;
    219   } else if (!strcasecmp(loglevel, "warn")) {
    220     level = LOG_WARN;
    221   } else if (!strcasecmp(loglevel, "error")) {
    222     level = LOG_ERROR;
    223   } else if (!strcasecmp(loglevel, "fatal")) {
    224     level = LOG_FATAL;
    225   } else {
    226     fprintf(stderr, "Unknown log level: %s\n", loglevel);
    227     return 1;
    228   }
    229   log_set_level(level);
    230   setvbuf(stderr, NULL, _IOLBF, 0);
    231 
    232   log_file        = NULL;
    233   log_path        = NULL;
    234   sighup_received = 0;
    235 
    236   if (logfile_path && logfile_path[0]) {
    237     log_path = strdup(logfile_path);
    238     log_file = fopen(log_path, "a");
    239     if (log_file) {
    240       log_add_callback(logfile_callback, log_path, level);
    241       signal(SIGHUP, sighup_handler);
    242     } else {
    243       fprintf(stderr, "Could not open log file: %s\n", logfile_path);
    244       free(log_path);
    245       log_path = NULL;
    246     }
    247   }
    248 
    249   return cli_execute_command(argc, argv);
    250 }