]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: implement TimerContext/Runtime for io.systemd.Unit.List + tests
authorIvan Kruglov <mail@ikruglov.com>
Thu, 7 May 2026 12:12:48 +0000 (05:12 -0700)
committerIvan Kruglov <mail@ikruglov.com>
Mon, 11 May 2026 11:32:14 +0000 (04:32 -0700)
Add varlink context and runtime builders for .timer units:

TimerContext: MonotonicTimers (with MonotonicTimerBase enum), CalendarTimers, Unit, OnClockChange, OnTimezoneChange, AccuracyUSec, RandomizedDelayUSec, RandomizedOffsetUSec, FixedRandomDelay, Persistent, WakeSystem, RemainAfterElapse, DeferReactivation
TimerRuntime: Result (TimerResult enum), NextElapseUSecRealtime, NextElapseUSecMonotonic, LastTriggerUSec

MonotonicTimerSpec and CalendarTimerSpec are separate types since they have different value types (int vs string). MonotonicTimerBase and TimerResult are proper varlink enum types.

Compared to the old io-systemd-Unit-List branch, this adds RandomizedOffsetUSec and DeferReactivation (both present in D-Bus but previously missing), and adds full runtime fields.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
src/core/meson.build
src/core/varlink-timer.c [new file with mode: 0644]
src/core/varlink-timer.h [new file with mode: 0644]
src/core/varlink-unit.c
src/shared/varlink-io.systemd.Unit.c
src/shared/varlink-io.systemd.Unit.h
src/test/test-varlink-idl-unit.c
test/units/TEST-74-AUX-UTILS.varlinkctl.sh

index e59a683c9a50b421c660404e2a7a1ba89180911a..eef53be94bc7557f0cf0a90c8450634a704ea59e 100644 (file)
@@ -76,6 +76,7 @@ libcore_sources = files(
         'varlink-path.c',
         'varlink-scope.c',
         'varlink-swap.c',
+        'varlink-timer.c',
         'varlink-unit.c',
 )
 
diff --git a/src/core/varlink-timer.c b/src/core/varlink-timer.c
new file mode 100644 (file)
index 0000000..e7858fa
--- /dev/null
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-json.h"
+
+#include "calendarspec.h"
+#include "json-util.h"
+#include "timer.h"
+#include "varlink-timer.h"
+
+static int timers_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        TimerValue *values = userdata;
+        int r;
+
+        assert(ret);
+
+        LIST_FOREACH(value, value, values) {
+                _cleanup_free_ char *base_name = NULL;
+
+                base_name = timer_base_to_usec_string(value->base);
+                if (!base_name)
+                        return -ENOMEM;
+
+                if (value->base == TIMER_CALENDAR) {
+                        _cleanup_free_ char *calendar = NULL;
+
+                        r = calendar_spec_to_string(value->calendar_spec, &calendar);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to convert calendar spec into string: %m");
+
+                        r = sd_json_variant_append_arraybo(
+                                        &v,
+                                        JSON_BUILD_PAIR_ENUM("base", base_name),
+                                        SD_JSON_BUILD_PAIR_STRING("calendar", calendar));
+                } else
+                        r = sd_json_variant_append_arraybo(
+                                        &v,
+                                        JSON_BUILD_PAIR_ENUM("base", base_name),
+                                        SD_JSON_BUILD_PAIR_UNSIGNED("usec", value->value));
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(v);
+        return 0;
+}
+
+int timer_context_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Timer *t = ASSERT_PTR(TIMER(userdata));
+        Unit *trigger = UNIT_TRIGGER(UNIT(t));
+
+        return sd_json_buildo(
+                        ASSERT_PTR(ret),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("Timers", timers_build_json, t->values),
+                        JSON_BUILD_PAIR_STRING_NON_EMPTY("Unit", trigger ? trigger->id : NULL),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("OnClockChange", t->on_clock_change),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("OnTimezoneChange", t->on_timezone_change),
+                        JSON_BUILD_PAIR_FINITE_USEC("AccuracyUSec", t->accuracy_usec),
+                        JSON_BUILD_PAIR_FINITE_USEC("RandomizedDelayUSec", t->random_delay_usec),
+                        JSON_BUILD_PAIR_FINITE_USEC("RandomizedOffsetUSec", t->random_offset_usec),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("FixedRandomDelay", t->fixed_random_delay),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("Persistent", t->persistent),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("WakeSystem", t->wake_system),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("RemainAfterElapse", t->remain_after_elapse),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("DeferReactivation", t->defer_reactivation));
+}
+
+int timer_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Timer *t = ASSERT_PTR(TIMER(userdata));
+
+        return sd_json_buildo(
+                        ASSERT_PTR(ret),
+                        JSON_BUILD_PAIR_ENUM("Result", timer_result_to_string(t->result)),
+                        JSON_BUILD_PAIR_FINITE_USEC("NextElapseUSecRealtime", t->next_elapse_realtime),
+                        JSON_BUILD_PAIR_FINITE_USEC("NextElapseUSecMonotonic", timer_next_elapse_monotonic(t)),
+                        JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("LastTriggerUSec", &t->last_trigger));
+}
diff --git a/src/core/varlink-timer.h b/src/core/varlink-timer.h
new file mode 100644 (file)
index 0000000..e96387a
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "core-forward.h"
+
+int timer_context_build_json(sd_json_variant **ret, const char *name, void *userdata);
+int timer_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata);
index fa017e955157f4b3a34dcd636c963a684ad7062b..2e3b1dd1961b8c780235d233c62fc93704611985 100644 (file)
@@ -32,6 +32,7 @@
 #include "varlink-path.h"
 #include "varlink-scope.h"
 #include "varlink-swap.h"
+#include "varlink-timer.h"
 #include "varlink-unit.h"
 #include "varlink-util.h"
 
@@ -169,6 +170,7 @@ static int unit_context_build_json(sd_json_variant **ret, const char *name, void
                 [UNIT_SCOPE]     = scope_context_build_json,
                 [UNIT_SERVICE]   = service_context_build_json,
                 [UNIT_SWAP]      = swap_context_build_json,
+                [UNIT_TIMER]     = timer_context_build_json,
         };
 
         return sd_json_buildo(
@@ -337,6 +339,7 @@ static int unit_runtime_build_json(sd_json_variant **ret, const char *name, void
                 [UNIT_PATH]      = path_runtime_build_json,
                 [UNIT_SCOPE]     = scope_runtime_build_json,
                 [UNIT_SWAP]      = swap_runtime_build_json,
+                [UNIT_TIMER]     = timer_runtime_build_json,
         };
 
         return sd_json_buildo(
index 4d77e64a740dbbdecdc8e4fbb48dfa0bd5d785e9..883154b2f1951c77937fec20888a1bcc510444ce 100644 (file)
@@ -1081,6 +1081,70 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_FIELD_COMMENT("Reference GID"),
                 SD_VARLINK_DEFINE_FIELD(GID, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
 
+/* TimerContext
+ * https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html */
+SD_VARLINK_DEFINE_ENUM_TYPE(
+                TimerBase,
+                SD_VARLINK_DEFINE_ENUM_VALUE(OnActiveUSec),
+                SD_VARLINK_DEFINE_ENUM_VALUE(OnBootUSec),
+                SD_VARLINK_DEFINE_ENUM_VALUE(OnStartupUSec),
+                SD_VARLINK_DEFINE_ENUM_VALUE(OnUnitActiveUSec),
+                SD_VARLINK_DEFINE_ENUM_VALUE(OnUnitInactiveUSec),
+                SD_VARLINK_DEFINE_ENUM_VALUE(OnCalendar));
+
+static SD_VARLINK_DEFINE_STRUCT_TYPE(
+                TimerSpec,
+                SD_VARLINK_FIELD_COMMENT("Timer base type"),
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(base, TimerBase, 0),
+                SD_VARLINK_FIELD_COMMENT("Timer value in microseconds (for monotonic timers)"),
+                SD_VARLINK_DEFINE_FIELD(usec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Calendar specification string (for calendar timers)"),
+                SD_VARLINK_DEFINE_FIELD(calendar, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
+
+static SD_VARLINK_DEFINE_STRUCT_TYPE(
+                TimerContext,
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#OnActiveSec="),
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Timers, TimerSpec, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#Unit="),
+                SD_VARLINK_DEFINE_FIELD(Unit, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#OnClockChange="),
+                SD_VARLINK_DEFINE_FIELD(OnClockChange, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#OnClockChange="),
+                SD_VARLINK_DEFINE_FIELD(OnTimezoneChange, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#AccuracySec="),
+                SD_VARLINK_DEFINE_FIELD(AccuracyUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#RandomizedDelaySec="),
+                SD_VARLINK_DEFINE_FIELD(RandomizedDelayUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#RandomizedOffsetSec="),
+                SD_VARLINK_DEFINE_FIELD(RandomizedOffsetUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#FixedRandomDelay="),
+                SD_VARLINK_DEFINE_FIELD(FixedRandomDelay, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#Persistent="),
+                SD_VARLINK_DEFINE_FIELD(Persistent, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#WakeSystem="),
+                SD_VARLINK_DEFINE_FIELD(WakeSystem, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#RemainAfterElapse="),
+                SD_VARLINK_DEFINE_FIELD(RemainAfterElapse, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.timer.html#DeferReactivation="),
+                SD_VARLINK_DEFINE_FIELD(DeferReactivation, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
+
+SD_VARLINK_DEFINE_ENUM_TYPE(
+                TimerResult,
+                SD_VARLINK_DEFINE_ENUM_VALUE(success),
+                SD_VARLINK_DEFINE_ENUM_VALUE(resources),
+                SD_VARLINK_DEFINE_ENUM_VALUE(start_limit_hit));
+
+static SD_VARLINK_DEFINE_STRUCT_TYPE(
+                TimerRuntime,
+                SD_VARLINK_FIELD_COMMENT("Result of timer operation"),
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Result, TimerResult, 0),
+                SD_VARLINK_FIELD_COMMENT("Next elapse time in realtime clock"),
+                SD_VARLINK_DEFINE_FIELD(NextElapseUSecRealtime, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Next elapse time in monotonic clock"),
+                SD_VARLINK_DEFINE_FIELD(NextElapseUSecMonotonic, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Last time the timer triggered"),
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(LastTriggerUSec, Timestamp, SD_VARLINK_NULLABLE));
+
 /* Service-specific types */
 
 /* Keep in sync with service_type_table[] in src/core/service.c */
@@ -1279,7 +1343,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_FIELD_COMMENT("The scope context of the unit"),
                 SD_VARLINK_DEFINE_FIELD_BY_TYPE(Scope, ScopeContext, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("The swap context of the unit"),
-                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Swap, SwapContext, SD_VARLINK_NULLABLE));
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Swap, SwapContext, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The timer context of the unit"),
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Timer, TimerContext, SD_VARLINK_NULLABLE));
 
 static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 ActivationDetails,
@@ -1459,7 +1525,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_FIELD_COMMENT("The scope runtime of the unit"),
                 SD_VARLINK_DEFINE_FIELD_BY_TYPE(Scope, ScopeRuntime, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("The swap runtime of the unit"),
-                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Swap, SwapRuntime, SD_VARLINK_NULLABLE));
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Swap, SwapRuntime, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The timer runtime of the unit"),
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(Timer, TimerRuntime, SD_VARLINK_NULLABLE));
 
 static SD_VARLINK_DEFINE_METHOD_FULL(
                 List,
@@ -1633,6 +1701,11 @@ SD_VARLINK_DEFINE_INTERFACE(
                 &vl_type_SwapContext,
                 &vl_type_SwapResult,
                 &vl_type_SwapRuntime,
+                &vl_type_TimerBase,
+                &vl_type_TimerSpec,
+                &vl_type_TimerContext,
+                &vl_type_TimerResult,
+                &vl_type_TimerRuntime,
 
                 /* UnitContext enums */
                 &vl_type_CollectMode,
index 230fc5c43eeab1b64ec9ab0f03d966c37282baeb..128170eaf40797dd69ed9d927110fc060b4b9280 100644 (file)
@@ -34,6 +34,8 @@ extern const sd_varlink_symbol vl_type_PathType;
 extern const sd_varlink_symbol vl_type_PathResult;
 extern const sd_varlink_symbol vl_type_ScopeResult;
 extern const sd_varlink_symbol vl_type_SwapResult;
+extern const sd_varlink_symbol vl_type_TimerBase;
+extern const sd_varlink_symbol vl_type_TimerResult;
 extern const sd_varlink_symbol vl_type_CollectMode;
 extern const sd_varlink_symbol vl_type_JobMode;
 extern const sd_varlink_symbol vl_type_ServiceType;
index eba3a7d96a520d805dfff8a071bf99fe01ddab69..e03f5edff5aa28f633bb32a24225da3576fe1f0b 100644 (file)
@@ -13,6 +13,7 @@
 #include "swap.h"
 #include "tests.h"
 #include "test-varlink-idl-util.h"
+#include "timer.h"
 #include "unit.h"
 #include "varlink-idl-common.h"
 #include "varlink-io.systemd.Unit.h"
@@ -78,6 +79,16 @@ TEST(unit_enums_idl) {
         /* SwapRuntime enums */
         TEST_IDL_ENUM(SwapResult, swap_result, vl_type_SwapResult);
 
+        /* TimerContext enums */
+        for (TimerBase b = 0; b < _TIMER_BASE_MAX; b++) {
+                _cleanup_free_ char *s = timer_base_to_usec_string(b);
+                assert_se(s);
+                test_enum_to_string_name(s, &vl_type_TimerBase);
+        }
+
+        /* TimerRuntime enums */
+        TEST_IDL_ENUM(TimerResult, timer_result, vl_type_TimerResult);
+
         /* UnitContext enums */
         TEST_IDL_ENUM(CollectMode, collect_mode, vl_type_CollectMode);
         TEST_IDL_ENUM(EmergencyAction, emergency_action, vl_type_EmergencyAction);
index 4d9649f2d702df96fc99b7bb5c27dbb694e4371e..ee5d762b5aa77d8f1e97372b47df2aa7c862f8b2 100755 (executable)
@@ -265,6 +265,12 @@ if test -n "$swap_id"; then
     varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$swap_params" | jq -e '.context.Swap'
     varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$swap_params" | jq -e '.runtime.Swap'
 fi
+# test for TimerContext/Runtime
+timer_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "timer" and .runtime.LoadState == "loaded") .context.ID' | grep -v null | tail -n 1)
+test -n "$timer_id"
+timer_params=$(jq -cn --arg name "$timer_id" '{name: $name}')
+varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$timer_params" | jq -e '.context.Timer'
+varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$timer_params" | jq -e '.runtime.Timer'
 
 # test io.systemd.Metrics
 varlinkctl info /run/systemd/report/io.systemd.Manager