]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
logind: load scheduled shutdown on restart
authorLudwig Nussel <ludwig.nussel@suse.de>
Tue, 4 Jan 2022 13:02:15 +0000 (14:02 +0100)
committerLudwig Nussel <ludwig.nussel@suse.de>
Mon, 28 Feb 2022 10:29:45 +0000 (11:29 +0100)
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
src/login/logind-dbus.h
src/login/logind-utmp.c
src/login/logind.c

index cd0dcea0e505fb23c59d87c4673d68a1577e83aa..e22e82cd7c71ca506a36fbc85d155dcb17951c81 100644 (file)
 #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"
  */
 #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 (!isempty(m->wall_message)) {
-                _cleanup_free_ char *t = NULL;
+        if (m->scheduled_shutdown_tty)
+                serialize_item_format(f, "TTY", "%s", m->scheduled_shutdown_tty);
 
-                t = cescape(m->wall_message);
-                if (!t) {
-                        r = -ENOMEM;
+        if (!isempty(m->wall_message)) {
+                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,52 +2273,52 @@ 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 = 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) {
-                m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+                reset_scheduled_shutdown(m);
                 return r;
         }
 
-        r = update_schedule_file(m);
+        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)
-                return r;
+                goto fail;
 
-        return sd_bus_reply_method_return(message, NULL);
+        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) {
index 13aff11bba8e7b5f3341bc6019c2bc6cf9221bff..1cdfd3eec9346a214128a16372391b3ceb7131af 100644 (file)
@@ -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;
index 5a9e8db79d339a67381befc3b2b9a743ac5ea02d..b21699ea26bdce01298b652e552875920b458d54 100644 (file)
@@ -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;
index c561b75951fd5da7cffc38e8d6ea2abc3830dc2d..3b4de5b7991069eecea1fe364e626435051ea48e 100644 (file)
@@ -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);