From c2283986f96ee6fde0765ca7da611d2047de3b40 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 20 Mar 2026 13:37:42 +0100 Subject: [PATCH 1/5] logind: move reset_scheduled_shutdown() to new logind-shutdown.c This function operates on generic Manager state and will be needed by the varlink shutdown interface too. Move it out of logind-dbus.c into a new logind-shutdown.c, alongside the SHUTDOWN_SCHEDULE_FILE define and use `manager_reset_scheduled_shutdown() as the new name. --- src/login/logind-dbus.c | 42 +++++++------------------------------ src/login/logind-shutdown.c | 32 ++++++++++++++++++++++++++++ src/login/logind-shutdown.h | 8 +++++++ src/login/meson.build | 1 + 4 files changed, 49 insertions(+), 34 deletions(-) create mode 100644 src/login/logind-shutdown.c create mode 100644 src/login/logind-shutdown.h diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 14ec1f90ab4..d02d836c75e 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -45,6 +45,7 @@ #include "logind-seat.h" #include "logind-seat-dbus.h" #include "logind-session-dbus.h" +#include "logind-shutdown.h" #include "logind-user.h" #include "logind-user-dbus.h" #include "logind-utmp.h" @@ -76,10 +77,6 @@ */ #define WALL_MESSAGE_MAX 4096U -#define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled" - -static void reset_scheduled_shutdown(Manager *m); - static int get_sender_session( Manager *m, sd_bus_message *message, @@ -2454,7 +2451,7 @@ static int method_do_shutdown_or_sleep( /* reset case we're shorting a scheduled shutdown */ m->unlink_nologin = false; - reset_scheduled_shutdown(m); + manager_reset_scheduled_shutdown(m); m->scheduled_shutdown_timeout = 0; m->scheduled_shutdown_action = action; @@ -2568,29 +2565,6 @@ static usec_t nologin_timeout_usec(usec_t elapse) { return LESS_BY(elapse, 5 * USEC_PER_MINUTE); } -static void reset_scheduled_shutdown(Manager *m) { - assert(m); - - m->scheduled_shutdown_timeout_source = sd_event_source_disable_unref(m->scheduled_shutdown_timeout_source); - m->wall_message_timeout_source = sd_event_source_disable_unref(m->wall_message_timeout_source); - m->nologin_timeout_source = sd_event_source_disable_unref(m->nologin_timeout_source); - - m->scheduled_shutdown_action = _HANDLE_ACTION_INVALID; - m->scheduled_shutdown_timeout = USEC_INFINITY; - m->scheduled_shutdown_uid = UID_INVALID; - m->scheduled_shutdown_tty = mfree(m->scheduled_shutdown_tty); - m->shutdown_dry_run = false; - - if (m->unlink_nologin) { - (void) unlink_or_warn("/run/nologin"); - m->unlink_nologin = false; - } - - (void) unlink(SHUTDOWN_SCHEDULE_FILE); - - manager_send_changed(m, "ScheduledShutdown"); -} - static int update_schedule_file(Manager *m) { _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -2669,7 +2643,7 @@ static int manager_scheduled_shutdown_handler( bus_manager_log_shutdown(m, a); log_info("Running in dry run, suppressing action."); - reset_scheduled_shutdown(m); + manager_reset_scheduled_shutdown(m); return 0; } @@ -2683,7 +2657,7 @@ static int manager_scheduled_shutdown_handler( return 0; error: - reset_scheduled_shutdown(m); + manager_reset_scheduled_shutdown(m); return r; } @@ -2738,7 +2712,7 @@ void manager_load_scheduled_shutdown(Manager *m) { "TTY", &tty); /* reset will delete the file */ - reset_scheduled_shutdown(m); + manager_reset_scheduled_shutdown(m); if (r == -ENOENT) return; @@ -2784,7 +2758,7 @@ void manager_load_scheduled_shutdown(Manager *m) { r = manager_setup_shutdown_timers(m); if (r < 0) - return reset_scheduled_shutdown(m); + return manager_reset_scheduled_shutdown(m); (void) manager_setup_wall_message_timer(m); (void) update_schedule_file(m); @@ -2853,7 +2827,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ r = update_schedule_file(m); if (r < 0) { - reset_scheduled_shutdown(m); + manager_reset_scheduled_shutdown(m); return r; } @@ -2913,7 +2887,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd } cancel_delayed_action(m); - reset_scheduled_shutdown(m); + manager_reset_scheduled_shutdown(m); return sd_bus_reply_method_return(message, "b", true); } diff --git a/src/login/logind-shutdown.c b/src/login/logind-shutdown.c new file mode 100644 index 00000000000..eec4832ea85 --- /dev/null +++ b/src/login/logind-shutdown.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-event.h" + +#include "alloc-util.h" +#include "fs-util.h" +#include "logind.h" +#include "logind-dbus.h" +#include "logind-shutdown.h" + +void manager_reset_scheduled_shutdown(Manager *m) { + assert(m); + + m->scheduled_shutdown_timeout_source = sd_event_source_disable_unref(m->scheduled_shutdown_timeout_source); + m->wall_message_timeout_source = sd_event_source_disable_unref(m->wall_message_timeout_source); + m->nologin_timeout_source = sd_event_source_disable_unref(m->nologin_timeout_source); + + m->scheduled_shutdown_action = _HANDLE_ACTION_INVALID; + m->scheduled_shutdown_timeout = USEC_INFINITY; + m->scheduled_shutdown_uid = UID_INVALID; + m->scheduled_shutdown_tty = mfree(m->scheduled_shutdown_tty); + m->shutdown_dry_run = false; + + if (m->unlink_nologin) { + (void) unlink_or_warn("/run/nologin"); + m->unlink_nologin = false; + } + + (void) unlink(SHUTDOWN_SCHEDULE_FILE); + + manager_send_changed(m, "ScheduledShutdown"); +} diff --git a/src/login/logind-shutdown.h b/src/login/logind-shutdown.h new file mode 100644 index 00000000000..46f37325a29 --- /dev/null +++ b/src/login/logind-shutdown.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "logind-forward.h" + +#define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled" + +void manager_reset_scheduled_shutdown(Manager *m); diff --git a/src/login/meson.build b/src/login/meson.build index d6654ff5ced..390960d5f6c 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -21,6 +21,7 @@ systemd_logind_extract_sources = files( 'logind-session-dbus.c', 'logind-session-device.c', 'logind-session.c', + 'logind-shutdown.c', 'logind-user-dbus.c', 'logind-user.c', 'logind-utmp.c', From d1d72563e0d90f924c8e789a978ff95196e1b969 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 20 Mar 2026 13:37:59 +0100 Subject: [PATCH 2/5] logind: add io.systemd.Shutdown varlink Shutdown interface The shutdown interface is currently only exposed via dbus. This commit adds a comparable varlink implementation. It is inspired by the existing dbus methods and implements PowerOff, Reboot, Halt, Kexec, SoftReboot. It is (intentional) simpler than the dbus for now, i.e. strictly root only. To match dbus we will need to the functionality of verify_shutdown_creds() which is dbus-ish right now and would need some refactor. For the same reason it does not do the Can* methods - we will need the verify_shutdown_creds() equivalent first. --- man/systemd-logind.service.xml | 9 ++ src/login/logind-varlink.c | 116 ++++++++++++++++++++++- src/shared/meson.build | 1 + src/shared/varlink-io.systemd.Shutdown.c | 52 ++++++++++ src/shared/varlink-io.systemd.Shutdown.h | 6 ++ units/systemd-logind-varlink.socket | 2 +- 6 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 src/shared/varlink-io.systemd.Shutdown.c create mode 100644 src/shared/varlink-io.systemd.Shutdown.h diff --git a/man/systemd-logind.service.xml b/man/systemd-logind.service.xml index 34f6330bf7c..30d9dab14e8 100644 --- a/man/systemd-logind.service.xml +++ b/man/systemd-logind.service.xml @@ -84,6 +84,15 @@ org.freedesktop.LogControl15 for information about the D-Bus APIs systemd-logind provides. + In addition to the D-Bus interface, systemd-logind also provides a Varlink + interface io.systemd.Shutdown for shutting down or rebooting the system. It + supports PowerOff, Reboot, Halt, + KExec, and SoftReboot methods. Each method accepts an + optional skipInhibitors boolean parameter to bypass active block inhibitors + (matching the SD_LOGIND_SKIP_INHIBITORS flag of the D-Bus interface). The + interface can be queried with + varlinkctl introspect /run/systemd/io.systemd.Login io.systemd.Shutdown. + For more information see Inhibitor Locks. diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c index ae2e32d3faf..a14569cacd7 100644 --- a/src/login/logind-varlink.c +++ b/src/login/logind-varlink.c @@ -4,15 +4,19 @@ #include "sd-event.h" #include "alloc-util.h" +#include "bus-error.h" +#include "bus-polkit.h" #include "cgroup-util.h" #include "fd-util.h" #include "format-util.h" #include "hashmap.h" #include "json-util.h" -#include "logind-session.h" +#include "login-util.h" #include "logind.h" #include "logind-dbus.h" #include "logind-seat.h" +#include "logind-session.h" +#include "logind-shutdown.h" #include "logind-user.h" #include "logind-varlink.h" #include "strv.h" @@ -20,6 +24,7 @@ #include "user-record.h" #include "user-util.h" #include "varlink-io.systemd.Login.h" +#include "varlink-io.systemd.Shutdown.h" #include "varlink-io.systemd.service.h" #include "varlink-util.h" @@ -336,6 +341,107 @@ static int vl_method_release_session(sd_varlink *link, sd_json_variant *paramete return sd_varlink_reply(link, NULL); } +static int setup_wall_message_timer(Manager *m, sd_varlink *link) { + uid_t uid = UID_INVALID; + int r; + + (void) sd_varlink_get_peer_uid(link, &uid); + m->scheduled_shutdown_uid = uid; + + _cleanup_free_ char *tty = NULL; + pid_t pid = 0; + r = sd_varlink_get_peer_pid(link, &pid); + if (r >= 0) + (void) get_ctty(pid, /* ret_devnr= */ NULL, &tty); + + r = free_and_strdup_warn(&m->scheduled_shutdown_tty, tty); + if (r < 0) + return log_oom(); + + return manager_setup_wall_message_timer(m); +} + +static int manager_do_shutdown_action(sd_varlink *link, sd_json_variant *parameters, HandleAction action) { + Manager *m = ASSERT_PTR(sd_varlink_get_userdata(link)); + int skip_inhibitors = -1; + uid_t uid; + int r; + + static const sd_json_dispatch_field dispatch_table[] = { + { "skipInhibitors", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, 0, 0 }, + {} + }; + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &skip_inhibitors); + if (r != 0) + return r; + + uint64_t flags = skip_inhibitors > 0 ? SD_LOGIND_SKIP_INHIBITORS : 0; + + const HandleActionData *a = handle_action_lookup(action); + assert(a); + + /* TODO: provide full polkit support (matching the D-Bus verify_shutdown_creds() with + * multiple-sessions and inhibitor-override checks). This requires some refactor. */ + r = varlink_check_privileged_peer(link); + if (r < 0) + return r; + + r = sd_varlink_get_peer_uid(link, &uid); + if (r < 0) + return r; + + /* Check for active inhibitors, mirroring the D-Bus verify_shutdown_creds() logic, + * we need this to ensure we handle the strong INHIBIT_BLOCK + * TODO: drop once we do the verify_shutdown_creds() */ + if (manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, /* ret_offending= */ NULL) && + !FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS)) + return sd_varlink_error(link, "io.systemd.Shutdown.BlockedByInhibitor", /* parameters= */ NULL); + + if (m->delayed_action) + return sd_varlink_error(link, "io.systemd.Shutdown.AlreadyInProgress", /* parameters= */ NULL); + + /* Reset in case we're short-circuiting a scheduled shutdown */ + m->unlink_nologin = false; + manager_reset_scheduled_shutdown(m); + + m->scheduled_shutdown_timeout = 0; + m->scheduled_shutdown_action = action; + + (void) setup_wall_message_timer(m, link); + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + r = bus_manager_shutdown_or_sleep_now_or_later(m, a, &error); + if (r < 0) { + log_warning_errno(r, "Failed to execute %s: %s", + handle_action_to_string(action), + bus_error_message(&error, r)); + return sd_varlink_error_errno(link, r); + } + + return sd_varlink_reply(link, NULL); +} + +static int vl_method_power_off(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_shutdown_action(link, parameters, HANDLE_POWEROFF); +} + +static int vl_method_reboot(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_shutdown_action(link, parameters, HANDLE_REBOOT); +} + +static int vl_method_halt(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_shutdown_action(link, parameters, HANDLE_HALT); +} + +static int vl_method_kexec(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_shutdown_action(link, parameters, HANDLE_KEXEC); +} + +static int vl_method_soft_reboot(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return manager_do_shutdown_action(link, parameters, HANDLE_SOFT_REBOOT); +} + int manager_varlink_init(Manager *m, int fd) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL; _unused_ _cleanup_close_ int fd_close = fd; @@ -358,14 +464,20 @@ int manager_varlink_init(Manager *m, int fd) { r = sd_varlink_server_add_interface_many( s, &vl_interface_io_systemd_Login, + &vl_interface_io_systemd_Shutdown, &vl_interface_io_systemd_service); if (r < 0) - return log_error_errno(r, "Failed to add Login interface to varlink server: %m"); + return log_error_errno(r, "Failed to add varlink interfaces: %m"); r = sd_varlink_server_bind_method_many( s, "io.systemd.Login.CreateSession", vl_method_create_session, "io.systemd.Login.ReleaseSession", vl_method_release_session, + "io.systemd.Shutdown.PowerOff", vl_method_power_off, + "io.systemd.Shutdown.Reboot", vl_method_reboot, + "io.systemd.Shutdown.Halt", vl_method_halt, + "io.systemd.Shutdown.KExec", vl_method_kexec, + "io.systemd.Shutdown.SoftReboot", vl_method_soft_reboot, "io.systemd.service.Ping", varlink_method_ping, "io.systemd.service.SetLogLevel", varlink_method_set_log_level, "io.systemd.service.GetEnvironment", varlink_method_get_environment); diff --git a/src/shared/meson.build b/src/shared/meson.build index 2c6207d4944..22dccf0e2a7 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -230,6 +230,7 @@ shared_sources = files( 'varlink-io.systemd.Resolve.c', 'varlink-io.systemd.Resolve.Hook.c', 'varlink-io.systemd.Resolve.Monitor.c', + 'varlink-io.systemd.Shutdown.c', 'varlink-io.systemd.Udev.c', 'varlink-io.systemd.Unit.c', 'varlink-io.systemd.UserDatabase.c', diff --git a/src/shared/varlink-io.systemd.Shutdown.c b/src/shared/varlink-io.systemd.Shutdown.c new file mode 100644 index 00000000000..96f4fef04d6 --- /dev/null +++ b/src/shared/varlink-io.systemd.Shutdown.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "bus-polkit.h" +#include "varlink-io.systemd.Shutdown.h" + +static SD_VARLINK_DEFINE_METHOD( + PowerOff, + VARLINK_DEFINE_POLKIT_INPUT, + SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"), + SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD( + Reboot, + VARLINK_DEFINE_POLKIT_INPUT, + SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"), + SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD( + Halt, + VARLINK_DEFINE_POLKIT_INPUT, + SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"), + SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD( + KExec, + VARLINK_DEFINE_POLKIT_INPUT, + SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"), + SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD( + SoftReboot, + VARLINK_DEFINE_POLKIT_INPUT, + SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"), + SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE)); + +static SD_VARLINK_DEFINE_ERROR(AlreadyInProgress); +static SD_VARLINK_DEFINE_ERROR(BlockedByInhibitor); + +SD_VARLINK_DEFINE_INTERFACE( + io_systemd_Shutdown, + "io.systemd.Shutdown", + SD_VARLINK_INTERFACE_COMMENT("APIs for shutting down or rebooting the system."), + 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("Reboot userspace only"), + &vl_method_SoftReboot, + SD_VARLINK_SYMBOL_COMMENT("Another shutdown or sleep operation is already in progress"), + &vl_error_AlreadyInProgress, + SD_VARLINK_SYMBOL_COMMENT("Operation denied due to active block inhibitor"), + &vl_error_BlockedByInhibitor); diff --git a/src/shared/varlink-io.systemd.Shutdown.h b/src/shared/varlink-io.systemd.Shutdown.h new file mode 100644 index 00000000000..e97853b0bc7 --- /dev/null +++ b/src/shared/varlink-io.systemd.Shutdown.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-varlink-idl.h" + +extern const sd_varlink_interface vl_interface_io_systemd_Shutdown; diff --git a/units/systemd-logind-varlink.socket b/units/systemd-logind-varlink.socket index 1d3652e0497..377eac7006f 100644 --- a/units/systemd-logind-varlink.socket +++ b/units/systemd-logind-varlink.socket @@ -13,7 +13,7 @@ Documentation=man:systemd-logind.service(8) [Socket] ListenStream=/run/systemd/io.systemd.Login -Symlinks=/run/varlink/registry/io.systemd.Login +Symlinks=/run/varlink/registry/io.systemd.Login /run/varlink/registry/io.systemd.Shutdown /run/systemd/io.systemd.Shutdown FileDescriptorName=varlink SocketMode=0666 Service=systemd-logind.service From c1b928c810c8d231657393ad3499d6d2940059b3 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 20 Mar 2026 13:42:30 +0100 Subject: [PATCH 3/5] logind: move verify_shutdown_creds() to logind-shutdown.c Move verify_shutdown_creds() and its helper have_multiple_sessions() from logind-dbus.c to logind-shutdown.c so that they can be reused by the varlink transport. No functional changes. Also prefix both with `manager_` now that they are public. --- src/login/logind-dbus.c | 139 +-------------------------------- src/login/logind-shutdown.c | 148 ++++++++++++++++++++++++++++++++++++ src/login/logind-shutdown.h | 2 + 3 files changed, 153 insertions(+), 136 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index d02d836c75e..52bdde1f08a 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1863,24 +1863,6 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_reply_method_return(message, NULL); } -static int have_multiple_sessions( - Manager *m, - uid_t uid) { - - Session *session; - - assert(m); - - /* Check for other users' sessions. Greeter sessions do not - * count, and non-login sessions do not count either. */ - HASHMAP_FOREACH(session, m->sessions) - if (SESSION_CLASS_IS_INHIBITOR_LIKE(session->class) && - session->user->user_record->uid != uid) - return true; - - return false; -} - static int bus_manager_log_shutdown( Manager *m, const HandleActionData *a) { @@ -2186,121 +2168,6 @@ int bus_manager_shutdown_or_sleep_now_or_later( return r; } -static int verify_shutdown_creds( - Manager *m, - sd_bus_message *message, - const HandleActionData *a, - uint64_t flags, - sd_bus_error *error) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - bool multiple_sessions, blocked, interactive; - _unused_ bool error_or_denial = false; - Inhibitor *offending = NULL; - uid_t uid; - int r; - - assert(m); - assert(a); - assert(message); - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = have_multiple_sessions(m, uid); - if (r < 0) - return r; - - multiple_sessions = r > 0; - blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, &offending); - interactive = flags & SD_LOGIND_INTERACTIVE; - - if (multiple_sessions) { - r = bus_verify_polkit_async_full( - message, - a->polkit_action_multiple_sessions, - /* details= */ NULL, - /* good_user= */ UID_INVALID, - interactive ? POLKIT_ALLOW_INTERACTIVE : 0, - &m->polkit_registry, - error); - if (r < 0) { - /* If we get -EBUSY, it means a polkit decision was made, but not for - * this action in particular. Assuming we are blocked on inhibitors, - * ignore that error and allow the decision to be revealed below. */ - if (blocked && r == -EBUSY) - error_or_denial = true; - else - return r; - } - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - if (blocked) { - PolkitFlags polkit_flags = 0; - - /* With a strong inhibitor, if the skip flag is not set, reject outright. - * With a weak inhibitor, if root is asking and the root flag is set, reject outright. - * All else, check polkit first. */ - if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) && - (offending->mode != INHIBIT_BLOCK_WEAK || - (uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS)))) - return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK, - "Operation denied due to active block inhibitor"); - - /* We want to always ask here, even for root, to only allow bypassing if explicitly allowed - * by polkit, unless a weak blocker is used, in which case it will be authorized. */ - if (offending->mode != INHIBIT_BLOCK_WEAK) - polkit_flags |= POLKIT_ALWAYS_QUERY; - - if (interactive) - polkit_flags |= POLKIT_ALLOW_INTERACTIVE; - - r = bus_verify_polkit_async_full( - message, - a->polkit_action_ignore_inhibit, - /* details= */ NULL, - /* good_user= */ UID_INVALID, - polkit_flags, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - if (!multiple_sessions && !blocked) { - r = bus_verify_polkit_async_full( - message, - a->polkit_action, - /* details= */ NULL, - /* good_user= */ UID_INVALID, - interactive ? POLKIT_ALLOW_INTERACTIVE : 0, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - /* If error_or_denial was set above, it means that a polkit denial or - * error was deferred for a future call to bus_verify_polkit_async_full() - * to catch. In any case, it also means that the payload guarded by - * these polkit calls should never be executed, and hence we should - * never reach this point. */ - assert(!error_or_denial); - - return 0; -} - static int setup_wall_message_timer(Manager *m, sd_bus_message* message) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; int r; @@ -2439,7 +2306,7 @@ static int method_do_shutdown_or_sleep( } else if (!a) assert_se(a = handle_action_lookup(action)); - r = verify_shutdown_creds(m, message, a, flags, error); + r = manager_verify_shutdown_creds(m, message, a, flags, error); if (r != 0) return r; @@ -2793,7 +2660,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ assert_se(a = handle_action_lookup(handle)); assert(a->polkit_action); - r = verify_shutdown_creds(m, message, a, 0, error); + r = manager_verify_shutdown_creds(m, message, a, 0, error); if (r != 0) return r; @@ -2943,7 +2810,7 @@ static int method_can_shutdown_or_sleep( if (r < 0) return r; - r = have_multiple_sessions(m, uid); + r = manager_have_multiple_sessions(m, uid); if (r < 0) return r; diff --git a/src/login/logind-shutdown.c b/src/login/logind-shutdown.c index eec4832ea85..96698cb55bc 100644 --- a/src/login/logind-shutdown.c +++ b/src/login/logind-shutdown.c @@ -1,12 +1,160 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + +#include "sd-bus.h" #include "sd-event.h" #include "alloc-util.h" +#include "bus-common-errors.h" +#include "bus-polkit.h" #include "fs-util.h" +#include "hashmap.h" +#include "login-util.h" #include "logind.h" #include "logind-dbus.h" +#include "logind-inhibit.h" +#include "logind-session.h" #include "logind-shutdown.h" +#include "logind-user.h" +#include "user-record.h" + +int manager_have_multiple_sessions( + Manager *m, + uid_t uid) { + + Session *session; + + assert(m); + + /* Check for other users' sessions. Greeter sessions do not + * count, and non-login sessions do not count either. */ + HASHMAP_FOREACH(session, m->sessions) + if (SESSION_CLASS_IS_INHIBITOR_LIKE(session->class) && + session->user->user_record->uid != uid) + return true; + + return false; +} + +int manager_verify_shutdown_creds( + Manager *m, + sd_bus_message *message, + const HandleActionData *a, + uint64_t flags, + sd_bus_error *error) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + bool multiple_sessions, blocked, interactive; + _unused_ bool error_or_denial = false; + Inhibitor *offending = NULL; + uid_t uid; + int r; + + assert(m); + assert(a); + assert(message); + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + r = manager_have_multiple_sessions(m, uid); + if (r < 0) + return r; + + multiple_sessions = r > 0; + blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, &offending); + interactive = flags & SD_LOGIND_INTERACTIVE; + + if (multiple_sessions) { + r = bus_verify_polkit_async_full( + message, + a->polkit_action_multiple_sessions, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + interactive ? POLKIT_ALLOW_INTERACTIVE : 0, + &m->polkit_registry, + error); + if (r < 0) { + /* If we get -EBUSY, it means a polkit decision was made, but not for + * this action in particular. Assuming we are blocked on inhibitors, + * ignore that error and allow the decision to be revealed below. */ + if (blocked && r == -EBUSY) + error_or_denial = true; + else + return r; + } + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + if (blocked) { + PolkitFlags polkit_flags = 0; + + /* With a strong inhibitor, if the skip flag is not set, reject outright. + * With a weak inhibitor, if root is asking and the root flag is set, reject outright. + * All else, check polkit first. */ + if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) && + (offending->mode != INHIBIT_BLOCK_WEAK || + (uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS)))) { + if (error) + return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK, + "Operation denied due to active block inhibitor"); + else + return -EACCES; + } + + /* We want to always ask here, even for root, to only allow bypassing if explicitly allowed + * by polkit, unless a weak blocker is used, in which case it will be authorized. */ + if (offending->mode != INHIBIT_BLOCK_WEAK) + polkit_flags |= POLKIT_ALWAYS_QUERY; + + if (interactive) + polkit_flags |= POLKIT_ALLOW_INTERACTIVE; + + r = bus_verify_polkit_async_full( + message, + a->polkit_action_ignore_inhibit, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + polkit_flags, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + if (!multiple_sessions && !blocked) { + r = bus_verify_polkit_async_full( + message, + a->polkit_action, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + interactive ? POLKIT_ALLOW_INTERACTIVE : 0, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + /* If error_or_denial was set above, it means that a polkit denial or + * error was deferred for a future call to bus_verify_polkit_async_full() + * to catch. In any case, it also means that the payload guarded by + * these polkit calls should never be executed, and hence we should + * never reach this point. */ + assert(!error_or_denial); + + return 0; +} void manager_reset_scheduled_shutdown(Manager *m) { assert(m); diff --git a/src/login/logind-shutdown.h b/src/login/logind-shutdown.h index 46f37325a29..b8c70a9630b 100644 --- a/src/login/logind-shutdown.h +++ b/src/login/logind-shutdown.h @@ -5,4 +5,6 @@ #define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled" +int manager_have_multiple_sessions(Manager *m, uid_t uid); +int manager_verify_shutdown_creds(Manager *m, sd_bus_message *message, const HandleActionData *a, uint64_t flags, sd_bus_error *error); void manager_reset_scheduled_shutdown(Manager *m); From db426d147d0ea8082bd8c69628aba63d3ddd635e Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 20 Mar 2026 13:49:50 +0100 Subject: [PATCH 4/5] logind: extend verify_shutdown_creds() to take `sd_varlink *link` To properly support a shutdown interface with varlink we need the functionality of verify_shutdown_creds(). This is currently dbus only. There are some options: 1. refactor and abstract so that verify_shutdown_creds() is agnostic 2. provide a equivalent function with varlink 3. allow to call it with either a dbus or a varlink message. The most elegant of course is (1) but it makes reviewing harder and has a higher regression risk. It will also be more code. Doing (2) has the risk of drift, i.e. we will need to keep the two functions in sync and not forget about it ever. So this commit opts for (3): allowing either dbus or varlink. This is quite ugly, however the big advantage is that its very simple to review as the dbus/varlink branches mirror each other. And there is no risk of drift the dbus/varlink options are close to each other. It unlikely we get a third bus, so it will most likely stay this way. It is still ugly though so I can understand if this is undesired and I can look into (1) if its too ugly. With this function avaialble logind-varlink.c is now updated to use it. --- src/login/logind-dbus.c | 4 +- src/login/logind-shutdown.c | 117 ++++++++++++++++------- src/login/logind-shutdown.h | 7 +- src/login/logind-varlink.c | 19 +--- src/shared/varlink-io.systemd.Shutdown.c | 7 +- 5 files changed, 99 insertions(+), 55 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 52bdde1f08a..ac5da453d64 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2306,7 +2306,7 @@ static int method_do_shutdown_or_sleep( } else if (!a) assert_se(a = handle_action_lookup(action)); - r = manager_verify_shutdown_creds(m, message, a, flags, error); + r = manager_verify_shutdown_creds(m, message, /* link= */ NULL, a, flags, error); if (r != 0) return r; @@ -2660,7 +2660,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ assert_se(a = handle_action_lookup(handle)); assert(a->polkit_action); - r = manager_verify_shutdown_creds(m, message, a, 0, error); + r = manager_verify_shutdown_creds(m, message, /* link= */ NULL, a, 0, error); if (r != 0) return r; diff --git a/src/login/logind-shutdown.c b/src/login/logind-shutdown.c index 96698cb55bc..d79843a287f 100644 --- a/src/login/logind-shutdown.c +++ b/src/login/logind-shutdown.c @@ -4,8 +4,8 @@ #include "sd-bus.h" #include "sd-event.h" +#include "sd-varlink.h" -#include "alloc-util.h" #include "bus-common-errors.h" #include "bus-polkit.h" #include "fs-util.h" @@ -40,11 +40,11 @@ int manager_have_multiple_sessions( int manager_verify_shutdown_creds( Manager *m, sd_bus_message *message, + sd_varlink *link, const HandleActionData *a, uint64_t flags, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; bool multiple_sessions, blocked, interactive; _unused_ bool error_or_denial = false; Inhibitor *offending = NULL; @@ -53,15 +53,24 @@ int manager_verify_shutdown_creds( assert(m); assert(a); - assert(message); + assert(!!message != !!link); /* exactly one transport */ + assert(!link || !error); /* varlink doesn't use sd_bus_error */ - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; + if (message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + } else { + r = sd_varlink_get_peer_uid(link, &uid); + if (r < 0) + return r; + } r = manager_have_multiple_sessions(m, uid); if (r < 0) @@ -72,14 +81,25 @@ int manager_verify_shutdown_creds( interactive = flags & SD_LOGIND_INTERACTIVE; if (multiple_sessions) { - r = bus_verify_polkit_async_full( - message, - a->polkit_action_multiple_sessions, - /* details= */ NULL, - /* good_user= */ UID_INVALID, - interactive ? POLKIT_ALLOW_INTERACTIVE : 0, - &m->polkit_registry, - error); + if (message) + r = bus_verify_polkit_async_full( + message, + a->polkit_action_multiple_sessions, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + interactive ? POLKIT_ALLOW_INTERACTIVE : 0, + &m->polkit_registry, + error); + else + r = varlink_verify_polkit_async_full( + link, + m->bus, + a->polkit_action_multiple_sessions, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + interactive ? POLKIT_ALLOW_INTERACTIVE : 0, + &m->polkit_registry); + if (r < 0) { /* If we get -EBUSY, it means a polkit decision was made, but not for * this action in particular. Assuming we are blocked on inhibitors, @@ -102,11 +122,16 @@ int manager_verify_shutdown_creds( if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) && (offending->mode != INHIBIT_BLOCK_WEAK || (uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS)))) { + if (link) + return sd_varlink_errorbo( + link, + "io.systemd.Shutdown.BlockedByInhibitor", + SD_JSON_BUILD_PAIR_STRING("who", offending->who), + SD_JSON_BUILD_PAIR_STRING("why", offending->why)); if (error) return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK, "Operation denied due to active block inhibitor"); - else - return -EACCES; + return -EACCES; } /* We want to always ask here, even for root, to only allow bypassing if explicitly allowed @@ -117,14 +142,25 @@ int manager_verify_shutdown_creds( if (interactive) polkit_flags |= POLKIT_ALLOW_INTERACTIVE; - r = bus_verify_polkit_async_full( - message, - a->polkit_action_ignore_inhibit, - /* details= */ NULL, - /* good_user= */ UID_INVALID, - polkit_flags, - &m->polkit_registry, - error); + if (message) + r = bus_verify_polkit_async_full( + message, + a->polkit_action_ignore_inhibit, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + polkit_flags, + &m->polkit_registry, + error); + else + r = varlink_verify_polkit_async_full( + link, + m->bus, + a->polkit_action_ignore_inhibit, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + polkit_flags, + &m->polkit_registry); + if (r < 0) return r; if (r == 0) @@ -132,14 +168,25 @@ int manager_verify_shutdown_creds( } if (!multiple_sessions && !blocked) { - r = bus_verify_polkit_async_full( - message, - a->polkit_action, - /* details= */ NULL, - /* good_user= */ UID_INVALID, - interactive ? POLKIT_ALLOW_INTERACTIVE : 0, - &m->polkit_registry, - error); + if (message) + r = bus_verify_polkit_async_full( + message, + a->polkit_action, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + interactive ? POLKIT_ALLOW_INTERACTIVE : 0, + &m->polkit_registry, + error); + else + r = varlink_verify_polkit_async_full( + link, + m->bus, + a->polkit_action, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + interactive ? POLKIT_ALLOW_INTERACTIVE : 0, + &m->polkit_registry); + if (r < 0) return r; if (r == 0) diff --git a/src/login/logind-shutdown.h b/src/login/logind-shutdown.h index b8c70a9630b..38b8b7a9c08 100644 --- a/src/login/logind-shutdown.h +++ b/src/login/logind-shutdown.h @@ -6,5 +6,10 @@ #define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled" int manager_have_multiple_sessions(Manager *m, uid_t uid); -int manager_verify_shutdown_creds(Manager *m, sd_bus_message *message, const HandleActionData *a, uint64_t flags, sd_bus_error *error); + +/* manager_verify_shutdown_creds() takes *either* a "message" or "link" depending on if it is used + * to validate a D-Bus or Varlink shutdown request. When varlink is used the sd_bus_error *error + * must be NULL */ +int manager_verify_shutdown_creds(Manager *m, sd_bus_message *message, sd_varlink *link, const HandleActionData *a, uint64_t flags, sd_bus_error *error); + void manager_reset_scheduled_shutdown(Manager *m); diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c index a14569cacd7..f9ba74c6320 100644 --- a/src/login/logind-varlink.c +++ b/src/login/logind-varlink.c @@ -364,11 +364,11 @@ static int setup_wall_message_timer(Manager *m, sd_varlink *link) { static int manager_do_shutdown_action(sd_varlink *link, sd_json_variant *parameters, HandleAction action) { Manager *m = ASSERT_PTR(sd_varlink_get_userdata(link)); int skip_inhibitors = -1; - uid_t uid; int r; static const sd_json_dispatch_field dispatch_table[] = { { "skipInhibitors", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, 0, 0 }, + VARLINK_DISPATCH_POLKIT_FIELD, {} }; @@ -381,23 +381,10 @@ static int manager_do_shutdown_action(sd_varlink *link, sd_json_variant *paramet const HandleActionData *a = handle_action_lookup(action); assert(a); - /* TODO: provide full polkit support (matching the D-Bus verify_shutdown_creds() with - * multiple-sessions and inhibitor-override checks). This requires some refactor. */ - r = varlink_check_privileged_peer(link); - if (r < 0) + r = manager_verify_shutdown_creds(m, /* message= */ NULL, link, a, flags, /* error= */ NULL); + if (r != 0) return r; - r = sd_varlink_get_peer_uid(link, &uid); - if (r < 0) - return r; - - /* Check for active inhibitors, mirroring the D-Bus verify_shutdown_creds() logic, - * we need this to ensure we handle the strong INHIBIT_BLOCK - * TODO: drop once we do the verify_shutdown_creds() */ - if (manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, /* ret_offending= */ NULL) && - !FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS)) - return sd_varlink_error(link, "io.systemd.Shutdown.BlockedByInhibitor", /* parameters= */ NULL); - if (m->delayed_action) return sd_varlink_error(link, "io.systemd.Shutdown.AlreadyInProgress", /* parameters= */ NULL); diff --git a/src/shared/varlink-io.systemd.Shutdown.c b/src/shared/varlink-io.systemd.Shutdown.c index 96f4fef04d6..55728b7463e 100644 --- a/src/shared/varlink-io.systemd.Shutdown.c +++ b/src/shared/varlink-io.systemd.Shutdown.c @@ -30,7 +30,12 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE)); static SD_VARLINK_DEFINE_ERROR(AlreadyInProgress); -static SD_VARLINK_DEFINE_ERROR(BlockedByInhibitor); +static SD_VARLINK_DEFINE_ERROR( + BlockedByInhibitor, + SD_VARLINK_FIELD_COMMENT("Who is holding the inhibitor"), + SD_VARLINK_DEFINE_FIELD(who, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Why the inhibitor is held"), + SD_VARLINK_DEFINE_FIELD(why, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); SD_VARLINK_DEFINE_INTERFACE( io_systemd_Shutdown, From 768b507adc08b47e0dfebb30e6dd0cb30c9a517d Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 24 Mar 2026 15:40:40 +0100 Subject: [PATCH 5/5] logind: log peer ID when shutdown is called The io.systemd.Manager.{PowerOff,SoftReboot,Halt,Kexec} manager varlink and bus methods log the peer ID when calling shutdown. The logind code is missing this, so this commit adds a similar logging now. The code is quite similar to the one in existing in src/core/manager.c but its hard to share code so this adds a bit of duplication. --- src/login/logind-dbus.c | 7 +++++++ src/login/logind-shutdown.c | 23 +++++++++++++++++++++++ src/login/logind-shutdown.h | 2 ++ src/login/logind-varlink.c | 7 +++++++ 4 files changed, 39 insertions(+) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index ac5da453d64..98c651896d2 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2310,6 +2310,13 @@ static int method_do_shutdown_or_sleep( if (r != 0) return r; + { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + + (void) bus_query_sender_pidref(message, &pidref); + log_shutdown_caller(&pidref, handle_action_to_string(a->handle)); + } + if (m->delayed_action) return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "Action %s already in progress, refusing requested %s operation.", diff --git a/src/login/logind-shutdown.c b/src/login/logind-shutdown.c index d79843a287f..064ebf8e2ff 100644 --- a/src/login/logind-shutdown.c +++ b/src/login/logind-shutdown.c @@ -8,8 +8,11 @@ #include "bus-common-errors.h" #include "bus-polkit.h" +#include "cgroup-util.h" +#include "format-util.h" #include "fs-util.h" #include "hashmap.h" +#include "log.h" #include "login-util.h" #include "logind.h" #include "logind-dbus.h" @@ -17,6 +20,8 @@ #include "logind-session.h" #include "logind-shutdown.h" #include "logind-user.h" +#include "pidref.h" +#include "process-util.h" #include "user-record.h" int manager_have_multiple_sessions( @@ -37,6 +42,24 @@ int manager_have_multiple_sessions( return false; } +void log_shutdown_caller(const PidRef *caller, const char *method) { + _cleanup_free_ char *comm = NULL, *unit = NULL; + + assert(method); + + if (!pidref_is_set(caller)) { + return log_notice("%s requested from unknown client PID...", method); + } + + (void) pidref_get_comm(caller, &comm); + (void) cg_pidref_get_unit(caller, &unit); + + log_notice("%s requested from client PID " PID_FMT "%s%s%s%s%s%s...", + method, caller->pid, + comm ? " ('" : "", strempty(comm), comm ? "')" : "", + unit ? " (unit " : "", strempty(unit), unit ? ")" : ""); +} + int manager_verify_shutdown_creds( Manager *m, sd_bus_message *message, diff --git a/src/login/logind-shutdown.h b/src/login/logind-shutdown.h index 38b8b7a9c08..e6bcc8c4f5d 100644 --- a/src/login/logind-shutdown.h +++ b/src/login/logind-shutdown.h @@ -7,6 +7,8 @@ int manager_have_multiple_sessions(Manager *m, uid_t uid); +void log_shutdown_caller(const PidRef *caller, const char *method); + /* manager_verify_shutdown_creds() takes *either* a "message" or "link" depending on if it is used * to validate a D-Bus or Varlink shutdown request. When varlink is used the sd_bus_error *error * must be NULL */ diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c index f9ba74c6320..40dee113c42 100644 --- a/src/login/logind-varlink.c +++ b/src/login/logind-varlink.c @@ -385,6 +385,13 @@ static int manager_do_shutdown_action(sd_varlink *link, sd_json_variant *paramet if (r != 0) return r; + { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + + (void) varlink_get_peer_pidref(link, &pidref); + log_shutdown_caller(&pidref, handle_action_to_string(action)); + } + if (m->delayed_action) return sd_varlink_error(link, "io.systemd.Shutdown.AlreadyInProgress", /* parameters= */ NULL);