diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index ac89fb174f..343df66754 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -335,10 +335,12 @@ All automount unit setting is available to transient units: Most timer unit settings are available to transient units. ``` -✓ OnCalendar= ✓ OnActiveSec= ✓ OnBootSec= +✓ OnCalendar= +✓ OnClockChange= ✓ OnStartupSec= +✓ OnTimezoneChange ✓ OnUnitActiveSec= ✓ OnUnitInactiveSec= ✓ Persistent= diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index faed632e90..807ca8022a 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -123,6 +123,8 @@ const sd_bus_vtable bus_timer_vtable[] = { SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_PROPERTY("OnClockChange", "b", bus_property_get_bool, offsetof(Timer, on_clock_change), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OnTimezoneChange", "b", bus_property_get_bool, offsetof(Timer, on_timezone_change), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -171,6 +173,12 @@ static int bus_timer_set_transient_property( if (streq(name, "RemainAfterElapse")) return bus_set_transient_bool(u, name, &t->remain_after_elapse, message, flags, error); + if (streq(name, "OnTimezoneChange")) + return bus_set_transient_bool(u, name, &t->on_timezone_change, message, flags, error); + + if (streq(name, "OnClockChange")) + return bus_set_transient_bool(u, name, &t->on_clock_change, message, flags, error); + if (streq(name, "TimersMonotonic")) { const char *base_name; usec_t usec = 0; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index ca0cf5894d..a2ed7f2abe 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -433,6 +433,8 @@ Timer.OnBootSec, config_parse_timer, TIMER_BOOT, Timer.OnStartupSec, config_parse_timer, TIMER_STARTUP, 0 Timer.OnUnitActiveSec, config_parse_timer, TIMER_UNIT_ACTIVE, 0 Timer.OnUnitInactiveSec, config_parse_timer, TIMER_UNIT_INACTIVE, 0 +Timer.OnClockChange, config_parse_bool, 0, offsetof(Timer, on_clock_change) +Timer.OnTimezoneChange, config_parse_bool, 0, offsetof(Timer, on_timezone_change) Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent) Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system) Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse) diff --git a/src/core/timer.c b/src/core/timer.c index 0e5214afe8..8440bb27ba 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -77,7 +77,7 @@ static int timer_verify(Timer *t) { if (UNIT(t)->load_state != UNIT_LOADED) return 0; - if (!t->values) { + if (!t->values && !t->on_clock_change && !t->on_timezone_change) { log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing."); return -ENOEXEC; } @@ -215,14 +215,18 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { "%sPersistent: %s\n" "%sWakeSystem: %s\n" "%sAccuracy: %s\n" - "%sRemainAfterElapse: %s\n", + "%sRemainAfterElapse: %s\n" + "%sOnClockChange: %s\n" + "%sOnTimeZoneChange %s\n", prefix, timer_state_to_string(t->state), prefix, timer_result_to_string(t->result), prefix, trigger ? trigger->id : "n/a", prefix, yes_no(t->persistent), prefix, yes_no(t->wake_system), prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1), - prefix, yes_no(t->remain_after_elapse)); + prefix, yes_no(t->remain_after_elapse), + prefix, yes_no(t->on_clock_change), + prefix, yes_no(t->on_timezone_change)); LIST_FOREACH(value, v, t->values) { @@ -474,7 +478,7 @@ static void timer_enter_waiting(Timer *t, bool time_change) { } } - if (!found_monotonic && !found_realtime) { + if (!found_monotonic && !found_realtime && !t->on_timezone_change && !t->on_clock_change) { log_unit_debug(UNIT(t), "Timer is elapsed."); timer_enter_elapsed(t, leave_around); return; @@ -815,8 +819,13 @@ static void timer_time_change(Unit *u) { if (t->last_trigger.realtime > ts) t->last_trigger.realtime = ts; - log_unit_debug(u, "Time change, recalculating next elapse."); - timer_enter_waiting(t, true); + if (t->on_clock_change) { + log_unit_debug(u, "Time change, triggering activation."); + timer_enter_running(t); + } else { + log_unit_debug(u, "Time change, recalculating next elapse."); + timer_enter_waiting(t, true); + } } static void timer_timezone_change(Unit *u) { @@ -827,8 +836,13 @@ static void timer_timezone_change(Unit *u) { if (t->state != TIMER_WAITING) return; - log_unit_debug(u, "Timezone change, recalculating next elapse."); - timer_enter_waiting(t, false); + if (t->on_timezone_change) { + log_unit_debug(u, "Timezone change, triggering activation."); + timer_enter_running(t); + } else { + log_unit_debug(u, "Timezone change, recalculating next elapse."); + timer_enter_waiting(t, false); + } } static const char* const timer_base_table[_TIMER_BASE_MAX] = { diff --git a/src/core/timer.h b/src/core/timer.h index 833aadb0b8..ab66a201ad 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -57,6 +57,8 @@ struct Timer { bool persistent; bool wake_system; bool remain_after_elapse; + bool on_clock_change; + bool on_timezone_change; char *stamp_path; }; diff --git a/src/run/run.c b/src/run/run.c index ba49e0daa2..56aa9aaee6 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -124,6 +124,8 @@ static int help(void) { " --on-unit-active=SECONDS Run SECONDS after the last activation\n" " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n" " --on-calendar=SPEC Realtime timer\n" + " --on-timezone-change Run when the timezone changes\n" + " --on-clock-change Run when the realtime clock jumps\n" " --timer-property=NAME=VALUE Set timer unit property\n" "\nSee the %s for details.\n" , program_invocation_short_name @@ -170,6 +172,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_ON_UNIT_ACTIVE, ARG_ON_UNIT_INACTIVE, ARG_ON_CALENDAR, + ARG_ON_TIMEZONE_CHANGE, + ARG_ON_CLOCK_CHANGE, ARG_TIMER_PROPERTY, ARG_PATH_PROPERTY, ARG_SOCKET_PROPERTY, @@ -210,6 +214,8 @@ static int parse_argv(int argc, char *argv[]) { { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE }, { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE }, { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, + { "on-timezone-change",no_argument, NULL, ARG_ON_TIMEZONE_CHANGE}, + { "on-clock-change", no_argument, NULL, ARG_ON_CLOCK_CHANGE }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, { "path-property", required_argument, NULL, ARG_PATH_PROPERTY }, { "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY }, @@ -382,6 +388,22 @@ static int parse_argv(int argc, char *argv[]) { arg_with_timer = true; break; + case ARG_ON_TIMEZONE_CHANGE: + r = add_timer_property("OnTimezoneChange", "yes"); + if (r < 0) + return r; + + arg_with_timer = true; + break; + + case ARG_ON_CLOCK_CHANGE: + r = add_timer_property("OnClockChange", "yes"); + if (r < 0) + return r; + + arg_with_timer = true; + break; + case ARG_TIMER_PROPERTY: if (strv_extend(&arg_timer_property, optarg) < 0) diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 968f91b28c..b27227aeb6 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1499,7 +1499,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) { int r; - if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent")) + if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent", + "OnTimezoneChange", "OnClockChange")) return bus_append_parse_boolean(m, field, eq);