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