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