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