/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include "shared/utils.h" #include "minecctl.h" #include "server.h" #include "misc.h" #define INVALID(msg) do { *error = (msg); return false; } while(0) void server_load_all_known(struct cfg *cfg) { struct dirent *dent; _cleanup_free_ char *cpath = NULL; _cleanup_free_ char *dpath = NULL; if (cfg->server_list_loaded) return; cfg->server_list_loaded = true; cfg->cfg_dir = open_cfg_dir(NULL, &cpath); if (!cfg->cfg_dir) { error("Failed to open config directory %s", cpath); return; } cfg->data_dir = open_data_dir(NULL, &dpath); if (!cfg->data_dir) { error("Failed to open server directory %s", dpath); return; } while ((dent = readdir(cfg->cfg_dir))) { struct server *server; if (!config_valid_server_filename(dent, NULL)) continue; server = server_new(dent->d_name); list_add(&server->list, &cfg->servers); } } static bool read_file(int dfd, const char *filename, char *buf, size_t len, const char **error) { _cleanup_close_ int fd = -1; size_t off; ssize_t r; fd = openat(dfd, filename, O_RDONLY | O_CLOEXEC); if (fd < 0) INVALID("failed to open file"); for (off = 0; off < len; off += r) { r = read(fd, buf + off, len - off); if (r < 0) { if (errno == EAGAIN || errno == EINTR) continue; INVALID("failed to read file"); } else if (r == 0) break; } if (off >= len - 1) INVALID("file too large"); buf[off] = '\0'; return true; } bool server_read_config(struct cfg *cfg, struct server *server, unsigned *lineno, const char **error) { _cleanup_close_ int dfd = -1; char buf[4096]; if (!error) return false; if (!server) INVALID("Invalid argument, server missing"); if (!lineno) INVALID("Invalid argument, lineno not set"); if (!server->scfg.filename) INVALID("Invalid argument, filename not set"); if (server->file_read) return true; if (!cfg->cfg_dir) INVALID("Configuration directory not opened"); if (!cfg->data_dir) INVALID("Server directory not opened"); *lineno = 0; server->file_read = true; if (!read_file(dirfd(cfg->cfg_dir), server->scfg.filename, buf, sizeof(buf), error)) return false; if (!scfg_parse(&server->scfg, buf, false, lineno, error)) return false; /* fill in missing parameters from server.properties */ dfd = open_subdir(dirfd(cfg->data_dir), server->scfg.name, false); if (dfd < 0) goto out; if (!read_file(dfd, MC_SERVER_PROPERTIES, buf, sizeof(buf), error)) goto out; sprop_parse(&server->scfg, buf, lineno, error); out: if (!server->scfg.rcon_password) verbose("rcon password not set"); if (list_empty(&server->scfg.rcons)) verbose("rcon address not found in %s", server->scfg.filename); if (list_empty(&server->scfg.remotes)) verbose("mc server address not found in %s", server->scfg.filename); return true; } bool server_read_all_configs(struct cfg *cfg, bool print_results) { struct server *server; unsigned lineno; const char *error; bool rv = true; server_load_all_known(cfg); /* server->scfg.filename check excludes servers created from cmdline */ list_for_each_entry(server, &cfg->servers, list) { if (!server->scfg.filename) continue; if (!server_read_config(cfg, server, &lineno, &error)) { if (lineno != 0 && print_results) info("• %s: %sfail%s - line %u: %s", server->scfg.name, ansi_red, ansi_normal, lineno, error); else if (print_results) info("• %s: %sfail%s - %s", server->scfg.name, ansi_red, ansi_normal, error); rv = false; } else if (!scfg_validate(&server->scfg, &error) && print_results) { info("• %s: %sfail%s - %s", server->scfg.name, ansi_red, ansi_normal, error); rv = false; } else if (print_results) { info("• %s: %sok%s", server->scfg.name, ansi_green, ansi_normal); } } return rv; } struct server *server_get_default(struct cfg *cfg) { struct server *server; unsigned lineno; const char *error; server = list_first_entry_or_null(&cfg->servers, struct server, list); if (!server) die("No servers defined"); if (!server_read_config(cfg, server, &lineno, &error)) { error("%s: server_read_config error - %s", server->scfg.name, error); return NULL; } return server; } bool server_set_default(struct cfg *cfg, const char *name) { struct server *server; assert_die(cfg, "invalid arguments"); server_load_all_known(cfg); list_for_each_entry(server, &cfg->servers, list) { if (streq(name, server->scfg.name)) { list_rotate_to_front(&server->list, &cfg->servers); cfg->default_set = true; return true; } } return false; } void server_free_all(struct cfg *cfg) { struct server *server, *tmp; list_for_each_entry_safe(server, tmp, &cfg->servers, list) server_free(server); } void server_free(struct server *server) { scfg_delete(&server->scfg); xfree(server); } struct server *server_new(const char *filename) { struct server *server; server = zmalloc(sizeof(*server)); scfg_init(&server->scfg, filename); INIT_LIST_HEAD(&server->list); return server; }