]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/timer.c
udev: link-config - fix crash due to missing hwaddr
[thirdparty/systemd.git] / src / core / timer.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5cb5a6ff 2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
a7334b09
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
d46de8a1
LP
22#include <errno.h>
23
87f0e418 24#include "unit.h"
871d7de4 25#include "unit-name.h"
5cb5a6ff 26#include "timer.h"
871d7de4 27#include "dbus-timer.h"
a40eb732 28#include "special.h"
718db961
LP
29#include "bus-util.h"
30#include "bus-error.h"
06642d17 31#include "mkdir.h"
871d7de4
LP
32
33static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
34 [TIMER_DEAD] = UNIT_INACTIVE,
35 [TIMER_WAITING] = UNIT_ACTIVE,
36 [TIMER_RUNNING] = UNIT_ACTIVE,
37 [TIMER_ELAPSED] = UNIT_ACTIVE,
fdf20a31 38 [TIMER_FAILED] = UNIT_FAILED
871d7de4
LP
39};
40
718db961
LP
41static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
42
871d7de4
LP
43static void timer_init(Unit *u) {
44 Timer *t = TIMER(u);
45
46 assert(u);
ac155bb8 47 assert(u->load_state == UNIT_STUB);
871d7de4 48
3a43da28
KS
49 t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
50 t->next_elapse_realtime = USEC_INFINITY;
bd8f585b 51 t->accuracy_usec = u->manager->default_timer_accuracy_usec;
871d7de4 52}
5cb5a6ff 53
74051b9b 54void timer_free_values(Timer *t) {
871d7de4
LP
55 TimerValue *v;
56
57 assert(t);
58
59 while ((v = t->values)) {
71fda00f 60 LIST_REMOVE(value, t->values, v);
36697dc0
LP
61
62 if (v->calendar_spec)
63 calendar_spec_free(v->calendar_spec);
64
871d7de4
LP
65 free(v);
66 }
74051b9b
LP
67}
68
69static void timer_done(Unit *u) {
70 Timer *t = TIMER(u);
71
72 assert(t);
73
74 timer_free_values(t);
871d7de4 75
718db961
LP
76 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
77 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
06642d17
LP
78
79 free(t->stamp_path);
871d7de4
LP
80}
81
82static int timer_verify(Timer *t) {
83 assert(t);
84
1124fe6f 85 if (UNIT(t)->load_state != UNIT_LOADED)
871d7de4
LP
86 return 0;
87
88 if (!t->values) {
06642d17 89 log_error_unit(UNIT(t)->id, "%s lacks value setting. Refusing.", UNIT(t)->id);
871d7de4
LP
90 return -EINVAL;
91 }
92
93 return 0;
94}
95
6c155fe3
LP
96static int timer_add_default_dependencies(Timer *t) {
97 int r;
98
99 assert(t);
100
e3d84721
LP
101 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
102 if (r < 0)
103 return r;
2a77d31d 104
e3d84721 105 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
36697dc0
LP
106 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
107 if (r < 0)
6c155fe3 108 return r;
2a77d31d 109 }
6c155fe3 110
ead8e478 111 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
6c155fe3
LP
112}
113
06642d17
LP
114static int timer_setup_persistent(Timer *t) {
115 int r;
116
117 assert(t);
118
119 if (!t->persistent)
120 return 0;
121
122 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
123
124 r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
125 if (r < 0)
126 return r;
127
128 t->stamp_path = strappend("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
129 } else {
130 const char *e;
131
132 e = getenv("XDG_DATA_HOME");
133 if (e)
bd34b310 134 t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL);
06642d17
LP
135 else {
136
137 _cleanup_free_ char *h = NULL;
138
139 r = get_home_dir(&h);
140 if (r < 0) {
141 log_error("Failed to determine home directory: %s", strerror(-r));
142 return r;
143 }
144
145 t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL);
146 }
147 }
148
149 if (!t->stamp_path)
150 return log_oom();
151
152 return 0;
153}
154
871d7de4
LP
155static int timer_load(Unit *u) {
156 Timer *t = TIMER(u);
157 int r;
158
159 assert(u);
ac155bb8 160 assert(u->load_state == UNIT_STUB);
871d7de4 161
36697dc0
LP
162 r = unit_load_fragment_and_dropin(u);
163 if (r < 0)
871d7de4
LP
164 return r;
165
ac155bb8 166 if (u->load_state == UNIT_LOADED) {
871d7de4 167
3ecaa09b 168 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
57020a3a
LP
169 Unit *x;
170
171 r = unit_load_related_unit(u, ".service", &x);
172 if (r < 0)
871d7de4
LP
173 return r;
174
3ecaa09b
LP
175 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
176 if (r < 0)
177 return r;
57020a3a
LP
178 }
179
06642d17
LP
180 r = timer_setup_persistent(t);
181 if (r < 0)
182 return r;
183
184 if (u->default_dependencies) {
36697dc0
LP
185 r = timer_add_default_dependencies(t);
186 if (r < 0)
a40eb732 187 return r;
36697dc0 188 }
871d7de4
LP
189 }
190
191 return timer_verify(t);
192}
193
194static void timer_dump(Unit *u, FILE *f, const char *prefix) {
9f5eb56a 195 char buf[FORMAT_TIMESPAN_MAX];
871d7de4 196 Timer *t = TIMER(u);
3ecaa09b 197 Unit *trigger;
871d7de4 198 TimerValue *v;
034c6ed7 199
3ecaa09b
LP
200 trigger = UNIT_TRIGGER(u);
201
871d7de4
LP
202 fprintf(f,
203 "%sTimer State: %s\n"
067d72c9 204 "%sResult: %s\n"
9f5eb56a 205 "%sUnit: %s\n"
dedabea4
LP
206 "%sPersistent: %s\n"
207 "%sWakeSystem: %s\n"
9f5eb56a 208 "%sAccuracy: %s\n",
871d7de4 209 prefix, timer_state_to_string(t->state),
067d72c9 210 prefix, timer_result_to_string(t->result),
9f5eb56a 211 prefix, trigger ? trigger->id : "n/a",
dedabea4
LP
212 prefix, yes_no(t->persistent),
213 prefix, yes_no(t->wake_system),
9f5eb56a 214 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
871d7de4 215
36697dc0
LP
216 LIST_FOREACH(value, v, t->values) {
217
218 if (v->base == TIMER_CALENDAR) {
219 _cleanup_free_ char *p = NULL;
220
221 calendar_spec_to_string(v->calendar_spec, &p);
222
223 fprintf(f,
224 "%s%s: %s\n",
225 prefix,
226 timer_base_to_string(v->base),
227 strna(p));
228 } else {
229 char timespan1[FORMAT_TIMESPAN_MAX];
230
231 fprintf(f,
232 "%s%s: %s\n",
233 prefix,
234 timer_base_to_string(v->base),
2fa4092c 235 strna(format_timespan(timespan1, sizeof(timespan1), v->value, 0)));
36697dc0
LP
236 }
237 }
871d7de4
LP
238}
239
240static void timer_set_state(Timer *t, TimerState state) {
241 TimerState old_state;
034c6ed7 242 assert(t);
871d7de4
LP
243
244 old_state = t->state;
245 t->state = state;
246
36697dc0 247 if (state != TIMER_WAITING) {
718db961
LP
248 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
249 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
36697dc0 250 }
871d7de4
LP
251
252 if (state != old_state)
66870f90
ZJS
253 log_debug_unit(UNIT(t)->id,
254 "%s changed %s -> %s", UNIT(t)->id,
255 timer_state_to_string(old_state),
256 timer_state_to_string(state));
871d7de4 257
e2f3b44c 258 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
871d7de4
LP
259}
260
261static void timer_enter_waiting(Timer *t, bool initial);
262
263static int timer_coldplug(Unit *u) {
264 Timer *t = TIMER(u);
265
266 assert(t);
267 assert(t->state == TIMER_DEAD);
268
269 if (t->deserialized_state != t->state) {
270
b363ca6f 271 if (t->deserialized_state == TIMER_WAITING)
871d7de4
LP
272 timer_enter_waiting(t, false);
273 else
274 timer_set_state(t, t->deserialized_state);
275 }
276
277 return 0;
278}
279
067d72c9 280static void timer_enter_dead(Timer *t, TimerResult f) {
871d7de4
LP
281 assert(t);
282
067d72c9
LP
283 if (f != TIMER_SUCCESS)
284 t->result = f;
871d7de4 285
067d72c9 286 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
871d7de4
LP
287}
288
dedabea4
LP
289static usec_t monotonic_to_boottime(usec_t t) {
290 usec_t a, b;
291
292 if (t <= 0)
293 return 0;
294
295 a = now(CLOCK_BOOTTIME);
296 b = now(CLOCK_MONOTONIC);
297
298 if (t + a > b)
299 return t + a - b;
300 else
301 return 0;
302}
303
871d7de4 304static void timer_enter_waiting(Timer *t, bool initial) {
36697dc0 305 bool found_monotonic = false, found_realtime = false;
dedabea4
LP
306 usec_t ts_realtime, ts_monotonic;
307 usec_t base = 0;
308 TimerValue *v;
871d7de4
LP
309 int r;
310
dedabea4
LP
311 /* If we shall wake the system we use the boottime clock
312 * rather than the monotonic clock. */
313
314 ts_realtime = now(CLOCK_REALTIME);
315 ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
316 t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
871d7de4
LP
317
318 LIST_FOREACH(value, v, t->values) {
319
320 if (v->disabled)
321 continue;
322
36697dc0 323 if (v->base == TIMER_CALENDAR) {
06642d17
LP
324 usec_t b;
325
326 /* If we know the last time this was
327 * triggered, schedule the job based relative
328 * to that. If we don't just start from
329 * now. */
36697dc0 330
dedabea4 331 b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
06642d17
LP
332
333 r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
36697dc0
LP
334 if (r < 0)
335 continue;
336
36697dc0
LP
337 if (!found_realtime)
338 t->next_elapse_realtime = v->next_elapse;
10717a1a 339 else
36697dc0 340 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
871d7de4 341
36697dc0 342 found_realtime = true;
871d7de4 343
36697dc0
LP
344 } else {
345 switch (v->base) {
871d7de4 346
36697dc0
LP
347 case TIMER_ACTIVE:
348 if (state_translation_table[t->state] == UNIT_ACTIVE)
349 base = UNIT(t)->inactive_exit_timestamp.monotonic;
350 else
dedabea4 351 base = ts_monotonic;
36697dc0 352 break;
871d7de4 353
36697dc0
LP
354 case TIMER_BOOT:
355 /* CLOCK_MONOTONIC equals the uptime on Linux */
356 base = 0;
357 break;
871d7de4 358
36697dc0
LP
359 case TIMER_STARTUP:
360 base = UNIT(t)->manager->userspace_timestamp.monotonic;
361 break;
871d7de4 362
36697dc0 363 case TIMER_UNIT_ACTIVE:
871d7de4 364
e41e1943
LP
365 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
366
367 if (base <= 0)
06642d17 368 base = t->last_trigger.monotonic;
e41e1943
LP
369
370 if (base <= 0)
36697dc0 371 continue;
871d7de4 372
36697dc0 373 break;
871d7de4 374
36697dc0 375 case TIMER_UNIT_INACTIVE:
871d7de4 376
e41e1943
LP
377 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
378
379 if (base <= 0)
06642d17 380 base = t->last_trigger.monotonic;
e41e1943
LP
381
382 if (base <= 0)
36697dc0 383 continue;
871d7de4 384
36697dc0 385 break;
871d7de4 386
36697dc0
LP
387 default:
388 assert_not_reached("Unknown timer base");
389 }
871d7de4 390
dedabea4
LP
391 if (t->wake_system)
392 base = monotonic_to_boottime(base);
393
36697dc0
LP
394 v->next_elapse = base + v->value;
395
dedabea4 396 if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
e41e1943 397 /* This is a one time trigger, disable it now */
36697dc0
LP
398 v->disabled = true;
399 continue;
400 }
401
402 if (!found_monotonic)
dedabea4 403 t->next_elapse_monotonic_or_boottime = v->next_elapse;
36697dc0 404 else
dedabea4 405 t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
36697dc0
LP
406
407 found_monotonic = true;
408 }
871d7de4
LP
409 }
410
36697dc0 411 if (!found_monotonic && !found_realtime) {
66870f90 412 log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
871d7de4
LP
413 timer_set_state(t, TIMER_ELAPSED);
414 return;
415 }
416
36697dc0
LP
417 if (found_monotonic) {
418 char buf[FORMAT_TIMESPAN_MAX];
dedabea4
LP
419
420 log_debug_unit(UNIT(t)->id, "%s: Monotonic timer elapses in %s.",
421 UNIT(t)->id,
422 format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
871d7de4 423
718db961 424 if (t->monotonic_event_source) {
dedabea4 425 r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
718db961
LP
426 if (r < 0)
427 goto fail;
428
429 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
430 } else
6a0f1f6d
LP
431 r = sd_event_add_time(
432 UNIT(t)->manager->event,
433 &t->monotonic_event_source,
dedabea4
LP
434 t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
435 t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
6a0f1f6d 436 timer_dispatch, t);
36697dc0
LP
437 if (r < 0)
438 goto fail;
718db961
LP
439
440 } else if (t->monotonic_event_source) {
718db961 441
06642d17 442 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
718db961
LP
443 if (r < 0)
444 goto fail;
445 }
36697dc0
LP
446
447 if (found_realtime) {
448 char buf[FORMAT_TIMESTAMP_MAX];
06642d17 449 log_debug_unit(UNIT(t)->id, "%s: Realtime timer elapses at %s.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
36697dc0 450
718db961
LP
451 if (t->realtime_event_source) {
452 r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
453 if (r < 0)
454 goto fail;
455
456 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
457 } else
6a0f1f6d
LP
458 r = sd_event_add_time(
459 UNIT(t)->manager->event,
460 &t->realtime_event_source,
dedabea4 461 t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
6a0f1f6d
LP
462 t->next_elapse_realtime, t->accuracy_usec,
463 timer_dispatch, t);
36697dc0
LP
464 if (r < 0)
465 goto fail;
718db961
LP
466
467 } else if (t->realtime_event_source) {
718db961 468
06642d17 469 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
718db961
LP
470 if (r < 0)
471 goto fail;
472 }
871d7de4
LP
473
474 timer_set_state(t, TIMER_WAITING);
475 return;
476
477fail:
06642d17 478 log_warning_unit(UNIT(t)->id, "%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
067d72c9 479 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
480}
481
482static void timer_enter_running(Timer *t) {
718db961 483 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
871d7de4 484 int r;
398ef8ba 485
871d7de4
LP
486 assert(t);
487
ba3e67a7 488 /* Don't start job if we are supposed to go down */
31afa0a4 489 if (unit_stop_pending(UNIT(t)))
ba3e67a7
LP
490 return;
491
3ecaa09b
LP
492 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
493 JOB_REPLACE, true, &error, NULL);
36697dc0 494 if (r < 0)
871d7de4
LP
495 goto fail;
496
06642d17
LP
497 dual_timestamp_get(&t->last_trigger);
498
472fc28f 499 if (t->stamp_path)
359efc59 500 touch_file(t->stamp_path, true, t->last_trigger.realtime, (uid_t) -1, (gid_t) -1, 0);
e41e1943 501
871d7de4
LP
502 timer_set_state(t, TIMER_RUNNING);
503 return;
504
505fail:
66870f90
ZJS
506 log_warning_unit(UNIT(t)->id,
507 "%s failed to queue unit startup job: %s",
718db961 508 UNIT(t)->id, bus_error_message(&error, r));
067d72c9 509 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
510}
511
512static int timer_start(Unit *u) {
513 Timer *t = TIMER(u);
514
515 assert(t);
fdf20a31 516 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
01f78473 517
3ecaa09b 518 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
01f78473 519 return -ENOENT;
871d7de4 520
06642d17
LP
521 t->last_trigger = DUAL_TIMESTAMP_NULL;
522
523 if (t->stamp_path) {
524 struct stat st;
525
526 if (stat(t->stamp_path, &st) >= 0)
527 t->last_trigger.realtime = timespec_load(&st.st_atim);
472fc28f
TB
528 else if (errno == ENOENT)
529 /* The timer has never run before,
530 * make sure a stamp file exists.
531 */
359efc59 532 touch_file(t->stamp_path, true, (usec_t) -1, (uid_t) -1, (gid_t) -1, 0);
06642d17
LP
533 }
534
067d72c9 535 t->result = TIMER_SUCCESS;
871d7de4
LP
536 timer_enter_waiting(t, true);
537 return 0;
538}
539
540static int timer_stop(Unit *u) {
541 Timer *t = TIMER(u);
542
543 assert(t);
544 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
545
067d72c9 546 timer_enter_dead(t, TIMER_SUCCESS);
871d7de4
LP
547 return 0;
548}
549
550static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
551 Timer *t = TIMER(u);
552
553 assert(u);
554 assert(f);
555 assert(fds);
556
557 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
067d72c9 558 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
871d7de4 559
06642d17
LP
560 if (t->last_trigger.realtime > 0)
561 unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime);
562
563 if (t->last_trigger.monotonic > 0)
564 unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic);
565
871d7de4
LP
566 return 0;
567}
568
569static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
570 Timer *t = TIMER(u);
06642d17 571 int r;
871d7de4
LP
572
573 assert(u);
574 assert(key);
575 assert(value);
576 assert(fds);
577
578 if (streq(key, "state")) {
579 TimerState state;
580
36697dc0
LP
581 state = timer_state_from_string(value);
582 if (state < 0)
66870f90 583 log_debug_unit(u->id, "Failed to parse state value %s", value);
871d7de4
LP
584 else
585 t->deserialized_state = state;
067d72c9
LP
586 } else if (streq(key, "result")) {
587 TimerResult f;
588
589 f = timer_result_from_string(value);
590 if (f < 0)
66870f90 591 log_debug_unit(u->id, "Failed to parse result value %s", value);
067d72c9
LP
592 else if (f != TIMER_SUCCESS)
593 t->result = f;
06642d17
LP
594 } else if (streq(key, "last-trigger-realtime")) {
595
596 r = safe_atou64(value, &t->last_trigger.realtime);
597 if (r < 0)
598 log_debug_unit(u->id, "Failed to parse last-trigger-realtime value %s", value);
599
600 } else if (streq(key, "last-trigger-monotonic")) {
601
602 r = safe_atou64(value, &t->last_trigger.monotonic);
603 if (r < 0)
604 log_debug_unit(u->id, "Failed to parse last-trigger-monotonic value %s", value);
067d72c9 605
871d7de4 606 } else
66870f90 607 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
871d7de4
LP
608
609 return 0;
034c6ed7
LP
610}
611
44a6b1b6 612_pure_ static UnitActiveState timer_active_state(Unit *u) {
871d7de4
LP
613 assert(u);
614
615 return state_translation_table[TIMER(u)->state];
616}
617
44a6b1b6 618_pure_ static const char *timer_sub_state_to_string(Unit *u) {
871d7de4
LP
619 assert(u);
620
621 return timer_state_to_string(TIMER(u)->state);
622}
623
718db961
LP
624static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
625 Timer *t = TIMER(userdata);
871d7de4
LP
626
627 assert(t);
5cb5a6ff 628
871d7de4 629 if (t->state != TIMER_WAITING)
718db961 630 return 0;
5cb5a6ff 631
718db961 632 log_debug_unit(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id);
871d7de4 633 timer_enter_running(t);
718db961 634 return 0;
5cb5a6ff
LP
635}
636
3ecaa09b
LP
637static void timer_trigger_notify(Unit *u, Unit *other) {
638 Timer *t = TIMER(u);
639 TimerValue *v;
871d7de4 640
3ecaa09b
LP
641 assert(u);
642 assert(other);
871d7de4 643
3ecaa09b
LP
644 if (other->load_state != UNIT_LOADED)
645 return;
871d7de4 646
3ecaa09b
LP
647 /* Reenable all timers that depend on unit state */
648 LIST_FOREACH(value, v, t->values)
649 if (v->base == TIMER_UNIT_ACTIVE ||
650 v->base == TIMER_UNIT_INACTIVE)
651 v->disabled = false;
01f78473 652
3ecaa09b 653 switch (t->state) {
871d7de4 654
3ecaa09b
LP
655 case TIMER_WAITING:
656 case TIMER_ELAPSED:
871d7de4 657
3ecaa09b
LP
658 /* Recalculate sleep time */
659 timer_enter_waiting(t, false);
660 break;
871d7de4 661
3ecaa09b 662 case TIMER_RUNNING:
871d7de4 663
3ecaa09b 664 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
06642d17 665 log_debug_unit(UNIT(t)->id, "%s got notified about unit deactivation.", UNIT(t)->id);
871d7de4 666 timer_enter_waiting(t, false);
3ecaa09b
LP
667 }
668 break;
871d7de4 669
3ecaa09b
LP
670 case TIMER_DEAD:
671 case TIMER_FAILED:
672 break;
871d7de4 673
3ecaa09b
LP
674 default:
675 assert_not_reached("Unknown timer state");
871d7de4 676 }
871d7de4
LP
677}
678
fdf20a31 679static void timer_reset_failed(Unit *u) {
5632e374
LP
680 Timer *t = TIMER(u);
681
682 assert(t);
683
fdf20a31 684 if (t->state == TIMER_FAILED)
5632e374
LP
685 timer_set_state(t, TIMER_DEAD);
686
067d72c9 687 t->result = TIMER_SUCCESS;
5632e374
LP
688}
689
8742514c
LP
690static void timer_time_change(Unit *u) {
691 Timer *t = TIMER(u);
692
693 assert(u);
694
695 if (t->state != TIMER_WAITING)
696 return;
697
06642d17 698 log_debug_unit(u->id, "%s: time change, recalculating next elapse.", u->id);
8742514c
LP
699 timer_enter_waiting(t, false);
700}
701
871d7de4
LP
702static const char* const timer_state_table[_TIMER_STATE_MAX] = {
703 [TIMER_DEAD] = "dead",
704 [TIMER_WAITING] = "waiting",
705 [TIMER_RUNNING] = "running",
706 [TIMER_ELAPSED] = "elapsed",
fdf20a31 707 [TIMER_FAILED] = "failed"
871d7de4
LP
708};
709
710DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
711
712static const char* const timer_base_table[_TIMER_BASE_MAX] = {
03fae018
LP
713 [TIMER_ACTIVE] = "OnActiveSec",
714 [TIMER_BOOT] = "OnBootSec",
715 [TIMER_STARTUP] = "OnStartupSec",
716 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
36697dc0
LP
717 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
718 [TIMER_CALENDAR] = "OnCalendar"
871d7de4
LP
719};
720
721DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
722
067d72c9
LP
723static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
724 [TIMER_SUCCESS] = "success",
725 [TIMER_FAILURE_RESOURCES] = "resources"
726};
727
728DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
729
87f0e418 730const UnitVTable timer_vtable = {
7d17cfbc 731 .object_size = sizeof(Timer),
718db961 732
f975e971
LP
733 .sections =
734 "Unit\0"
735 "Timer\0"
736 "Install\0",
5cb5a6ff 737
871d7de4 738 .init = timer_init,
034c6ed7 739 .done = timer_done,
871d7de4
LP
740 .load = timer_load,
741
742 .coldplug = timer_coldplug,
743
744 .dump = timer_dump,
745
746 .start = timer_start,
747 .stop = timer_stop,
748
749 .serialize = timer_serialize,
750 .deserialize_item = timer_deserialize_item,
751
752 .active_state = timer_active_state,
753 .sub_state_to_string = timer_sub_state_to_string,
754
3ecaa09b
LP
755 .trigger_notify = timer_trigger_notify,
756
fdf20a31 757 .reset_failed = timer_reset_failed,
8742514c 758 .time_change = timer_time_change,
5632e374 759
c4e2ceae 760 .bus_interface = "org.freedesktop.systemd1.Timer",
718db961 761 .bus_vtable = bus_timer_vtable,
5cb5a6ff 762};