core: add KExecsCount property
Use LUO to count kexec reboots, and expose through a new property Fixes https://github.com/systemd/systemd/issues/42581
This commit is contained in:
@@ -576,6 +576,8 @@ node /org/freedesktop/systemd1 {
|
||||
readonly s CtrlAltDelBurstAction = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u SoftRebootsCount = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u KExecsCount = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t ReloadCount = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@@ -1293,6 +1295,8 @@ node /org/freedesktop/systemd1 {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SoftRebootsCount"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="KExecsCount"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ReloadCount"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryZSwapWriteback"/>
|
||||
@@ -1882,6 +1886,11 @@ node /org/freedesktop/systemd1 {
|
||||
<para><varname>SoftRebootsCount</varname> encodes how many soft-reboots were successfully completed
|
||||
since the last full boot. Starts at <literal>0</literal>.</para>
|
||||
|
||||
<para><varname>KExecsCount</varname> encodes how many kexec-based live updates were successfully
|
||||
completed since the last full boot. Starts at <literal>0</literal>. The counter is preserved across the
|
||||
kexec through the Live Update Orchestrator (LUO), and is hence <literal>0</literal> when the kernel does
|
||||
not provide LUO support.</para>
|
||||
|
||||
<para><varname>ReloadCount</varname> encodes the number of successfully completed configuration
|
||||
reloads of the manager. The counter is reset to <literal>0</literal> on
|
||||
<command>daemon-reexec</command> and on the initial boot.</para>
|
||||
@@ -12749,6 +12758,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>ReloadCount</varname>,
|
||||
<varname>EventLoopRateLimitIntervalUSec</varname>, and
|
||||
<varname>EventLoopRateLimitBurst</varname> were added in version 261.</para>
|
||||
<para><varname>KExecsCount</varname> was added in version 262.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Unit Objects</title>
|
||||
|
||||
@@ -3010,6 +3010,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
|
||||
SD_BUS_PROPERTY("DefaultRestrictSUIDSGID", "b", bus_property_get_bool, offsetof(Manager, defaults.restrict_suid_sgid), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CtrlAltDelBurstAction", "s", bus_property_get_emergency_action, offsetof(Manager, cad_burst_action), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SoftRebootsCount", "u", bus_property_get_unsigned, offsetof(Manager, soft_reboots_count), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("KExecsCount", "u", bus_property_get_unsigned, offsetof(Manager, kexecs_count), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadCount", "t", NULL, offsetof(Manager, reload_count), 0),
|
||||
SD_BUS_PROPERTY("DefaultMemoryZSwapWriteback", "b", bus_property_get_bool, offsetof(Manager, defaults.memory_zswap_writeback), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
||||
|
||||
+21
-7
@@ -85,11 +85,13 @@ int manager_luo_restore_fd_stores(Manager *m) {
|
||||
|
||||
struct {
|
||||
uint64_t version;
|
||||
sd_json_variant *state;
|
||||
sd_json_variant *units;
|
||||
} q = {};
|
||||
|
||||
static const sd_json_dispatch_field mapping_dispatch_table[] = {
|
||||
{ "version", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(q, version), SD_JSON_MANDATORY },
|
||||
{ "state", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, voffsetof(q, state), 0 },
|
||||
{ "units", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, voffsetof(q, units), 0 },
|
||||
{}
|
||||
};
|
||||
@@ -103,6 +105,22 @@ int manager_luo_restore_fd_stores(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct {
|
||||
unsigned kexecs_count;
|
||||
} 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 },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(q.state, state_dispatch_table, SD_JSON_ALLOW_EXTENSIONS|SD_JSON_LOG, &state_data);
|
||||
if (r >= 0)
|
||||
m->kexecs_count = state_data.kexecs_count;
|
||||
|
||||
/* If we found a LUO session then by definition we have just successfully kexec rebooted */
|
||||
(void) INC_SAFE(&m->kexecs_count, 1);
|
||||
|
||||
/* Retrieve all fds from the session and dispatch each to the named unit, eagerly loading the
|
||||
* unit if necessary. */
|
||||
JSON_VARIANT_OBJECT_FOREACH(unit_id, unit_json, q.units) {
|
||||
@@ -284,16 +302,12 @@ int manager_luo_serialize_fd_stores(Manager *m, FILE **ret_f, FDSet **ret_fds) {
|
||||
return log_error_errno(r, "Failed to add unit to LUO serialization JSON: %m");
|
||||
}
|
||||
|
||||
if (n_serialized == 0) {
|
||||
log_debug("No fd store entries to serialize for LUO.");
|
||||
*ret_f = NULL;
|
||||
*ret_fds = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_json_buildo(
|
||||
&root,
|
||||
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_CONDITION(!!units, "units", SD_JSON_BUILD_VARIANT(units)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUO serialization JSON: %m");
|
||||
|
||||
@@ -124,6 +124,7 @@ int manager_serialize(
|
||||
|
||||
(void) serialize_item(f, "previous-objective", manager_objective_to_string(m->objective));
|
||||
(void) serialize_item_format(f, "soft-reboots-count", "%u", m->soft_reboots_count);
|
||||
(void) serialize_item_format(f, "kexecs-count", "%u", m->kexecs_count);
|
||||
(void) serialize_item_format(f, "fd-store-upstream-next-index", "%" PRIu64, m->fd_store_upstream_next_index);
|
||||
|
||||
for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
|
||||
@@ -758,6 +759,13 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
|
||||
log_notice("Failed to parse soft reboots counter '%s', ignoring.", val);
|
||||
else
|
||||
m->soft_reboots_count = n;
|
||||
} else if ((val = startswith(l, "kexecs-count="))) {
|
||||
unsigned n;
|
||||
|
||||
if (safe_atou(val, &n) < 0)
|
||||
log_notice("Failed to parse kexecs counter '%s', ignoring.", val);
|
||||
else
|
||||
m->kexecs_count = n;
|
||||
} else if ((val = startswith(l, "fd-store-upstream-next-index="))) {
|
||||
if (safe_atou64(val, &m->fd_store_upstream_next_index) < 0)
|
||||
log_notice("Failed to parse fd-store-upstream-next-index '%s', ignoring.", val);
|
||||
|
||||
@@ -510,6 +510,9 @@ typedef struct Manager {
|
||||
|
||||
unsigned soft_reboots_count;
|
||||
|
||||
/* When LUO is enabled we can count consecutive kexec reboots. */
|
||||
unsigned kexecs_count;
|
||||
|
||||
/* The number of successfully completed configuration reloads. */
|
||||
uint64_t reload_count;
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ static int manager_runtime_build_json(sd_json_variant **ret, const char *name, v
|
||||
SD_JSON_BUILD_PAIR_STRING("SystemState", manager_state_to_string(manager_state(m))),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("ExitCode", m->return_value),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("SoftRebootsCount", m->soft_reboots_count),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("KExecsCount", m->kexecs_count),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("ReloadCount", m->reload_count));
|
||||
}
|
||||
|
||||
|
||||
@@ -342,9 +342,12 @@ int luo_preserve_fd_stores(sd_json_variant *serialization, int *ret_session_fd)
|
||||
if (!sd_json_variant_is_unsigned(version))
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "LUO serialization 'version' field is missing or not an unsigned integer");
|
||||
|
||||
sd_json_variant *state = sd_json_variant_by_key(serialization, "state");
|
||||
|
||||
r = sd_json_buildo(
|
||||
&mapping,
|
||||
SD_JSON_BUILD_PAIR("version", SD_JSON_BUILD_VARIANT(version)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!state, "state", SD_JSON_BUILD_VARIANT(state)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!units, "units", SD_JSON_BUILD_VARIANT(units)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUO mapping: %m");
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*
|
||||
* {
|
||||
* "version": 1,
|
||||
* "state": { },
|
||||
* "units": {
|
||||
* "unit-name.service": {
|
||||
* "fdstore": [
|
||||
|
||||
@@ -189,6 +189,8 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SD_VARLINK_DEFINE_FIELD(ExitCode, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Encodes how many soft-reboots were successfully completed"),
|
||||
SD_VARLINK_DEFINE_FIELD(SoftRebootsCount, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Encodes how many consecutive kexec reboots were successfully completed since the last full boot. Zero if the Live Update Orchestrator is not available."),
|
||||
SD_VARLINK_DEFINE_FIELD(KExecsCount, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Number of successfully completed configuration reloads"),
|
||||
SD_VARLINK_DEFINE_FIELD(ReloadCount, SD_VARLINK_INT, 0));
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ if grep -qw luo_nboot=1 /proc/cmdline; then
|
||||
# Verify that the fd store of the main test service survived the kexec.
|
||||
/usr/lib/systemd/tests/unit-tests/manual/test-luo check
|
||||
|
||||
assert_eq "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager KExecsCount | jq -r '.data')" "1"
|
||||
|
||||
# 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