]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add ReloadCount to Manager and bump on successful reload
authorSimon Lucido <simonlucido@meta.com>
Mon, 20 Apr 2026 15:05:27 +0000 (17:05 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Sun, 3 May 2026 09:23:55 +0000 (10:23 +0100)
Introduce a counter that tracks how many configuration reloads have
been successfully completed by the manager. The increment lives in
manager_reload() right after the "point of no return", so failed
reload attempts that bail out earlier (e.g. during serialization)
do not bump the counter.

It is accessible as a new ReloadCount property to
org.freedesktop.systemd1.Manager (D-Bus) and ReloadCount to
io.systemd.Manager.Describe (Varlink).

Also add an integration test for ReloadCount
that verifies that the new ReloadCount property increments by one per
daemon-reload, accumulates correctly across multiple reloads, and that
D-Bus and Varlink return identical values. Also tests that the counter
reset after a reexec.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Simon Lucido <simonlucido@meta.com>
NEWS
man/org.freedesktop.systemd1.xml
src/core/dbus-manager.c
src/core/manager.c
src/core/manager.h
src/core/varlink-manager.c
src/shared/varlink-io.systemd.Manager.c
test/units/TEST-07-PID1.reload-count.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 451e3f1b7960330f3d27e65ebd23009f1e8bb7f5..49061c5e11a2268108ece8b4e89f073b7f215a5f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -63,6 +63,12 @@ CHANGES WITH 261 in spe:
           require direct IMDS access. The new meson option "-Dimds-network="
           can be used to change the default mode to "locked" at build-time.
 
+        * The manager exposes a new ReloadCount property on its D-Bus and
+          Varlink interfaces (org.freedesktop.systemd1.Manager and
+          io.systemd.Manager respectively). The counter increments after
+          each successfully completed daemon-reload. It is not preserved
+          across daemon-reexec.
+
         Changes in systemd-sysext/systemd-confext:
 
         * New initrd services systemd-sysext-sysroot.service and
index 76a8dd045f6c660f74719799dce056d62fa92ade..847e76f95c7676694634fd9aea3dfbe51cdd1ae0 100644 (file)
@@ -572,6 +572,8 @@ node /org/freedesktop/systemd1 {
       readonly s CtrlAltDelBurstAction = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly u SoftRebootsCount = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t ReloadCount = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b DefaultMemoryZSwapWriteback = ...;
   };
@@ -1279,6 +1281,8 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SoftRebootsCount"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="ReloadCount"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryZSwapWriteback"/>
 
     <!--End of Autogenerated section-->
@@ -1866,6 +1870,10 @@ 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>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>
+
       <para><varname>Virtualization</varname> contains a short ID string describing the virtualization
       technology the system runs in. On bare-metal hardware this is the empty string. Otherwise, it contains
       an identifier such as <literal>kvm</literal>, <literal>vmware</literal> and so on. For a full list of
@@ -12646,8 +12654,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <para><varname>DefaultMemoryZSwapWriteback</varname>,
       <varname>DefaultCPUPressureThresholdUSec</varname>,
       <varname>DefaultCPUPressureWatch</varname>,
-      <varname>DefaultIOPressureThresholdUSec</varname>, and
-      <varname>DefaultIOPressureWatch</varname> were added in version 261.</para>
+      <varname>DefaultIOPressureThresholdUSec</varname>,
+      <varname>DefaultIOPressureWatch</varname>, and
+      <varname>ReloadCount</varname> were added in version 261.</para>
     </refsect2>
     <refsect2>
       <title>Unit Objects</title>
index 076a26c6fd1715052c1e920407507e8fd186a2c7..88de34c4ea434ca8ab6138b9101df104cc6ad4dc 100644 (file)
@@ -2978,6 +2978,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("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),
 
         /* deprecated cgroup v1 property */
index 17908d4db864e2437807b2dd76e7d980a537d7c3..da4e9ca408127ce172811b49990d9849455a822d 100644 (file)
@@ -3650,6 +3650,10 @@ int manager_reload(Manager *m) {
         /* ðŸ’€ This is the point of no return, from here on there is no way back. ðŸ’€ */
         reloading = NULL;
 
+        /* Bump before sending the Reloading signal, so any client that reads
+         * ReloadCount in response to that signal observes the new value. */
+        m->reload_count = saturate_add(m->reload_count, 1, UINT64_MAX);
+
         bus_manager_send_reloading(m, true);
 
         /* Start by flushing out all jobs and units, all generated units, all runtime environments, all dynamic users
index 7d58c330a1b826f63beb3b386979d6e0db4f7416..abf1764d7859ecf89f2c4357673bbe05659ff941 100644 (file)
@@ -492,6 +492,9 @@ typedef struct Manager {
 
         unsigned soft_reboots_count;
 
+        /* The number of successfully completed configuration reloads. */
+        uint64_t reload_count;
+
         /* Original ambient capabilities when we were initialized */
         uint64_t saved_ambient_set;
 } Manager;
index 0bef5cbe9848dbd15a866f97bc138c81ca209d24..384d4709c97867b946c53a1206c6710a47bb6276 100644 (file)
@@ -193,7 +193,8 @@ static int manager_runtime_build_json(sd_json_variant **ret, const char *name, v
                 JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("WatchdogLastPingTimestamp", watchdog_get_last_ping_as_dual_timestamp(&watchdog_last_ping)),
                 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("SoftRebootsCount", m->soft_reboots_count),
+                SD_JSON_BUILD_PAIR_UNSIGNED("ReloadCount", m->reload_count));
 }
 
 int vl_method_describe_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
index 0c5ab53702b0daa1541d39b100f1c54a61a68a96..81b3e894a348df24b6eae1aa6bda03858199d158 100644 (file)
@@ -194,7 +194,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_FIELD_COMMENT("Exit code of the manager"),
                 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_DEFINE_FIELD(SoftRebootsCount, SD_VARLINK_INT, 0),
+                SD_VARLINK_FIELD_COMMENT("Number of successfully completed configuration reloads"),
+                SD_VARLINK_DEFINE_FIELD(ReloadCount, SD_VARLINK_INT, 0));
 
 static SD_VARLINK_DEFINE_METHOD(
                 Describe,
diff --git a/test/units/TEST-07-PID1.reload-count.sh b/test/units/TEST-07-PID1.reload-count.sh
new file mode 100755 (executable)
index 0000000..7c31b65
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Verify that the manager exposes a ReloadCount property that increments on
+# every daemon-reload, resets to zero across daemon-reexec (since the count
+# is not serialized), and is reachable over both D-Bus and Varlink.
+
+read_count_dbus() {
+    busctl -j get-property org.freedesktop.systemd1 \
+                           /org/freedesktop/systemd1 \
+                           org.freedesktop.systemd1.Manager \
+                           ReloadCount | jq -r '.data'
+}
+
+read_count_varlink() {
+    varlinkctl call /run/systemd/io.systemd.Manager \
+                    io.systemd.Manager.Describe '{}' | jq -r '.runtime.ReloadCount'
+}
+
+# Sanity: both transports must agree.
+dbus_count=$(read_count_dbus)
+varlink_count=$(read_count_varlink)
+(( dbus_count == varlink_count ))
+
+# A single reload bumps the counter by one.
+before=$(read_count_dbus)
+systemctl daemon-reload
+(( $(read_count_dbus) == before + 1 ))
+
+# Multiple reloads accumulate.
+systemctl daemon-reload
+systemctl daemon-reload
+(( $(read_count_dbus) == before + 3 ))
+
+# And both transports still agree after the reload.
+dbus_count=$(read_count_dbus)
+varlink_count=$(read_count_varlink)
+(( dbus_count == varlink_count ))
+
+# A daemon-reexec resets the counter back to zero on both transports, since
+# reload_count lives only in memory and is not carried across the reexec.
+# `systemctl daemon-reexec` returns as soon as the old PID 1 closes its bus
+# connection, which is before the new PID 1 has rebound /run/systemd/private.
+# Use --watch-bind=yes to block on inotify until the new socket is live.
+systemctl daemon-reexec
+busctl --watch-bind=yes call org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+                           org.freedesktop.DBus.Peer Ping >/dev/null
+(( $(read_count_dbus) == 0 ))
+(( $(read_count_varlink) == 0 ))