/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include "utils.h" #include "config-parser.h" #include "systemd.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"); }