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; 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); 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);