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 }