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 }