/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include "minecproxy.h" #include "uring.h" #include "shared/config-parser.h" #include "server.h" #include "server-config.h" #include "config.h" void server_cfg_async_dns_update(struct server_config *scfg) { struct server *server = container_of(scfg, struct server, scfg); scfg_async_dns_update(scfg); if (list_empty(&server->scfg.dnslookups)) { uring_task_put(&server->task); server_commit(server); } } static void server_reading_done(struct server *server) { if (!list_empty(&server->scfg.dnslookups)) { uring_task_get(&server->task); scfg_async_dns_start(&server->scfg); return; } server_commit(server); } static void server_prop_read_cb(struct uring_task *task, int res) { struct server *server = container_of(task, struct server, task); const char *error; unsigned lineno; assert_return(task); assert_task_alive(DBG_CFG, task); if (res <= 0) { error("error reading prop file for %s: %s", server->scfg.name, strerror(-res)); uring_task_close_fd(&server->task); server_reading_done(server); return; } debug(DBG_CFG, "%s: parsing cfg (%i bytes)", server->scfg.name, res); uring_task_close_fd(&server->task); sprop_parse(&server->scfg, server->tbuf.buf, &lineno, &error); server_reading_done(server); } static void server_prop_open_cb(struct uring_task *task, int res) { struct server *server = container_of(task, struct server, task); assert_return(task); assert_task_alive(DBG_CFG, task); if (res < 0) { debug(DBG_CFG, "open prop(%s) failed: %s", server->scfg.name, strerror(-res)); server_reading_done(server); return; } debug(DBG_CFG, "reading server prop %s (fd %i)", server->scfg.name, res); uring_task_set_fd(&server->task, res); uring_tbuf_read_until_eof(&server->task, server_prop_read_cb); } static void server_cfg_read_cb(struct uring_task *task, int res) { struct server *server = container_of(task, struct server, task); const char *error; unsigned lineno; _cleanup_free_ char *sprop = NULL; assert_return(task); assert_task_alive(DBG_CFG, task); if (res <= 0) { error("error reading config file for %s: %s", server->scfg.name, strerror(-res)); server_delete(server); } debug(DBG_CFG, "%s: parsing cfg (%i bytes)", server->scfg.name, res); uring_task_close_fd(&server->task); if (!scfg_parse(&server->scfg, server->tbuf.buf, true, &lineno, &error)) { error("%s: failed to parse config file, line %u: %s", server->scfg.name, lineno, error); server_delete(server); return; } sprop = xstrcat(server->scfg.name, "/" MC_SERVER_PROPERTIES, NULL); if (!sprop) { error("%s: xstrcat %m", server->scfg.name); server_delete(server); return; } uring_openat(&server->task, dirfd(cfg->data_dir), sprop, server_prop_open_cb); } static void server_cfg_open_cb(struct uring_task *task, int res) { struct server *server = container_of(task, struct server, task); assert_return(task); assert_task_alive(DBG_CFG, task); if (res < 0) { error("open cfg(%s) failed: %s", server->scfg.name, strerror(-res)); server_delete(server); return; } debug(DBG_CFG, "reading server cfg %s (fd %i)", server->scfg.name, res); uring_task_set_fd(&server->task, res); uring_tbuf_read_until_eof(&server->task, server_cfg_read_cb); } struct server_cfg_monitor { struct uring_task task; char buf[4096] _alignas_(struct inotify_event); }; static void scfgm_free(struct uring_task *task) { struct server_cfg_monitor *scfgm = container_of(task, struct server_cfg_monitor, task); assert_return(task); debug(DBG_CFG, "called"); xfree(scfgm); cfg->server_cfg_monitor = NULL; } static void inotify_event_dump(const struct inotify_event *event) { assert_return(event); debug(DBG_CFG, "inotify event:"); debug(DBG_CFG, " * WD : %i", event->wd); debug(DBG_CFG, " * Cookie : %" PRIu32, event->cookie); debug(DBG_CFG, " * Length : %" PRIu32, event->len); debug(DBG_CFG, " * Name : %s", event->name); debug(DBG_CFG, " * Mask : %" PRIu32, event->mask); if (event->mask & IN_ACCESS) debug(DBG_CFG, "\tIN_ACCESS"); else if (event->mask & IN_MODIFY) debug(DBG_CFG, "\tIN_MODIFY"); else if (event->mask & IN_ATTRIB) debug(DBG_CFG, "\tIN_ATTRIB"); else if (event->mask & IN_CLOSE_WRITE) debug(DBG_CFG, "\tIN_CLOSE_WRITE"); else if (event->mask & IN_CLOSE_NOWRITE) debug(DBG_CFG, "\tIN_CLOSE_NOWRITE"); else if (event->mask & IN_OPEN) debug(DBG_CFG, "\tIN_OPEN"); else if (event->mask & IN_MOVED_FROM) debug(DBG_CFG, "\tIN_MOVED_FROM"); else if (event->mask & IN_MOVED_TO) debug(DBG_CFG, "\tIN_MOVED_TO"); else if (event->mask & IN_CREATE) debug(DBG_CFG, "\tIN_CREATE"); else if (event->mask & IN_DELETE) debug(DBG_CFG, "\tIN_DELETE"); else if (event->mask & IN_DELETE_SELF) debug(DBG_CFG, "\tIN_DELETE_SELF"); else if (event->mask & IN_MOVE_SELF) debug(DBG_CFG, "\tIN_MOVE_SELF"); else if (event->mask & IN_UNMOUNT) debug(DBG_CFG, "\tIN_UNMOUNT"); else if (event->mask & IN_Q_OVERFLOW) debug(DBG_CFG, "\tIN_Q_OVERFLOW"); else if (event->mask & IN_IGNORED) debug(DBG_CFG, "\tIN_IGNORED"); } static void inotify_cb(struct uring_task *task, int res) { struct server_cfg_monitor *scfgm = container_of(task, struct server_cfg_monitor, task); const struct inotify_event *event; char *ptr; struct server *server; assert_return(task); assert_task_alive(DBG_CFG, task); if (res <= 0) { error("inotify_read: %i", res); return; } for (ptr = scfgm->buf; ptr < scfgm->buf + res; ptr += sizeof(struct inotify_event) + event->len) { event = (const struct inotify_event *)ptr; if (debug_enabled(DBG_CFG)) inotify_event_dump(event); if (event->mask & (IN_IGNORED | IN_MOVE_SELF | IN_DELETE_SELF | IN_UNMOUNT)) die("configuration directory gone, exiting"); if (event->mask & IN_Q_OVERFLOW) { error("inotify queue overflow"); continue; } if (!config_valid_server_filename(NULL, event->name)) continue; if (event->mask & (IN_MOVED_FROM | IN_DELETE)) server_delete_by_filename(event->name); else if (event->mask & (IN_MOVED_TO | IN_CREATE | IN_CLOSE_WRITE)) { server = server_new(event->name); verbose("New server config file detected: %s", event->name); uring_openat(&server->task, dirfd(cfg->cfg_dir), event->name, server_cfg_open_cb); } else error("inotify: unknown event: 0x%08x", event->mask); } uring_read(&scfgm->task, scfgm->buf, sizeof(scfgm->buf), inotify_cb); } void server_cfg_monitor_refdump() { assert_return_silent(cfg->server_cfg_monitor); uring_task_refdump(&cfg->server_cfg_monitor->task); } void server_cfg_monitor_delete() { assert_return(cfg->server_cfg_monitor); debug(DBG_CFG, "closing fd %i", cfg->server_cfg_monitor->task.fd); uring_task_destroy(&cfg->server_cfg_monitor->task); cfg->server_cfg_monitor = NULL; } void server_cfg_monitor_init() { int ifd; int iwd; struct server_cfg_monitor *scfgm; struct dirent *dent; struct server *server; assert_return(!cfg->server_cfg_monitor && cfg->cfg_dir); scfgm = zmalloc(sizeof(*scfgm)); if (!scfgm) die("malloc: %m"); ifd = inotify_init1(IN_CLOEXEC); if (ifd < 0) die("inotify_init1: %m"); /* ln = IN_CREATE, cp/vi/mv = IN_CREATE, IN_OPEN, IN_CLOSE_WRITE */ iwd = inotify_add_watch( ifd, cfg->cfg_real_path, IN_CLOSE_WRITE | IN_DELETE | IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO | IN_MOVED_FROM | IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_ONLYDIR); if (iwd < 0) die("inotify_add_watch: %m"); uring_task_init(&scfgm->task, "server-config-monitor", uring_parent(), scfgm_free); uring_task_set_fd(&scfgm->task, ifd); cfg->server_cfg_monitor = scfgm; uring_read(&scfgm->task, scfgm->buf, sizeof(scfgm->buf), inotify_cb); while ((dent = readdir(cfg->cfg_dir)) != NULL) { if (!config_valid_server_filename(dent, NULL)) continue; server = server_new(dent->d_name); if (server) uring_openat(&server->task, dirfd(cfg->cfg_dir), server->scfg.filename, server_cfg_open_cb); } }