#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"
#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,
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;
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;
}
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");
}
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) {
m->unlink_nologin = false;
}
- (void) unlink("/run/systemd/shutdown/scheduled");
+ (void) unlink(SHUTDOWN_SCHEDULE_FILE);
}
static int manager_scheduled_shutdown_handler(
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) {