1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "unit-name.h"
27 #include "dbus-timer.h"
30 #include "bus-error.h"
32 static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
33 [TIMER_DEAD] = UNIT_INACTIVE,
34 [TIMER_WAITING] = UNIT_ACTIVE,
35 [TIMER_RUNNING] = UNIT_ACTIVE,
36 [TIMER_ELAPSED] = UNIT_ACTIVE,
37 [TIMER_FAILED] = UNIT_FAILED
40 static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
42 static void timer_init(Unit *u) {
46 assert(u->load_state == UNIT_STUB);
48 t->next_elapse_monotonic = (usec_t) -1;
49 t->next_elapse_realtime = (usec_t) -1;
50 t->accuracy_usec = USEC_PER_MINUTE;
53 void timer_free_values(Timer *t) {
58 while ((v = t->values)) {
59 LIST_REMOVE(value, t->values, v);
62 calendar_spec_free(v->calendar_spec);
68 static void timer_done(Unit *u) {
75 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
76 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
79 static int timer_verify(Timer *t) {
82 if (UNIT(t)->load_state != UNIT_LOADED)
86 log_error_unit(UNIT(t)->id,
87 "%s lacks value setting. Refusing.", UNIT(t)->id);
94 static int timer_add_default_dependencies(Timer *t) {
99 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
103 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
104 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
109 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
112 static int timer_load(Unit *u) {
117 assert(u->load_state == UNIT_STUB);
119 r = unit_load_fragment_and_dropin(u);
123 if (u->load_state == UNIT_LOADED) {
125 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
128 r = unit_load_related_unit(u, ".service", &x);
132 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
137 if (UNIT(t)->default_dependencies) {
138 r = timer_add_default_dependencies(t);
144 return timer_verify(t);
147 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
148 char buf[FORMAT_TIMESPAN_MAX];
153 trigger = UNIT_TRIGGER(u);
156 "%sTimer State: %s\n"
160 prefix, timer_state_to_string(t->state),
161 prefix, timer_result_to_string(t->result),
162 prefix, trigger ? trigger->id : "n/a",
163 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
165 LIST_FOREACH(value, v, t->values) {
167 if (v->base == TIMER_CALENDAR) {
168 _cleanup_free_ char *p = NULL;
170 calendar_spec_to_string(v->calendar_spec, &p);
175 timer_base_to_string(v->base),
178 char timespan1[FORMAT_TIMESPAN_MAX];
183 timer_base_to_string(v->base),
184 strna(format_timespan(timespan1, sizeof(timespan1), v->value, 0)));
189 static void timer_set_state(Timer *t, TimerState state) {
190 TimerState old_state;
193 old_state = t->state;
196 if (state != TIMER_WAITING) {
197 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
198 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
201 if (state != old_state)
202 log_debug_unit(UNIT(t)->id,
203 "%s changed %s -> %s", UNIT(t)->id,
204 timer_state_to_string(old_state),
205 timer_state_to_string(state));
207 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
210 static void timer_enter_waiting(Timer *t, bool initial);
212 static int timer_coldplug(Unit *u) {
216 assert(t->state == TIMER_DEAD);
218 if (t->deserialized_state != t->state) {
220 if (t->deserialized_state == TIMER_WAITING)
221 timer_enter_waiting(t, false);
223 timer_set_state(t, t->deserialized_state);
229 static void timer_enter_dead(Timer *t, TimerResult f) {
232 if (f != TIMER_SUCCESS)
235 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
239 static void timer_enter_waiting(Timer *t, bool initial) {
243 bool found_monotonic = false, found_realtime = false;
246 dual_timestamp_get(&ts);
247 t->next_elapse_monotonic = t->next_elapse_realtime = 0;
249 LIST_FOREACH(value, v, t->values) {
254 if (v->base == TIMER_CALENDAR) {
256 r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
261 t->next_elapse_realtime = v->next_elapse;
263 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
265 found_realtime = true;
271 if (state_translation_table[t->state] == UNIT_ACTIVE)
272 base = UNIT(t)->inactive_exit_timestamp.monotonic;
278 /* CLOCK_MONOTONIC equals the uptime on Linux */
283 base = UNIT(t)->manager->userspace_timestamp.monotonic;
286 case TIMER_UNIT_ACTIVE:
288 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
291 base = t->last_trigger_monotonic;
298 case TIMER_UNIT_INACTIVE:
300 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
303 base = t->last_trigger_monotonic;
311 assert_not_reached("Unknown timer base");
314 v->next_elapse = base + v->value;
317 v->next_elapse < ts.monotonic &&
318 (v->base == TIMER_ACTIVE || v->base == TIMER_BOOT || v->base == TIMER_STARTUP)) {
319 /* This is a one time trigger, disable it now */
324 if (!found_monotonic)
325 t->next_elapse_monotonic = v->next_elapse;
327 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
329 found_monotonic = true;
333 if (!found_monotonic && !found_realtime) {
334 log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
335 timer_set_state(t, TIMER_ELAPSED);
339 if (found_monotonic) {
340 char buf[FORMAT_TIMESPAN_MAX];
341 log_debug_unit(UNIT(t)->id,
342 "%s: Monotonic timer elapses in %s.",
344 format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0));
346 if (t->monotonic_event_source) {
347 r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic);
351 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
353 r = sd_event_add_monotonic(UNIT(t)->manager->event, t->next_elapse_monotonic, t->accuracy_usec, timer_dispatch, t, &t->monotonic_event_source);
358 } else if (t->monotonic_event_source) {
359 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
365 if (found_realtime) {
366 char buf[FORMAT_TIMESTAMP_MAX];
367 log_debug_unit(UNIT(t)->id,
368 "%s: Realtime timer elapses at %s.",
370 format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
372 if (t->realtime_event_source) {
373 r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
377 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
379 r = sd_event_add_realtime(UNIT(t)->manager->event, t->next_elapse_realtime, t->accuracy_usec, timer_dispatch, t, &t->realtime_event_source);
384 } else if (t->realtime_event_source) {
385 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
391 timer_set_state(t, TIMER_WAITING);
395 log_warning_unit(UNIT(t)->id,
396 "%s failed to enter waiting state: %s",
397 UNIT(t)->id, strerror(-r));
398 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
401 static void timer_enter_running(Timer *t) {
402 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
407 /* Don't start job if we are supposed to go down */
408 if (unit_stop_pending(UNIT(t)))
411 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
412 JOB_REPLACE, true, &error, NULL);
416 t->last_trigger_monotonic = now(CLOCK_MONOTONIC);
418 timer_set_state(t, TIMER_RUNNING);
422 log_warning_unit(UNIT(t)->id,
423 "%s failed to queue unit startup job: %s",
424 UNIT(t)->id, bus_error_message(&error, r));
425 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
428 static int timer_start(Unit *u) {
432 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
434 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
437 t->result = TIMER_SUCCESS;
438 timer_enter_waiting(t, true);
442 static int timer_stop(Unit *u) {
446 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
448 timer_enter_dead(t, TIMER_SUCCESS);
452 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
459 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
460 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
465 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
473 if (streq(key, "state")) {
476 state = timer_state_from_string(value);
478 log_debug_unit(u->id, "Failed to parse state value %s", value);
480 t->deserialized_state = state;
481 } else if (streq(key, "result")) {
484 f = timer_result_from_string(value);
486 log_debug_unit(u->id, "Failed to parse result value %s", value);
487 else if (f != TIMER_SUCCESS)
491 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
496 _pure_ static UnitActiveState timer_active_state(Unit *u) {
499 return state_translation_table[TIMER(u)->state];
502 _pure_ static const char *timer_sub_state_to_string(Unit *u) {
505 return timer_state_to_string(TIMER(u)->state);
508 static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
509 Timer *t = TIMER(userdata);
513 if (t->state != TIMER_WAITING)
516 log_debug_unit(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id);
517 timer_enter_running(t);
521 static void timer_trigger_notify(Unit *u, Unit *other) {
528 if (other->load_state != UNIT_LOADED)
531 /* Reenable all timers that depend on unit state */
532 LIST_FOREACH(value, v, t->values)
533 if (v->base == TIMER_UNIT_ACTIVE ||
534 v->base == TIMER_UNIT_INACTIVE)
542 /* Recalculate sleep time */
543 timer_enter_waiting(t, false);
548 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
549 log_debug_unit(UNIT(t)->id,
550 "%s got notified about unit deactivation.",
552 timer_enter_waiting(t, false);
561 assert_not_reached("Unknown timer state");
565 static void timer_reset_failed(Unit *u) {
570 if (t->state == TIMER_FAILED)
571 timer_set_state(t, TIMER_DEAD);
573 t->result = TIMER_SUCCESS;
576 static void timer_time_change(Unit *u) {
581 if (t->state != TIMER_WAITING)
584 log_debug_unit(u->id,
585 "%s: time change, recalculating next elapse.", u->id);
586 timer_enter_waiting(t, false);
589 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
590 [TIMER_DEAD] = "dead",
591 [TIMER_WAITING] = "waiting",
592 [TIMER_RUNNING] = "running",
593 [TIMER_ELAPSED] = "elapsed",
594 [TIMER_FAILED] = "failed"
597 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
599 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
600 [TIMER_ACTIVE] = "OnActiveSec",
601 [TIMER_BOOT] = "OnBootSec",
602 [TIMER_STARTUP] = "OnStartupSec",
603 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
604 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
605 [TIMER_CALENDAR] = "OnCalendar"
608 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
610 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
611 [TIMER_SUCCESS] = "success",
612 [TIMER_FAILURE_RESOURCES] = "resources"
615 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
617 const UnitVTable timer_vtable = {
618 .object_size = sizeof(Timer),
629 .coldplug = timer_coldplug,
633 .start = timer_start,
636 .serialize = timer_serialize,
637 .deserialize_item = timer_deserialize_item,
639 .active_state = timer_active_state,
640 .sub_state_to_string = timer_sub_state_to_string,
642 .trigger_notify = timer_trigger_notify,
644 .reset_failed = timer_reset_failed,
645 .time_change = timer_time_change,
647 .bus_interface = "org.freedesktop.systemd1.Timer",
648 .bus_vtable = bus_timer_vtable,
649 .bus_changing_properties = bus_timer_changing_properties,