chiark / gitweb /
core: fix return value on OOM
[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_coldplug(Unit *u) {
271         Timer *t = TIMER(u);
272
273         assert(t);
274         assert(t->state == TIMER_DEAD);
275
276         if (t->deserialized_state != t->state) {
277
278                 if (t->deserialized_state == TIMER_WAITING)
279                         timer_enter_waiting(t, false);
280                 else
281                         timer_set_state(t, t->deserialized_state);
282         }
283
284         return 0;
285 }
286
287 static void timer_enter_dead(Timer *t, TimerResult f) {
288         assert(t);
289
290         if (f != TIMER_SUCCESS)
291                 t->result = f;
292
293         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
294 }
295
296 static usec_t monotonic_to_boottime(usec_t t) {
297         usec_t a, b;
298
299         if (t <= 0)
300                 return 0;
301
302         a = now(CLOCK_BOOTTIME);
303         b = now(CLOCK_MONOTONIC);
304
305         if (t + a > b)
306                 return t + a - b;
307         else
308                 return 0;
309 }
310
311 static void timer_enter_waiting(Timer *t, bool initial) {
312         bool found_monotonic = false, found_realtime = false;
313         usec_t ts_realtime, ts_monotonic;
314         usec_t base = 0;
315         TimerValue *v;
316         int r;
317
318         /* If we shall wake the system we use the boottime clock
319          * rather than the monotonic clock. */
320
321         ts_realtime = now(CLOCK_REALTIME);
322         ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
323         t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
324
325         LIST_FOREACH(value, v, t->values) {
326
327                 if (v->disabled)
328                         continue;
329
330                 if (v->base == TIMER_CALENDAR) {
331                         usec_t b;
332
333                         /* If we know the last time this was
334                          * triggered, schedule the job based relative
335                          * to that. If we don't just start from
336                          * now. */
337
338                         b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
339
340                         r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
341                         if (r < 0)
342                                 continue;
343
344                         if (!found_realtime)
345                                 t->next_elapse_realtime = v->next_elapse;
346                         else
347                                 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
348
349                         found_realtime = true;
350
351                 } else  {
352                         switch (v->base) {
353
354                         case TIMER_ACTIVE:
355                                 if (state_translation_table[t->state] == UNIT_ACTIVE)
356                                         base = UNIT(t)->inactive_exit_timestamp.monotonic;
357                                 else
358                                         base = ts_monotonic;
359                                 break;
360
361                         case TIMER_BOOT:
362                                 /* CLOCK_MONOTONIC equals the uptime on Linux */
363                                 base = 0;
364                                 break;
365
366                         case TIMER_STARTUP:
367                                 base = UNIT(t)->manager->userspace_timestamp.monotonic;
368                                 break;
369
370                         case TIMER_UNIT_ACTIVE:
371
372                                 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
373
374                                 if (base <= 0)
375                                         base = t->last_trigger.monotonic;
376
377                                 if (base <= 0)
378                                         continue;
379
380                                 break;
381
382                         case TIMER_UNIT_INACTIVE:
383
384                                 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
385
386                                 if (base <= 0)
387                                         base = t->last_trigger.monotonic;
388
389                                 if (base <= 0)
390                                         continue;
391
392                                 break;
393
394                         default:
395                                 assert_not_reached("Unknown timer base");
396                         }
397
398                         if (t->wake_system)
399                                 base = monotonic_to_boottime(base);
400
401                         v->next_elapse = base + v->value;
402
403                         if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
404                                 /* This is a one time trigger, disable it now */
405                                 v->disabled = true;
406                                 continue;
407                         }
408
409                         if (!found_monotonic)
410                                 t->next_elapse_monotonic_or_boottime = v->next_elapse;
411                         else
412                                 t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
413
414                         found_monotonic = true;
415                 }
416         }
417
418         if (!found_monotonic && !found_realtime) {
419                 log_unit_debug(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
420                 timer_set_state(t, TIMER_ELAPSED);
421                 return;
422         }
423
424         if (found_monotonic) {
425                 char buf[FORMAT_TIMESPAN_MAX];
426
427                 log_unit_debug(UNIT(t)->id, "%s: Monotonic timer elapses in %s.",
428                                UNIT(t)->id,
429                                format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
430
431                 if (t->monotonic_event_source) {
432                         r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
433                         if (r < 0)
434                                 goto fail;
435
436                         r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
437                 } else
438                         r = sd_event_add_time(
439                                         UNIT(t)->manager->event,
440                                         &t->monotonic_event_source,
441                                         t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
442                                         t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
443                                         timer_dispatch, t);
444                 if (r < 0)
445                         goto fail;
446
447         } else if (t->monotonic_event_source) {
448
449                 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
450                 if (r < 0)
451                         goto fail;
452         }
453
454         if (found_realtime) {
455                 char buf[FORMAT_TIMESTAMP_MAX];
456                 log_unit_debug(UNIT(t)->id, "%s: Realtime timer elapses at %s.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
457
458                 if (t->realtime_event_source) {
459                         r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
460                         if (r < 0)
461                                 goto fail;
462
463                         r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
464                 } else
465                         r = sd_event_add_time(
466                                         UNIT(t)->manager->event,
467                                         &t->realtime_event_source,
468                                         t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
469                                         t->next_elapse_realtime, t->accuracy_usec,
470                                         timer_dispatch, t);
471                 if (r < 0)
472                         goto fail;
473
474         } else if (t->realtime_event_source) {
475
476                 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
477                 if (r < 0)
478                         goto fail;
479         }
480
481         timer_set_state(t, TIMER_WAITING);
482         return;
483
484 fail:
485         log_unit_warning_errno(UNIT(t)->id, r, "%s failed to enter waiting state: %m", UNIT(t)->id);
486         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
487 }
488
489 static void timer_enter_running(Timer *t) {
490         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
491         int r;
492
493         assert(t);
494
495         /* Don't start job if we are supposed to go down */
496         if (unit_stop_pending(UNIT(t)))
497                 return;
498
499         r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
500                             JOB_REPLACE, true, &error, NULL);
501         if (r < 0)
502                 goto fail;
503
504         dual_timestamp_get(&t->last_trigger);
505
506         if (t->stamp_path)
507                 touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
508
509         timer_set_state(t, TIMER_RUNNING);
510         return;
511
512 fail:
513         log_unit_warning(UNIT(t)->id,
514                          "%s failed to queue unit startup job: %s",
515                          UNIT(t)->id, bus_error_message(&error, r));
516         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
517 }
518
519 static int timer_start(Unit *u) {
520         Timer *t = TIMER(u);
521         TimerValue *v;
522
523         assert(t);
524         assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
525
526         if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
527                 return -ENOENT;
528
529         t->last_trigger = DUAL_TIMESTAMP_NULL;
530
531         /* Reenable all timers that depend on unit activation time */
532         LIST_FOREACH(value, v, t->values)
533                 if (v->base == TIMER_ACTIVE)
534                         v->disabled = false;
535
536         if (t->stamp_path) {
537                 struct stat st;
538
539                 if (stat(t->stamp_path, &st) >= 0)
540                         t->last_trigger.realtime = timespec_load(&st.st_atim);
541                 else if (errno == ENOENT)
542                         /* The timer has never run before,
543                          * make sure a stamp file exists.
544                          */
545                         touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
546         }
547
548         t->result = TIMER_SUCCESS;
549         timer_enter_waiting(t, true);
550         return 1;
551 }
552
553 static int timer_stop(Unit *u) {
554         Timer *t = TIMER(u);
555
556         assert(t);
557         assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
558
559         timer_enter_dead(t, TIMER_SUCCESS);
560         return 1;
561 }
562
563 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
564         Timer *t = TIMER(u);
565
566         assert(u);
567         assert(f);
568         assert(fds);
569
570         unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
571         unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
572
573         if (t->last_trigger.realtime > 0)
574                 unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime);
575
576         if (t->last_trigger.monotonic > 0)
577                 unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic);
578
579         return 0;
580 }
581
582 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
583         Timer *t = TIMER(u);
584         int r;
585
586         assert(u);
587         assert(key);
588         assert(value);
589         assert(fds);
590
591         if (streq(key, "state")) {
592                 TimerState state;
593
594                 state = timer_state_from_string(value);
595                 if (state < 0)
596                         log_unit_debug(u->id, "Failed to parse state value %s", value);
597                 else
598                         t->deserialized_state = state;
599         } else if (streq(key, "result")) {
600                 TimerResult f;
601
602                 f = timer_result_from_string(value);
603                 if (f < 0)
604                         log_unit_debug(u->id, "Failed to parse result value %s", value);
605                 else if (f != TIMER_SUCCESS)
606                         t->result = f;
607         } else if (streq(key, "last-trigger-realtime")) {
608
609                 r = safe_atou64(value, &t->last_trigger.realtime);
610                 if (r < 0)
611                         log_unit_debug(u->id, "Failed to parse last-trigger-realtime value %s", value);
612
613         } else if (streq(key, "last-trigger-monotonic")) {
614
615                 r = safe_atou64(value, &t->last_trigger.monotonic);
616                 if (r < 0)
617                         log_unit_debug(u->id, "Failed to parse last-trigger-monotonic value %s", value);
618
619         } else
620                 log_unit_debug(u->id, "Unknown serialization key '%s'", key);
621
622         return 0;
623 }
624
625 _pure_ static UnitActiveState timer_active_state(Unit *u) {
626         assert(u);
627
628         return state_translation_table[TIMER(u)->state];
629 }
630
631 _pure_ static const char *timer_sub_state_to_string(Unit *u) {
632         assert(u);
633
634         return timer_state_to_string(TIMER(u)->state);
635 }
636
637 static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
638         Timer *t = TIMER(userdata);
639
640         assert(t);
641
642         if (t->state != TIMER_WAITING)
643                 return 0;
644
645         log_unit_debug(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id);
646         timer_enter_running(t);
647         return 0;
648 }
649
650 static void timer_trigger_notify(Unit *u, Unit *other) {
651         Timer *t = TIMER(u);
652         TimerValue *v;
653
654         assert(u);
655         assert(other);
656
657         if (other->load_state != UNIT_LOADED)
658                 return;
659
660         /* Reenable all timers that depend on unit state */
661         LIST_FOREACH(value, v, t->values)
662                 if (v->base == TIMER_UNIT_ACTIVE ||
663                     v->base == TIMER_UNIT_INACTIVE)
664                         v->disabled = false;
665
666         switch (t->state) {
667
668         case TIMER_WAITING:
669         case TIMER_ELAPSED:
670
671                 /* Recalculate sleep time */
672                 timer_enter_waiting(t, false);
673                 break;
674
675         case TIMER_RUNNING:
676
677                 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
678                         log_unit_debug(UNIT(t)->id, "%s got notified about unit deactivation.", UNIT(t)->id);
679                         timer_enter_waiting(t, false);
680                 }
681                 break;
682
683         case TIMER_DEAD:
684         case TIMER_FAILED:
685                 break;
686
687         default:
688                 assert_not_reached("Unknown timer state");
689         }
690 }
691
692 static void timer_reset_failed(Unit *u) {
693         Timer *t = TIMER(u);
694
695         assert(t);
696
697         if (t->state == TIMER_FAILED)
698                 timer_set_state(t, TIMER_DEAD);
699
700         t->result = TIMER_SUCCESS;
701 }
702
703 static void timer_time_change(Unit *u) {
704         Timer *t = TIMER(u);
705
706         assert(u);
707
708         if (t->state != TIMER_WAITING)
709                 return;
710
711         log_unit_debug(u->id, "%s: time change, recalculating next elapse.", u->id);
712         timer_enter_waiting(t, false);
713 }
714
715 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
716         [TIMER_DEAD] = "dead",
717         [TIMER_WAITING] = "waiting",
718         [TIMER_RUNNING] = "running",
719         [TIMER_ELAPSED] = "elapsed",
720         [TIMER_FAILED] = "failed"
721 };
722
723 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
724
725 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
726         [TIMER_ACTIVE] = "OnActiveSec",
727         [TIMER_BOOT] = "OnBootSec",
728         [TIMER_STARTUP] = "OnStartupSec",
729         [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
730         [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
731         [TIMER_CALENDAR] = "OnCalendar"
732 };
733
734 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
735
736 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
737         [TIMER_SUCCESS] = "success",
738         [TIMER_FAILURE_RESOURCES] = "resources"
739 };
740
741 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
742
743 const UnitVTable timer_vtable = {
744         .object_size = sizeof(Timer),
745
746         .sections =
747                 "Unit\0"
748                 "Timer\0"
749                 "Install\0",
750         .private_section = "Timer",
751
752         .init = timer_init,
753         .done = timer_done,
754         .load = timer_load,
755
756         .coldplug = timer_coldplug,
757
758         .dump = timer_dump,
759
760         .start = timer_start,
761         .stop = timer_stop,
762
763         .serialize = timer_serialize,
764         .deserialize_item = timer_deserialize_item,
765
766         .active_state = timer_active_state,
767         .sub_state_to_string = timer_sub_state_to_string,
768
769         .trigger_notify = timer_trigger_notify,
770
771         .reset_failed = timer_reset_failed,
772         .time_change = timer_time_change,
773
774         .bus_interface = "org.freedesktop.systemd1.Timer",
775         .bus_vtable = bus_timer_vtable,
776         .bus_set_property = bus_timer_set_property,
777
778         .can_transient = true,
779 };