#include #include #include #include #include "utils.h" #include "minecctl.h" #include "server.h" #include "misc.h" #include "config-parser.h" #include "server-config-options.h" void server_read_config(struct cfg *cfg, struct server *server) { char buf[4096]; size_t off = 0; ssize_t r; int dfd; int fd; char *pos = buf; if (!server || !server->filename || server->file_read) return; server->file_read = true; dfd = open(cfg->cfgdir, O_DIRECTORY | O_PATH | O_CLOEXEC); if (dfd < 0) die("Failed to open %s: %m", cfg->cfgdir); fd = openat(dfd, server->filename, O_RDONLY | O_CLOEXEC); if (fd < 0) die("Failed to open %s: %m", server->filename); close(dfd); while (true) { r = read(fd, buf + off, sizeof(buf) - off - 1); if (r < 0) die("Failed to read %s: %m", server->filename); else if (r == 0) break; off += r; if (off == sizeof(buf) - 1) die("Failed to read %s: file too large", server->filename); } buf[off] = '\0'; close(fd); if (!config_parse_header(SERVER_CFG_HEADER, &pos)) die("Unable to parse %s: invalid/missing header", server->filename); /* FIXME: this will cause superflous DNS lookups of other cfg entries */ while (true) { int key; const char *keyname; struct cfg_value value; if (!config_parse_line(server->filename, &pos, scfg_key_map, &key, &keyname, &value, false)) break; switch (key) { case SCFG_KEY_RCON: if (!list_empty(&server->rcon_addrs)) die("rcon address defined twice in %s", server->filename); list_replace(&value.saddrs, &server->rcon_addrs); break; case SCFG_KEY_RCON_PASSWORD: if (server->rcon_password) die("rcon password defined twice in %s", server->filename); server->rcon_password = xstrdup(value.str); break; case SCFG_KEY_REMOTE: if (!list_empty(&server->mc_addrs)) die("rcon address defined twice in %s", server->filename); list_replace(&value.saddrs, &server->mc_addrs); default: continue; } } if (!server->rcon_password) verbose("rcon password not found in %s", server->filename); if (list_empty(&server->rcon_addrs)) verbose("rcon address not found in %s", server->filename); if (list_empty(&server->mc_addrs)) verbose("mc server address not found in %s", server->filename); } struct server * server_get_default(struct cfg *cfg) { struct server *server; server = list_first_entry_or_null(&cfg->servers, struct server, list); if (!server) die("No servers defined"); server_read_config(cfg, server); return server; } bool server_set_default(struct cfg *cfg, const char *name) { struct server *server; assert_die(cfg, "invalid arguments"); list_for_each_entry(server, &cfg->servers, list) { if (streq(name, server->name)) { list_rotate_to_front(&server->list, &cfg->servers); return true; } } return false; } void server_load_known(struct cfg *cfg) { struct dirent *dent; DIR *dir; dir = opendir(cfg->cfgdir); if (!dir) { info("Can't open config directory %s: %m", cfg->cfgdir); return; } while ((dent = readdir(dir))) { struct server *server; char *suffix; if (!is_valid_server_config_filename(dent, NULL)) continue; server = server_new(); server->filename = xstrdup(dent->d_name); suffix = strrchr(dent->d_name, '.'); assert_die(suffix, "Error parsing filename"); *suffix = '\0'; server->name = xstrdup(dent->d_name); list_add(&server->list, &cfg->servers); } closedir(dir); } void server_free(struct server *server) { struct saddr *saddr, *tmp; xfree(server->name); xfree(server->filename); free_password(&server->rcon_password); list_for_each_entry_safe(saddr, tmp, &server->rcon_addrs, list) { list_del(&saddr->list); xfree(saddr); } list_for_each_entry_safe(saddr, tmp, &server->mc_addrs, list) { list_del(&saddr->list); xfree(saddr); } xfree(server); } struct server * server_new() { struct server *server; server = zmalloc(sizeof(*server)); INIT_LIST_HEAD(&server->rcon_addrs); INIT_LIST_HEAD(&server->mc_addrs); INIT_LIST_HEAD(&server->list); return server; }