From: Ivan Kruglov Date: Thu, 7 May 2026 12:12:48 +0000 (-0700) Subject: core: implement TimerContext/Runtime for io.systemd.Unit.List + tests X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ae619b12240b5eff5d580a5def3a7fc4b3cbb9dc;p=thirdparty%2Fsystemd.git core: implement TimerContext/Runtime for io.systemd.Unit.List + tests 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 --- diff --git a/src/core/meson.build b/src/core/meson.build index e59a683c9a5..eef53be94bc 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -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 index 00000000000..e7858fa1d25 --- /dev/null +++ b/src/core/varlink-timer.c @@ -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 index 00000000000..e96387a55a9 --- /dev/null +++ b/src/core/varlink-timer.h @@ -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); diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index fa017e95515..2e3b1dd1961 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -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( diff --git a/src/shared/varlink-io.systemd.Unit.c b/src/shared/varlink-io.systemd.Unit.c index 4d77e64a740..883154b2f19 100644 --- a/src/shared/varlink-io.systemd.Unit.c +++ b/src/shared/varlink-io.systemd.Unit.c @@ -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, diff --git a/src/shared/varlink-io.systemd.Unit.h b/src/shared/varlink-io.systemd.Unit.h index 230fc5c43ee..128170eaf40 100644 --- a/src/shared/varlink-io.systemd.Unit.h +++ b/src/shared/varlink-io.systemd.Unit.h @@ -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; diff --git a/src/test/test-varlink-idl-unit.c b/src/test/test-varlink-idl-unit.c index eba3a7d96a5..e03f5edff5a 100644 --- a/src/test/test-varlink-idl-unit.c +++ b/src/test/test-varlink-idl-unit.c @@ -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); diff --git a/test/units/TEST-74-AUX-UTILS.varlinkctl.sh b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh index 4d9649f2d70..ee5d762b5aa 100755 --- a/test/units/TEST-74-AUX-UTILS.varlinkctl.sh +++ b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh @@ -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