From 9fa9c66dbdc007313772c92436a96e5e62fd638e Mon Sep 17 00:00:00 2001 From: Ludwig Nussel Date: Wed, 23 Feb 2022 08:49:56 +0100 Subject: [PATCH 1/3] logind: fix handle_action_valid() --- src/login/logind-action.h | 2 +- src/login/logind-button.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/login/logind-action.h b/src/login/logind-action.h index 621c5f97bc..4dcf719488 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -21,7 +21,7 @@ typedef enum HandleAction { typedef struct ActionTableItem ActionTableItem; -#define handle_action_valid(x) (x && (x < _HANDLE_ACTION_MAX)) +#define handle_action_valid(x) (x >= 0 && x < _HANDLE_ACTION_MAX) #include "logind-inhibit.h" #include "logind.h" diff --git a/src/login/logind-button.c b/src/login/logind-button.c index 0f4e1f1b41..a2b43d3684 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -84,7 +84,7 @@ static void button_lid_switch_handle_action(Manager *manager, bool is_edge) { * differently */ if (manager_is_docked_or_external_displays(manager)) handle_action = manager->handle_lid_switch_docked; - else if (!handle_action_valid(manager->handle_lid_switch_ep) && manager_is_on_external_power()) + else if (handle_action_valid(manager->handle_lid_switch_ep) && manager_is_on_external_power()) handle_action = manager->handle_lid_switch_ep; else handle_action = manager->handle_lid_switch; From aa6123e85e0baea21b6daad175da43ffea30629f Mon Sep 17 00:00:00 2001 From: Ludwig Nussel Date: Tue, 4 Jan 2022 14:02:15 +0100 Subject: [PATCH 2/3] logind: load scheduled shutdown on restart Make sure logind doesn't forget about a scheduled shutdown, even if it gets restarted eg due to a package update. --- src/login/logind-dbus.c | 196 ++++++++++++++++++++++++++++------------ src/login/logind-dbus.h | 2 + src/login/logind-utmp.c | 21 ++--- src/login/logind.c | 2 + 4 files changed, 151 insertions(+), 70 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index cd0dcea0e5..e22e82cd7c 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -22,8 +22,10 @@ #include "dirent-util.h" #include "efi-loader.h" #include "efivars.h" +#include "env-file.h" #include "env-util.h" #include "escape.h" +#include "event-util.h" #include "fd-util.h" #include "fileio-label.h" #include "fileio.h" @@ -45,6 +47,7 @@ #include "selinux-util.h" #include "sleep-config.h" #include "special.h" +#include "serialize.h" #include "stdio-util.h" #include "strv.h" #include "terminal-util.h" @@ -63,7 +66,11 @@ */ #define WALL_MESSAGE_MAX 4096 +#define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled" + +static int update_schedule_file(Manager *m); static void reset_scheduled_shutdown(Manager *m); +static int manager_setup_shutdown_timers(Manager* m); static int get_sender_session( Manager *m, @@ -2038,6 +2045,81 @@ static usec_t nologin_timeout_usec(usec_t elapse) { return LESS_BY(elapse, 5 * USEC_PER_MINUTE); } +void manager_load_scheduled_shutdown(Manager *m) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *usec = NULL, + *warn_wall = NULL, + *mode = NULL, + *wall_message = NULL, + *uid = NULL, + *tty = NULL; + int r; + + assert(m); + + r = parse_env_file(f, SHUTDOWN_SCHEDULE_FILE, + "USEC", &usec, + "WARN_WALL", &warn_wall, + "MODE", &mode, + "WALL_MESSAGE", &wall_message, + "UID", &uid, + "TTY", &tty); + + /* reset will delete the file */ + reset_scheduled_shutdown(m); + + if (r == -ENOENT) + return; + if (r < 0) + return (void) log_debug_errno(r, "Failed to parse " SHUTDOWN_SCHEDULE_FILE ": %m"); + + HandleAction handle = handle_action_from_string(mode); + if (handle < 0) + return (void) log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse scheduled shutdown type: %s", mode); + + if (!usec) + return (void) log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "USEC is required"); + if (deserialize_usec(usec, &m->scheduled_shutdown_timeout) < 0) + return; + + /* assign parsed type only after we know usec is also valid */ + m->scheduled_shutdown_type = manager_item_for_handle(handle); + + if (warn_wall) { + r = parse_boolean(warn_wall); + if (r < 0) + log_debug_errno(r, "Failed to parse enabling wall messages"); + else + m->enable_wall_messages = r; + } + + if (wall_message) { + _cleanup_free_ char *unescaped = NULL; + r = cunescape(wall_message, 0, &unescaped); + if (r < 0) + log_debug_errno(r, "Failed to parse wall message: %s", wall_message); + else + free_and_replace(m->wall_message, unescaped); + } + + if (uid) { + r = parse_uid(uid, &m->scheduled_shutdown_uid); + if (r < 0) + log_debug_errno(r, "Failed to parse wall uid: %s", uid); + } + + free_and_replace(m->scheduled_shutdown_tty, tty); + + r = manager_setup_shutdown_timers(m); + if (r < 0) + return reset_scheduled_shutdown(m); + + (void) manager_setup_wall_message_timer(m); + (void) update_schedule_file(m); + + return; +} + static int update_schedule_file(Manager *m) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -2046,41 +2128,35 @@ static int update_schedule_file(Manager *m) { assert(m); assert(m->scheduled_shutdown_type); - r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0, MKDIR_WARN_MODE); + r = mkdir_parents_label(SHUTDOWN_SCHEDULE_FILE, 0755); if (r < 0) return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); - r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); + r = fopen_temporary(SHUTDOWN_SCHEDULE_FILE, &f, &temp_path); if (r < 0) return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); (void) fchmod(fileno(f), 0644); - fprintf(f, - "USEC="USEC_FMT"\n" - "WARN_WALL=%s\n" - "MODE=%s\n", - m->scheduled_shutdown_timeout, - one_zero(m->enable_wall_messages), - handle_action_to_string(m->scheduled_shutdown_type->handle)); + serialize_usec(f, "USEC", m->scheduled_shutdown_timeout); + serialize_item_format(f, "WARN_WALL", "%s", one_zero(m->enable_wall_messages)); + serialize_item_format(f, "MODE", "%s", handle_action_to_string(m->scheduled_shutdown_type->handle)); + serialize_item_format(f, "UID", UID_FMT, m->scheduled_shutdown_uid); + + if (m->scheduled_shutdown_tty) + serialize_item_format(f, "TTY", "%s", m->scheduled_shutdown_tty); if (!isempty(m->wall_message)) { - _cleanup_free_ char *t = NULL; - - t = cescape(m->wall_message); - if (!t) { - r = -ENOMEM; + r = serialize_item_escaped(f, "WALL_MESSAGE", m->wall_message); + if (r < 0) goto fail; - } - - fprintf(f, "WALL_MESSAGE=%s\n", t); } r = fflush_and_check(f); if (r < 0) goto fail; - if (rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { + if (rename(temp_path, SHUTDOWN_SCHEDULE_FILE) < 0) { r = -errno; goto fail; } @@ -2089,7 +2165,7 @@ static int update_schedule_file(Manager *m) { fail: (void) unlink(temp_path); - (void) unlink("/run/systemd/shutdown/scheduled"); + (void) unlink(SHUTDOWN_SCHEDULE_FILE); return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m"); } @@ -2102,6 +2178,10 @@ static void reset_scheduled_shutdown(Manager *m) { m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); m->scheduled_shutdown_type = NULL; + m->scheduled_shutdown_timeout = USEC_INFINITY; + m->scheduled_shutdown_uid = UID_INVALID; + freep(&m->scheduled_shutdown_tty); + freep(&m->wall_message); m->shutdown_dry_run = false; if (m->unlink_nologin) { @@ -2109,7 +2189,7 @@ static void reset_scheduled_shutdown(Manager *m) { m->unlink_nologin = false; } - (void) unlink("/run/systemd/shutdown/scheduled"); + (void) unlink(SHUTDOWN_SCHEDULE_FILE); } static int manager_scheduled_shutdown_handler( @@ -2193,54 +2273,54 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ if (r != 0) return r; - if (m->scheduled_shutdown_timeout_source) { - r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source, - CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - m->scheduled_shutdown_type = a; m->shutdown_dry_run = dry_run; - - if (m->nologin_timeout_source) { - r = sd_event_source_set_time(m->nologin_timeout_source, nologin_timeout_usec(elapse)); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->nologin_timeout_source, - CLOCK_REALTIME, nologin_timeout_usec(elapse), 0, nologin_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - m->scheduled_shutdown_timeout = elapse; - r = setup_wall_message_timer(m, message); - if (r < 0) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - return r; - } - - r = update_schedule_file(m); + r = manager_setup_shutdown_timers(m); if (r < 0) return r; + r = setup_wall_message_timer(m, message); + if (r >= 0) + r = update_schedule_file(m); + + if (r < 0) { + reset_scheduled_shutdown(m); + return r; + } + return sd_bus_reply_method_return(message, NULL); } +static int manager_setup_shutdown_timers(Manager* m) { + int r; + + r = event_reset_time(m->event, &m->scheduled_shutdown_timeout_source, + CLOCK_REALTIME, + m->scheduled_shutdown_timeout, 0, + manager_scheduled_shutdown_handler, m, + 0, "scheduled-shutdown-timeout", true); + if (r < 0) + goto fail; + + r = event_reset_time(m->event, &m->nologin_timeout_source, + CLOCK_REALTIME, + nologin_timeout_usec(m->scheduled_shutdown_timeout), 0, + nologin_timeout_handler, m, + 0, "nologin-timeout", true); + if (r < 0) + goto fail; + + return 0; + +fail: + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); + + return r; +} + static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; const ActionTableItem *a; diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h index 13aff11bba..1cdfd3eec9 100644 --- a/src/login/logind-dbus.h +++ b/src/login/logind-dbus.h @@ -32,4 +32,6 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error); int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error); +void manager_load_scheduled_shutdown(Manager *m); + extern const BusObjectImplementation manager_object; diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c index 5a9e8db79d..b21699ea26 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-utmp.c @@ -10,6 +10,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" +#include "event-util.h" #include "format-util.h" #include "logind.h" #include "path-util.h" @@ -147,19 +148,15 @@ int manager_setup_wall_message_timer(Manager *m) { if (elapse == 0) return 0; - if (m->wall_message_timeout_source) { - r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed. %m"); + r = event_reset_time(m->event, &m->wall_message_timeout_source, + CLOCK_REALTIME, + n + elapse, 0, + wall_message_timeout_handler, m, + 0, "wall-message-timer", true); - r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); - } else { - r = sd_event_add_time(m->event, &m->wall_message_timeout_source, - CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed. %m"); + if (r < 0) { + m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); + return log_error_errno(r, "Failed to set up wall message timer: %m"); } return 0; diff --git a/src/login/logind.c b/src/login/logind.c index c561b75951..3b4de5b799 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -1090,6 +1090,8 @@ static int manager_startup(Manager *m) { if (r < 0) log_warning_errno(r, "Button enumeration failed: %m"); + manager_load_scheduled_shutdown(m); + /* Remove stale objects before we start them */ manager_gc(m, false); From 00886e06e8dbb9449cef1f4ab1fdc53a212162f7 Mon Sep 17 00:00:00 2001 From: Ludwig Nussel Date: Tue, 8 Feb 2022 11:23:04 +0100 Subject: [PATCH 3/3] systemctl: use action_table for scheduling shutdown The fall-through to reboot doesn't seem to make sense. It won't happen in the current code. Filtering the actions on client side is not needed either as the server will refuse unsupported operations anyway. --- src/systemctl/systemctl-logind.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c index d502a7edcf..897d936aba 100644 --- a/src/systemctl/systemctl-logind.c +++ b/src/systemctl/systemctl-logind.c @@ -304,24 +304,9 @@ int logind_schedule_shutdown(void) { if (r < 0) return r; - switch (arg_action) { - case ACTION_HALT: - action = "halt"; - break; - case ACTION_POWEROFF: - action = "poweroff"; - break; - case ACTION_KEXEC: - action = "kexec"; - break; - case ACTION_EXIT: - action = "exit"; - break; - case ACTION_REBOOT: - default: - action = "reboot"; - break; - } + action = action_table[arg_action].verb; + if (!action) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Scheduling not supported for this action."); if (arg_dry_run) action = strjoina("dry-", action);