]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/timer.c
Bug #944: Deletion of unnecessary checks before a few calls of systemd functions
[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 29#include "bus-util.h"
871d7de4
LP
30
31static 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,
fdf20a31 36 [TIMER_FAILED] = UNIT_FAILED
871d7de4
LP
37};
38
718db961
LP
39static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
40
871d7de4
LP
41static void timer_init(Unit *u) {
42 Timer *t = TIMER(u);
43
44 assert(u);
ac155bb8 45 assert(u->load_state == UNIT_STUB);
871d7de4 46
3a43da28
KS
47 t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
48 t->next_elapse_realtime = USEC_INFINITY;
bd8f585b 49 t->accuracy_usec = u->manager->default_timer_accuracy_usec;
871d7de4 50}
5cb5a6ff 51
74051b9b 52void timer_free_values(Timer *t) {
871d7de4
LP
53 TimerValue *v;
54
55 assert(t);
56
57 while ((v = t->values)) {
71fda00f 58 LIST_REMOVE(value, t->values, v);
3e044c49 59 calendar_spec_free(v->calendar_spec);
871d7de4
LP
60 free(v);
61 }
74051b9b
LP
62}
63
64static void timer_done(Unit *u) {
65 Timer *t = TIMER(u);
66
67 assert(t);
68
69 timer_free_values(t);
871d7de4 70
718db961
LP
71 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
72 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
06642d17
LP
73
74 free(t->stamp_path);
871d7de4
LP
75}
76
77static int timer_verify(Timer *t) {
78 assert(t);
79
1124fe6f 80 if (UNIT(t)->load_state != UNIT_LOADED)
871d7de4
LP
81 return 0;
82
83 if (!t->values) {
f2341e0a 84 log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing.");
871d7de4
LP
85 return -EINVAL;
86 }
87
88 return 0;
89}
90
6c155fe3
LP
91static int timer_add_default_dependencies(Timer *t) {
92 int r;
19f8d037 93 TimerValue *v;
6c155fe3
LP
94
95 assert(t);
96
e3d84721
LP
97 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
98 if (r < 0)
99 return r;
2a77d31d 100
b2c23da8 101 if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
36697dc0
LP
102 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
103 if (r < 0)
6c155fe3 104 return r;
19f8d037
TGR
105
106 LIST_FOREACH(value, v, t->values) {
107 if (v->base == TIMER_CALENDAR) {
108 r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true);
109 if (r < 0)
110 return r;
111 break;
112 }
113 }
2a77d31d 114 }
6c155fe3 115
ead8e478 116 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
6c155fe3
LP
117}
118
06642d17
LP
119static int timer_setup_persistent(Timer *t) {
120 int r;
121
122 assert(t);
123
124 if (!t->persistent)
125 return 0;
126
b2c23da8 127 if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
06642d17
LP
128
129 r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
130 if (r < 0)
131 return r;
132
133 t->stamp_path = strappend("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
134 } else {
135 const char *e;
136
137 e = getenv("XDG_DATA_HOME");
138 if (e)
bd34b310 139 t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL);
06642d17
LP
140 else {
141
142 _cleanup_free_ char *h = NULL;
143
144 r = get_home_dir(&h);
23bbb0de 145 if (r < 0)
f2341e0a 146 return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m");
06642d17
LP
147
148 t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL);
149 }
150 }
151
152 if (!t->stamp_path)
153 return log_oom();
154
155 return 0;
156}
157
871d7de4
LP
158static int timer_load(Unit *u) {
159 Timer *t = TIMER(u);
160 int r;
161
162 assert(u);
ac155bb8 163 assert(u->load_state == UNIT_STUB);
871d7de4 164
36697dc0
LP
165 r = unit_load_fragment_and_dropin(u);
166 if (r < 0)
871d7de4
LP
167 return r;
168
ac155bb8 169 if (u->load_state == UNIT_LOADED) {
871d7de4 170
3ecaa09b 171 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
57020a3a
LP
172 Unit *x;
173
174 r = unit_load_related_unit(u, ".service", &x);
175 if (r < 0)
871d7de4
LP
176 return r;
177
3ecaa09b
LP
178 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
179 if (r < 0)
180 return r;
57020a3a
LP
181 }
182
06642d17
LP
183 r = timer_setup_persistent(t);
184 if (r < 0)
185 return r;
186
187 if (u->default_dependencies) {
36697dc0
LP
188 r = timer_add_default_dependencies(t);
189 if (r < 0)
a40eb732 190 return r;
36697dc0 191 }
871d7de4
LP
192 }
193
194 return timer_verify(t);
195}
196
197static void timer_dump(Unit *u, FILE *f, const char *prefix) {
9f5eb56a 198 char buf[FORMAT_TIMESPAN_MAX];
871d7de4 199 Timer *t = TIMER(u);
3ecaa09b 200 Unit *trigger;
871d7de4 201 TimerValue *v;
034c6ed7 202
3ecaa09b
LP
203 trigger = UNIT_TRIGGER(u);
204
871d7de4
LP
205 fprintf(f,
206 "%sTimer State: %s\n"
067d72c9 207 "%sResult: %s\n"
9f5eb56a 208 "%sUnit: %s\n"
dedabea4
LP
209 "%sPersistent: %s\n"
210 "%sWakeSystem: %s\n"
9f5eb56a 211 "%sAccuracy: %s\n",
871d7de4 212 prefix, timer_state_to_string(t->state),
067d72c9 213 prefix, timer_result_to_string(t->result),
9f5eb56a 214 prefix, trigger ? trigger->id : "n/a",
dedabea4
LP
215 prefix, yes_no(t->persistent),
216 prefix, yes_no(t->wake_system),
9f5eb56a 217 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
871d7de4 218
36697dc0
LP
219 LIST_FOREACH(value, v, t->values) {
220
221 if (v->base == TIMER_CALENDAR) {
222 _cleanup_free_ char *p = NULL;
223
224 calendar_spec_to_string(v->calendar_spec, &p);
225
226 fprintf(f,
227 "%s%s: %s\n",
228 prefix,
229 timer_base_to_string(v->base),
230 strna(p));
231 } else {
232 char timespan1[FORMAT_TIMESPAN_MAX];
233
234 fprintf(f,
235 "%s%s: %s\n",
236 prefix,
237 timer_base_to_string(v->base),
b1d6dcf5 238 format_timespan(timespan1, sizeof(timespan1), v->value, 0));
36697dc0
LP
239 }
240 }
871d7de4
LP
241}
242
243static void timer_set_state(Timer *t, TimerState state) {
244 TimerState old_state;
034c6ed7 245 assert(t);
871d7de4
LP
246
247 old_state = t->state;
248 t->state = state;
249
36697dc0 250 if (state != TIMER_WAITING) {
718db961
LP
251 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
252 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
36697dc0 253 }
871d7de4
LP
254
255 if (state != old_state)
f2341e0a 256 log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), 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
be847e82 263static int timer_coldplug(Unit *u) {
871d7de4
LP
264 Timer *t = TIMER(u);
265
266 assert(t);
267 assert(t->state == TIMER_DEAD);
268
269 if (t->deserialized_state != t->state) {
270
be847e82
LP
271 if (t->deserialized_state == TIMER_WAITING)
272 timer_enter_waiting(t, false);
273 else
871d7de4
LP
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) {
f2341e0a 412 log_unit_debug(UNIT(t), "Timer is elapsed.");
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 419
f2341e0a 420 log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
871d7de4 421
718db961 422 if (t->monotonic_event_source) {
dedabea4 423 r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
718db961
LP
424 if (r < 0)
425 goto fail;
426
427 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
cbf60d0a
LP
428 if (r < 0)
429 goto fail;
430 } else {
431
6a0f1f6d
LP
432 r = sd_event_add_time(
433 UNIT(t)->manager->event,
434 &t->monotonic_event_source,
dedabea4
LP
435 t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
436 t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
6a0f1f6d 437 timer_dispatch, t);
cbf60d0a
LP
438 if (r < 0)
439 goto fail;
718db961 440
cbf60d0a
LP
441 (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic");
442 }
7dfbe2e3 443
718db961 444 } else if (t->monotonic_event_source) {
718db961 445
06642d17 446 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
718db961
LP
447 if (r < 0)
448 goto fail;
449 }
36697dc0
LP
450
451 if (found_realtime) {
452 char buf[FORMAT_TIMESTAMP_MAX];
f2341e0a 453 log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
36697dc0 454
718db961
LP
455 if (t->realtime_event_source) {
456 r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
457 if (r < 0)
458 goto fail;
459
460 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
cbf60d0a
LP
461 if (r < 0)
462 goto fail;
463 } else {
6a0f1f6d
LP
464 r = sd_event_add_time(
465 UNIT(t)->manager->event,
466 &t->realtime_event_source,
dedabea4 467 t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
6a0f1f6d
LP
468 t->next_elapse_realtime, t->accuracy_usec,
469 timer_dispatch, t);
cbf60d0a
LP
470 if (r < 0)
471 goto fail;
718db961 472
cbf60d0a
LP
473 (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime");
474 }
7dfbe2e3 475
718db961 476 } else if (t->realtime_event_source) {
718db961 477
06642d17 478 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
718db961
LP
479 if (r < 0)
480 goto fail;
481 }
871d7de4
LP
482
483 timer_set_state(t, TIMER_WAITING);
484 return;
485
486fail:
f2341e0a 487 log_unit_warning_errno(UNIT(t), r, "Failed to enter waiting state: %m");
067d72c9 488 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
489}
490
491static void timer_enter_running(Timer *t) {
718db961 492 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
871d7de4 493 int r;
398ef8ba 494
871d7de4
LP
495 assert(t);
496
ba3e67a7 497 /* Don't start job if we are supposed to go down */
31afa0a4 498 if (unit_stop_pending(UNIT(t)))
ba3e67a7
LP
499 return;
500
3ecaa09b
LP
501 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
502 JOB_REPLACE, true, &error, NULL);
36697dc0 503 if (r < 0)
871d7de4
LP
504 goto fail;
505
06642d17
LP
506 dual_timestamp_get(&t->last_trigger);
507
472fc28f 508 if (t->stamp_path)
fed1e721 509 touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
e41e1943 510
871d7de4
LP
511 timer_set_state(t, TIMER_RUNNING);
512 return;
513
514fail:
f2341e0a 515 log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
067d72c9 516 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
517}
518
519static int timer_start(Unit *u) {
520 Timer *t = TIMER(u);
779042e7 521 TimerValue *v;
871d7de4
LP
522
523 assert(t);
fdf20a31 524 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
01f78473 525
3ecaa09b 526 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
01f78473 527 return -ENOENT;
871d7de4 528
06642d17
LP
529 t->last_trigger = DUAL_TIMESTAMP_NULL;
530
779042e7
MC
531 /* Reenable all timers that depend on unit activation time */
532 LIST_FOREACH(value, v, t->values)
533 if (v->base == TIMER_ACTIVE)
534 v->disabled = false;
535
06642d17
LP
536 if (t->stamp_path) {
537 struct stat st;
538
539 if (stat(t->stamp_path, &st) >= 0)
540 t->last_trigger.realtime = timespec_load(&st.st_atim);
472fc28f
TB
541 else if (errno == ENOENT)
542 /* The timer has never run before,
543 * make sure a stamp file exists.
544 */
46bcf492 545 touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
06642d17
LP
546 }
547
067d72c9 548 t->result = TIMER_SUCCESS;
871d7de4 549 timer_enter_waiting(t, true);
82a2b6bb 550 return 1;
871d7de4
LP
551}
552
553static int timer_stop(Unit *u) {
554 Timer *t = TIMER(u);
555
556 assert(t);
557 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
558
067d72c9 559 timer_enter_dead(t, TIMER_SUCCESS);
82a2b6bb 560 return 1;
871d7de4
LP
561}
562
563static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
564 Timer *t = TIMER(u);
565
566 assert(u);
567 assert(f);
568 assert(fds);
569
570 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
067d72c9 571 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
871d7de4 572
06642d17
LP
573 if (t->last_trigger.realtime > 0)
574 unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime);
575
576 if (t->last_trigger.monotonic > 0)
577 unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic);
578
871d7de4
LP
579 return 0;
580}
581
582static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
583 Timer *t = TIMER(u);
06642d17 584 int r;
871d7de4
LP
585
586 assert(u);
587 assert(key);
588 assert(value);
589 assert(fds);
590
591 if (streq(key, "state")) {
592 TimerState state;
593
36697dc0
LP
594 state = timer_state_from_string(value);
595 if (state < 0)
f2341e0a 596 log_unit_debug(u, "Failed to parse state value: %s", value);
871d7de4
LP
597 else
598 t->deserialized_state = state;
067d72c9
LP
599 } else if (streq(key, "result")) {
600 TimerResult f;
601
602 f = timer_result_from_string(value);
603 if (f < 0)
f2341e0a 604 log_unit_debug(u, "Failed to parse result value: %s", value);
067d72c9
LP
605 else if (f != TIMER_SUCCESS)
606 t->result = f;
06642d17
LP
607 } else if (streq(key, "last-trigger-realtime")) {
608
609 r = safe_atou64(value, &t->last_trigger.realtime);
610 if (r < 0)
f2341e0a 611 log_unit_debug(u, "Failed to parse last-trigger-realtime value: %s", value);
06642d17
LP
612
613 } else if (streq(key, "last-trigger-monotonic")) {
614
615 r = safe_atou64(value, &t->last_trigger.monotonic);
616 if (r < 0)
f2341e0a 617 log_unit_debug(u, "Failed to parse last-trigger-monotonic value: %s", value);
067d72c9 618
871d7de4 619 } else
f2341e0a 620 log_unit_debug(u, "Unknown serialization key: %s", key);
871d7de4
LP
621
622 return 0;
034c6ed7
LP
623}
624
44a6b1b6 625_pure_ static UnitActiveState timer_active_state(Unit *u) {
871d7de4
LP
626 assert(u);
627
628 return state_translation_table[TIMER(u)->state];
629}
630
44a6b1b6 631_pure_ static const char *timer_sub_state_to_string(Unit *u) {
871d7de4
LP
632 assert(u);
633
634 return timer_state_to_string(TIMER(u)->state);
635}
636
718db961
LP
637static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
638 Timer *t = TIMER(userdata);
871d7de4
LP
639
640 assert(t);
5cb5a6ff 641
871d7de4 642 if (t->state != TIMER_WAITING)
718db961 643 return 0;
5cb5a6ff 644
f2341e0a 645 log_unit_debug(UNIT(t), "Timer elapsed.");
871d7de4 646 timer_enter_running(t);
718db961 647 return 0;
5cb5a6ff
LP
648}
649
3ecaa09b
LP
650static void timer_trigger_notify(Unit *u, Unit *other) {
651 Timer *t = TIMER(u);
652 TimerValue *v;
871d7de4 653
3ecaa09b
LP
654 assert(u);
655 assert(other);
871d7de4 656
3ecaa09b
LP
657 if (other->load_state != UNIT_LOADED)
658 return;
871d7de4 659
3ecaa09b
LP
660 /* Reenable all timers that depend on unit state */
661 LIST_FOREACH(value, v, t->values)
662 if (v->base == TIMER_UNIT_ACTIVE ||
663 v->base == TIMER_UNIT_INACTIVE)
664 v->disabled = false;
01f78473 665
3ecaa09b 666 switch (t->state) {
871d7de4 667
3ecaa09b
LP
668 case TIMER_WAITING:
669 case TIMER_ELAPSED:
871d7de4 670
3ecaa09b
LP
671 /* Recalculate sleep time */
672 timer_enter_waiting(t, false);
673 break;
871d7de4 674
3ecaa09b 675 case TIMER_RUNNING:
871d7de4 676
3ecaa09b 677 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
f2341e0a 678 log_unit_debug(UNIT(t), "Got notified about unit deactivation.");
871d7de4 679 timer_enter_waiting(t, false);
3ecaa09b
LP
680 }
681 break;
871d7de4 682
3ecaa09b
LP
683 case TIMER_DEAD:
684 case TIMER_FAILED:
685 break;
871d7de4 686
3ecaa09b
LP
687 default:
688 assert_not_reached("Unknown timer state");
871d7de4 689 }
871d7de4
LP
690}
691
fdf20a31 692static void timer_reset_failed(Unit *u) {
5632e374
LP
693 Timer *t = TIMER(u);
694
695 assert(t);
696
fdf20a31 697 if (t->state == TIMER_FAILED)
5632e374
LP
698 timer_set_state(t, TIMER_DEAD);
699
067d72c9 700 t->result = TIMER_SUCCESS;
5632e374
LP
701}
702
8742514c
LP
703static void timer_time_change(Unit *u) {
704 Timer *t = TIMER(u);
705
706 assert(u);
707
708 if (t->state != TIMER_WAITING)
709 return;
710
f2341e0a 711 log_unit_debug(u, "Time change, recalculating next elapse.");
8742514c
LP
712 timer_enter_waiting(t, false);
713}
714
871d7de4
LP
715static const char* const timer_state_table[_TIMER_STATE_MAX] = {
716 [TIMER_DEAD] = "dead",
717 [TIMER_WAITING] = "waiting",
718 [TIMER_RUNNING] = "running",
719 [TIMER_ELAPSED] = "elapsed",
fdf20a31 720 [TIMER_FAILED] = "failed"
871d7de4
LP
721};
722
723DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
724
725static const char* const timer_base_table[_TIMER_BASE_MAX] = {
03fae018
LP
726 [TIMER_ACTIVE] = "OnActiveSec",
727 [TIMER_BOOT] = "OnBootSec",
728 [TIMER_STARTUP] = "OnStartupSec",
729 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
36697dc0
LP
730 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
731 [TIMER_CALENDAR] = "OnCalendar"
871d7de4
LP
732};
733
734DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
735
067d72c9
LP
736static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
737 [TIMER_SUCCESS] = "success",
738 [TIMER_FAILURE_RESOURCES] = "resources"
739};
740
741DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
742
87f0e418 743const UnitVTable timer_vtable = {
7d17cfbc 744 .object_size = sizeof(Timer),
718db961 745
f975e971
LP
746 .sections =
747 "Unit\0"
748 "Timer\0"
749 "Install\0",
d8a812d1 750 .private_section = "Timer",
5cb5a6ff 751
871d7de4 752 .init = timer_init,
034c6ed7 753 .done = timer_done,
871d7de4
LP
754 .load = timer_load,
755
756 .coldplug = timer_coldplug,
757
758 .dump = timer_dump,
759
760 .start = timer_start,
761 .stop = timer_stop,
762
763 .serialize = timer_serialize,
764 .deserialize_item = timer_deserialize_item,
765
766 .active_state = timer_active_state,
767 .sub_state_to_string = timer_sub_state_to_string,
768
3ecaa09b
LP
769 .trigger_notify = timer_trigger_notify,
770
fdf20a31 771 .reset_failed = timer_reset_failed,
8742514c 772 .time_change = timer_time_change,
5632e374 773
c4e2ceae 774 .bus_interface = "org.freedesktop.systemd1.Timer",
718db961 775 .bus_vtable = bus_timer_vtable,
d8a812d1
WC
776 .bus_set_property = bus_timer_set_property,
777
778 .can_transient = true,
5cb5a6ff 779};