From: Michael Vogt Date: Fri, 20 Mar 2026 15:25:42 +0000 (+0100) Subject: core: add `io.systemd.Manager.{PowerOff,Reboot,SoftReboot,Halt,Kexec}` X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=621514762443bc536cd489a221a8669fdfd54241;p=thirdparty%2Fsystemd.git core: add `io.systemd.Manager.{PowerOff,Reboot,SoftReboot,Halt,Kexec}` This adds the low-level io.systemd.Manager shutdown support. This is (much) simpler than the logind one. It mimics dbus but uses a shared helper for the simple cases. Note that this is more restrictive than the dbus version. The dbus version uses SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT) but the varlink version uses varlink_check_privileged_peer(link). This is mostly because I'm not sure how to do the equivalent in a race-free way. Thanks to Daan for suggesting this. --- diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 5e02d189072..fec53341cae 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1690,6 +1690,7 @@ static int method_soft_reboot(sd_bus_message *message, void *userdata, sd_bus_er return sd_bus_error_set(reterr_error, SD_BUS_ERROR_NOT_SUPPORTED, "Soft reboot is only supported by system manager."); + /* Keep the checks in sync with varlink-manager.c:vl_method_soft_reboot_manager() */ r = mac_selinux_access_check(message, "reboot", reterr_error); if (r < 0) return r; diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c index d00f7e5a248..53db0a5a2e6 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -16,6 +16,7 @@ #include "glyph-util.h" #include "json-util.h" #include "manager.h" +#include "path-util.h" #include "pidref.h" #include "selinux-access.h" #include "set.h" @@ -398,3 +399,84 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par return ret; } + +static int manager_do_set_objective(sd_varlink *link, sd_json_variant *parameters, ManagerObjective objective, const char *selinux_permission, bool can_do_root) { + Manager *m = ASSERT_PTR(sd_varlink_get_userdata(link)); + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + _cleanup_free_ char *rt = NULL; + const char *root = NULL; + int r; + + assert(link); + assert(parameters); + + if (!MANAGER_IS_SYSTEM(m)) + return sd_varlink_error(link, SD_VARLINK_ERROR_METHOD_NOT_IMPLEMENTED, NULL); + + if (can_do_root) { + static const sd_json_dispatch_field dispatch_table[] = { + { "root", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 }, + {} + }; + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &root); + } else + r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL); + if (r != 0) + return r; + + r = mac_selinux_access_check_varlink(link, selinux_permission); + if (r < 0) + return r; + + /* dbus uses SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT) in its checking. We cannot do the same + * because reading capabilities from /proc is racy (TOCTOU). So we use the stricter check + * TODO: figure out a way to check for CAP_SYS_BOOT */ + r = varlink_check_privileged_peer(link); + if (r < 0) + return r; + + if (!isempty(root)) { + if (!path_is_valid(root)) + return sd_varlink_error_invalid_parameter_name(link, "root"); + if (!path_is_absolute(root)) + return sd_varlink_error_invalid_parameter_name(link, "root"); + + r = path_simplify_alloc(root, &rt); + if (r < 0) + return r; + } + + /* We need at least the pidref, otherwise there's nothing to log about. */ + r = varlink_get_peer_pidref(link, &pidref); + if (r < 0) + log_debug_errno(r, "Failed to get peer pidref, ignoring: %m"); + else + manager_log_caller(m, &pidref, manager_objective_to_string(objective)); + + if (can_do_root) + free_and_replace(m->switch_root, rt); + m->objective = objective; + + return sd_varlink_reply(link, NULL); +} + +int vl_method_poweroff_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_set_objective(link, parameters, MANAGER_POWEROFF, "halt", /* can_do_root= */ false); +} + +int vl_method_reboot_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_set_objective(link, parameters, MANAGER_REBOOT, "reboot", /* can_do_root= */ false); +} + +int vl_method_halt_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_set_objective(link, parameters, MANAGER_HALT, "halt", /* can_do_root= */ false); +} + +int vl_method_kexec_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_set_objective(link, parameters, MANAGER_KEXEC, "reboot", /* can_do_root= */ false); +} + +int vl_method_soft_reboot_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_set_objective(link, parameters, MANAGER_SOFT_REBOOT, "reboot", /* can_do_root= */ true); +} diff --git a/src/core/varlink-manager.h b/src/core/varlink-manager.h index e5111eb58dc..0e477e761b0 100644 --- a/src/core/varlink-manager.h +++ b/src/core/varlink-manager.h @@ -9,3 +9,8 @@ int vl_method_describe_manager(sd_varlink *link, sd_json_variant *parameters, sd int vl_method_reexecute_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_reload_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_poweroff_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_reboot_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_halt_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_kexec_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_soft_reboot_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/core/varlink.c b/src/core/varlink.c index ec4f8abad95..77b2d3bd829 100644 --- a/src/core/varlink.c +++ b/src/core/varlink.c @@ -390,6 +390,11 @@ int manager_setup_varlink_server(Manager *m) { "io.systemd.Manager.Reexecute", vl_method_reexecute_manager, "io.systemd.Manager.Reload", vl_method_reload_manager, "io.systemd.Manager.EnqueueMarkedJobs", vl_method_enqueue_marked_jobs_manager, + "io.systemd.Manager.PowerOff", vl_method_poweroff_manager, + "io.systemd.Manager.Reboot", vl_method_reboot_manager, + "io.systemd.Manager.Halt", vl_method_halt_manager, + "io.systemd.Manager.KExec", vl_method_kexec_manager, + "io.systemd.Manager.SoftReboot", vl_method_soft_reboot_manager, "io.systemd.Unit.List", vl_method_list_units, "io.systemd.Unit.SetProperties", vl_method_set_unit_properties, "io.systemd.service.Ping", varlink_method_ping, diff --git a/src/shared/varlink-io.systemd.Manager.c b/src/shared/varlink-io.systemd.Manager.c index cb304f22950..f33cab34b3d 100644 --- a/src/shared/varlink-io.systemd.Manager.c +++ b/src/shared/varlink-io.systemd.Manager.c @@ -193,6 +193,15 @@ static SD_VARLINK_DEFINE_METHOD_FULL( SD_VARLINK_FIELD_COMMENT("Job enqueue error message (on failure)"), SD_VARLINK_DEFINE_OUTPUT(errorMessage, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD(PowerOff); +static SD_VARLINK_DEFINE_METHOD(Reboot); +static SD_VARLINK_DEFINE_METHOD(Halt); +static SD_VARLINK_DEFINE_METHOD(KExec); +static SD_VARLINK_DEFINE_METHOD( + SoftReboot, + SD_VARLINK_FIELD_COMMENT("New root directory for the soft reboot"), + SD_VARLINK_DEFINE_INPUT(root, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); + static SD_VARLINK_DEFINE_ERROR(RateLimitReached); SD_VARLINK_DEFINE_INTERFACE( @@ -205,6 +214,16 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_method_Reload, SD_VARLINK_SYMBOL_COMMENT("Enqueue all marked jobs"), &vl_method_EnqueueMarkedJobs, + SD_VARLINK_SYMBOL_COMMENT("Power off the system"), + &vl_method_PowerOff, + SD_VARLINK_SYMBOL_COMMENT("Reboot the system"), + &vl_method_Reboot, + SD_VARLINK_SYMBOL_COMMENT("Halt the system"), + &vl_method_Halt, + SD_VARLINK_SYMBOL_COMMENT("Reboot the system via kexec"), + &vl_method_KExec, + SD_VARLINK_SYMBOL_COMMENT("Soft-reboot the userspace"), + &vl_method_SoftReboot, &vl_error_RateLimitReached, &vl_type_ManagerContext, &vl_type_ManagerRuntime,