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