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