summaryrefslogtreecommitdiff
path: root/minecctl/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'minecctl/server.c')
-rw-r--r--minecctl/server.c244
1 files changed, 195 insertions, 49 deletions
diff --git a/minecctl/server.c b/minecctl/server.c
index 91e7842..3743526 100644
--- a/minecctl/server.c
+++ b/minecctl/server.c
@@ -3,6 +3,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <errno.h>
#include "shared/utils.h"
#include "minecctl.h"
@@ -12,37 +13,39 @@
#define INVALID(msg) do { *error = (msg); return false; } while(0)
-static DIR *open_cfg_dir(const char *cmdline_path)
+static DIR *__open_dir(int (*base_dir)(bool nofail), const char *fallback)
{
- int dfd, mfd;
+ _cleanup_close_ int xfd = -1;
+ _cleanup_close_ int mfd = -1;
DIR *dir;
- if (cmdline_path) {
- dir = opendir(cmdline_path);
- if (!dir)
- error("opendir(%s): %m", cmdline_path);
- return dir;
- }
-
/* First, attempt per-user config dir... */
- dfd = open_xdg_cfg_dir(false);
- if (dfd >= 0) {
- mfd = openat(dfd, "minecproxy/config",
- O_CLOEXEC | O_DIRECTORY | O_RDONLY);
- close(dfd);
- if (mfd >= 0) {
- dir = fdopendir(mfd);
- if (!dir)
- error("fdopendir: %m");
- return dir;
- }
- }
+ xfd = base_dir(false);
+ if (xfd < 0)
+ goto fallback;
- /* ...and fallback on the system dir */
- dir = opendir(DEFAULT_CFG_DIR);
- if (!dir)
- error("Failed to open configuration directory: %m");
+ mfd = openat(xfd, "minecproxy", O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+ if (mfd < 0)
+ goto fallback;
+
+ dir = fdopendir(mfd);
+ if (dir)
+ mfd = -1;
return dir;
+
+ /* ...and fallback on the system dir */
+fallback:
+ return opendir(fallback);
+}
+
+static inline DIR *open_cfg_dir()
+{
+ return __open_dir(open_xdg_cfg_dir, DEFAULT_CFG_DIR);
+}
+
+static inline DIR *open_data_dir()
+{
+ return __open_dir(open_xdg_data_dir, DEFAULT_DATA_DIR);
}
void server_load_all_known(struct cfg *cfg)
@@ -53,13 +56,20 @@ void server_load_all_known(struct cfg *cfg)
return;
cfg->server_list_loaded = true;
- cfg->dir = open_cfg_dir(cfg->dir_path);
- if (!cfg->dir) {
+
+ cfg->cfg_dir = open_cfg_dir();
+ if (!cfg->cfg_dir) {
error("Failed to open config directory");
return;
}
- while ((dent = readdir(cfg->dir))) {
+ cfg->data_dir = open_data_dir();
+ if (!cfg->data_dir) {
+ error("Failed to open server directory");
+ return;
+ }
+
+ while ((dent = readdir(cfg->cfg_dir))) {
struct server *server;
char *suffix;
@@ -77,13 +87,149 @@ void server_load_all_known(struct cfg *cfg)
}
}
+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;
+}
+
+enum sprop_keys {
+ SPROP_KEY_INVALID = 0,
+ SPROP_KEY_SERVER_PORT,
+ SPROP_KEY_RCON_PORT,
+ SPROP_KEY_RCON_PASSWORD,
+};
+
+struct cfg_key_value_map sprop_key_map[] = {
+ {
+ .key_name = "server-port",
+ .key_value = SPROP_KEY_SERVER_PORT,
+ .value_type = CFG_VAL_TYPE_ADDRS,
+ },
+ {
+ .key_name = "rcon.port",
+ .key_value = SPROP_KEY_RCON_PORT,
+ .value_type = CFG_VAL_TYPE_ADDRS,
+ },
+ {
+ .key_name = "rcon.password",
+ .key_value = SPROP_KEY_RCON_PASSWORD,
+ .value_type = CFG_VAL_TYPE_STRING,
+ }
+};
+
+static void sprop_parse(struct server_config *scfg, char *buf,
+ unsigned *lineno, const char **error)
+{
+ char *pos = buf;
+ struct saddr *saddr, *tmp;
+
+ *lineno = 0;
+
+ while (true) {
+ int key;
+ const char *keyname;
+ struct cfg_value value;
+
+ if (!config_parse_line("server.properties", &pos, sprop_key_map,
+ &key, &keyname, &value, false, lineno,
+ error))
+ break;
+
+ switch (key) {
+ case SPROP_KEY_SERVER_PORT:
+ error("Got a server port");
+
+ /* FIXME: these should use scfg_queue_dns */
+ if (value.type != CFG_VAL_TYPE_ADDRS) {
+ error("Got async DNS results!?");
+ break;
+ }
+
+ if (!list_empty(&scfg->remotes)) {
+ error("mc server address set both in %s and "
+ "server.properties", scfg->filename);
+ break;
+ }
+
+ list_for_each_entry_safe(saddr, tmp, &value.saddrs, list) {
+ list_del(&saddr->list);
+ list_add(&saddr->list, &scfg->remotes);
+ }
+
+ break;
+
+ case SPROP_KEY_RCON_PORT:
+ error("Got a rcon port");
+
+ if (value.type != CFG_VAL_TYPE_ADDRS) {
+ error("Got async DNS results!?");
+ break;
+ }
+
+ if (!list_empty(&scfg->rcons)) {
+ error("rcon address set both in %s and "
+ "server.properties", scfg->filename);
+ break;
+ }
+
+ list_for_each_entry_safe(saddr, tmp, &value.saddrs, list) {
+ list_del(&saddr->list);
+ list_add(&saddr->list, &scfg->rcons);
+ }
+
+ break;
+
+ case SPROP_KEY_RCON_PASSWORD:
+ error("Got an rcon password");
+
+ if (scfg->rcon_password) {
+ error("rcon password set both in %s and "
+ "server.properties (%smatching)",
+ scfg->filename,
+ streq(scfg->rcon_password, value.str) ?
+ "" : "not");
+ break;
+ }
+
+ scfg->rcon_password = xstrdup(value.str);
+ break;
+
+ case SPROP_KEY_INVALID:
+ _fallthrough_;
+ default:
+ break;
+ }
+ }
+}
+
bool server_read_config(struct cfg *cfg, struct server *server,
unsigned *lineno, const char **error)
{
+ _cleanup_close_ int dfd = -1;
char buf[4096];
- size_t off = 0;
- ssize_t r;
- int fd;
if (!error)
return false;
@@ -100,34 +246,34 @@ bool server_read_config(struct cfg *cfg, struct server *server,
if (server->file_read)
return true;
- if (!cfg->dir)
+ if (!cfg->cfg_dir)
INVALID("Configuration directory not opened");
+ if (!cfg->data_dir)
+ INVALID("Server directory not opened");
+
*lineno = 0;
server->file_read = true;
- fd = openat(dirfd(cfg->dir), server->scfg.filename, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- INVALID("failed to open configuration file");
+ if (!read_file(dirfd(cfg->cfg_dir), server->scfg.filename, buf,
+ sizeof(buf), error))
+ return false;
- while (true) {
- r = read(fd, buf + off, sizeof(buf) - off - 1);
- if (r < 0)
- INVALID("failed to read configuration file");
- else if (r == 0)
- break;
+ if (!scfg_parse(&server->scfg, buf, false, lineno, error))
+ return false;
- off += r;
- if (off == sizeof(buf) - 1)
- INVALID("invalid, file too large");
- }
+ /* fill in missing parameters from server.properties */
+ dfd = open_subdir(dirfd(cfg->data_dir), server->name, false);
+ if (dfd < 0)
+ goto out;
- buf[off] = '\0';
- close(fd);
+ if (!read_file(dfd, "server.properties", buf, sizeof(buf),
+ error))
+ goto out;
- if (!scfg_parse(&server->scfg, buf, NULL, lineno, error))
- return false;
+ sprop_parse(&server->scfg, buf, lineno, error);
+out:
if (!server->scfg.rcon_password)
verbose("rcon password not set");