mirror of
https://github.com/systemd/systemd.git
synced 2026-08-12 22:17:19 +00:00
core: preserve shutdown timestamps via LUO
This commit is contained in:
@@ -107,16 +107,27 @@ int manager_luo_restore_fd_stores(Manager *m) {
|
||||
|
||||
struct {
|
||||
unsigned kexecs_count;
|
||||
dual_timestamp previous_shutdown_start, previous_shutdown_finish,
|
||||
previous_shutdown_late_start, previous_shutdown_late_finish;
|
||||
} state_data = {};
|
||||
|
||||
static const sd_json_dispatch_field state_dispatch_table[] = {
|
||||
{ "kexecsCount", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, voffsetof(state_data, kexecs_count), 0 },
|
||||
{ "kexecsCount", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, voffsetof(state_data, kexecs_count), 0 },
|
||||
{ "ShutdownStartTimestamp", SD_JSON_VARIANT_OBJECT, json_dispatch_dual_timestamp, voffsetof(state_data, previous_shutdown_start), 0 },
|
||||
{ "ShutdownFinishTimestamp", SD_JSON_VARIANT_OBJECT, json_dispatch_dual_timestamp, voffsetof(state_data, previous_shutdown_finish), 0 },
|
||||
{ "ShutdownLateStartTimestamp", SD_JSON_VARIANT_OBJECT, json_dispatch_dual_timestamp, voffsetof(state_data, previous_shutdown_late_start), 0 },
|
||||
{ "ShutdownLateFinishTimestamp", SD_JSON_VARIANT_OBJECT, json_dispatch_dual_timestamp, voffsetof(state_data, previous_shutdown_late_finish), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(q.state, state_dispatch_table, SD_JSON_ALLOW_EXTENSIONS|SD_JSON_LOG, &state_data);
|
||||
if (r >= 0)
|
||||
if (r >= 0) {
|
||||
m->kexecs_count = state_data.kexecs_count;
|
||||
m->timestamps[MANAGER_TIMESTAMP_PREVIOUS_SHUTDOWN_START] = state_data.previous_shutdown_start;
|
||||
m->timestamps[MANAGER_TIMESTAMP_PREVIOUS_SHUTDOWN_FINISH] = state_data.previous_shutdown_finish;
|
||||
m->timestamps[MANAGER_TIMESTAMP_PREVIOUS_SHUTDOWN_LATE_START] = state_data.previous_shutdown_late_start;
|
||||
m->timestamps[MANAGER_TIMESTAMP_PREVIOUS_SHUTDOWN_LATE_FINISH] = state_data.previous_shutdown_late_finish;
|
||||
}
|
||||
|
||||
/* If we found a LUO session then by definition we have just successfully kexec rebooted */
|
||||
(void) INC_SAFE(&m->kexecs_count, 1);
|
||||
@@ -307,7 +318,9 @@ int manager_luo_serialize_fd_stores(Manager *m, FILE **ret_f, FDSet **ret_fds) {
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("version", LUO_PROTOCOL_VERSION),
|
||||
SD_JSON_BUILD_PAIR("state",
|
||||
SD_JSON_BUILD_OBJECT(
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("kexecsCount", m->kexecs_count))),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("kexecsCount", m->kexecs_count),
|
||||
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("ShutdownStartTimestamp", &m->timestamps[MANAGER_TIMESTAMP_SHUTDOWN_START]),
|
||||
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("ShutdownFinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_SHUTDOWN_FINISH]))),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!units, "units", SD_JSON_BUILD_VARIANT(units)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUO serialization JSON: %m");
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
/* Kernel API defined at https://docs.kernel.org/userspace-api/liveupdate.html The /dev/liveupdate is a
|
||||
* single-owner singleton, only a single process at any given time can open it. Callers can create named
|
||||
@@ -210,6 +211,42 @@ int luo_parse_serialization(sd_json_variant **ret, int **ret_fds, size_t *ret_n_
|
||||
return 0;
|
||||
}
|
||||
|
||||
int luo_serialization_add_shutdown_timestamps(
|
||||
sd_json_variant **serialization,
|
||||
const dual_timestamp *shutdown_late_start,
|
||||
const dual_timestamp *shutdown_late_finish) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(serialization);
|
||||
|
||||
/* sd-shutdown calls this to add its timestamps to the preserved JSON payload, so that the next
|
||||
* boot can expose them as the previous boot's shutdown timestamps. */
|
||||
|
||||
if (!*serialization)
|
||||
return 0; /* No LUO serialization, nothing to augment */
|
||||
|
||||
if ((!shutdown_late_start || !dual_timestamp_is_set(shutdown_late_start)) &&
|
||||
(!shutdown_late_finish || !dual_timestamp_is_set(shutdown_late_finish)))
|
||||
return 0; /* Nothing to add */
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *new_state =
|
||||
sd_json_variant_ref(sd_json_variant_by_key(*serialization, "state"));
|
||||
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&new_state,
|
||||
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("ShutdownLateStartTimestamp", (dual_timestamp*) shutdown_late_start),
|
||||
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("ShutdownLateFinishTimestamp", (dual_timestamp*) shutdown_late_finish));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to merge LUO shutdown timestamps into state: %m");
|
||||
|
||||
r = sd_json_variant_set_field(serialization, "state", new_state);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update LUO 'state' object: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luo_preserve_fd_stores(sd_json_variant *serialization, int *ret_session_fd) {
|
||||
_cleanup_close_ int device_fd = -EBADF, session_fd = -EBADF;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *mapping = NULL, *units = NULL;
|
||||
|
||||
@@ -40,6 +40,7 @@ int luo_session_finish(int session_fd);
|
||||
bool luo_session_name_is_valid(const char *name);
|
||||
|
||||
int luo_parse_serialization(sd_json_variant **ret, int **ret_fds, size_t *ret_n_fds);
|
||||
int luo_serialization_add_shutdown_timestamps(sd_json_variant **serialization, const dual_timestamp *shutdown_late_start, const dual_timestamp *shutdown_late_finish);
|
||||
int luo_preserve_fd_stores(sd_json_variant *serialization, int *ret_session_fd);
|
||||
|
||||
int fd_is_luo_session(int fd);
|
||||
|
||||
@@ -650,6 +650,7 @@ int main(int argc, char *argv[]) {
|
||||
* down is left, so record the late shutdown timestamp here. */
|
||||
dual_timestamp shutdown_late_finish;
|
||||
dual_timestamp_now(&shutdown_late_finish);
|
||||
(void) luo_serialization_add_shutdown_timestamps(&luo_serialization, &shutdown_late_start, &shutdown_late_finish);
|
||||
|
||||
if (streq(arg_verb, "exit")) {
|
||||
if (in_container) {
|
||||
|
||||
@@ -80,6 +80,23 @@ if grep -qw luo_nboot=1 /proc/cmdline; then
|
||||
|
||||
assert_eq "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager KExecsCount | jq -r '.data')" "1"
|
||||
|
||||
# The previous boot's shutdown timestamps were preserved across the kexec via LUO and are now exposed
|
||||
# as the PreviousShutdown* properties (the live Shutdown* ones describe this boot, which is not shutting
|
||||
# down, so they are unset here).
|
||||
ts_shutdown_start=$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager PreviousShutdownStartTimestampMonotonic | jq -r '.data')
|
||||
ts_shutdown_finish=$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager PreviousShutdownFinishTimestampMonotonic | jq -r '.data')
|
||||
ts_shutdown_late_start=$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager PreviousShutdownLateStartTimestampMonotonic | jq -r '.data')
|
||||
ts_shutdown_late_finish=$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager PreviousShutdownLateFinishTimestampMonotonic | jq -r '.data')
|
||||
|
||||
assert_ge "${ts_shutdown_start}" 1
|
||||
assert_ge "${ts_shutdown_finish}" 1
|
||||
assert_ge "${ts_shutdown_late_start}" 1
|
||||
assert_ge "${ts_shutdown_late_finish}" 1
|
||||
|
||||
assert_le "${ts_shutdown_start}" "${ts_shutdown_finish}"
|
||||
assert_le "${ts_shutdown_finish}" "${ts_shutdown_late_start}"
|
||||
assert_le "${ts_shutdown_late_start}" "${ts_shutdown_late_finish}"
|
||||
|
||||
# Negative path: a unit stored a child LUO session named like PID 1's own
|
||||
# ("systemd") on the first boot. PID 1's serialize step must have refused to
|
||||
# serialize that fd store entry (anti-hijack guard in
|
||||
|
||||
Reference in New Issue
Block a user