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