summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-30 08:10:04 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-30 08:10:04 +0200
commita89a0f918925a662503c1bcb28bdb06ab9b7ef25 (patch)
tree733b4c1ff841f99eeb3840c5948fef6c5bf76109
parent8f29a4d23dd13a80aa26951b17e2a71f4e651cb1 (diff)
Share config parsing fully between server and cmdline tool
-rw-r--r--config.h.in6
-rw-r--r--minecctl/mc-commands.c4
-rw-r--r--minecctl/minecctl.c16
-rw-r--r--minecctl/misc-commands.c4
-rw-r--r--minecctl/rcon-commands.c14
-rw-r--r--minecctl/server.c87
-rw-r--r--minecctl/server.h7
-rw-r--r--minecproxy/server-config.c268
-rw-r--r--minecproxy/server-proxy.c10
-rw-r--r--minecproxy/server-rcon.c9
-rw-r--r--minecproxy/server.c388
-rw-r--r--minecproxy/server.h73
-rw-r--r--minecproxy/systemd.c76
-rw-r--r--minecproxy/systemd.h2
-rw-r--r--shared/config-parser.c449
-rw-r--r--shared/config-parser.h52
16 files changed, 702 insertions, 763 deletions
diff --git a/config.h.in b/config.h.in
index bf3bd99..0a93079 100644
--- a/config.h.in
+++ b/config.h.in
@@ -26,3 +26,9 @@
#define SERVER_CONFIG_FILE_SUFFIX "mcserver"
+#define SYSTEMD_DBUS_SERVICE "org.freedesktop.systemd1"
+
+#define SYSTEMD_DBUS_INTERFACE "org.freedesktop.systemd1.Unit"
+
+#define SYSTEMD_DBUS_PATH_PREFIX "/org/freedesktop/systemd1/unit/"
+
diff --git a/minecctl/mc-commands.c b/minecctl/mc-commands.c
index d4d2846..424eae9 100644
--- a/minecctl/mc-commands.c
+++ b/minecctl/mc-commands.c
@@ -19,14 +19,14 @@ bool do_mc_pcount(struct cfg *cfg, unsigned *online, unsigned *max)
server = server_get_default(cfg);
- fd = connect_any(&server->mc_addrs, true);
+ fd = connect_any(&server->scfg.remotes, true);
if (fd < 0) {
error("%s: unable to connect", server->name);
return false;
}
/* FIXME: connect_any needs to indicate the address it used */
- saddr = list_first_entry(&server->mc_addrs, struct saddr, list);
+ saddr = list_first_entry(&server->scfg.remotes, struct saddr, list);
if (!saddr) {
error("No saddr");
goto out;
diff --git a/minecctl/minecctl.c b/minecctl/minecctl.c
index 3d21ae6..231b150 100644
--- a/minecctl/minecctl.c
+++ b/minecctl/minecctl.c
@@ -44,13 +44,13 @@ static void dump_config(struct cfg *cfg)
list_for_each_entry(server, &cfg->servers, list) {
info(" * server");
info(" name : %s", server->name);
- info(" filename : %s", server->filename);
- info(" rcon_password : %s", server->rcon_password);
+ info(" filename : %s", server->scfg.filename);
+ info(" rcon_password : %s", server->scfg.rcon_password);
info(" file_read : %s",
server->file_read ? "yes" : "no");
- list_for_each_entry(saddr, &server->rcon_addrs, list)
+ list_for_each_entry(saddr, &server->scfg.rcons, list)
info(" * rcon addr : %s", saddr->addrstr);
- list_for_each_entry(saddr, &server->mc_addrs, list)
+ list_for_each_entry(saddr, &server->scfg.remotes, list)
info(" * mc addr : %s", saddr->addrstr);
}
@@ -133,10 +133,10 @@ static bool create_server_from_cmdline_args(struct cfg *cfg)
if (!cfg->rcon_addrstr && !cfg->mc_addrstr)
return false;
- server = server_new();
+ server = server_new(NULL);
if (cfg->rcon_addrstr) {
- if (!str_to_addrs(cfg->rcon_addrstr, &server->rcon_addrs))
+ if (!str_to_addrs(cfg->rcon_addrstr, &server->scfg.rcons))
goto error;
server->name = cfg->rcon_addrstr;
@@ -144,7 +144,7 @@ static bool create_server_from_cmdline_args(struct cfg *cfg)
}
if (cfg->mc_addrstr) {
- if (!str_to_addrs(cfg->mc_addrstr, &server->mc_addrs))
+ if (!str_to_addrs(cfg->mc_addrstr, &server->scfg.remotes))
goto error;
if (!server->name)
@@ -156,7 +156,7 @@ static bool create_server_from_cmdline_args(struct cfg *cfg)
}
if (cfg->rcon_password) {
- server->rcon_password = cfg->rcon_password;
+ server->scfg.rcon_password = cfg->rcon_password;
cfg->rcon_password = NULL;
}
diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c
index 52797da..f20cac5 100644
--- a/minecctl/misc-commands.c
+++ b/minecctl/misc-commands.c
@@ -9,9 +9,9 @@ bool do_list(struct cfg *cfg)
{
struct server *server;
- /* server->filename check excludes servers created from cmdline */
+ /* server->scfg.filename check excludes servers created from cmdline */
list_for_each_entry(server, &cfg->servers, list)
- if (server->filename)
+ if (server->scfg.filename)
info("%s", server->name);
return true;
diff --git a/minecctl/rcon-commands.c b/minecctl/rcon-commands.c
index 5f98b72..2f1687b 100644
--- a/minecctl/rcon-commands.c
+++ b/minecctl/rcon-commands.c
@@ -110,30 +110,30 @@ static int rcon_login(struct cfg *cfg, struct server *server)
assert_die(cfg && server, "invalid arguments");
- if (list_empty(&server->rcon_addrs)) {
+ if (list_empty(&server->scfg.rcons)) {
error("%s: rcon address unknown", server->name);
goto error;
}
- fd = connect_any(&server->rcon_addrs, true);
+ fd = connect_any(&server->scfg.rcons, true);
if (fd < 0) {
error("%s: unable to connect", server->name);
goto error;
}
- if (!server->rcon_password)
- server->rcon_password = ask_password();
+ if (!server->scfg.rcon_password)
+ server->scfg.rcon_password = ask_password();
- if (!server->rcon_password) {
+ if (!server->scfg.rcon_password) {
error("%s: can't login - password missing", server->name);
goto error;
}
- send_msg(fd, buf, sizeof(buf), RCON_PACKET_LOGIN, server->rcon_password,
+ send_msg(fd, buf, sizeof(buf), RCON_PACKET_LOGIN, server->scfg.rcon_password,
&rtype, &reply);
explicit_bzero(buf, sizeof(buf));
- free_password(&server->rcon_password);
+ free_password(&server->scfg.rcon_password);
if (rtype == RCON_PACKET_LOGIN_OK)
verbose("%s: login ok", server->name);
diff --git a/minecctl/server.c b/minecctl/server.c
index aca69a0..21df7f4 100644
--- a/minecctl/server.c
+++ b/minecctl/server.c
@@ -7,8 +7,6 @@
#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)
{
@@ -17,9 +15,8 @@ void server_read_config(struct cfg *cfg, struct server *server)
ssize_t r;
int dfd;
int fd;
- char *pos = buf;
- if (!server || !server->filename || server->file_read)
+ if (!server || !server->scfg.filename || server->file_read)
return;
server->file_read = true;
@@ -28,73 +25,39 @@ void server_read_config(struct cfg *cfg, struct server *server)
if (dfd < 0)
die("Failed to open %s: %m", cfg->cfgdir);
- fd = openat(dfd, server->filename, O_RDONLY | O_CLOEXEC);
+ fd = openat(dfd, server->scfg.filename, O_RDONLY | O_CLOEXEC);
if (fd < 0)
- die("Failed to open %s: %m", server->filename);
+ die("Failed to open %s: %m", server->scfg.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);
+ die("Failed to read %s: %m", server->scfg.filename);
else if (r == 0)
break;
off += r;
if (off == sizeof(buf) - 1)
die("Failed to read %s: file too large",
- server->filename);
+ server->scfg.filename);
}
buf[off] = '\0';
close(fd);
- if (!config_parse_header(SERVER_CFG_HEADER, &pos))
- die("Unable to parse %s: invalid/missing header",
- server->filename);
+ if (!scfg_parse(&server->scfg, buf, NULL))
+ die("Unable to parse %s", server->scfg.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 (!server->scfg.rcon_password)
+ verbose("rcon password not found in %s", server->scfg.filename);
- if (list_empty(&server->rcon_addrs))
- verbose("rcon address not found in %s", server->filename);
+ if (list_empty(&server->scfg.rcons))
+ verbose("rcon address not found in %s", server->scfg.filename);
- if (list_empty(&server->mc_addrs))
- verbose("mc server address not found in %s", server->filename);
+ if (list_empty(&server->scfg.remotes))
+ verbose("mc server address not found in %s", server->scfg.filename);
}
struct server *server_get_default(struct cfg *cfg)
@@ -144,8 +107,7 @@ void server_load_all_known(struct cfg *cfg)
if (!is_valid_server_config_filename(dent, NULL))
continue;
- server = server_new();
- server->filename = xstrdup(dent->d_name);
+ server = server_new(dent->d_name);
suffix = strrchr(dent->d_name, '.');
assert_die(suffix, "Error parsing filename");
@@ -168,32 +130,17 @@ void server_free_all(struct cfg *cfg)
void server_free(struct server *server)
{
- struct saddr *saddr, *tmp;
-
+ scfg_delete(&server->scfg);
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_new(const char *filename)
{
struct server *server;
server = zmalloc(sizeof(*server));
- INIT_LIST_HEAD(&server->rcon_addrs);
- INIT_LIST_HEAD(&server->mc_addrs);
+ scfg_init(&server->scfg, filename);
INIT_LIST_HEAD(&server->list);
return server;
diff --git a/minecctl/server.h b/minecctl/server.h
index 89ee44c..a0c0a76 100644
--- a/minecctl/server.h
+++ b/minecctl/server.h
@@ -3,13 +3,12 @@
#include <stdbool.h>
+#include "config-parser.h"
+
struct server {
bool file_read;
char *name;
- char *filename;
- char *rcon_password;
- struct list_head rcon_addrs;
- struct list_head mc_addrs;
+ struct server_config scfg;
struct list_head list;
};
diff --git a/minecproxy/server-config.c b/minecproxy/server-config.c
index 71463ee..6fdd73a 100644
--- a/minecproxy/server-config.c
+++ b/minecproxy/server-config.c
@@ -14,262 +14,7 @@
#include "config-parser.h"
#include "server.h"
#include "server-config.h"
-#include "server-config-options.h"
-
-static void scfg_dns_cb(struct dns_async *dns,
- bool (*server_cb)(struct server *, struct saddr *))
-{
- struct server *server;
- struct sockaddr_in *in4;
- struct sockaddr_in6 *in6;
- struct saddr *saddr;
- struct addrinfo *results = NULL, *ai;
- int r;
-
- assert_return(dns && dns->priv && server_cb);
-
- server = dns->priv;
- debug(DBG_DNS,
- "called, dns: %p, name: %s, server: %p, server->name: %s", dns,
- dns->name, server, server->name);
-
- r = gai_error(&dns->gcb);
- if (r == EAI_INPROGRESS) {
- /* This shouldn't happen, assume we'll get called again */
- error("called with request in progress");
- return;
- } else if (r == EAI_CANCELED) {
- /* The server must be in the process of going away */
- goto out;
- } else if (r < 0) {
- error("DNS lookup of %s:%s failed: %s", dns->name, dns->port,
- gai_strerror(r));
- goto out;
- }
-
- results = dns->gcb.ar_result;
-
- for (ai = results; ai; ai = ai->ai_next) {
- saddr = zmalloc(sizeof(*saddr));
- if (!saddr) {
- error("DNS lookup of %s:%s failed: %m", dns->name,
- dns->port);
- goto out;
- }
-
- switch (ai->ai_family) {
- case AF_INET:
- in4 = (struct sockaddr_in *)ai->ai_addr;
- saddr_set_ipv4(saddr, in4->sin_addr.s_addr,
- in4->sin_port);
- server_cb(server, saddr);
- break;
-
- case AF_INET6:
- in6 = (struct sockaddr_in6 *)ai->ai_addr;
- saddr_set_ipv6(saddr, &in6->sin6_addr, in6->sin6_port);
- server_cb(server, saddr);
- break;
-
- default:
- error("getaddrinfo(%s:%s): unknown address family (%i)",
- dns->name, dns->port, ai->ai_family);
- xfree(saddr);
- break;
- }
- }
-
-out:
- freeaddrinfo(results);
- list_del(&dns->list);
- xfree(dns);
- uring_task_put(&server->task);
- server_commit(server);
-}
-
-static void scfg_local_dns_cb(struct dns_async *dns)
-{
- assert_return(dns);
-
- scfg_dns_cb(dns, server_add_local);
-}
-
-static void scfg_remote_dns_cb(struct dns_async *dns)
-{
- assert_return(dns);
-
- scfg_dns_cb(dns, server_add_remote);
-}
-
-static void scfg_rcon_dns_cb(struct dns_async *dns)
-{
- assert_return(dns);
-
- scfg_dns_cb(dns, server_add_rcon);
-}
-
-static bool handle_dns(struct server *server, const char *type,
- struct cfg_value *value, dns_cb_t *async_cb,
- bool (*sync_cb)(struct server *, struct saddr *))
-{
- struct saddr *saddr, *tmp;
- struct dns_async *dns;
-
- assert_return(server && type && value && async_cb && sync_cb, false);
-
- switch (value->type) {
- case CFG_VAL_TYPE_ADDRS:
- debug(DBG_DNS, "%s: got immediate addrs", type);
-
- list_for_each_entry_safe(saddr, tmp, &value->saddrs, list) {
- list_del(&saddr->list);
- sync_cb(server, saddr);
- }
- return true;
-
- case CFG_VAL_TYPE_ASYNC_ADDRS:
- debug(DBG_DNS, "%s: doing async lookup of DNS record: %p", type,
- value->dns_async);
-
- dns = value->dns_async;
- dns->cb = async_cb;
- dns->priv = server;
- list_add(&dns->list, &server->dnslookups);
- uring_task_get(&server->task);
- return true;
-
- default:
- return false;
- }
-}
-
-static void scfg_parse(struct server *server)
-{
- char *pos;
-
- assert_return(server);
-
- pos = server->tbuf.buf;
-
- if (!config_parse_header(SERVER_CFG_HEADER, &pos)) {
- verbose("%s: missing/invalid header", server->name);
- return;
- }
-
- while (true) {
- int key;
- const char *keyname;
- struct cfg_value value;
-
- if (!config_parse_line(server->name, &pos, scfg_key_map, &key,
- &keyname, &value, true))
- break;
-
- if (key == SCFG_KEY_INVALID)
- break;
-
- debug(DBG_CFG, "%s: key %s", server->name, keyname);
-
- switch (key) {
- case SCFG_KEY_TYPE:
- if (streq(value.str, "proxy")) {
- if (!server_set_type(server, SERVER_TYPE_PROXY))
- return;
- } else if (streq(value.str, "announce")) {
- if (!server_set_type(server,
- SERVER_TYPE_ANNOUNCE))
- return;
- }
- break;
-
- case SCFG_KEY_NAME:
- if (!server_set_pretty_name(server, value.str))
- return;
- break;
-
- case SCFG_KEY_PORT:
- if (!server_set_port(server, value.uint16))
- return;
- break;
-
- case SCFG_KEY_LOCAL:
- if (!handle_dns(server, "local", &value,
- scfg_local_dns_cb, server_add_local))
- return;
- break;
-
- case SCFG_KEY_REMOTE:
- if (!handle_dns(server, "remote", &value,
- scfg_remote_dns_cb, server_add_remote))
- return;
- break;
-
- case SCFG_KEY_IDLE_TIMEOUT:
- if (!server_set_idle_timeout(server, value.uint16))
- return;
- break;
-
- case SCFG_KEY_STOP_METHOD:
- if (streq(value.str, "exec")) {
- if (server_set_stop_method(
- server, SERVER_STOP_METHOD_EXEC))
- break;
- } else if (streq(value.str, "rcon")) {
- if (server_set_stop_method(
- server, SERVER_STOP_METHOD_RCON))
- break;
- } else if (streq(value.str, "systemd")) {
- if (server_set_stop_method(
- server, SERVER_STOP_METHOD_SYSTEMD))
- break;
- }
- return;
-
- case SCFG_KEY_START_METHOD:
- if (streq(value.str, "exec")) {
- if (server_set_start_method(
- server, SERVER_START_METHOD_EXEC))
- break;
- } else if (streq(value.str, "systemd")) {
- if (server_set_start_method(
- server,
- SERVER_START_METHOD_SYSTEMD))
- break;
- }
- return;
-
- case SCFG_KEY_STOP_EXEC:
- if (!server_set_stop_exec(server, value.str))
- return;
- break;
-
- case SCFG_KEY_START_EXEC:
- if (!server_set_start_exec(server, value.str))
- return;
- break;
-
- case SCFG_KEY_RCON:
- if (!handle_dns(server, "rcon", &value,
- scfg_rcon_dns_cb, server_add_rcon))
- return;
- break;
-
- case SCFG_KEY_RCON_PASSWORD:
- if (!server_set_rcon_password(server, value.str))
- return;
- break;
-
- case SCFG_KEY_SYSTEMD_SERVICE:
- if (!server_set_systemd_service(server, value.str))
- return;
- break;
-
- case SCFG_KEY_INVALID:
- default:
- break;
- }
- }
-}
+#include "config.h"
static void scfg_read_cb(struct uring_task *task, int res)
{
@@ -286,7 +31,14 @@ static void scfg_read_cb(struct uring_task *task, int res)
debug(DBG_CFG, "%s: parsing cfg (%i bytes)", server->name, res);
uring_task_close_fd(&server->task);
- scfg_parse(server);
+
+ if (!scfg_parse(&server->scfg, server->tbuf.buf,
+ server_async_dns_update)) {
+ error("%s: failed to parse config file", server->name);
+ server_delete(server);
+ return;
+ }
+
server_commit(server);
}
@@ -403,7 +155,7 @@ static void inotify_cb(struct uring_task *task, int res)
continue;
if (event->mask & (IN_MOVED_FROM | IN_DELETE))
- server_delete_by_name(event->name);
+ server_delete_by_filename(event->name);
else if (event->mask &
(IN_MOVED_TO | IN_CREATE | IN_CLOSE_WRITE)) {
server = server_new(event->name);
diff --git a/minecproxy/server-proxy.c b/minecproxy/server-proxy.c
index b80a220..787934d 100644
--- a/minecproxy/server-proxy.c
+++ b/minecproxy/server-proxy.c
@@ -351,7 +351,7 @@ static void proxy_connect_timer_cb(struct ptimer_task *ptask)
return;
proxy->connecting = true;
- connect_any(&proxy->servertask, &proxy->server->remotes,
+ connect_any(&proxy->servertask, &proxy->server->scfg.remotes,
&proxy->server_conn, proxy_connected_cb);
}
@@ -412,8 +412,8 @@ struct server_proxy *proxy_new(struct server *server, struct saddr *client,
}
proxy->connecting = true;
- connect_any(&proxy->servertask, &server->remotes, &proxy->server_conn,
- proxy_connected_cb);
+ connect_any(&proxy->servertask, &server->scfg.remotes,
+ &proxy->server_conn, proxy_connected_cb);
return proxy;
@@ -448,9 +448,9 @@ static void local_accept(struct uring_task *task, int res)
verbose("%s: incoming proxy connection: %s -> %s", server->name,
local->client.addrstr, local->local.addrstr);
- if (list_empty(&server->remotes)) {
+ if (list_empty(&server->scfg.remotes)) {
/* This shouldn't be possible, checked before opening local */
- error("server->remotes empty!");
+ error("server->scfg.remotes empty!");
uring_close(&local->task, res);
goto out;
}
diff --git a/minecproxy/server-rcon.c b/minecproxy/server-rcon.c
index 9562aa9..9ae49ef 100644
--- a/minecproxy/server-rcon.c
+++ b/minecproxy/server-rcon.c
@@ -168,7 +168,8 @@ static void rcon_connected_cb(struct connection *conn, bool connected)
rcon_protocol_create_packet(server->rcon_tbuf.buf,
sizeof(server->rcon_tbuf.buf),
&server->rcon_tbuf.len, 1,
- RCON_PACKET_LOGIN, server->rcon_password);
+ RCON_PACKET_LOGIN,
+ server->scfg.rcon_password);
uring_tbuf_write(&server->rcon_task, rcon_login_sent);
}
@@ -183,11 +184,11 @@ static void rcon_free(struct uring_task *task)
void rcon_stop(struct server *server)
{
- assert_return(server && !list_empty(&server->rcons) &&
- !empty_str(server->rcon_password));
+ assert_return(server && !list_empty(&server->scfg.rcons) &&
+ !empty_str(server->scfg.rcon_password));
assert_task_alive(DBG_RCON, &server->rcon_task);
- connect_any(&server->rcon_task, &server->rcons, &server->rcon_conn,
+ connect_any(&server->rcon_task, &server->scfg.rcons, &server->rcon_conn,
rcon_connected_cb);
}
diff --git a/minecproxy/server.c b/minecproxy/server.c
index 8c521cf..81d148b 100644
--- a/minecproxy/server.c
+++ b/minecproxy/server.c
@@ -14,26 +14,12 @@
#include "uring.h"
#include "ptimer.h"
#include "server.h"
+#include "server-config.h"
#include "server-proxy.h"
#include "server-rcon.h"
-#include "config-parser.h"
#include "idle.h"
#include "systemd.h"
-static bool set_property(struct server *server, char **property,
- const char *value)
-{
- assert_return(server && !*property && !empty_str(value), false);
-
- *property = xstrdup(value);
- if (!*property) {
- error("strdup: %m");
- return false;
- }
-
- return true;
-}
-
void server_refdump(struct server *server)
{
struct server_local *local;
@@ -45,7 +31,7 @@ void server_refdump(struct server *server)
uring_task_refdump(&server->exec_task);
uring_task_refdump(&server->ann_task);
uring_task_refdump(&server->idle_task);
- list_for_each_entry(local, &server->locals, list)
+ list_for_each_entry(local, &server->listenings, list)
local_refdump(local);
list_for_each_entry(proxy, &server->proxys, list)
proxy_refdump(proxy);
@@ -60,12 +46,6 @@ static void server_free(struct uring_task *task)
debug(DBG_SRV, "freeing server %s (%p)", server->name, server);
list_del(&server->list);
- xfree(server->pretty_name);
- xfree(server->start_exec);
- xfree(server->stop_exec);
- xfree(server->systemd_service);
- xfree(server->systemd_obj);
- xfree(server->rcon_password);
xfree(server->name);
xfree(server);
}
@@ -74,10 +54,6 @@ void server_delete(struct server *server)
{
struct server_local *local, *ltmp;
struct server_proxy *proxy, *ptmp;
- struct saddr *remote;
- struct saddr *rcon;
- struct saddr *tmp;
- struct dns_async *dns, *dtmp;
assert_return(server);
@@ -86,40 +62,28 @@ void server_delete(struct server *server)
rcon_delete(server);
- list_for_each_entry_safe(local, ltmp, &server->locals, list)
+ list_for_each_entry_safe(local, ltmp, &server->listenings, list)
local_delete(local);
list_for_each_entry_safe(proxy, ptmp, &server->proxys, list)
proxy_delete(proxy);
- list_for_each_entry_safe(rcon, tmp, &server->rcons, list) {
- list_del(&rcon->list);
- xfree(rcon);
- }
-
- list_for_each_entry_safe(remote, tmp, &server->remotes, list) {
- list_del(&remote->list);
- xfree(remote);
- }
-
- list_for_each_entry_safe(dns, dtmp, &server->dnslookups, list)
- gai_cancel(&dns->gcb);
-
uring_task_destroy(&server->idle_task);
uring_poll_cancel(&server->exec_task);
uring_task_put(&server->exec_task);
uring_task_destroy(&server->task);
uring_task_put(&server->ann_task);
+ scfg_delete(&server->scfg);
}
-void server_delete_by_name(const char *name)
+void server_delete_by_filename(const char *filename)
{
struct server *server;
- assert_return(!empty_str(name));
+ assert_return(!empty_str(filename));
list_for_each_entry(server, &cfg->servers, list) {
- if (streq(server->name, name)) {
+ if (streq(server->scfg.filename, filename)) {
server_delete(server);
return;
}
@@ -135,7 +99,7 @@ static void server_dump(struct server *server)
assert_return(server);
verbose("Server %s:", server->name);
- switch (server->type) {
+ switch (server->scfg.type) {
case SERVER_TYPE_ANNOUNCE:
verbose(" * Type: announce");
break;
@@ -147,24 +111,24 @@ static void server_dump(struct server *server)
break;
}
verbose(" * Name: %s",
- server->pretty_name ? server->pretty_name : "<undefined>");
- verbose(" * Announce port: %" PRIu16, server->announce_port);
+ server->scfg.pretty_name ? server->scfg.pretty_name : "<undefined>");
+ verbose(" * Announce port: %" PRIu16, server->scfg.announce_port);
- if (!list_empty(&server->locals)) {
+ if (!list_empty(&server->listenings)) {
verbose(" * Local:");
- list_for_each_entry(local, &server->locals, list)
+ list_for_each_entry(local, &server->listenings, list)
verbose(" * %s", local->local.addrstr);
}
- if (!list_empty(&server->remotes)) {
+ if (!list_empty(&server->scfg.remotes)) {
verbose(" * Remote:");
- list_for_each_entry(remote, &server->remotes, list)
+ list_for_each_entry(remote, &server->scfg.remotes, list)
verbose(" * %s", remote->addrstr);
}
- if (!list_empty(&server->rcons)) {
+ if (!list_empty(&server->scfg.rcons)) {
verbose(" * RCon:");
- list_for_each_entry(rcon, &server->rcons, list)
+ list_for_each_entry(rcon, &server->scfg.rcons, list)
verbose(" * %s", rcon->addrstr);
}
@@ -253,7 +217,7 @@ static bool server_check_running(struct server *server)
assert_return(server, false);
/* FIXME: other methods, rcon? */
- if (server->systemd_service) {
+ if (server->scfg.systemd_service) {
verbose("%s: checking if systemd service is running",
server->name);
if (systemd_service_running(server)) {
@@ -273,14 +237,16 @@ bool server_start(struct server *server)
assert_return(server, false);
assert_task_alive_or(DBG_SRV, &server->task, return false);
- switch (server->start_method) {
+ switch (server->scfg.start_method) {
case SERVER_START_METHOD_EXEC:
- verbose("Starting server %s via external cmd", server->name);
- return server_exec(server, server->start_exec);
+ verbose("Starting server %s via external cmd",
+ server->name);
+ return server_exec(server, server->scfg.start_exec);
case SERVER_START_METHOD_SYSTEMD:
- verbose("Starting server %s via systemd (%s)", server->name,
- server->systemd_service);
+ verbose("Starting server %s via systemd (%s)",
+ server->name,
+ server->scfg.systemd_service);
if (systemd_service_start(server)) {
server->state = SERVER_STATE_RUNNING;
@@ -301,14 +267,16 @@ bool server_stop(struct server *server)
assert_return(server, false);
assert_task_alive_or(DBG_SRV, &server->task, return false);
- switch (server->stop_method) {
+ switch (server->scfg.stop_method) {
case SERVER_STOP_METHOD_EXEC:
- verbose("Stopping server %s via external cmd", server->name);
- return server_exec(server, server->stop_exec);
+ verbose("Stopping server %s via external cmd",
+ server->name);
+ return server_exec(server, server->scfg.stop_exec);
case SERVER_STOP_METHOD_SYSTEMD:
- verbose("Stopping server %s via systemd (%s)", server->name,
- server->systemd_service);
+ verbose("Stopping server %s via systemd (%s)",
+ server->name,
+ server->scfg.systemd_service);
if (systemd_service_stop(server)) {
server->state = SERVER_STATE_STOPPED;
return true;
@@ -340,7 +308,8 @@ void server_set_active_players(struct server *server, int count)
assert_return(server);
assert_task_alive(DBG_IDLE, &server->idle_task);
- debug(DBG_IDLE, "%s: currently %i active players", server->name, count);
+ debug(DBG_IDLE, "%s: currently %i active players",
+ server->name, count);
if (count < 0)
return;
@@ -351,12 +320,24 @@ void server_set_active_players(struct server *server, int count)
else if (count == 0)
server->idle_count++;
- if (server->idle_count > server->idle_timeout) {
+ if (server->idle_count > server->scfg.idle_timeout) {
verbose("stopping idle server %s", server->name);
server_stop(server);
}
}
+void server_async_dns_update(struct server_config *scfg, bool done)
+{
+ struct server *server = container_of(scfg, struct server, scfg);
+
+ if (done) {
+ uring_task_put(&server->task);
+ server_commit(server);
+ } else {
+ uring_task_get(&server->task);
+ }
+}
+
static void server_idle_connected_cb(struct connection *conn, bool connected)
{
struct server *server = container_of(conn, struct server, idle_conn);
@@ -385,10 +366,10 @@ bool server_idle_check(struct server *server)
server->state == SERVER_STATE_DEAD)
return false;
- if (server->idle_timeout < 1)
+ if (server->scfg.idle_timeout < 1)
return false;
- if (list_empty(&server->remotes))
+ if (list_empty(&server->scfg.remotes))
return false;
if (!list_empty(&server->proxys)) {
@@ -396,8 +377,8 @@ bool server_idle_check(struct server *server)
return true;
}
- connect_any(&server->idle_task, &server->remotes, &server->idle_conn,
- server_idle_connected_cb);
+ connect_any(&server->idle_task, &server->scfg.remotes,
+ &server->idle_conn, server_idle_connected_cb);
return true;
}
@@ -440,7 +421,7 @@ bool server_announce(struct server *server, int fd)
bool server_commit(struct server *server)
{
- struct server_local *local;
+ struct saddr *saddr, *tmp;
uint16_t port;
int r;
@@ -457,70 +438,67 @@ bool server_commit(struct server *server)
return false;
}
- if (!list_empty(&server->dnslookups)) {
+ if (!list_empty(&server->scfg.dnslookups)) {
debug(DBG_SRV, "called with pending DNS requests");
return true;
}
- if (server->stop_method == SERVER_STOP_METHOD_RCON &&
- list_empty(&server->rcons)) {
+ if (server->scfg.stop_method == SERVER_STOP_METHOD_RCON &&
+ list_empty(&server->scfg.rcons)) {
error("%s: rcon stop method missing rcon address",
server->name);
return false;
}
- if (server->stop_method == SERVER_STOP_METHOD_RCON &&
- !server->rcon_password) {
+ if (server->scfg.stop_method == SERVER_STOP_METHOD_RCON &&
+ !server->scfg.rcon_password) {
error("%s: rcon stop method missing rcon password",
server->name);
return false;
}
- if ((server->start_method == SERVER_START_METHOD_SYSTEMD ||
- server->stop_method == SERVER_STOP_METHOD_SYSTEMD) &&
- !server->systemd_service) {
+ if ((server->scfg.start_method == SERVER_START_METHOD_SYSTEMD ||
+ server->scfg.stop_method == SERVER_STOP_METHOD_SYSTEMD) &&
+ !server->scfg.systemd_service) {
error("%s: systemd start/stop method missing systemd service",
server->name);
return false;
}
- if (server->systemd_service && !server->systemd_obj) {
- server->systemd_obj =
- systemd_object_path(server->systemd_service);
- if (!server->systemd_obj) {
- error("%s: failed to create systemd object path (%s)",
- server->name, server->systemd_service);
- return false;
- }
+ if ((server->scfg.systemd_service && !server->scfg.systemd_obj) ||
+ (!server->scfg.systemd_service && server->scfg.systemd_obj)) {
+ error("%s: systemd service but no object path or vice versa",
+ server->name);
+ return false;
}
- if (server->idle_timeout > 0 &&
- server->stop_method == SERVER_STOP_METHOD_UNDEFINED) {
+ if (server->scfg.idle_timeout > 0 &&
+ server->scfg.stop_method == SERVER_STOP_METHOD_UNDEFINED) {
error("%s: idle_timeout set but missing stop method",
server->name);
return false;
}
- switch (server->type) {
+ switch (server->scfg.type) {
case SERVER_TYPE_ANNOUNCE:
- if (server->announce_port < 1) {
+ if (server->scfg.announce_port == 0) {
error("%s: missing announce port", server->name);
return false;
}
- if (server->start_method != SERVER_START_METHOD_UNDEFINED) {
+ if (server->scfg.start_method != SERVER_START_METHOD_UNDEFINED) {
error("%s: can't set start_method for announce server",
server->name);
return false;
}
- if (!list_empty(&server->locals)) {
+ if (!list_empty(&server->scfg.locals)) {
error("%s: can't set local addresses for announce server",
server->name);
return false;
}
- if (!list_empty(&server->remotes)) {
+ if (!list_empty(&server->scfg.remotes)) {
error("%s: can't set remote addresses for announce server",
server->name);
return false;
@@ -529,42 +507,42 @@ bool server_commit(struct server *server)
break;
case SERVER_TYPE_PROXY:
- if (server->announce_port >= 1) {
+ if (server->scfg.announce_port == 0) {
error("%s: can't set announce port for proxy server",
server->name);
return false;
}
- if (list_empty(&server->locals)) {
+ if (list_empty(&server->scfg.locals)) {
error("%s: missing local addresses for proxy server",
server->name);
return false;
}
- if (list_empty(&server->remotes)) {
+ if (list_empty(&server->scfg.remotes)) {
error("%s: missing remote addresses for proxy server",
server->name);
return false;
}
- list_for_each_entry(local, &server->locals, list) {
- port = saddr_port(&local->local);
+ list_for_each_entry(saddr, &server->scfg.locals, list) {
+ port = saddr_port(saddr);
if (port == 0) {
error("%s: invalid local port", server->name);
return false;
}
- if (server->announce_port < 1)
- server->announce_port = port;
+ if (server->scfg.announce_port == 0)
+ server->scfg.announce_port = port;
- if (server->announce_port != port) {
+ if (server->scfg.announce_port != port) {
error("%s: multiple local ports", server->name);
return false;
}
}
- if (server->announce_port < 1) {
+ if (server->scfg.announce_port == 0) {
error("%s: can't determine which port to announce",
server->name);
return false;
@@ -577,7 +555,7 @@ bool server_commit(struct server *server)
return false;
}
- if (!server->pretty_name) {
+ if (!server->scfg.pretty_name) {
char *suffix;
suffix = strrchr(server->name, '.');
@@ -586,9 +564,9 @@ bool server_commit(struct server *server)
return false;
}
- server->pretty_name =
+ server->scfg.pretty_name =
xstrndup(server->name, suffix - server->name);
- if (!server->pretty_name) {
+ if (!server->scfg.pretty_name) {
error("failed to create display name: %s",
server->name);
return false;
@@ -596,8 +574,9 @@ bool server_commit(struct server *server)
}
r = snprintf(server->ann_buf.buf, sizeof(server->ann_buf.buf),
- "[MOTD]%s[/MOTD][AD]%" PRIu16 "[/AD]", server->pretty_name,
- server->announce_port);
+ "[MOTD]%s[/MOTD][AD]%" PRIu16 "[/AD]",
+ server->scfg.pretty_name,
+ server->scfg.announce_port);
if (r < 1 || r >= sizeof(server->ann_buf.buf)) {
error("%s: unable to create announce msg: %i\n", server->name,
r);
@@ -609,8 +588,15 @@ bool server_commit(struct server *server)
* available before this is called */
server_dump(server);
- list_for_each_entry(local, &server->locals, list)
+ list_for_each_entry_safe(saddr, tmp, &server->scfg.locals, list) {
+ struct server_local *local;
+
+ /* FIXME: error checks */
+ list_del(&saddr->list);
+ local = local_new(server, saddr);
local_open(local);
+ list_add(&local->list, &server->listenings);
+ }
server->state = SERVER_STATE_CFG_OK;
@@ -620,177 +606,48 @@ bool server_commit(struct server *server)
return true;
}
-bool server_add_remote(struct server *server, struct saddr *remote)
-{
- assert_return(server && remote, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- debug(DBG_SRV, "adding remote: %s", remote->addrstr);
- list_add(&remote->list, &server->remotes);
- return true;
-}
-
-bool server_add_local(struct server *server, struct saddr *saddr)
-{
- struct server_local *local;
-
- assert_return(server && saddr, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- local = local_new(server, saddr);
- if (!local)
- return false;
-
- list_add(&local->list, &server->locals);
- return true;
-}
-
-bool server_add_rcon(struct server *server, struct saddr *rcon)
-{
- assert_return(server && rcon, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- debug(DBG_SRV, "adding rcon: %s", rcon->addrstr);
- list_add(&rcon->list, &server->rcons);
- return true;
-}
-
-bool server_set_rcon_password(struct server *server, const char *password)
-{
- assert_return(server && !empty_str(password), false);
-
- return set_property(server, &server->rcon_password, password);
-}
-
-bool server_set_systemd_service(struct server *server, const char *service)
-{
- const char *suffix;
- char *tmp;
-
- assert_return(server && !empty_str(service) && !server->systemd_service,
- false);
-
- suffix = strrchr(service, '.');
- if (!suffix || !streq(suffix, ".service")) {
- tmp = zmalloc(strlen(service) + STRLEN(".service") + 1);
- if (tmp)
- sprintf(tmp, "%s.service", service);
- } else
- tmp = xstrdup(service);
-
- if (!tmp) {
- error("malloc/strdup: %m");
- return false;
- }
-
- server->systemd_service = tmp;
- return true;
-}
-
-bool server_set_stop_method(struct server *server,
- enum server_stop_method stop_method)
-{
- assert_return(server->stop_method == SERVER_STOP_METHOD_UNDEFINED &&
- stop_method != SERVER_STOP_METHOD_UNDEFINED,
- false);
-
- server->stop_method = stop_method;
- return true;
-}
-
-bool server_set_start_method(struct server *server,
- enum server_start_method start_method)
-{
- assert_return(server->start_method == SERVER_START_METHOD_UNDEFINED &&
- start_method != SERVER_START_METHOD_UNDEFINED,
- false);
-
- server->start_method = start_method;
- return true;
-}
-
-bool server_set_stop_exec(struct server *server, const char *cmd)
-{
- assert_return(server && !empty_str(cmd), false);
-
- return set_property(server, &server->stop_exec, cmd);
-}
-
-bool server_set_start_exec(struct server *server, const char *cmd)
-{
- assert_return(server && !empty_str(cmd), false);
-
- return set_property(server, &server->start_exec, cmd);
-}
-
-bool server_set_idle_timeout(struct server *server, uint16_t timeout)
-{
- assert_return(server && timeout > 0 && server->idle_timeout == 0,
- false);
-
- server->idle_timeout = timeout;
- return true;
-}
-
-bool server_set_port(struct server *server, uint16_t port)
-{
- assert_return(server && port > 0 && server->announce_port == 0, false);
-
- server->announce_port = htons(port);
- return true;
-}
-
-bool server_set_type(struct server *server, enum server_type type)
-{
- assert_return(server && type != SERVER_TYPE_UNDEFINED, false);
-
- switch (type) {
- case SERVER_TYPE_ANNOUNCE:
- server->type = SERVER_TYPE_ANNOUNCE;
- break;
- case SERVER_TYPE_PROXY:
- server->type = SERVER_TYPE_PROXY;
- break;
- default:
- return false;
- }
-
- return true;
-}
-
-bool server_set_pretty_name(struct server *server, const char *pretty_name)
-{
- assert_return(server && !empty_str(pretty_name), false);
-
- return set_property(server, &server->pretty_name, pretty_name);
-}
-
-struct server *server_new(const char *name)
+struct server *server_new(const char *filename)
{
struct server *server;
+ const char *suffix;
- assert_return(!empty_str(name), NULL);
+ assert_return(!empty_str(filename), NULL);
list_for_each_entry(server, &cfg->servers, list) {
- if (!streq(name, server->name))
+ if (!streq(filename, server->name))
continue;
- error("attempt to add duplicate server: %s", name);
+ error("attempt to add duplicate server: %s", filename);
return server;
}
- verbose("Adding server %s", name);
+ verbose("Adding server %s", filename);
server = zmalloc(sizeof(*server));
if (!server) {
error("malloc: %m");
return NULL;
}
+ suffix = strrchr(filename, '.');
+ if (!suffix || suffix == filename) {
+ error("invalid filename: %s", filename);
+ xfree(server);
+ return NULL;
+ }
+
+ server->name = xstrndup(filename, suffix - filename);
+ if (!server->name) {
+ error("failed to create server name: %s", filename);
+ xfree(server);
+ return NULL;
+ }
+
+ if (!scfg_init(&server->scfg, filename)) {
+ xfree(server->name);
+ xfree(server);
+ return NULL;
+ }
+
server->state = SERVER_STATE_INIT;
- server->type = SERVER_TYPE_UNDEFINED;
- server->name = xstrdup(name);
- server->stop_method = SERVER_STOP_METHOD_UNDEFINED;
- server->start_method = SERVER_START_METHOD_UNDEFINED;
- server->idle_timeout = 0;
uring_task_init(&server->task, server->name, uring_parent(),
server_free);
@@ -811,11 +668,8 @@ struct server *server_new(const char *name)
rcon_init(server);
- INIT_LIST_HEAD(&server->remotes);
- INIT_LIST_HEAD(&server->locals);
+ INIT_LIST_HEAD(&server->listenings);
INIT_LIST_HEAD(&server->proxys);
- INIT_LIST_HEAD(&server->rcons);
- INIT_LIST_HEAD(&server->dnslookups);
list_add(&server->list, &cfg->servers);
return server;
diff --git a/minecproxy/server.h b/minecproxy/server.h
index 937fa67..7201ba6 100644
--- a/minecproxy/server.h
+++ b/minecproxy/server.h
@@ -1,6 +1,8 @@
#ifndef fooserverhfoo
#define fooserverhfoo
+#include "config-parser.h"
+
/* clang-format off */
enum server_state {
SERVER_STATE_INIT = 0,
@@ -11,51 +13,17 @@ enum server_state {
};
/* clang-format on */
-enum server_type {
- SERVER_TYPE_UNDEFINED,
- SERVER_TYPE_ANNOUNCE,
- SERVER_TYPE_PROXY,
-};
-
-enum server_stop_method {
- SERVER_STOP_METHOD_UNDEFINED,
- SERVER_STOP_METHOD_RCON,
- SERVER_STOP_METHOD_SYSTEMD,
- SERVER_STOP_METHOD_EXEC,
-};
-
-enum server_start_method {
- SERVER_START_METHOD_UNDEFINED,
- SERVER_START_METHOD_SYSTEMD,
- SERVER_START_METHOD_EXEC,
-};
-
struct server {
- enum server_type type;
char *name;
- char *pretty_name;
- uint16_t announce_port;
- struct list_head locals;
- struct list_head remotes;
+ struct server_config scfg;
+ struct list_head listenings;
struct list_head proxys;
- struct list_head rcons;
- struct list_head dnslookups;
enum server_state state;
- enum server_stop_method stop_method;
- enum server_start_method start_method;
-
/* For calling external start/stop executables */
- char *stop_exec;
- char *start_exec;
struct uring_task exec_task;
- /* For systemd services */
- char *systemd_service;
- char *systemd_obj;
-
/* For rcon connections */
- char *rcon_password;
struct connection rcon_conn;
struct uring_task rcon_task;
struct uring_task_buf rcon_tbuf;
@@ -68,7 +36,6 @@ struct server {
struct uring_task idle_task;
struct connection idle_conn;
struct uring_task_buf idle_buf;
- unsigned idle_timeout;
unsigned idle_count;
/* For reading config files */
@@ -82,7 +49,7 @@ void server_refdump(struct server *server);
void server_delete(struct server *server);
-void server_delete_by_name(const char *name);
+void server_delete_by_filename(const char *filename);
bool server_start(struct server *server);
@@ -90,40 +57,14 @@ bool server_stop(struct server *server);
void server_set_active_players(struct server *server, int count);
+void server_async_dns_update(struct server_config *scfg, bool done);
+
bool server_idle_check(struct server *server);
bool server_announce(struct server *server, int fd);
bool server_commit(struct server *server);
-bool server_add_remote(struct server *server, struct saddr *remote);
-
-bool server_add_local(struct server *server, struct saddr *saddr);
-
-bool server_add_rcon(struct server *server, struct saddr *rcon);
-
-bool server_set_rcon_password(struct server *server, const char *password);
-
-bool server_set_systemd_service(struct server *server, const char *service);
-
-bool server_set_stop_method(struct server *server,
- enum server_stop_method stop_method);
-
-bool server_set_start_method(struct server *server,
- enum server_start_method start_method);
-
-bool server_set_stop_exec(struct server *server, const char *cmd);
-
-bool server_set_start_exec(struct server *server, const char *cmd);
-
-bool server_set_idle_timeout(struct server *server, uint16_t timeout);
-
-bool server_set_port(struct server *server, uint16_t port);
-
-bool server_set_type(struct server *server, enum server_type type);
-
-bool server_set_pretty_name(struct server *server, const char *pretty_name);
-
struct server *server_new(const char *name);
#endif
diff --git a/minecproxy/systemd.c b/minecproxy/systemd.c
index a973a55..96ff50f 100644
--- a/minecproxy/systemd.c
+++ b/minecproxy/systemd.c
@@ -5,59 +5,7 @@
#include "main.h"
#include "server.h"
#include "systemd.h"
-
-#define SYSTEMD_DBUS_SERVICE "org.freedesktop.systemd1"
-#define SYSTEMD_DBUS_INTERFACE "org.freedesktop.systemd1.Unit"
-#define SYSTEMD_DBUS_PATH_PREFIX "/org/freedesktop/systemd1/unit/"
-
-static inline char tohex(uint8_t val)
-{
- static const char hex[] = "0123456789abcdef";
-
- return hex[val & 0x0f];
-}
-
-/*
- * Creates an escaped D-Bus object path for a given systemd service
- *
- * Escaping rules are documented here:
- * https://dbus.freedesktop.org/doc/dbus-specification.html
- *
- * Essentially, everyting but a-z, A-Z, 0-9 is replaced by _xx where xx is
- * the hexadecimal value of the character.
- *
- * Example: minecraft@world1.service -> minecraft_40world1_2eservice
- */
-char *systemd_object_path(const char *service)
-{
- char *r;
- char *d;
- const char *s;
-
- assert_return(service && !empty_str(service), NULL);
-
- r = zmalloc(STRLEN(SYSTEMD_DBUS_PATH_PREFIX) + strlen(service) * 3 + 1);
- if (!r)
- return NULL;
-
- memcpy(r, SYSTEMD_DBUS_PATH_PREFIX, STRLEN(SYSTEMD_DBUS_PATH_PREFIX));
- d = r + STRLEN(SYSTEMD_DBUS_PATH_PREFIX);
-
- for (s = service; *s; s++) {
- if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
- (*s >= '0' && *s <= '9')) {
- *(d++) = *s;
- continue;
- }
-
- *(d++) = '_';
- *(d++) = tohex(*s >> 4);
- *(d++) = tohex(*s);
- }
-
- *d = '\0';
- return r;
-}
+#include "config.h"
void systemd_delete()
{
@@ -106,17 +54,17 @@ bool systemd_service_running(struct server *server)
bool running = false;
int r;
- assert_return(server && bus && server->systemd_service &&
- server->systemd_obj,
+ assert_return(server && bus && server->scfg.systemd_service &&
+ server->scfg.systemd_obj,
false);
r = sd_bus_get_property_string(bus, SYSTEMD_DBUS_SERVICE,
- server->systemd_obj,
+ server->scfg.systemd_obj,
SYSTEMD_DBUS_INTERFACE, "ActiveState",
&error, &status);
if (r < 0) {
error("failed to get status for service %s (%s): %s",
- server->systemd_service, server->systemd_obj,
+ server->scfg.systemd_service, server->scfg.systemd_obj,
error.message);
goto out;
}
@@ -124,10 +72,10 @@ bool systemd_service_running(struct server *server)
if (streq(status, "active")) {
running = true;
debug(DBG_SYSD, "systemd service %s (%s) is active",
- server->systemd_service, server->systemd_obj);
+ server->scfg.systemd_service, server->scfg.systemd_obj);
} else
debug(DBG_SYSD, "systemd service %s (%s) is not active",
- server->systemd_service, server->systemd_obj);
+ server->scfg.systemd_service, server->scfg.systemd_obj);
out:
free(status);
@@ -144,16 +92,16 @@ static bool systemd_service_action(struct server *server, const char *action)
bool performed = false;
int r;
- assert_return(server && bus && server->systemd_service &&
- server->systemd_obj && action,
+ assert_return(server && bus && server->scfg.systemd_service &&
+ server->scfg.systemd_obj && action,
false);
- r = sd_bus_call_method(bus, SYSTEMD_DBUS_SERVICE, server->systemd_obj,
+ r = sd_bus_call_method(bus, SYSTEMD_DBUS_SERVICE, server->scfg.systemd_obj,
SYSTEMD_DBUS_INTERFACE, action, &error, &m, "s",
"fail");
if (r < 0) {
error("failed to perform action %s on systemd service %s: %s",
- action, server->systemd_service, error.message);
+ action, server->scfg.systemd_service, error.message);
goto out;
}
@@ -164,7 +112,7 @@ static bool systemd_service_action(struct server *server, const char *action)
}
verbose("action %s queued for service %s", action,
- server->systemd_service);
+ server->scfg.systemd_service);
performed = true;
out:
diff --git a/minecproxy/systemd.h b/minecproxy/systemd.h
index d455044..fb9e3a4 100644
--- a/minecproxy/systemd.h
+++ b/minecproxy/systemd.h
@@ -1,8 +1,6 @@
#ifndef foosystemdhfoo
#define foosystemdhfoo
-char *systemd_object_path(const char *service);
-
void systemd_delete();
bool systemd_service_running(struct server *server);
diff --git a/shared/config-parser.c b/shared/config-parser.c
index 13b26e1..ee30156 100644
--- a/shared/config-parser.c
+++ b/shared/config-parser.c
@@ -11,8 +11,456 @@
#include "utils.h"
#include "config-parser.h"
+#include "server-config-options.h"
#include "config.h"
+static void handle_dns_reply(struct server_config *scfg, enum scfg_keys type,
+ struct saddr *saddr)
+{
+ switch (type) {
+ case SCFG_KEY_LOCAL:
+ list_add(&saddr->list, &scfg->locals);
+ break;
+ case SCFG_KEY_REMOTE:
+ list_add(&saddr->list, &scfg->remotes);
+ break;
+ case SCFG_KEY_RCON:
+ list_add(&saddr->list, &scfg->rcons);
+ break;
+ default:
+ error("invalid DNS reply type");
+ break;
+ }
+}
+
+static void scfg_dns_cb(struct dns_async *dns, enum scfg_keys type)
+{
+ struct server_config *scfg;
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+ struct saddr *saddr;
+ struct addrinfo *results = NULL, *ai;
+ int r;
+
+ assert_return(dns);
+
+ scfg = dns->priv;
+ debug(DBG_DNS, "called, dns: %p, name: %s", dns, dns->name);
+
+ r = gai_error(&dns->gcb);
+ if (r == EAI_INPROGRESS) {
+ /* This shouldn't happen, assume we'll get called again */
+ error("called with request in progress");
+ return;
+ } else if (r == EAI_CANCELED) {
+ /* The server must be in the process of going away */
+ goto out;
+ } else if (r < 0) {
+ error("DNS lookup of %s:%s failed: %s", dns->name, dns->port,
+ gai_strerror(r));
+ goto out;
+ }
+
+ results = dns->gcb.ar_result;
+
+ for (ai = results; ai; ai = ai->ai_next) {
+ saddr = zmalloc(sizeof(*saddr));
+ if (!saddr) {
+ error("DNS lookup of %s:%s failed: %m", dns->name,
+ dns->port);
+ goto out;
+ }
+
+ switch (ai->ai_family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in *)ai->ai_addr;
+ saddr_set_ipv4(saddr, in4->sin_addr.s_addr,
+ in4->sin_port);
+ break;
+
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)ai->ai_addr;
+ saddr_set_ipv6(saddr, &in6->sin6_addr, in6->sin6_port);
+ break;
+
+ default:
+ error("getaddrinfo(%s:%s): unknown address family (%i)",
+ dns->name, dns->port, ai->ai_family);
+ xfree(saddr);
+ continue;
+ }
+
+ handle_dns_reply(scfg, type, saddr);
+ }
+
+out:
+ freeaddrinfo(results);
+ list_del(&dns->list);
+ if (dns->notification_cb)
+ dns->notification_cb(scfg, true);
+ xfree(dns);
+}
+
+static void scfg_dns_local_cb(struct dns_async *dns)
+{
+ scfg_dns_cb(dns, SCFG_KEY_LOCAL);
+}
+
+static void scfg_dns_remote_cb(struct dns_async *dns)
+{
+ scfg_dns_cb(dns, SCFG_KEY_REMOTE);
+}
+
+static void scfg_dns_rcon_cb(struct dns_async *dns)
+{
+ scfg_dns_cb(dns, SCFG_KEY_RCON);
+}
+
+static bool handle_dns(struct server_config *scfg, enum scfg_keys type,
+ struct cfg_value *value,
+ notification_cb_t notification_cb)
+{
+ struct saddr *saddr, *tmp;
+ struct dns_async *dns;
+
+ assert_return(scfg && type && value, false);
+
+ switch (value->type) {
+ case CFG_VAL_TYPE_ADDRS:
+ debug(DBG_DNS, "%i: got immediate addrs", type);
+
+ list_for_each_entry_safe(saddr, tmp, &value->saddrs, list) {
+ list_del(&saddr->list);
+ handle_dns_reply(scfg, type, saddr);
+ }
+ return true;
+
+ case CFG_VAL_TYPE_ASYNC_ADDRS:
+ debug(DBG_DNS, "doing async lookup of DNS record: %p",
+ value->dns_async);
+
+ dns = value->dns_async;
+ switch (type) {
+ case SCFG_KEY_LOCAL:
+ dns->cb = scfg_dns_local_cb;
+ break;
+ case SCFG_KEY_REMOTE:
+ dns->cb = scfg_dns_remote_cb;
+ break;
+ case SCFG_KEY_RCON:
+ dns->cb = scfg_dns_rcon_cb;
+ break;
+ default:
+ error("invalid DNS lookup type");
+ return false;
+ }
+
+ if (!!notification_cb) {
+ error("async DNS lookup received but not requested");
+ xfree(dns);
+ return false;
+ }
+
+ dns->priv = scfg;
+ dns->notification_cb = notification_cb;
+ list_add(&dns->list, &scfg->dnslookups);
+ dns->notification_cb(scfg, false);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static inline char tohex(uint8_t val)
+{
+ static const char hex[] = "0123456789abcdef";
+
+ return hex[val & 0x0f];
+}
+
+/*
+ * Creates an escaped D-Bus object path for a given systemd service
+ *
+ * Escaping rules are documented here:
+ * https://dbus.freedesktop.org/doc/dbus-specification.html
+ *
+ * Essentially, everyting but a-z, A-Z, 0-9 is replaced by _xx where xx is
+ * the hexadecimal value of the character.
+ *
+ * Example: minecraft@world1.service -> minecraft_40world1_2eservice
+ */
+static char *systemd_object_path(const char *service)
+{
+ char *r;
+ char *d;
+ const char *s;
+
+ assert_return(service && !empty_str(service), NULL);
+
+ r = zmalloc(STRLEN(SYSTEMD_DBUS_PATH_PREFIX) + strlen(service) * 3 + 1);
+ if (!r)
+ return NULL;
+
+ memcpy(r, SYSTEMD_DBUS_PATH_PREFIX, STRLEN(SYSTEMD_DBUS_PATH_PREFIX));
+ d = r + STRLEN(SYSTEMD_DBUS_PATH_PREFIX);
+
+ for (s = service; *s; s++) {
+ if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ (*s >= '0' && *s <= '9')) {
+ *(d++) = *s;
+ continue;
+ }
+
+ *(d++) = '_';
+ *(d++) = tohex(*s >> 4);
+ *(d++) = tohex(*s);
+ }
+
+ *d = '\0';
+ return r;
+}
+
+#define INVALID(fmt, ...) \
+do { \
+ verbose("%s: " fmt, scfg->filename __VA_OPT__(,) __VA_ARGS__); \
+ valid = false; \
+} while(0)
+
+bool scfg_parse(struct server_config *scfg, char *buf,
+ notification_cb_t notification_cb)
+{
+ char *pos;
+ bool valid = true;
+
+ assert_return(scfg && buf, false);
+
+ pos = buf;
+
+ if (!config_parse_header(SERVER_CFG_HEADER, &pos)) {
+ INVALID("missing/invalid header");
+ goto out;
+ }
+
+ while (true) {
+ int key;
+ const char *keyname;
+ struct cfg_value value;
+
+ if (!config_parse_line(scfg->filename, &pos, scfg_key_map, &key,
+ &keyname, &value, !!notification_cb))
+ break;
+
+ /* FIXME: Improve error reporting */
+ if (key == SCFG_KEY_INVALID) {
+ INVALID("invalid line in config file");
+ continue;
+ }
+
+ debug(DBG_CFG, "%s: key %s", scfg->filename, keyname);
+
+ switch (key) {
+ case SCFG_KEY_TYPE:
+ if (scfg->type != SERVER_TYPE_UNDEFINED)
+ INVALID("type defined multiple times");
+ else if (streq(value.str, "proxy"))
+ scfg->type = SERVER_TYPE_PROXY;
+ else if (streq(value.str, "announce"))
+ scfg->type = SERVER_TYPE_ANNOUNCE;
+ else
+ INVALID("unknown type: %s", value.str);
+ break;
+
+ case SCFG_KEY_NAME:
+ if (scfg->pretty_name)
+ INVALID("name defined multiple times");
+ else if (empty_str(value.str))
+ INVALID("name is an empty string");
+ else if (!(scfg->pretty_name = xstrdup(value.str)))
+ INVALID("strdup: %m");
+ break;
+
+ case SCFG_KEY_PORT:
+ if (scfg->announce_port != 0)
+ INVALID("port defined multiple times");
+ else
+ scfg->announce_port = value.uint16;
+ break;
+
+ case SCFG_KEY_LOCAL:
+ if (!handle_dns(scfg, SCFG_KEY_LOCAL, &value,
+ notification_cb))
+ INVALID("handle_dns failed");
+ break;
+
+ case SCFG_KEY_REMOTE:
+ if (!handle_dns(scfg, SCFG_KEY_REMOTE, &value,
+ notification_cb))
+ INVALID("handle_dns failed");
+ break;
+
+ case SCFG_KEY_IDLE_TIMEOUT:
+ if (scfg->idle_timeout != 0)
+ INVALID("idle_timeout already set");
+ else
+ scfg->idle_timeout = value.uint16;
+ break;
+
+ case SCFG_KEY_STOP_METHOD:
+ if (scfg->stop_method != SERVER_STOP_METHOD_UNDEFINED)
+ INVALID("stop method defined multiple times");
+ else if (streq(value.str, "exec"))
+ scfg->stop_method = SERVER_STOP_METHOD_EXEC;
+ else if (streq(value.str, "rcon"))
+ scfg->stop_method = SERVER_STOP_METHOD_RCON;
+ else if (streq(value.str, "systemd"))
+ scfg->stop_method = SERVER_STOP_METHOD_SYSTEMD;
+ else
+ INVALID("unknown stop method: %s", value.str);
+ break;
+
+ case SCFG_KEY_START_METHOD:
+ if (scfg->start_method != SERVER_START_METHOD_UNDEFINED)
+ INVALID("start method defined multiple times");
+ else if (streq(value.str, "exec"))
+ scfg->start_method = SERVER_START_METHOD_EXEC;
+ else if (streq(value.str, "systemd"))
+ scfg->start_method = SERVER_START_METHOD_SYSTEMD;
+ else
+ INVALID("unknown start method: %s", value.str);
+ break;
+
+ case SCFG_KEY_STOP_EXEC:
+ if (scfg->stop_exec)
+ INVALID("stop exec defined multiple times");
+ else if (!(scfg->stop_exec = xstrdup(value.str)))
+ INVALID("strdup: %m");
+ break;
+
+ case SCFG_KEY_START_EXEC:
+ if (scfg->start_exec)
+ INVALID("start exec defined multiple times");
+ else if (!(scfg->start_exec = xstrdup(value.str)))
+ INVALID("strdup: %m");
+ break;
+
+ case SCFG_KEY_RCON:
+ if (!handle_dns(scfg, SCFG_KEY_RCON, &value,
+ notification_cb))
+ INVALID("handle_dns failed");
+ break;
+
+ case SCFG_KEY_RCON_PASSWORD:
+ if (scfg->rcon_password)
+ INVALID("rcon_password defined multiple times");
+ else if (!(scfg->rcon_password = xstrdup(value.str)))
+ INVALID("strdup: %m");
+ break;
+
+ case SCFG_KEY_SYSTEMD_SERVICE:
+ if (scfg->systemd_service)
+ INVALID("systemd_service defined multiple times");
+ else {
+ const char *suffix;
+ char *tmp;
+
+ suffix = strrchr(value.str, '.');
+ if (!suffix || !streq(suffix, ".service")) {
+ tmp = zmalloc(strlen(value.str) +
+ STRLEN(".service") + 1);
+ if (tmp)
+ sprintf(tmp, "%s.service",
+ value.str);
+ } else
+ tmp = xstrdup(value.str);
+
+ if (!tmp) {
+ INVALID("malloc/strdup: %m");
+ break;
+ }
+
+ scfg->systemd_service = tmp;
+ scfg->systemd_obj =
+ systemd_object_path(scfg->systemd_service);
+ if (!scfg->systemd_obj)
+ INVALID("object_path: %m");
+ }
+
+ break;
+
+ case SCFG_KEY_INVALID:
+ default:
+ INVALID("invalid line");
+ break;
+ }
+ }
+
+out:
+ return valid;
+}
+
+void scfg_delete(struct server_config *scfg)
+{
+ struct saddr *saddr, *tmp;
+ struct dns_async *dns;
+
+ xfree(scfg->filename);
+ xfree(scfg->pretty_name);
+ xfree(scfg->stop_exec);
+ xfree(scfg->start_exec);
+ /* FIXME: use free_password */
+ xfree(scfg->rcon_password);
+ xfree(scfg->systemd_service);
+ xfree(scfg->systemd_obj);
+
+ list_for_each_entry_safe(saddr, tmp, &scfg->remotes, list) {
+ list_del(&saddr->list);
+ xfree(saddr);
+ }
+
+ list_for_each_entry_safe(saddr, tmp, &scfg->rcons, list) {
+ list_del(&saddr->list);
+ xfree(saddr);
+ }
+
+ list_for_each_entry_safe(saddr, tmp, &scfg->locals, list) {
+ list_del(&saddr->list);
+ xfree(saddr);
+ }
+
+ list_for_each_entry(dns, &scfg->dnslookups, list)
+ gai_cancel(&dns->gcb);
+}
+
+bool scfg_init(struct server_config *scfg, const char *filename)
+{
+ assert_return(scfg, false);
+
+ if (filename) {
+ scfg->filename = xstrdup(filename);
+ if (!scfg->filename) {
+ error("strdup: %m");
+ return false;
+ }
+ }
+ scfg->type = SERVER_TYPE_UNDEFINED;
+ scfg->pretty_name = NULL;
+ scfg->announce_port = 0;
+ scfg->idle_timeout = 0;
+ scfg->stop_method = SERVER_STOP_METHOD_UNDEFINED;
+ scfg->start_method = SERVER_START_METHOD_UNDEFINED;
+ scfg->stop_exec = NULL;
+ scfg->start_exec = NULL;
+ scfg->rcon_password = NULL;
+ scfg->systemd_service = NULL;
+ scfg->systemd_obj = NULL;
+ INIT_LIST_HEAD(&scfg->remotes);
+ INIT_LIST_HEAD(&scfg->locals);
+ INIT_LIST_HEAD(&scfg->rcons);
+ INIT_LIST_HEAD(&scfg->dnslookups);
+ return true;
+}
+
static void eat_whitespace_and_comments(char **pos)
{
assert_return(pos && *pos);
@@ -126,6 +574,7 @@ static bool dnslookup(const char *name, uint16_t port, struct cfg_value *rvalue,
results = dns->gcb.ar_result;
+ /* FIXME: parts can be shared with the async cb? */
for (ai = results; ai; ai = ai->ai_next) {
saddr = zmalloc(sizeof(*saddr));
if (!saddr) {
diff --git a/shared/config-parser.h b/shared/config-parser.h
index 771ece1..2220221 100644
--- a/shared/config-parser.h
+++ b/shared/config-parser.h
@@ -7,6 +7,44 @@
#include <signal.h>
#include <dirent.h>
+enum server_type {
+ SERVER_TYPE_UNDEFINED,
+ SERVER_TYPE_ANNOUNCE,
+ SERVER_TYPE_PROXY,
+};
+
+enum server_stop_method {
+ SERVER_STOP_METHOD_UNDEFINED,
+ SERVER_STOP_METHOD_RCON,
+ SERVER_STOP_METHOD_SYSTEMD,
+ SERVER_STOP_METHOD_EXEC,
+};
+
+enum server_start_method {
+ SERVER_START_METHOD_UNDEFINED,
+ SERVER_START_METHOD_SYSTEMD,
+ SERVER_START_METHOD_EXEC,
+};
+
+struct server_config {
+ char *filename;
+ enum server_type type;
+ char *pretty_name;
+ uint16_t announce_port;
+ unsigned idle_timeout;
+ enum server_stop_method stop_method;
+ enum server_start_method start_method;
+ char *stop_exec;
+ char *start_exec;
+ char *rcon_password;
+ char *systemd_service;
+ char *systemd_obj;
+ struct list_head locals;
+ struct list_head remotes;
+ struct list_head rcons;
+ struct list_head dnslookups;
+};
+
enum cfg_value_type {
CFG_VAL_TYPE_INVALID,
CFG_VAL_TYPE_STRING,
@@ -16,9 +54,7 @@ enum cfg_value_type {
CFG_VAL_TYPE_BOOL,
};
-struct dns_async;
-
-typedef void(dns_cb_t)(struct dns_async *);
+typedef void (*notification_cb_t)(struct server_config *scfg, bool done);
struct dns_async {
char name[FQDN_STR_LEN + 1];
@@ -26,7 +62,8 @@ struct dns_async {
struct addrinfo req;
struct gaicb gcb;
struct sigevent sev;
- dns_cb_t *cb;
+ void (*cb)(struct dns_async *);
+ notification_cb_t notification_cb;
void *priv;
struct list_head list;
};
@@ -48,6 +85,13 @@ struct cfg_value {
};
};
+bool scfg_parse(struct server_config *scfg, char *buf,
+ notification_cb_t notification_cb);
+
+void scfg_delete(struct server_config *scfg);
+
+bool scfg_init(struct server_config *scfg, const char *filename);
+
bool strtosockaddrs(const char *str, struct cfg_value *rvalue, bool async);
bool config_parse_line(const char *filename, char **buf,