summaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/meson.build4
-rw-r--r--shared/systemd.c167
-rw-r--r--shared/systemd.h13
3 files changed, 184 insertions, 0 deletions
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 <string.h>
+#include <stdlib.h>
+#include <systemd/sd-bus.h>
+#include <errno.h>
+
+#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