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"
29 #include "bus-errors.h"
31 static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
32 [TIMER_DEAD] = UNIT_INACTIVE,
33 [TIMER_WAITING] = UNIT_ACTIVE,
34 [TIMER_RUNNING] = UNIT_ACTIVE,
35 [TIMER_ELAPSED] = UNIT_ACTIVE,
36 [TIMER_FAILED] = UNIT_FAILED
39 static void timer_init(Unit *u) {
43 assert(u->load_state == UNIT_STUB);
45 t->next_elapse_monotonic = (usec_t) -1;
46 t->next_elapse_realtime = (usec_t) -1;
47 watch_init(&t->monotonic_watch);
48 watch_init(&t->realtime_watch);
51 static void timer_done(Unit *u) {
57 while ((v = t->values)) {
58 LIST_REMOVE(TimerValue, value, t->values, v);
61 calendar_spec_free(v->calendar_spec);
66 unit_unwatch_timer(u, &t->monotonic_watch);
67 unit_unwatch_timer(u, &t->realtime_watch);
69 unit_ref_unset(&t->unit);
72 static int timer_verify(Timer *t) {
75 if (UNIT(t)->load_state != UNIT_LOADED)
79 log_error_unit(UNIT(t)->id,
80 "%s lacks value setting. Refusing.", UNIT(t)->id);
87 static int timer_add_default_dependencies(Timer *t) {
92 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
93 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true);
97 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
102 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
105 static int timer_load(Unit *u) {
110 assert(u->load_state == UNIT_STUB);
112 r = unit_load_fragment_and_dropin(u);
116 if (u->load_state == UNIT_LOADED) {
118 if (!UNIT_DEREF(t->unit)) {
121 r = unit_load_related_unit(u, ".service", &x);
125 unit_ref_set(&t->unit, x);
128 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
132 if (UNIT(t)->default_dependencies) {
133 r = timer_add_default_dependencies(t);
139 return timer_verify(t);
142 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
147 "%sTimer State: %s\n"
150 prefix, timer_state_to_string(t->state),
151 prefix, timer_result_to_string(t->result),
152 prefix, UNIT_DEREF(t->unit)->id);
154 LIST_FOREACH(value, v, t->values) {
156 if (v->base == TIMER_CALENDAR) {
157 _cleanup_free_ char *p = NULL;
159 calendar_spec_to_string(v->calendar_spec, &p);
164 timer_base_to_string(v->base),
167 char timespan1[FORMAT_TIMESPAN_MAX];
172 timer_base_to_string(v->base),
173 strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
178 static void timer_set_state(Timer *t, TimerState state) {
179 TimerState old_state;
182 old_state = t->state;
185 if (state != TIMER_WAITING) {
186 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
187 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
190 if (state != old_state)
191 log_debug_unit(UNIT(t)->id,
192 "%s changed %s -> %s", UNIT(t)->id,
193 timer_state_to_string(old_state),
194 timer_state_to_string(state));
196 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
199 static void timer_enter_waiting(Timer *t, bool initial);
201 static int timer_coldplug(Unit *u) {
205 assert(t->state == TIMER_DEAD);
207 if (t->deserialized_state != t->state) {
209 if (t->deserialized_state == TIMER_WAITING)
210 timer_enter_waiting(t, false);
212 timer_set_state(t, t->deserialized_state);
218 static void timer_enter_dead(Timer *t, TimerResult f) {
221 if (f != TIMER_SUCCESS)
224 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
227 static void timer_enter_waiting(Timer *t, bool initial) {
231 bool found_monotonic = false, found_realtime = false;
234 dual_timestamp_get(&ts);
235 t->next_elapse_monotonic = t->next_elapse_realtime = 0;
237 LIST_FOREACH(value, v, t->values) {
242 if (v->base == TIMER_CALENDAR) {
244 r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
248 if (!initial && v->next_elapse < ts.realtime) {
254 t->next_elapse_realtime = v->next_elapse;
256 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
258 found_realtime = true;
264 if (state_translation_table[t->state] == UNIT_ACTIVE)
265 base = UNIT(t)->inactive_exit_timestamp.monotonic;
271 /* CLOCK_MONOTONIC equals the uptime on Linux */
276 base = UNIT(t)->manager->userspace_timestamp.monotonic;
279 case TIMER_UNIT_ACTIVE:
281 if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
284 base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
287 case TIMER_UNIT_INACTIVE:
289 if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
292 base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
296 assert_not_reached("Unknown timer base");
299 v->next_elapse = base + v->value;
301 if (!initial && v->next_elapse < ts.monotonic) {
306 if (!found_monotonic)
307 t->next_elapse_monotonic = v->next_elapse;
309 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
311 found_monotonic = true;
315 if (!found_monotonic && !found_realtime) {
316 log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
317 timer_set_state(t, TIMER_ELAPSED);
321 if (found_monotonic) {
322 char buf[FORMAT_TIMESPAN_MAX];
323 log_debug_unit(UNIT(t)->id,
324 "%s: Monotonic timer elapses in %s the next time.",
326 format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic));
328 r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
332 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
334 if (found_realtime) {
335 char buf[FORMAT_TIMESTAMP_MAX];
336 log_debug_unit(UNIT(t)->id,
337 "%s: Realtime timer elapses at %s the next time.",
339 format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
341 r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch);
345 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
347 timer_set_state(t, TIMER_WAITING);
351 log_warning_unit(UNIT(t)->id,
352 "%s failed to enter waiting state: %s",
353 UNIT(t)->id, strerror(-r));
354 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
357 static void timer_enter_running(Timer *t) {
362 dbus_error_init(&error);
364 /* Don't start job if we are supposed to go down */
365 if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
368 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
372 timer_set_state(t, TIMER_RUNNING);
376 log_warning_unit(UNIT(t)->id,
377 "%s failed to queue unit startup job: %s",
378 UNIT(t)->id, bus_error(&error, r));
379 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
381 dbus_error_free(&error);
384 static int timer_start(Unit *u) {
388 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
390 if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
393 t->result = TIMER_SUCCESS;
394 timer_enter_waiting(t, true);
398 static int timer_stop(Unit *u) {
402 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
404 timer_enter_dead(t, TIMER_SUCCESS);
408 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
415 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
416 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
421 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
429 if (streq(key, "state")) {
432 state = timer_state_from_string(value);
434 log_debug_unit(u->id, "Failed to parse state value %s", value);
436 t->deserialized_state = state;
437 } else if (streq(key, "result")) {
440 f = timer_result_from_string(value);
442 log_debug_unit(u->id, "Failed to parse result value %s", value);
443 else if (f != TIMER_SUCCESS)
447 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
452 static UnitActiveState timer_active_state(Unit *u) {
455 return state_translation_table[TIMER(u)->state];
458 static const char *timer_sub_state_to_string(Unit *u) {
461 return timer_state_to_string(TIMER(u)->state);
464 static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
468 assert(elapsed == 1);
470 if (t->state != TIMER_WAITING)
473 log_debug_unit(u->id, "Timer elapsed on %s", u->id);
474 timer_enter_running(t);
477 void timer_unit_notify(Unit *u, UnitActiveState new_state) {
481 if (u->type == UNIT_TIMER)
484 SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
488 if (k->type != UNIT_TIMER)
491 if (k->load_state != UNIT_LOADED)
496 /* Reenable all timers that depend on unit state */
497 LIST_FOREACH(value, v, t->values)
498 if (v->base == TIMER_UNIT_ACTIVE ||
499 v->base == TIMER_UNIT_INACTIVE)
507 /* Recalculate sleep time */
508 timer_enter_waiting(t, false);
513 if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
514 log_debug_unit(UNIT(t)->id,
515 "%s got notified about unit deactivation.",
517 timer_enter_waiting(t, false);
527 assert_not_reached("Unknown timer state");
532 static void timer_reset_failed(Unit *u) {
537 if (t->state == TIMER_FAILED)
538 timer_set_state(t, TIMER_DEAD);
540 t->result = TIMER_SUCCESS;
543 static void timer_time_change(Unit *u) {
548 if (t->state != TIMER_WAITING)
552 "%s: time change, recalculating next elapse.", u->id);
553 timer_enter_waiting(t, false);
556 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
557 [TIMER_DEAD] = "dead",
558 [TIMER_WAITING] = "waiting",
559 [TIMER_RUNNING] = "running",
560 [TIMER_ELAPSED] = "elapsed",
561 [TIMER_FAILED] = "failed"
564 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
566 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
567 [TIMER_ACTIVE] = "OnActiveSec",
568 [TIMER_BOOT] = "OnBootSec",
569 [TIMER_STARTUP] = "OnStartupSec",
570 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
571 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
572 [TIMER_CALENDAR] = "OnCalendar"
575 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
577 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
578 [TIMER_SUCCESS] = "success",
579 [TIMER_FAILURE_RESOURCES] = "resources"
582 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
584 const UnitVTable timer_vtable = {
585 .object_size = sizeof(Timer),
595 .coldplug = timer_coldplug,
599 .start = timer_start,
602 .serialize = timer_serialize,
603 .deserialize_item = timer_deserialize_item,
605 .active_state = timer_active_state,
606 .sub_state_to_string = timer_sub_state_to_string,
608 .timer_event = timer_timer_event,
610 .reset_failed = timer_reset_failed,
611 .time_change = timer_time_change,
613 .bus_interface = "org.freedesktop.systemd1.Timer",
614 .bus_message_handler = bus_timer_message_handler,
615 .bus_invalidating_properties = bus_timer_invalidating_properties