From 8c27290245b7bcc7cd2f72f3b4a7562294b43bbe Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 23 Jun 2020 16:25:36 +0200 Subject: Split directories better --- main.c | 740 ----------------------------------------------------------------- 1 file changed, 740 deletions(-) delete mode 100644 main.c (limited to 'main.c') diff --git a/main.c b/main.c deleted file mode 100644 index 047e70e..0000000 --- a/main.c +++ /dev/null @@ -1,740 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "main.h" -#include "signal-handler.h" -#include "uring.h" -#include "config-parser.h" -#include "server.h" -#include "server-config.h" -#include "announce.h" -#include "systemd.h" -#include "igmp.h" -#include "idle.h" -#include "ptimer.h" -#include - -/* Global */ -struct cfg *cfg = NULL; -bool exiting = false; -unsigned debug_mask = DBG_ERROR | DBG_INFO; - -/* Local */ -static bool daemonize = false; -static FILE *log_file = NULL; -static const char *log_file_path = NULL; - -#define ANSI_RED "\x1B[0;31m" -#define ANSI_GREEN "\x1B[0;32m" -#define ANSI_YELLOW "\x1B[0;33m" -#define ANSI_BLUE "\x1B[0;34m" -#define ANSI_MAGENTA "\x1B[0;35m" -#define ANSI_GREY "\x1B[0;38;5;245m" -#define ANSI_NORMAL "\x1B[0m" - -static void -msg(enum debug_lvl lvl, const char *fmt, va_list ap) -{ - static bool first = true; - static bool use_colors = false; - static bool sd_daemon = false; - const char *color; - const char *sd_lvl; - - assert_return(lvl != 0 && !empty_str(fmt) && ap); - - while (first) { - int fd; - const char *e; - - first = false; - - /* assume we're not launched by systemd when daemonized */ - if (daemonize) { - sd_daemon = false; - use_colors = false; - break; - } - - if (log_file) { - sd_daemon = false; - use_colors = false; - break; - } - - if (getenv("NO_COLOR")) { - sd_daemon = false; - use_colors = false; - break; - } - - fd = fileno(stderr); - if (fd < 0) { - /* Umm... */ - sd_daemon = true; - use_colors = false; - break; - } - - if (!isatty(fd)) { - sd_daemon = true; - use_colors = false; - break; - } - - /* systemd wouldn't normally set TERM */ - e = getenv("TERM"); - if (!e) { - sd_daemon = true; - use_colors = false; - break; - } - - if (streq(e, "dumb")) { - sd_daemon = false; - use_colors = false; - break; - } - - sd_daemon = false; - use_colors = true; - } - - switch (lvl) { - case DBG_ERROR: - sd_lvl = SD_ERR; - color = use_colors ? ANSI_RED : NULL; - break; - case DBG_VERBOSE: - sd_lvl = SD_INFO; - color = NULL; - break; - case DBG_INFO: - sd_lvl = SD_NOTICE; - color = NULL; - break; - default: - sd_lvl = SD_DEBUG; - color = use_colors ? ANSI_GREY : NULL; - break; - } - - if (sd_daemon) - fprintf(stderr, sd_lvl); - else if (color) - fprintf(stderr, color); - - vfprintf(log_file ? log_file : stderr, fmt, ap); - - if (color) - fprintf(stderr, ANSI_NORMAL); -} - -void -__debug(enum debug_lvl lvl, const char *fmt, ...) -{ - va_list ap; - - assert_return(lvl != 0 && !empty_str(fmt)); - - va_start(ap, fmt); - msg(lvl, fmt, ap); - va_end(ap); -} - -__attribute__((noreturn)) void -__die(const char *fmt, ...) -{ - va_list ap; - - if (!empty_str(fmt)) { - va_start(ap, fmt); - msg(DBG_ERROR, fmt, ap); - va_end(ap); - } else - error("fmt not set"); - - sd_notifyf(0, "STATUS=Error, shutting down"); - exit(EXIT_FAILURE); -}; - -static void -cfg_free(struct uring_task *task) -{ - struct cfg *xcfg = container_of(task, struct cfg, task); - - assert_return(task && xcfg == cfg); - - debug(DBG_SIG, "called"); - systemd_delete(cfg); - xfree(cfg->igmp_iface); - cfg->igmp_iface = NULL; - exiting = true; - /* The cfg struct is free:d in main() */ -} - -enum mcfg_keys { - MCFG_KEY_INVALID = 0, - MCFG_KEY_IGMP, - MCFG_KEY_IGMP_IFACE, - MCFG_KEY_ANN_INTERVAL, - MCFG_KEY_PROXY_CONN_INTERVAL, - MCFG_KEY_PROXY_CONN_ATTEMPTS, - MCFG_KEY_SOCKET_DEFER, - MCFG_KEY_SOCKET_FREEBIND, - MCFG_KEY_SOCKET_KEEPALIVE, - MCFG_KEY_SOCKET_IPTOS, - MCFG_KEY_SOCKET_NODELAY, -}; - -struct cfg_key_value_map mcfg_key_map[] = { - { - .key_name = "igmp", - .key_value = MCFG_KEY_IGMP, - .value_type = CFG_VAL_TYPE_BOOL, - }, { - .key_name = "igmp_iface", - .key_value = MCFG_KEY_IGMP_IFACE, - .value_type = CFG_VAL_TYPE_STRING, - }, { - .key_name = "announce_interval", - .key_value = MCFG_KEY_ANN_INTERVAL, - .value_type = CFG_VAL_TYPE_UINT16, - }, { - .key_name = "proxy_connection_interval", - .key_value = MCFG_KEY_PROXY_CONN_INTERVAL, - .value_type = CFG_VAL_TYPE_UINT16, - }, { - .key_name = "proxy_connection_attempts", - .key_value = MCFG_KEY_PROXY_CONN_ATTEMPTS, - .value_type = CFG_VAL_TYPE_UINT16, - }, { - .key_name = "socket_defer", - .key_value = MCFG_KEY_SOCKET_DEFER, - .value_type = CFG_VAL_TYPE_BOOL, - }, { - .key_name = "socket_freebind", - .key_value = MCFG_KEY_SOCKET_FREEBIND, - .value_type = CFG_VAL_TYPE_BOOL, - }, { - .key_name = "socket_keepalive", - .key_value = MCFG_KEY_SOCKET_KEEPALIVE, - .value_type = CFG_VAL_TYPE_BOOL, - }, { - .key_name = "socket_iptos", - .key_value = MCFG_KEY_SOCKET_IPTOS, - .value_type = CFG_VAL_TYPE_BOOL, - }, { - .key_name = "socket_nodelay", - .key_value = MCFG_KEY_SOCKET_NODELAY, - .value_type = CFG_VAL_TYPE_BOOL, - }, { - .key_name = NULL, - .key_value = MCFG_KEY_INVALID, - .value_type = CFG_VAL_TYPE_INVALID, - } -}; - -static void -cfg_read() -{ - FILE *cfgfile; - const char *path; - char buf[4096]; - char *pos = buf; - size_t rd = 0; - size_t r; - - assert_return(cfg); - - if (cfg->cfg_file) - path = cfg->cfg_file; - else - path = DEFAULT_MAIN_CFG_FILE; - - cfgfile = fopen(path, "re"); - if (!cfgfile) { - /* ENOENT is only an error with an explicitly set path */ - if (errno == ENOENT && !cfg->cfg_file) - return; - else if (errno == ENOENT) - die("main config file (%s) missing", path); - else - die("fopen(%s): %m", path); - } - - debug(DBG_CFG, "opened main config file (%s)", path); - - while (rd < sizeof(buf)) { - r = fread(pos, 1, sizeof(buf) - rd - 1, cfgfile); - if (r == 0) - break; - rd += r; - pos += r; - } - - if (rd == 0) - die("main config file (%s) zero size", path); - - if (rd >= sizeof(buf)) - die("main config file (%s) too large", path); - - fclose(cfgfile); - *pos = '\0'; - pos = buf; - - if (!config_parse_header(path, "mcproxy", &pos)) - die("main config file (%s) invalid", path); - - while (true) { - int key; - const char *keyname; - struct cfg_value value; - - if (!config_parse_line(path, &pos, mcfg_key_map, - &key, &keyname, &value)) - break; - - if (key == MCFG_KEY_INVALID) - die("main config file (%s) invalid", path); - - debug(DBG_CFG, "main cfg: key %s", keyname); - - switch (key) { - - case MCFG_KEY_IGMP: - cfg->do_igmp = value.boolean; - break; - - case MCFG_KEY_IGMP_IFACE: - cfg->igmp_iface = xstrdup(value.str); - if (!cfg->igmp_iface) - die("xstrdup: %m"); - - break; - - case MCFG_KEY_ANN_INTERVAL: - cfg->announce_interval = value.uint16; - break; - - case MCFG_KEY_PROXY_CONN_INTERVAL: - cfg->proxy_connection_interval = value.uint16; - break; - - case MCFG_KEY_PROXY_CONN_ATTEMPTS: - cfg->proxy_connection_attempts = value.uint16; - break; - - case MCFG_KEY_SOCKET_DEFER: - cfg->socket_defer = value.boolean; - break; - - case MCFG_KEY_SOCKET_FREEBIND: - cfg->socket_freebind = value.boolean; - break; - - case MCFG_KEY_SOCKET_KEEPALIVE: - cfg->socket_keepalive = value.boolean; - break; - - case MCFG_KEY_SOCKET_IPTOS: - cfg->socket_iptos = value.boolean; - break; - - case MCFG_KEY_SOCKET_NODELAY: - cfg->socket_nodelay = value.boolean; - break; - - case MCFG_KEY_INVALID: - default: - die("main config file (%s) invalid", path); - } - } -} - -const struct { - const char *name; - unsigned val; -} debug_category_str[] = { - { - .name = "config", - .val = DBG_CFG, - },{ - .name = "refcount", - .val = DBG_REF, - },{ - .name = "malloc", - .val = DBG_MALLOC, - },{ - .name = "announce", - .val = DBG_ANN, - },{ - .name = "signal", - .val = DBG_SIG, - },{ - .name = "uring", - .val = DBG_UR, - },{ - .name = "server", - .val = DBG_SRV, - },{ - .name = "proxy", - .val = DBG_PROXY, - },{ - .name = "rcon", - .val = DBG_RCON, - },{ - .name = "idle", - .val = DBG_IDLE, - },{ - .name = "igmp", - .val = DBG_IGMP, - },{ - .name = "systemd", - .val = DBG_SYSD, - },{ - .name = "dns", - .val = DBG_DNS, - },{ - .name = "timer", - .val = DBG_TIMER, - },{ - .name = NULL, - .val = 0, - } -}; - -__attribute__((noreturn)) static void -usage(int argc, char **argv, bool invalid) -{ - if (invalid) - info("Invalid option(s)"); - - info("Usage: %s [OPTIONS]\n" - "\n" - "Valid options:\n" - " -c, --cfgdir=DIR\tlook for configuration files in DIR\n" - " -u, --user=USER\trun as USER\n" - " -D, --daemonize\trun in daemon mode (disables stderr output)\n" - " -l, --logfile=FILE\tlog to FILE instead of stderr\n" - " -h, --help\t\tprint this information\n" - " -v, --verbose\t\tenable verbose logging\n" - " -d, --debug=CATEGORY\tenable debugging for CATEGORY\n" - "\t\t\t(use \"list\" to see available categories,\n" - "\t\t\t or \"all\" to enable all categories)\n", - argv ? argv[0] : "mcproxy"); - - exit(invalid ? EXIT_FAILURE : EXIT_SUCCESS); -} - -static void -cfg_init(int argc, char **argv) -{ - int c; - unsigned i; - - assert_die(argc > 0 && argv, "invalid arguments"); - - cfg = zmalloc(sizeof(*cfg)); - if (!cfg) - die("malloc: %m"); - - uring_task_init(&cfg->task, "main", NULL, cfg_free); - list_init(&cfg->servers); - - cfg->cfg_dir = DEFAULT_CFG_DIR; - cfg->announce_interval = DEFAULT_ANNOUNCE_INTERVAL; - cfg->proxy_connection_interval = DEFAULT_PROXY_CONN_INTERVAL; - cfg->proxy_connection_attempts = DEFAULT_PROXY_CONN_ATTEMPTS; - cfg->socket_defer = DEFAULT_SOCKET_DEFER; - cfg->socket_freebind = DEFAULT_SOCKET_FREEBIND; - cfg->socket_keepalive = DEFAULT_SOCKET_KEEPALIVE; - cfg->socket_iptos = DEFAULT_SOCKET_IPTOS; - cfg->socket_nodelay = DEFAULT_SOCKET_NODELAY; - cfg->uid = geteuid(); - cfg->gid = getegid(); - - while (true) { - int option_index = 0; - static struct option long_options[] = { - { "cfgdir", required_argument, 0, 'c' }, - { "cfgfile", required_argument, 0, 'C' }, - { "user", required_argument, 0, 'u' }, - { "daemonize", no_argument, 0, 'D' }, - { "logfile", required_argument, 0, 'l' }, - { "help", no_argument, 0, 'h' }, - { "verbose", no_argument, 0, 'v' }, - { "debug", required_argument, 0, 'd' }, - { 0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, ":c:C:u:Dl:hvd:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'c': - cfg->cfg_dir = optarg; - break; - - case 'C': - cfg->cfg_file = optarg; - break; - - case 'v': - debug_mask |= DBG_VERBOSE; - break; - - case 'D': - daemonize = true; - break; - - case 'l': - log_file_path = optarg; - break; - - case 'u': { - struct passwd *pwd; - - errno = 0; - pwd = getpwnam(optarg); - if (!pwd) { - if (errno == 0) - errno = ESRCH; - if (errno == ESRCH) - die("failed to find user %s", optarg); - else - die("failed to find user %s (%m)", optarg); - } - - debug(DBG_CFG, "asked to execute with uid %ji gid %ji", - (intmax_t)pwd->pw_uid, - (intmax_t)pwd->pw_gid); - cfg->uid = pwd->pw_uid; - cfg->gid = pwd->pw_gid; - break; - } - - case 'd': - if (strcaseeq(optarg, "all")) { - debug_mask = ~0; - break; - } else if (strcaseeq(optarg, "list")) { - info("Debug categories:"); - info(" * all"); - for (i = 0; debug_category_str[i].name; i++) - info(" * %s", debug_category_str[i].name); - exit(EXIT_FAILURE); - } - - for (i = 0; debug_category_str[i].name; i++) { - if (strcaseeq(optarg, debug_category_str[i].name)) - break; - } - - if (!debug_category_str[i].name) - usage(argc, argv, true); - - debug_mask |= DBG_VERBOSE; - debug_mask |= debug_category_str[i].val; - break; - - case 'h': - usage(argc, argv, false); - - default: - usage(argc, argv, true); - } - - } - - if (optind < argc) - usage(argc, argv, true); -} - -static void -cfg_apply() -{ - if (cfg->uid == 0 || cfg->gid == 0) - /* This catches both -u root and running as root without -u */ - die("Execution as root is not supported (use -u )"); - - capng_clear(CAPNG_SELECT_BOTH); - if (capng_updatev(CAPNG_ADD, - CAPNG_EFFECTIVE | CAPNG_PERMITTED, - CAP_NET_RAW, CAP_NET_BIND_SERVICE, -1)) - die("capng_updatev failed"); - - if (geteuid() != cfg->uid) { - if (capng_change_id(cfg->uid, - cfg->gid, - CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING)) - die("capng_change_id failed"); - } else { - /* - * This can fail if any of the caps are lacking, but it'll - * be re-checked later. - */ - capng_apply(CAPNG_SELECT_BOTH); - setgroups(0, NULL); - } - - if (daemonize) { - if (daemon(1, 0) < 0) - die("daemon() failed: %m"); - } - - if (log_file_path) { - log_file = fopen(log_file_path, "ae"); - if (!log_file) - die("fopen(%s) failed: %m", log_file_path); - } - - /* - * Do this after caps have been dropped to make sure we're not - * accessing a directory we should have permissions to. - */ - if (chdir(cfg->cfg_dir)) - die("chdir(%s): %m", cfg->cfg_dir); - - if (debug_enabled(DBG_VERBOSE)) { - char *wd; - - wd = get_current_dir_name(); - verbose("Working directory: %s", wd ? wd : ""); - free(wd); - } -} - -void -dump_tree() -{ - struct server *server; - - if (!debug_enabled(DBG_REF)) - return; - - info("\n\n"); - info("Dumping Tree"); - info("============"); - uring_task_refdump(&cfg->task); - uring_refdump(); - signal_refdump(); - ptimer_refdump(); - idle_refdump(); - igmp_refdump(); - announce_refdump(); - server_cfg_monitor_refdump(); - list_for_each_entry(server, &cfg->servers, list) - server_refdump(server); - info("============"); - info("\n\n"); -} - -int -main(int argc, char **argv) -{ - struct server *server; - unsigned server_count; - struct rlimit old_rlimit; - - cfg_init(argc, argv); - - cfg_apply(); - - cfg_read(); - - /* - * In the splice case we use 4 fds per proxy connection... - */ - if (prlimit(0, RLIMIT_NOFILE, NULL, &old_rlimit) == 0) { - struct rlimit new_rlimit; - - new_rlimit.rlim_cur = old_rlimit.rlim_max; - new_rlimit.rlim_max = old_rlimit.rlim_max; - - if (prlimit(0, RLIMIT_NOFILE, &new_rlimit, NULL) == 0) - debug(DBG_MALLOC, "prlimit(NOFILE): %u/%u -> %u/%u", - (unsigned)old_rlimit.rlim_cur, - (unsigned)old_rlimit.rlim_max, - (unsigned)new_rlimit.rlim_cur, - (unsigned)new_rlimit.rlim_cur); - } - - uring_init(); - - ptimer_init(); - - igmp_init(); - - /* Drop CAP_NET_RAW (if we have it), only used for igmp */ - capng_clear(CAPNG_SELECT_BOTH); - if (capng_update(CAPNG_ADD, - CAPNG_EFFECTIVE | CAPNG_PERMITTED, - CAP_NET_BIND_SERVICE)) - die("capng_update failed"); - - if (capng_apply(CAPNG_SELECT_BOTH)) { - /* Try clearing all caps, shouldn't fail */ - capng_clear(CAPNG_SELECT_BOTH); - if (capng_apply(CAPNG_SELECT_BOTH)) - die("capng_apply failed"); - } - - signal_init(); - - server_cfg_monitor_init(); - - announce_init(); - - if (!cfg->igmp) - announce_start(0); - - idle_init(); - - uring_task_put(&cfg->task); - - server_count = 0; - list_for_each_entry(server, &cfg->servers, list) - server_count++; - - sd_notifyf(0, "READY=1\n" - "STATUS=Running, %u server configurations loaded\n" - "MAINPID=%lu", - server_count, - (unsigned long)getpid()); - - info("mcproxy (%s) started, %u server configurations loaded", - VERSION, server_count); - - uring_event_loop(); - - verbose("Exiting"); - - xfree(cfg); - cfg = NULL; - - if (debug_enabled(DBG_MALLOC)) - debug_resource_usage(); - - fflush(stdout); - fflush(stderr); - exit(EXIT_SUCCESS); -} -- cgit v1.2.3