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