diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 2acb737c38f..2e843be2e54 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -3698,8 +3698,10 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX $TRIGGER_UNIT $TRIGGER_PATH + $TRIGGER_TIMER_REALTIME_USEC + $TRIGGER_TIMER_MONOTONIC_USEC - If the unit was activated dynamically (e.g.: a corresponding path unit), the + If the unit was activated dynamically (e.g.: a corresponding path unit or timer unit), the unit that triggered it and other type-dependent information will be passed via these variables. Note that this information is provided in a best-effort way. For example, multiple triggers happening one after another will be coalesced and only one will be reported, with no guarantee as to which one it will be. diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 49bcb18be52..953faa9b334 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -366,6 +366,10 @@ See Also + Environment variables with details on the trigger will be set for triggered units. See the + Environment Variables Set on Triggered Units section in + systemd.exec1 + for more details. systemd1, systemctl1, diff --git a/src/core/timer.c b/src/core/timer.c index 9de325ba662..b89d593b759 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -576,8 +576,10 @@ fail: } static void timer_enter_running(Timer *t) { + _cleanup_(activation_details_unrefp) ActivationDetails *details = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Unit *trigger; + Job *job; int r; assert(t); @@ -593,11 +595,20 @@ static void timer_enter_running(Timer *t) { return; } - r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL); + details = activation_details_new(UNIT(t)); + if (!details) { + r = -ENOMEM; + goto fail; + } + + r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, &job); if (r < 0) goto fail; dual_timestamp_get(&t->last_trigger); + ACTIVATION_DETAILS_TIMER(details)->last_trigger = t->last_trigger; + + job_set_activation_details(job, details); if (t->stamp_path) touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID); @@ -892,6 +903,91 @@ static int timer_can_start(Unit *u) { return 1; } +static void activation_details_timer_serialize(ActivationDetails *details, FILE *f) { + ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(details); + + assert(details); + assert(f); + assert(t); + + (void) serialize_dual_timestamp(f, "activation-details-timer-last-trigger", &t->last_trigger); +} + +static int activation_details_timer_deserialize(const char *key, const char *value, ActivationDetails **details) { + int r; + + assert(key); + assert(value); + + if (!details || !*details) + return -EINVAL; + + ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(*details); + if (!t) + return -EINVAL; + + if (!streq(key, "activation-details-timer-last-trigger")) + return -EINVAL; + + r = deserialize_dual_timestamp(value, &t->last_trigger); + if (r < 0) + return r; + + return 0; +} + +static int activation_details_timer_append_env(ActivationDetails *details, char ***strv) { + ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(details); + int r; + + assert(details); + assert(strv); + assert(t); + + if (!dual_timestamp_is_set(&t->last_trigger)) + return 0; + + r = strv_extendf(strv, "TRIGGER_TIMER_REALTIME_USEC=%" USEC_FMT, t->last_trigger.realtime); + if (r < 0) + return r; + + r = strv_extendf(strv, "TRIGGER_TIMER_MONOTONIC_USEC=%" USEC_FMT, t->last_trigger.monotonic); + if (r < 0) + return r; + + return 2; /* Return the number of variables added to the env block */ +} + +static int activation_details_timer_append_pair(ActivationDetails *details, char ***strv) { + ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(details); + int r; + + assert(details); + assert(strv); + assert(t); + + if (!dual_timestamp_is_set(&t->last_trigger)) + return 0; + + r = strv_extend(strv, "trigger_timer_realtime_usec"); + if (r < 0) + return r; + + r = strv_extendf(strv, "%" USEC_FMT, t->last_trigger.realtime); + if (r < 0) + return r; + + r = strv_extend(strv, "trigger_timer_monotonic_usec"); + if (r < 0) + return r; + + r = strv_extendf(strv, "%" USEC_FMT, t->last_trigger.monotonic); + if (r < 0) + return r; + + return 2; /* Return the number of pairs added to the env block */ +} + static const char* const timer_base_table[_TIMER_BASE_MAX] = { [TIMER_ACTIVE] = "OnActiveSec", [TIMER_BOOT] = "OnBootSec", @@ -954,3 +1050,12 @@ const UnitVTable timer_vtable = { .can_start = timer_can_start, }; + +const ActivationDetailsVTable activation_details_timer_vtable = { + .object_size = sizeof(ActivationDetailsTimer), + + .serialize = activation_details_timer_serialize, + .deserialize = activation_details_timer_deserialize, + .append_env = activation_details_timer_append_env, + .append_pair = activation_details_timer_append_pair, +}; diff --git a/src/core/timer.h b/src/core/timer.h index 551e2833417..914e42580e3 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -2,6 +2,7 @@ #pragma once typedef struct Timer Timer; +typedef struct ActivationDetailsTimer ActivationDetailsTimer; #include "calendarspec.h" #include "unit.h" @@ -64,11 +65,17 @@ struct Timer { char *stamp_path; }; +struct ActivationDetailsTimer { + ActivationDetails meta; + dual_timestamp last_trigger; +}; + #define TIMER_MONOTONIC_CLOCK(t) ((t)->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC) void timer_free_values(Timer *t); extern const UnitVTable timer_vtable; +extern const ActivationDetailsVTable activation_details_timer_vtable; const char *timer_base_to_string(TimerBase i) _const_; TimerBase timer_base_from_string(const char *s) _pure_; @@ -77,3 +84,4 @@ const char* timer_result_to_string(TimerResult i) _const_; TimerResult timer_result_from_string(const char *s) _pure_; DEFINE_CAST(TIMER, Timer); +DEFINE_ACTIVATION_DETAILS_CAST(ACTIVATION_DETAILS_TIMER, ActivationDetailsTimer, TIMER); diff --git a/src/core/unit.c b/src/core/unit.c index 4255e0cf1f4..5f1c6109b0b 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5931,6 +5931,7 @@ int unit_get_dependency_array(const Unit *u, UnitDependencyAtom atom, Unit ***re const ActivationDetailsVTable * const activation_details_vtable[_UNIT_TYPE_MAX] = { [UNIT_PATH] = &activation_details_path_vtable, + [UNIT_TIMER] = &activation_details_timer_vtable, }; ActivationDetails *activation_details_new(Unit *trigger_unit) { diff --git a/test/units/testsuite-07.sh b/test/units/testsuite-07.sh index 5e9fe64ea9f..95ebe3876ff 100755 --- a/test/units/testsuite-07.sh +++ b/test/units/testsuite-07.sh @@ -8,6 +8,9 @@ set -o pipefail cat >/lib/systemd/system/my.service <