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 --- shared/meson.build | 4 ++ shared/systemd.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++ shared/systemd.h | 13 +++++ 3 files changed, 184 insertions(+) create mode 100644 shared/systemd.c create mode 100644 shared/systemd.h (limited to 'shared') 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