From 90e27b4356f2a6ab98e812c4096b0b76f94a8fb3 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 7 Jul 2020 18:41:38 +0200 Subject: Flesh out the ping implementation --- minecctl/mc-commands.c | 31 ++++----- minecctl/mc-commands.h | 3 +- minecctl/misc-commands.c | 63 ++++++++++++++++-- minecctl/misc-commands.h | 2 + minecctl/rcon-commands.c | 20 +++--- minecctl/rcon-commands.h | 5 +- minecproxy/main.c | 4 +- minecproxy/main.h | 2 - minecproxy/meson.build | 3 - minecproxy/server.c | 12 ++-- minecproxy/systemd.c | 164 ---------------------------------------------- minecproxy/systemd.h | 13 ---- shared/meson.build | 4 ++ shared/systemd.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++ shared/systemd.h | 13 ++++ 15 files changed, 277 insertions(+), 229 deletions(-) delete mode 100644 minecproxy/systemd.c delete mode 100644 minecproxy/systemd.h create mode 100644 shared/systemd.c create mode 100644 shared/systemd.h diff --git a/minecctl/mc-commands.c b/minecctl/mc-commands.c index 7d44451..c65f166 100644 --- a/minecctl/mc-commands.c +++ b/minecctl/mc-commands.c @@ -8,32 +8,23 @@ #include "misc.h" #include "shared/mc-protocol.h" -bool do_mc_pcount(struct cfg *cfg, unsigned *online, unsigned *max) +bool do_mc_pcount(struct cfg *cfg, struct server *server, unsigned *online, + unsigned *max, const char **error) { - struct server *server; struct saddr *saddr; - const char *error; char buf[4096]; size_t plen, off; ssize_t r; bool rv = false; int fd; - server = server_get_default(cfg); - if (!server) { - error("failed to get default server"); - return false; - } - fd = connect_any(&server->scfg.remotes, &saddr, &error); - if (fd < 0) { - error("%s: unable to connect - %s", server->name, error); + if (fd < 0) return false; - } if (!mc_protocol_create_status_request(buf, sizeof(buf), &plen, saddr)) { - error("Failed to create req"); + *error = "failed to create request"; goto out; } @@ -42,7 +33,7 @@ bool do_mc_pcount(struct cfg *cfg, unsigned *online, unsigned *max) while (off < plen) { r = write(fd, buf + off, plen - off); if (r <= 0) { - error("write failed: %zi (%m)", r); + *error = "write failed"; goto out; } off += r; @@ -52,23 +43,23 @@ bool do_mc_pcount(struct cfg *cfg, unsigned *online, unsigned *max) while (off < sizeof(buf)) { r = read(fd, buf + off, sizeof(buf) - off); if (r <= 0) { - error("Read failed %zi: %m", r); + *error = "read failed"; goto out; } off += r; - if (mc_is_handshake_complete(buf, off)) { - rv = true; + if (mc_is_handshake_complete(buf, off)) break; - } } if (!mc_protocol_parse_status_reply(buf, off, online, max)) { - error("Failed to get player count"); - return false; + *error = "failed to get player count"; + goto out; } + rv = true; + out: close(fd); return rv; diff --git a/minecctl/mc-commands.h b/minecctl/mc-commands.h index c6e2e6f..493a976 100644 --- a/minecctl/mc-commands.h +++ b/minecctl/mc-commands.h @@ -2,6 +2,7 @@ #ifndef foomccommandshfoo #define foomccomanndshfoo -bool do_mc_pcount(struct cfg *cfg, unsigned *online, unsigned *max); +bool do_mc_pcount(struct cfg *cfg, struct server *server, unsigned *online, + unsigned *max, const char **error); #endif diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c index f89afb6..1870e07 100644 --- a/minecctl/misc-commands.c +++ b/minecctl/misc-commands.c @@ -5,6 +5,7 @@ #include "misc-commands.h" #include "rcon-commands.h" #include "mc-commands.h" +#include "shared/systemd.h" bool do_list(struct cfg *cfg) { @@ -135,13 +136,65 @@ bool do_lint(struct cfg *cfg) bool do_pcount(struct cfg *cfg) { - unsigned x, y; + unsigned online, max; + struct server *server; + const char *error; + + server = server_get_default(cfg); + if (!server) { + error("failed to get default server"); + return false; + } - if (do_rcon_pcount(cfg, &y, &x)) - error("Rcon says %u/%u", y, x); + if (do_rcon_pcount(cfg, server, &online, &max, &error)) + info("Rcon says %u/%u", online, max); - if (do_mc_pcount(cfg, &y, &x)) - error("MC says %u/%u", y, x); + if (do_mc_pcount(cfg, server, &online, &max, &error)) + info("MC says %u/%u", online, max); return true; } + +bool do_ping(struct cfg *cfg) +{ + struct server *server; + const char *error; + unsigned online, max; + + /* FIXME: Do all servers when default not given */ + server = server_get_default(cfg); + if (!server) { + error("failed to get default server"); + return false; + } + + info("• %s", server->name); + + if (list_empty(&server->scfg.rcons)) + info(" rcon : not configured"); + else if (!do_rcon_pcount(cfg, server, &online, &max, &error)) + info(" rcon : %sfail%s (%s)", + ansi_red, ansi_normal, error); + else + info(" rcon : %sok%s", ansi_green, ansi_normal); + + if (list_empty(&server->scfg.remotes)) + info(" mc : not configured"); + else if (!do_mc_pcount(cfg, server, &online, &max, &error)) + info(" mc : %sfail%s (%s)", + ansi_red, ansi_normal, error); + else + info(" mc : %sok%s", ansi_green, ansi_normal); + + if (!server->scfg.systemd_service || !server->scfg.systemd_obj) + info(" systemd service : not configured"); + else if (!systemd_service_running(&server->scfg, &error)) + info(" systemd service : %sfail%s (%s)", + ansi_red, ansi_normal, error); + else + info(" systemd service : %sactive%s", ansi_green, ansi_normal); + systemd_delete(); + + return true; +} + diff --git a/minecctl/misc-commands.h b/minecctl/misc-commands.h index 8ea4127..ac2fd54 100644 --- a/minecctl/misc-commands.h +++ b/minecctl/misc-commands.h @@ -8,4 +8,6 @@ bool do_lint(struct cfg *cfg); bool do_pcount(struct cfg *cfg); +bool do_ping(struct cfg *cfg); + #endif diff --git a/minecctl/rcon-commands.c b/minecctl/rcon-commands.c index f18f082..61a682c 100644 --- a/minecctl/rcon-commands.c +++ b/minecctl/rcon-commands.c @@ -12,8 +12,8 @@ #include "shared/utils.h" #include "shared/rcon-protocol.h" #include "minecctl.h" -#include "rcon-commands.h" #include "server.h" +#include "rcon-commands.h" #include "misc.h" static void send_packet(int sfd, const char *buf, size_t len) @@ -93,6 +93,7 @@ static void send_msg(int sfd, char *buf, size_t len, enum rcon_packet_type type, rcon_packet_id++; } +/* FIXME: return error message */ static int rcon_login(struct cfg *cfg, struct server *server) { const char *error; @@ -296,12 +297,6 @@ bool do_status(struct cfg *cfg) return true; } -bool do_ping(_unused_ struct cfg *cfg) -{ - die("Not implemented"); - return false; -} - static bool get_player_count(int fd, unsigned *current, unsigned *max) { char buf[4096]; @@ -382,18 +377,21 @@ bool do_stop_all(struct cfg *cfg) return rv; } -bool do_rcon_pcount(struct cfg *cfg, unsigned *online, unsigned *max) +bool do_rcon_pcount(struct cfg *cfg, struct server *server, unsigned *online, + unsigned *max, const char **error) { - struct server *server; bool rv; int fd; - server = server_get_default(cfg); fd = rcon_login(cfg, server); - if (fd < 0) + if (fd < 0) { + *error = "failed to login"; return false; + } rv = get_player_count(fd, online, max); + if (!rv) + *error = "failed to get player count"; close(fd); diff --git a/minecctl/rcon-commands.h b/minecctl/rcon-commands.h index b2720b4..84d5529 100644 --- a/minecctl/rcon-commands.h +++ b/minecctl/rcon-commands.h @@ -4,13 +4,12 @@ bool do_status(struct cfg *cfg); -bool do_ping(struct cfg *cfg); - bool do_stop(struct cfg *cfg); bool do_stop_all(struct cfg *cfg); -bool do_rcon_pcount(struct cfg *cfg, unsigned *online, unsigned *max); +bool do_rcon_pcount(struct cfg *cfg, struct server *server, unsigned *online, + unsigned *max, const char **error); bool do_console(struct cfg *cfg); diff --git a/minecproxy/main.c b/minecproxy/main.c index 238b612..c51b01c 100644 --- a/minecproxy/main.c +++ b/minecproxy/main.c @@ -24,7 +24,7 @@ #include "server.h" #include "server-config.h" #include "announce.h" -#include "systemd.h" +#include "shared/systemd.h" #include "igmp.h" #include "idle.h" #include "ptimer.h" @@ -177,7 +177,7 @@ static void cfg_free(struct uring_task *task) assert_return(task && xcfg == cfg); debug(DBG_SIG, "called"); - systemd_delete(cfg); + systemd_delete(); xfree(cfg->igmp_iface); cfg->igmp_iface = NULL; exiting = true; diff --git a/minecproxy/main.h b/minecproxy/main.h index 12b16df..2c7be83 100644 --- a/minecproxy/main.h +++ b/minecproxy/main.h @@ -99,8 +99,6 @@ struct cfg { struct ptimer *ptimer; struct igmp *igmp; struct idle *idle; - struct sd_bus *sd_bus; - bool sd_bus_failed; struct uring_task task; struct list_head servers; }; diff --git a/minecproxy/meson.build b/minecproxy/meson.build index 959b975..155840c 100644 --- a/minecproxy/meson.build +++ b/minecproxy/meson.build @@ -11,17 +11,14 @@ minecproxy_sources = [ 'idle.c', 'ptimer.c', 'igmp.c', - 'systemd.c', 'misc.c', ] dep_liburing = dependency('liburing') -dep_libsystemd = dependency('libsystemd') dep_libcapng = dependency('libcap-ng') minecproxy_deps = [ dep_liburing, - dep_libsystemd, dep_libcapng, dep_config_h, dep_libshared, diff --git a/minecproxy/server.c b/minecproxy/server.c index d24c0ad..b1e9b05 100644 --- a/minecproxy/server.c +++ b/minecproxy/server.c @@ -18,9 +18,9 @@ #include "server-config.h" #include "server-proxy.h" #include "server-rcon.h" -#include "shared/config-parser.h" #include "idle.h" -#include "systemd.h" +#include "shared/config-parser.h" +#include "shared/systemd.h" void server_refdump(struct server *server) { @@ -218,13 +218,15 @@ static bool server_exec(struct server *server, const char *cmd) static bool server_check_running(struct server *server) { + const char *error; + assert_return(server, false); /* FIXME: other methods, rcon? */ if (server->scfg.systemd_service) { verbose("%s: checking if systemd service is running", server->name); - if (systemd_service_running(server)) { + if (systemd_service_running(&server->scfg, &error)) { server->state = SERVER_STATE_RUNNING; return true; } else { @@ -252,7 +254,7 @@ bool server_start(struct server *server) server->name, server->scfg.systemd_service); - if (systemd_service_start(server)) { + if (systemd_service_start(&server->scfg)) { server->state = SERVER_STATE_RUNNING; return true; } else @@ -281,7 +283,7 @@ bool server_stop(struct server *server) verbose("Stopping server %s via systemd (%s)", server->name, server->scfg.systemd_service); - if (systemd_service_stop(server)) { + if (systemd_service_stop(&server->scfg)) { server->state = SERVER_STATE_STOPPED; return true; } else diff --git a/minecproxy/systemd.c b/minecproxy/systemd.c deleted file mode 100644 index 94d5b8b..0000000 --- a/minecproxy/systemd.c +++ /dev/null @@ -1,164 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include -#include -#include -#include - -#include "main.h" -#include "server.h" -#include "systemd.h" -#include "config.h" - -void systemd_delete() -{ - assert_return_silent(cfg->sd_bus); - - sd_bus_unref(cfg->sd_bus); - cfg->sd_bus = NULL; -} - -static sd_bus *get_bus() -{ - int r; - - if (cfg->sd_bus_failed) - return NULL; - - if (!cfg->sd_bus) { - r = sd_bus_open_user(&cfg->sd_bus); - if (r < 0) { - error("failed to connect to user system bus: %s", - strerror(-r)); - cfg->sd_bus_failed = true; - return NULL; - } - } - - return cfg->sd_bus; -} - -/* - * Check if a systemd service is running. - * - * This is equivalent to (assuming service mc@world1): - * gdbus call --session - * --dest org.freedesktop.systemd1 - * --object-path /org/freedesktop/systemd1/unit/mc_40world1_2eservice - * --method org.freedesktop.DBus.Properties.Get - * "org.freedesktop.systemd1.Unit" - * "ActiveState" - */ -bool systemd_service_running(struct server *server) -{ - sd_bus *bus = get_bus(); - sd_bus_error error = SD_BUS_ERROR_NULL; - char *status = NULL; - bool running = false; - int r; - - assert_return(server && bus && server->scfg.systemd_service && - server->scfg.systemd_obj, - false); - -again: - r = sd_bus_get_property_string(bus, SYSTEMD_DBUS_SERVICE, - server->scfg.systemd_obj, - SYSTEMD_DBUS_INTERFACE, "ActiveState", - &error, &status); - if (r < 0) { - if (sd_bus_error_get_errno(&error) == EINTR) - goto again; - - error("failed to get status for service %s (%s): %s", - server->scfg.systemd_service, server->scfg.systemd_obj, - error.message); - goto out; - } - - if (streq(status, "active")) { - running = true; - debug(DBG_SYSD, "systemd service %s (%s) is active", - server->scfg.systemd_service, server->scfg.systemd_obj); - } else - debug(DBG_SYSD, "systemd service %s (%s) is not active", - server->scfg.systemd_service, server->scfg.systemd_obj); - -out: - free(status); - sd_bus_error_free(&error); - return running; -} - -static bool systemd_service_action(struct server *server, const char *action) -{ - sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus_message *m = NULL; - sd_bus *bus = get_bus(); - const char *path; - bool performed = false; - int r; - - assert_return(server && bus && server->scfg.systemd_service && - server->scfg.systemd_obj && action, - false); - -again: - r = sd_bus_call_method(bus, SYSTEMD_DBUS_SERVICE, server->scfg.systemd_obj, - SYSTEMD_DBUS_INTERFACE, action, &error, &m, "s", - "fail"); - if (r < 0) { - if (sd_bus_error_get_errno(&error) == EINTR) - goto again; - - error("failed to perform action %s on systemd service %s: %s", - action, server->scfg.systemd_service, error.message); - goto out; - } - - r = sd_bus_message_read(m, "o", &path); - if (r < 0) { - error("failed to parse response message: %s", strerror(-r)); - goto out; - } - - verbose("action %s queued for service %s", action, - server->scfg.systemd_service); - performed = true; - -out: - sd_bus_error_free(&error); - sd_bus_message_unref(m); - return performed; -} - -/* - * Stop systemd service. - * - * This is equivalent to (assuming service mc@world1): - * gdbus call --session - * --dest org.freedesktop.systemd1 - * --object-path /org/freedesktop/systemd1/unit/mc_40world1_2eservice - * --method org.freedesktop.systemd1.Unit.Stop "fail" - */ -bool systemd_service_stop(struct server *server) -{ - assert_return(server, false); - - return systemd_service_action(server, "Stop"); -} - -/* - * Start systemd service. - * - * This is equivalent to (assuming service mc@world1): - * gdbus call --session - * --dest org.freedesktop.systemd1 - * --object-path /org/freedesktop/systemd1/unit/mc_40world1_2eservice - * --method org.freedesktop.systemd1.Unit.Start "fail" - */ -bool systemd_service_start(struct server *server) -{ - assert_return(server, false); - - return systemd_service_action(server, "Start"); -} diff --git a/minecproxy/systemd.h b/minecproxy/systemd.h deleted file mode 100644 index f3f96e4..0000000 --- a/minecproxy/systemd.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef foosystemdhfoo -#define foosystemdhfoo - -void systemd_delete(); - -bool systemd_service_running(struct server *server); - -bool systemd_service_stop(struct server *server); - -bool systemd_service_start(struct server *server); - -#endif diff --git a/shared/meson.build b/shared/meson.build index 021fe11..a937e92 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -3,10 +3,14 @@ srcs_libshared = [ 'rcon-protocol.c', 'mc-protocol.c', 'config-parser.c', + 'systemd.c', 'utils.c', ] +dep_libsystemd = dependency('libsystemd') + deps_libshared = [ + dep_libsystemd, dep_config_h, ] diff --git a/shared/systemd.c b/shared/systemd.c new file mode 100644 index 0000000..c45c7c5 --- /dev/null +++ b/shared/systemd.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include + +#include "utils.h" +#include "config-parser.h" +#include "systemd.h" +#include "config.h" + +static sd_bus *bus = NULL; +static bool bus_failed = false; + +void systemd_delete() +{ + assert_return_silent(bus); + + sd_bus_unref(bus); + bus = NULL; + bus_failed = false; +} + +static sd_bus *get_bus() +{ + int r; + + if (bus_failed) + return NULL; + + if (!bus) { + r = sd_bus_open_user(&bus); + if (r < 0) { + error("failed to connect to user system bus: %s", + strerror(-r)); + bus_failed = true; + return NULL; + } + } + + return bus; +} + +/* + * Check if a systemd service is running. + * + * This is equivalent to (assuming service mc@world1): + * gdbus call --session + * --dest org.freedesktop.systemd1 + * --object-path /org/freedesktop/systemd1/unit/mc_40world1_2eservice + * --method org.freedesktop.DBus.Properties.Get + * "org.freedesktop.systemd1.Unit" + * "ActiveState" + */ +bool systemd_service_running(struct server_config *scfg, const char **error) +{ + sd_bus *bus = get_bus(); + sd_bus_error sderror = SD_BUS_ERROR_NULL; + char *status = NULL; + bool running = false; + int r; + + assert_return(bus && error && scfg && scfg->systemd_service && + scfg->systemd_obj, false); + +again: + r = sd_bus_get_property_string(bus, SYSTEMD_DBUS_SERVICE, + scfg->systemd_obj, + SYSTEMD_DBUS_INTERFACE, "ActiveState", + &sderror, &status); + if (r < 0) { + if (sd_bus_error_get_errno(&sderror) == EINTR) + goto again; + + debug(DBG_SYSD, "failed to get status for server %s (%s): %s", + scfg->systemd_service, scfg->systemd_obj, sderror.message); + *error = "failed to get service status"; + + } else if (streq(status, "active")) { + running = true; + debug(DBG_SYSD, "systemd service %s (%s) is active", + scfg->systemd_service, scfg->systemd_obj); + + } else { + debug(DBG_SYSD, "systemd service %s (%s) is not active (%s)", + scfg->systemd_service, scfg->systemd_obj, status); + *error = "service not active"; + } + + free(status); + sd_bus_error_free(&sderror); + return running; +} + +static bool systemd_service_action(struct server_config *scfg, + const char *action) +{ + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *m = NULL; + sd_bus *bus = get_bus(); + const char *path; + bool performed = false; + int r; + + assert_return(bus && scfg && scfg->systemd_service && + scfg->systemd_obj && action, false); + +again: + r = sd_bus_call_method(bus, SYSTEMD_DBUS_SERVICE, scfg->systemd_obj, + SYSTEMD_DBUS_INTERFACE, action, &error, &m, "s", + "fail"); + if (r < 0) { + if (sd_bus_error_get_errno(&error) == EINTR) + goto again; + + error("failed to perform action %s on systemd service %s: %s", + action, scfg->systemd_service, error.message); + goto out; + } + + r = sd_bus_message_read(m, "o", &path); + if (r < 0) { + error("failed to parse response message: %s", strerror(-r)); + goto out; + } + + verbose("action %s queued for service %s", action, + scfg->systemd_service); + performed = true; + +out: + sd_bus_error_free(&error); + sd_bus_message_unref(m); + return performed; +} + +/* + * Stop systemd service. + * + * This is equivalent to (assuming service mc@world1): + * gdbus call --session + * --dest org.freedesktop.systemd1 + * --object-path /org/freedesktop/systemd1/unit/mc_40world1_2eservice + * --method org.freedesktop.systemd1.Unit.Stop "fail" + */ +bool systemd_service_stop(struct server_config *scfg) +{ + assert_return(scfg, false); + + return systemd_service_action(scfg, "Stop"); +} + +/* + * Start systemd service. + * + * This is equivalent to (assuming service mc@world1): + * gdbus call --session + * --dest org.freedesktop.systemd1 + * --object-path /org/freedesktop/systemd1/unit/mc_40world1_2eservice + * --method org.freedesktop.systemd1.Unit.Start "fail" + */ +bool systemd_service_start(struct server_config *scfg) +{ + assert_return(scfg, false); + + return systemd_service_action(scfg, "Start"); +} diff --git a/shared/systemd.h b/shared/systemd.h new file mode 100644 index 0000000..0da1eca --- /dev/null +++ b/shared/systemd.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef foosharedsystemdhfoo +#define foosharedsystemdhfoo + +void systemd_delete(); + +bool systemd_service_running(struct server_config *scfg, const char **error); + +bool systemd_service_stop(struct server_config *scfg); + +bool systemd_service_start(struct server_config *scfg); + +#endif -- cgit v1.2.3