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);
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");
#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
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;
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);
* 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) {
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