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