summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-07-12 00:00:26 +0200
committerDavid Härdeman <david@hardeman.nu>2020-07-12 00:00:26 +0200
commitb56b003fc13a4e12f97c6cfd5dd650e928d6e016 (patch)
treea68b48604bccd9a96225bf1c3be21fc05a022b6b
parente6fdfd4c4c753fe3a06edc4ae3b767c57c10d3f7 (diff)
Teach minecctl to split things into data and cfg dir, also read config variables from the server.properties file
-rw-r--r--config.h.in2
-rw-r--r--examples/meson.build2
-rw-r--r--examples/minecserver@.service19
-rw-r--r--examples/server.properties4
-rw-r--r--meson.build2
-rw-r--r--minecctl/minecctl.c17
-rw-r--r--minecctl/minecctl.h4
-rw-r--r--minecctl/misc-commands.c136
-rw-r--r--minecctl/misc.h4
-rw-r--r--minecctl/rcon-commands.c36
-rw-r--r--minecctl/server.c244
-rw-r--r--minecproxy/main.c6
-rw-r--r--shared/config-parser.c44
-rw-r--r--shared/config-parser.h2
-rw-r--r--shared/utils.c22
-rw-r--r--shared/utils.h2
16 files changed, 353 insertions, 193 deletions
diff --git a/config.h.in b/config.h.in
index d0f028b..19fdccd 100644
--- a/config.h.in
+++ b/config.h.in
@@ -5,6 +5,8 @@
#define DEFAULT_CFG_DIR @DEFAULT_CFG_DIR@
+#define DEFAULT_DATA_DIR @DEFAULT_DATA_DIR@
+
#define DEFAULT_MAIN_CFG_FILE @DEFAULT_MAIN_CFG_FILE@
#define VERSION @VERSION@
diff --git a/examples/meson.build b/examples/meson.build
index e50f459..5d3564b 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -3,11 +3,11 @@
example_files = [
'eula.txt',
'example.mcserver',
+ 'minecctl.conf',
'minecproxy.conf',
'minecproxy.service',
'minecserver@.service',
'README.TXT',
- 'server.properties',
]
xxd = find_program('xxd')
diff --git a/examples/minecserver@.service b/examples/minecserver@.service
index bb72c5b..40aca86 100644
--- a/examples/minecserver@.service
+++ b/examples/minecserver@.service
@@ -3,29 +3,30 @@ Description=Minecraft Server %i
#Documentation= https://url https://url man:abc
Wants=network-online.target
After=network-online.target
-ConditionFileNotEmpty=%E/minecproxy/config/%i.mcserver
-ConditionPathExists=%E/minecproxy/servers/%i/eula.txt
-ConditionPathExists=%E/minecproxy/servers/%i/server.properties
-ConditionPathExists=%E/minecproxy/servers/%i/server.jar
+ConditionFileNotEmpty=%E/minecproxy/%i.mcserver
+# Unfortunately $XDG_DATA_DIR has no %E equivalent
+ConditionPathExists=%h/.local/share/minecproxy/%i/eula.txt
+ConditionPathExists=%h/.local/share/minecproxy/%i/server.properties
+ConditionPathExists=%h/.local/share/minecproxy/%i/server.jar
[Service]
Type=exec
ExecStart=/usr/bin/java -Xmx1024M -Xms1024M -jar server.jar --nogui
-ExecStop=-/usr/bin/minecctl -c %E/minecproxy/config/ -f stop %i
+ExecStop=-/usr/bin/minecctl -f stop %i
# Optional: this will autogenerate new servers on the fly
-# if this is used, comment out the ConditionPathExists checks above
+# if this is used, comment out all the ConditionPathExists checks above
#ExecStartPre=/usr/bin/minecctl new %i
TimeoutStopSec=120
KillSignal=SIGCONT
Restart=on-failure
-WorkingDirectory=%E/minecproxy/servers/%i
+WorkingDirectory=%h/.local/share/minecproxy/%i
Nice=5
LimitCORE=0
NoNewPrivileges=true
-KeyringMode=private
+KeyringMode=privat
PrivateUsers=true
ProtectSystem=strict
-ReadWritePaths=%E/minecproxy/servers/%i
+ReadWritePaths=%h/.local/share/minecproxy/%i
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
diff --git a/examples/server.properties b/examples/server.properties
index dddb380..c2609cd 100644
--- a/examples/server.properties
+++ b/examples/server.properties
@@ -3,7 +3,7 @@
# fleshed out version the first time the Minecraft
# server is executed.
motd=A Minecraft Server
-server-port=25565
+server-port=24000
enable-rcon=true
-rcon.port=25575
+rcon.port=28000
rcon.password=secret
diff --git a/meson.build b/meson.build
index a88b523..ffa2eca 100644
--- a/meson.build
+++ b/meson.build
@@ -39,11 +39,13 @@ ld_flags += cc.get_supported_link_arguments(ld_extra_flags)
add_project_link_arguments(ld_flags, language: 'c')
sysconfdir = join_paths(get_option('prefix'), get_option('sysconfdir'), meson.project_name())
+localstatedir = join_paths(get_option('prefix'), get_option('localstatedir'), meson.project_name())
mainconfname = meson.project_name() + '.conf'
conf = configuration_data()
conf.set_quoted('VERSION', '@0@-@VCS_TAG@'.format(meson.project_version()))
conf.set_quoted('DEFAULT_CFG_DIR', sysconfdir)
+conf.set_quoted('DEFAULT_DATA_DIR', localstatedir)
conf.set_quoted('DEFAULT_MAIN_CFG_FILE', mainconfname)
inc_config_h = include_directories('.')
diff --git a/minecctl/minecctl.c b/minecctl/minecctl.c
index 344d3d7..4a6bf0f 100644
--- a/minecctl/minecctl.c
+++ b/minecctl/minecctl.c
@@ -130,8 +130,8 @@ void dump_config(struct cfg *cfg)
info("Configuration");
info("┌────────────");
- info("│ dir_path : %s", cfg->dir_path);
- info("│ dir : %p", cfg->dir);
+ info("│ cfg_dir : %p", cfg->cfg_dir);
+ info("│ data_dir : %p", cfg->data_dir);
info("│ rcon_password : %s", cfg->rcon_password);
info("│ rcon_addrstr : %s", cfg->rcon_addrstr);
info("│ mc_addrstr : %s", cfg->mc_addrstr);
@@ -179,7 +179,6 @@ _noreturn_ static void usage(bool no_error)
" -m, --mc-address=ADDR connect to Minecraft server at ADDR\n"
" (only relevant for some commands, can also\n"
" use environment variable MC_ADDRESS)\n"
- " -c, --cfgdir=DIR look for configuration files in DIR\n"
" -f, --force stop server even if it has players\n"
" -v, --verbose enable extra logging\n"
" -d, --debug enable debugging information\n"
@@ -430,7 +429,6 @@ static void parse_cmdline(struct cfg *cfg, int argc, char *const *argv)
{ "rcon-password", required_argument, 0, 'p' },
{ "rcon-address", required_argument, 0, 'r' },
{ "mc-address", required_argument, 0, 'm' },
- { "cfgdir", required_argument, 0, 'c' },
{ "verbose", no_argument, 0, 'v' },
{ "debug", no_argument, 0, 'd' },
{ "force", no_argument, 0, 'f' },
@@ -439,7 +437,7 @@ static void parse_cmdline(struct cfg *cfg, int argc, char *const *argv)
};
/* clang-format on */
- c = getopt_long(argc, argv, ":p:r:m:c:vdfh", long_options,
+ c = getopt_long(argc, argv, ":p:r:m:vdfh", long_options,
&option_index);
if (c == -1)
@@ -455,9 +453,6 @@ static void parse_cmdline(struct cfg *cfg, int argc, char *const *argv)
case 'm':
cfg->mc_addrstr = xstrdup(optarg);
break;
- case 'c':
- cfg->dir_path = optarg;
- break;
case 'v':
debug_mask |= DBG_VERBOSE;
break;
@@ -532,7 +527,9 @@ out:
strv_free(cfg.commands);
xfree(cfg.rcon_addrstr);
xfree(cfg.mc_addrstr);
- if (cfg.dir)
- closedir(cfg.dir);
+ if (cfg.cfg_dir)
+ closedir(cfg.cfg_dir);
+ if (cfg.data_dir)
+ closedir(cfg.data_dir);
exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/minecctl/minecctl.h b/minecctl/minecctl.h
index 5778d13..1d118d7 100644
--- a/minecctl/minecctl.h
+++ b/minecctl/minecctl.h
@@ -6,8 +6,8 @@
#include <dirent.h>
struct cfg {
- const char *dir_path;
- DIR *dir;
+ DIR *cfg_dir;
+ DIR *data_dir;
char *rcon_password;
char *rcon_addrstr;
char *mc_addrstr;
diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c
index f3ddff2..8a54e64 100644
--- a/minecctl/misc-commands.c
+++ b/minecctl/misc-commands.c
@@ -21,11 +21,11 @@
#include "examples/eula.txt.h"
#include "examples/example.mcserver.h"
+#include "examples/minecctl.conf.h"
#include "examples/minecproxy.conf.h"
#include "examples/minecproxy.service.h"
#include "examples/minecserver@.service.h"
#include "examples/README.TXT.h"
-#include "examples/server.properties.h"
static bool create_link(int dfd, const char *source, const char *target)
{
@@ -99,7 +99,7 @@ static bool find_user_service(int xfd, const char *service)
return true;
}
- dfd = open_xdg_shared_dir();
+ dfd = open_xdg_data_dir(false);
if (dfd >= 0) {
if (faccessat(dfd, sub_path, R_OK, 0) == 0) {
info("User service %s already installed in "
@@ -142,105 +142,92 @@ static bool create_user_service(int xfd, const char *service,
return true;
}
-static bool create_server_cfg(int sfd, int cfd, const char *name,
+static bool create_server_cfg(struct cfg *cfg, const char *name,
const char *filename,
const unsigned char *properties,
size_t properties_len)
{
- _cleanup_close_ int efd = -1;
+ _cleanup_close_ int sfd = -1;
+ int cfd, dfd;
- efd = open_subdir(sfd, name, true);
- if (efd < 0) {
- error("Failed to create configuration directory \"%s\"", name);
+ if (!cfg->cfg_dir || !cfg->data_dir)
return false;
- }
- if (!write_cfg_file(efd, "eula.txt", ___examples_eula_txt,
- ___examples_eula_txt_len))
- return false;
-
- if (!write_cfg_file(efd, "server.properties",
- properties, properties_len))
- return false;
-
- if (!create_link(efd, "server.jar", "../server.jar"))
- return false;
+ cfd = dirfd(cfg->cfg_dir);
+ dfd = dirfd(cfg->data_dir);
if (!write_cfg_file(cfd, filename,
___examples_example_mcserver,
___examples_example_mcserver_len))
return false;
- return true;
-}
-
-static bool create_config_tree(int xfd)
-{
- _cleanup_close_ int mfd = -1;
- _cleanup_close_ int sfd = -1;
- _cleanup_close_ int cfd = -1;
-
- mfd = open_subdir(xfd, "minecproxy", true);
- if (mfd < 0)
- return false;
-
- sfd = open_subdir(mfd, "servers", true);
- if (sfd < 0)
- return false;
-
- if (!write_cfg_file(sfd, "README.TXT", ___examples_README_TXT,
- ___examples_README_TXT_len))
+ sfd = open_subdir(dfd, name, true);
+ if (sfd < 0) {
+ error("Failed to create server directory \"%s\"", name);
return false;
+ }
- cfd = open_subdir(mfd, "config", true);
- if (cfd < 0)
+ if (!write_cfg_file(sfd, "eula.txt", ___examples_eula_txt,
+ ___examples_eula_txt_len))
return false;
- if (!write_cfg_file(cfd, "minecproxy.conf", ___examples_minecproxy_conf,
- ___examples_minecproxy_conf_len))
+ if (!write_cfg_file(sfd, "server.properties",
+ properties, properties_len))
return false;
- if (!create_server_cfg(sfd, cfd, "example", "example.mcserver",
- ___examples_server_properties,
- ___examples_server_properties_len))
+ if (!create_link(sfd, "server.jar", "../server.jar"))
return false;
- info("Created configuration in $XDG_CONFIG_HOME/minecproxy");
return true;
}
-bool do_init(struct cfg *cfg)
+bool do_init(_unused_ struct cfg *cfg)
{
- _cleanup_close_ int xfd = -1;
+ _cleanup_close_ int xcfd = -1;
+ _cleanup_close_ int mcfd = -1;
+ _cleanup_close_ int xdfd = -1;
+ _cleanup_close_ int mdfd = -1;
- if (cfg->dir_path) {
- xfd = open(cfg->dir_path, O_PATH | O_CLOEXEC | O_DIRECTORY);
- if (xfd < 0)
- error("Failed to open dir %s: %m", cfg->dir_path);
- } else
- xfd = open_xdg_cfg_dir(true);
+ xcfd = open_xdg_cfg_dir(true);
+ if (xcfd < 0)
+ return false;
- if (xfd < 0)
+ mcfd = open_subdir(xcfd, "minecproxy", true);
+ if (mcfd < 0)
return false;
- if (!create_config_tree(xfd))
+ if (!write_cfg_file(mcfd, "minecproxy.conf",
+ ___examples_minecproxy_conf,
+ ___examples_minecproxy_conf_len))
return false;
- if (cfg->dir_path) {
- info("Option -c used, not creating systemd services");
- return true;
- }
+ if (!write_cfg_file(mcfd, "minecctl.conf",
+ ___examples_minecctl_conf,
+ ___examples_minecctl_conf_len))
+ return false;
- if (!create_user_service(xfd, "minecserver@.service",
+ if (!create_user_service(xcfd, "minecserver@.service",
___examples_minecserver__service,
___examples_minecserver__service_len))
return false;
- if (!create_user_service(xfd, "minecproxy.service",
+ if (!create_user_service(xcfd, "minecproxy.service",
___examples_minecproxy_service,
___examples_minecproxy_service_len))
return false;
+ xdfd = open_xdg_data_dir(true);
+ if (xdfd < 0)
+ return false;
+
+ mdfd = open_subdir(xdfd, "minecproxy", true);
+ if (mdfd < 0)
+ return false;
+
+ if (!write_cfg_file(mdfd, "README.TXT", ___examples_README_TXT,
+ ___examples_README_TXT_len))
+ return false;
+
return true;
}
@@ -390,12 +377,12 @@ static bool saddr_port_match(struct list_head *list, uint16_t port)
list_for_each_entry(a, list, list) {
switch (a->st.ss_family) {
case AF_INET:
- if (a->in4.sin_port == port)
+ if (htons(a->in4.sin_port) == port)
return true;
break;
case AF_INET6:
- if (a->in6.sin6_port == port)
+ if (htons(a->in6.sin6_port) == port)
return true;
break;
@@ -446,7 +433,9 @@ static bool select_free_ports(struct cfg *cfg, uint16_t *listen_port,
}
}
- if (!used ) {
+ if (!used) {
+ error("Found unused port, %" PRIu16, lport);
+
if (listen_port && *listen_port == 0)
*listen_port = lport;
@@ -470,7 +459,6 @@ bool do_new(struct cfg *cfg)
struct server *defserver = NULL;
_cleanup_free_ unsigned char *properties = NULL;
size_t properties_len;
- _cleanup_close_ int sfd = -1;
uint16_t rcon_port = 0, mc_port = 0;
const char *rcon_password = NULL;
@@ -480,9 +468,13 @@ bool do_new(struct cfg *cfg)
char filename[strlen(name) + STRLEN(".mcserver") + 1];
sprintf(filename, "%s.mcserver", name);
- server_load_all_known(cfg);
+ if (!server_read_all_configs(cfg, false)) {
+ error("Failed to read all existing server configurations, "
+ "try running the \"lint\" command.");
+ return false;
+ }
- if (!cfg->dir)
+ if (!cfg->cfg_dir || !cfg->data_dir)
return false;
list_for_each_entry(server, &cfg->servers, list) {
@@ -505,17 +497,13 @@ bool do_new(struct cfg *cfg)
} else if (cfg->rcon_password)
rcon_password = cfg->rcon_password;
+ dump_config(cfg);
+
if (!select_free_ports(cfg, NULL, &mc_port, &rcon_port)) {
error("Failed to find a free port");
return false;
}
- sfd = open_subdir(dirfd(cfg->dir), "../servers", false);
- if (sfd < 0) {
- error("Failed to open configuration directory \"servers\"");
- return false;
- }
-
properties = create_mc_properties(name, mc_port, rcon_port,
rcon_password, &properties_len);
if (!properties)
@@ -523,7 +511,7 @@ bool do_new(struct cfg *cfg)
error("Created config file (%zu):\n%s", properties_len, properties);
- if (!create_server_cfg(sfd, dirfd(cfg->dir), name, filename, properties,
+ if (!create_server_cfg(cfg, name, filename, properties,
properties_len))
return false;
diff --git a/minecctl/misc.h b/minecctl/misc.h
index d1c6816..02c01ea 100644
--- a/minecctl/misc.h
+++ b/minecctl/misc.h
@@ -6,9 +6,9 @@ int open_subdir(int dfd, const char *subdir, bool nofail);
int open_xdg_dir(const char *envname, const char *altpath, bool nofail);
-static inline int open_xdg_shared_dir()
+static inline int open_xdg_data_dir(bool nofail)
{
- return open_xdg_dir("XDG_DATA_HOME", ".local/share", false);
+ return open_xdg_dir("XDG_DATA_HOME", ".local/share", nofail);
}
static inline int open_xdg_cfg_dir(bool nofail)
diff --git a/minecctl/rcon-commands.c b/minecctl/rcon-commands.c
index 44bb286..cb6ea45 100644
--- a/minecctl/rcon-commands.c
+++ b/minecctl/rcon-commands.c
@@ -21,18 +21,13 @@ static void send_packet(int sfd, const char *buf, size_t len)
size_t off = 0;
ssize_t r;
- while (true) {
+ for (off = 0; off < len; off += r) {
r = write(sfd, buf + off, len - off);
if (r < 0) {
- if (errno == EINTR)
+ if (errno == EAGAIN || errno == EINTR)
continue;
- else
- die("Failed to write packet: %m");
+ die("Failed to write packet: %m");
}
-
- off += r;
- if (off == len)
- break;
}
}
@@ -40,31 +35,26 @@ static void send_packet(int sfd, const char *buf, size_t len)
static void read_packet(int sfd, char *buf, size_t len, int32_t *id,
int32_t *type, const char **msg)
{
- size_t off = 0;
+ size_t off;
ssize_t r;
const char *error;
- while (true) {
+ for (off = 0; off < len; off += r) {
+ if (rcon_protocol_packet_complete(buf, off))
+ break;
+
r = read(sfd, buf + off, len - off);
if (r < 0) {
if (errno == EINTR)
continue;
- else
- die("Failed to read reply: %m");
- }
-
- if (r == 0)
+ die("Failed to read reply: %m");
+ } else if (r == 0)
die("Failed, connection closed");
-
- off += r;
- if (rcon_protocol_packet_complete(buf, off))
- break;
-
- if (off >= len)
- die("Reply too large %zu and %zu", off, len);
}
- if (!rcon_protocol_read_packet(buf, off, id, type, msg, &error))
+ if (off >= len)
+ die("Reply too large %zu and %zu", off, len);
+ else if (!rcon_protocol_read_packet(buf, off, id, type, msg, &error))
die("Failed to parse response: %s", error);
}
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");
diff --git a/minecproxy/main.c b/minecproxy/main.c
index c51b01c..d8f33d0 100644
--- a/minecproxy/main.c
+++ b/minecproxy/main.c
@@ -311,13 +311,15 @@ static void cfg_read()
int key;
const char *keyname;
struct cfg_value value;
+ const char *error;
if (!config_parse_line(path, &pos, mcfg_key_map, &key, &keyname,
- &value, false, &lineno))
+ &value, false, &lineno, &error))
break;
if (key == MCFG_KEY_INVALID)
- die("main config file (%s) invalid", path);
+ die("main config file (%s) invalid: line %u: %s", path,
+ lineno, error);
debug(DBG_CFG, "main cfg: key %s", keyname);
diff --git a/shared/config-parser.c b/shared/config-parser.c
index f249cda..ea800ad 100644
--- a/shared/config-parser.c
+++ b/shared/config-parser.c
@@ -148,7 +148,7 @@ bool scfg_async_dns_start(struct server_config *scfg)
}
static void scfg_queue_dns(struct server_config *scfg, struct cfg_value *value,
- struct list_head *target_list)
+ struct list_head *target_list, bool any_ok)
{
struct saddr *saddr, *tmp;
struct dns_async *dns;
@@ -158,6 +158,8 @@ static void scfg_queue_dns(struct server_config *scfg, struct cfg_value *value,
debug(DBG_DNS, "got sync results");
list_for_each_entry_safe(saddr, tmp, &value->saddrs, list) {
list_del(&saddr->list);
+ if (!any_ok)
+ saddr_any_to_loopback(saddr);
list_add(&saddr->list, target_list);
}
break;
@@ -244,8 +246,11 @@ bool scfg_validate(struct server_config *scfg, const char **error)
if (list_empty(&scfg->locals))
ERROR("missing local addresses for proxy server");
+ /* FIXME: Reinstate once server.properties parsing is implemented */
+ /*
if (list_empty(&scfg->remotes))
ERROR("missing remote addresses for proxy server");
+ */
list_for_each_entry(saddr, &scfg->locals, list) {
port = saddr_port(saddr);
@@ -323,12 +328,10 @@ static char *systemd_object_path(const char *service)
bool scfg_parse(struct server_config *scfg, char *buf, bool async,
unsigned *lineno, const char **error)
{
- char *pos;
+ char *pos = buf;
assert_return(scfg && buf && lineno && error, false);
- pos = buf;
-
*lineno = 0;
if (!config_parse_header(SERVER_CFG_HEADER, &pos, lineno))
@@ -340,11 +343,11 @@ bool scfg_parse(struct server_config *scfg, char *buf, bool async,
struct cfg_value value;
if (!config_parse_line(scfg->filename, &pos, scfg_key_map, &key,
- &keyname, &value, async, lineno))
+ &keyname, &value, async, lineno, error))
break;
if (key == SCFG_KEY_INVALID)
- ERROR("invalid line in config file");
+ return false;
debug(DBG_CFG, "%s: key %s", scfg->filename, keyname);
@@ -390,11 +393,11 @@ bool scfg_parse(struct server_config *scfg, char *buf, bool async,
break;
case SCFG_KEY_LOCAL:
- scfg_queue_dns(scfg, &value, &scfg->locals);
+ scfg_queue_dns(scfg, &value, &scfg->locals, true);
break;
case SCFG_KEY_REMOTE:
- scfg_queue_dns(scfg, &value, &scfg->remotes);
+ scfg_queue_dns(scfg, &value, &scfg->remotes, false);
break;
case SCFG_KEY_IDLE_TIMEOUT:
@@ -443,7 +446,7 @@ bool scfg_parse(struct server_config *scfg, char *buf, bool async,
break;
case SCFG_KEY_RCON:
- scfg_queue_dns(scfg, &value, &scfg->rcons);
+ scfg_queue_dns(scfg, &value, &scfg->rcons, false);
break;
case SCFG_KEY_RCON_PASSWORD:
@@ -742,7 +745,7 @@ bool strtosockaddrs(const char *str, struct cfg_value *rvalue, bool async)
if (!saddr)
goto error;
- saddr_set_ipv4(saddr, INADDR_ANY, htons(port));
+ saddr_set_ipv4(saddr, htonl(INADDR_ANY), htons(port));
list_add(&saddr->list, list);
naddrs++;
@@ -776,7 +779,7 @@ bool strtosockaddrs(const char *str, struct cfg_value *rvalue, bool async)
if (!cfg_dns_lookup(str, port, rvalue, &naddrs, async))
goto error;
- } else if (strtou16_strict(tmp, &port) == 0) {
+ } else if (strtou16_strict(str, &port) == 0) {
/* Port */
debug(DBG_CFG, "attempting to parse a port number (%s)", str);
@@ -792,12 +795,13 @@ bool strtosockaddrs(const char *str, struct cfg_value *rvalue, bool async)
if (!saddr)
goto error;
- saddr_set_ipv4(saddr, INADDR_ANY, htons(port));
+ saddr_set_ipv4(saddr, htonl(INADDR_ANY), htons(port));
list_add(&saddr->list, list);
naddrs++;
} else {
/* Unknown */
+ debug(DBG_CFG, "cannot parse str (%s)", str);
goto error;
}
@@ -838,15 +842,16 @@ error:
bool config_parse_line(const char *filename, char **buf,
struct cfg_key_value_map *kvmap, int *rkey,
const char **rkeyname, struct cfg_value *rvalue,
- bool async_dns, unsigned *lineno)
+ bool async_dns, unsigned *lineno, const char **error)
{
char *line, *pos, *key, *value;
size_t linelen;
int i;
- assert_return(buf && *buf && kvmap && rkey && rkeyname && rvalue && lineno,
- false);
+ assert_return(buf && *buf && kvmap && rkey && rkeyname && rvalue &&
+ lineno && error, false);
+ *error = NULL;
line = get_line(buf, lineno);
if (!line)
return false;
@@ -902,8 +907,10 @@ bool config_parse_line(const char *filename, char **buf,
_fallthrough_;
case CFG_VAL_TYPE_ADDRS:
- if (!strtosockaddrs(value, rvalue, async_dns))
+ if (!strtosockaddrs(value, rvalue, async_dns)) {
+ *error = "Failed to parse address/port line";
goto error;
+ }
switch (rvalue->type) {
case CFG_VAL_TYPE_ADDRS:
@@ -940,7 +947,7 @@ bool config_parse_line(const char *filename, char **buf,
rvalue->type = CFG_VAL_TYPE_BOOL;
rvalue->boolean = false;
} else {
- error("invalid boolean value (%s)", value);
+ *error = "invalid boolean value";
goto error;
}
break;
@@ -965,7 +972,8 @@ bool config_parse_line(const char *filename, char **buf,
return true;
error:
- error("%s: invalid config line: %u:%s", filename, *lineno, line);
+ if (!*error)
+ *error = "invalid line";
rvalue->type = CFG_VAL_TYPE_INVALID;
*rkey = 0;
*rkeyname = NULL;
diff --git a/shared/config-parser.h b/shared/config-parser.h
index 7d0595d..129e085 100644
--- a/shared/config-parser.h
+++ b/shared/config-parser.h
@@ -111,7 +111,7 @@ bool strtosockaddrs(const char *str, struct cfg_value *rvalue, bool async);
bool config_parse_line(const char *filename, char **buf,
struct cfg_key_value_map *kvmap, int *rkey,
const char **rkeyname, struct cfg_value *rvalue,
- bool async_dns, unsigned *lineno);
+ bool async_dns, unsigned *lineno, const char **error);
bool config_parse_header(const char *title, char **buf, unsigned *lineno);
diff --git a/shared/utils.c b/shared/utils.c
index 6474e30..9bf8cd8 100644
--- a/shared/utils.c
+++ b/shared/utils.c
@@ -141,6 +141,28 @@ void saddr_set_ipv6(struct saddr *saddr, const struct in6_addr *ip,
saddr_set_addrstr(saddr);
}
+void saddr_any_to_loopback(struct saddr *saddr)
+{
+ switch (saddr->st.ss_family) {
+ case AF_INET:
+ if (saddr->in4.sin_addr.s_addr != htonl(INADDR_ANY))
+ return;
+ saddr->in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ break;
+
+ case AF_INET6:
+ if (memcmp(&saddr->in6.sin6_addr, &in6addr_any,
+ sizeof(in6addr_any)))
+ return;
+ saddr->in6.sin6_addr = in6addr_loopback;
+ break;
+
+ default:
+ return;
+ }
+ saddr_set_addrstr(saddr);
+}
+
void saddr_set_addrstr(struct saddr *saddr)
{
// assert_return(saddr);
diff --git a/shared/utils.h b/shared/utils.h
index c47f9a5..bf1b63a 100644
--- a/shared/utils.h
+++ b/shared/utils.h
@@ -83,6 +83,8 @@ void saddr_set_ipv4(struct saddr *saddr, in_addr_t ip, in_port_t port);
void saddr_set_ipv6(struct saddr *saddr, const struct in6_addr *ip,
in_port_t port);
+void saddr_any_to_loopback(struct saddr *saddr);
+
void saddr_set_addrstr(struct saddr *saddr);
int strtou16_strict(const char *str, uint16_t *result);