udphole

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

main.c (7157B)


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