diff --git a/src/core/meson.build b/src/core/meson.build index e59a683c9a..eef53be94b 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 0000000000..e7858fa1d2 --- /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 0000000000..e96387a55a --- /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 fa017e9551..2e3b1dd196 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 4d77e64a74..883154b2f1 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 230fc5c43e..128170eaf4 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 eba3a7d96a..e03f5edff5 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 4d9649f2d7..ee5d762b5a 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