]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/timer.c
Revert "man: systemd.service(5): clarify behavior of SuccessExitStatus"
[thirdparty/systemd.git] / src / core / timer.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5cb5a6ff 2
a7334b09
LP
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
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
d46de8a1
LP
22#include <errno.h>
23
87f0e418 24#include "unit.h"
871d7de4 25#include "unit-name.h"
5cb5a6ff 26#include "timer.h"
871d7de4 27#include "dbus-timer.h"
a40eb732 28#include "special.h"
718db961
LP
29#include "bus-util.h"
30#include "bus-error.h"
871d7de4
LP
31
32static 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,
fdf20a31 37 [TIMER_FAILED] = UNIT_FAILED
871d7de4
LP
38};
39
718db961
LP
40static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
41
871d7de4
LP
42static void timer_init(Unit *u) {
43 Timer *t = TIMER(u);
44
45 assert(u);
ac155bb8 46 assert(u->load_state == UNIT_STUB);
871d7de4 47
36697dc0
LP
48 t->next_elapse_monotonic = (usec_t) -1;
49 t->next_elapse_realtime = (usec_t) -1;
9f5eb56a 50 t->accuracy_usec = USEC_PER_MINUTE;
871d7de4 51}
5cb5a6ff 52
74051b9b 53void timer_free_values(Timer *t) {
871d7de4
LP
54 TimerValue *v;
55
56 assert(t);
57
58 while ((v = t->values)) {
71fda00f 59 LIST_REMOVE(value, t->values, v);
36697dc0
LP
60
61 if (v->calendar_spec)
62 calendar_spec_free(v->calendar_spec);
63
871d7de4
LP
64 free(v);
65 }
74051b9b
LP
66}
67
68static void timer_done(Unit *u) {
69 Timer *t = TIMER(u);
70
71 assert(t);
72
73 timer_free_values(t);
871d7de4 74
718db961
LP
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);
871d7de4
LP
77}
78
79static int timer_verify(Timer *t) {
80 assert(t);
81
1124fe6f 82 if (UNIT(t)->load_state != UNIT_LOADED)
871d7de4
LP
83 return 0;
84
85 if (!t->values) {
66870f90
ZJS
86 log_error_unit(UNIT(t)->id,
87 "%s lacks value setting. Refusing.", UNIT(t)->id);
871d7de4
LP
88 return -EINVAL;
89 }
90
91 return 0;
92}
93
6c155fe3
LP
94static int timer_add_default_dependencies(Timer *t) {
95 int r;
96
97 assert(t);
98
e3d84721
LP
99 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
100 if (r < 0)
101 return r;
2a77d31d 102
e3d84721 103 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
36697dc0
LP
104 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
105 if (r < 0)
6c155fe3 106 return r;
2a77d31d 107 }
6c155fe3 108
ead8e478 109 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
6c155fe3
LP
110}
111
871d7de4
LP
112static int timer_load(Unit *u) {
113 Timer *t = TIMER(u);
114 int r;
115
116 assert(u);
ac155bb8 117 assert(u->load_state == UNIT_STUB);
871d7de4 118
36697dc0
LP
119 r = unit_load_fragment_and_dropin(u);
120 if (r < 0)
871d7de4
LP
121 return r;
122
ac155bb8 123 if (u->load_state == UNIT_LOADED) {
871d7de4 124
3ecaa09b 125 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
57020a3a
LP
126 Unit *x;
127
128 r = unit_load_related_unit(u, ".service", &x);
129 if (r < 0)
871d7de4
LP
130 return r;
131
3ecaa09b
LP
132 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
133 if (r < 0)
134 return r;
57020a3a
LP
135 }
136
36697dc0
LP
137 if (UNIT(t)->default_dependencies) {
138 r = timer_add_default_dependencies(t);
139 if (r < 0)
a40eb732 140 return r;
36697dc0 141 }
871d7de4
LP
142 }
143
144 return timer_verify(t);
145}
146
147static void timer_dump(Unit *u, FILE *f, const char *prefix) {
9f5eb56a 148 char buf[FORMAT_TIMESPAN_MAX];
871d7de4 149 Timer *t = TIMER(u);
3ecaa09b 150 Unit *trigger;
871d7de4 151 TimerValue *v;
034c6ed7 152
3ecaa09b
LP
153 trigger = UNIT_TRIGGER(u);
154
871d7de4
LP
155 fprintf(f,
156 "%sTimer State: %s\n"
067d72c9 157 "%sResult: %s\n"
9f5eb56a
LP
158 "%sUnit: %s\n"
159 "%sAccuracy: %s\n",
871d7de4 160 prefix, timer_state_to_string(t->state),
067d72c9 161 prefix, timer_result_to_string(t->result),
9f5eb56a
LP
162 prefix, trigger ? trigger->id : "n/a",
163 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
871d7de4 164
36697dc0
LP
165 LIST_FOREACH(value, v, t->values) {
166
167 if (v->base == TIMER_CALENDAR) {
168 _cleanup_free_ char *p = NULL;
169
170 calendar_spec_to_string(v->calendar_spec, &p);
171
172 fprintf(f,
173 "%s%s: %s\n",
174 prefix,
175 timer_base_to_string(v->base),
176 strna(p));
177 } else {
178 char timespan1[FORMAT_TIMESPAN_MAX];
179
180 fprintf(f,
181 "%s%s: %s\n",
182 prefix,
183 timer_base_to_string(v->base),
2fa4092c 184 strna(format_timespan(timespan1, sizeof(timespan1), v->value, 0)));
36697dc0
LP
185 }
186 }
871d7de4
LP
187}
188
189static void timer_set_state(Timer *t, TimerState state) {
190 TimerState old_state;
034c6ed7 191 assert(t);
871d7de4
LP
192
193 old_state = t->state;
194 t->state = state;
195
36697dc0 196 if (state != TIMER_WAITING) {
718db961
LP
197 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
198 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
36697dc0 199 }
871d7de4
LP
200
201 if (state != old_state)
66870f90
ZJS
202 log_debug_unit(UNIT(t)->id,
203 "%s changed %s -> %s", UNIT(t)->id,
204 timer_state_to_string(old_state),
205 timer_state_to_string(state));
871d7de4 206
e2f3b44c 207 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
871d7de4
LP
208}
209
210static void timer_enter_waiting(Timer *t, bool initial);
211
212static int timer_coldplug(Unit *u) {
213 Timer *t = TIMER(u);
214
215 assert(t);
216 assert(t->state == TIMER_DEAD);
217
218 if (t->deserialized_state != t->state) {
219
b363ca6f 220 if (t->deserialized_state == TIMER_WAITING)
871d7de4
LP
221 timer_enter_waiting(t, false);
222 else
223 timer_set_state(t, t->deserialized_state);
224 }
225
226 return 0;
227}
228
067d72c9 229static void timer_enter_dead(Timer *t, TimerResult f) {
871d7de4
LP
230 assert(t);
231
067d72c9
LP
232 if (f != TIMER_SUCCESS)
233 t->result = f;
871d7de4 234
067d72c9 235 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
871d7de4
LP
236}
237
718db961 238
871d7de4
LP
239static void timer_enter_waiting(Timer *t, bool initial) {
240 TimerValue *v;
36697dc0
LP
241 usec_t base = 0;
242 dual_timestamp ts;
243 bool found_monotonic = false, found_realtime = false;
871d7de4
LP
244 int r;
245
36697dc0
LP
246 dual_timestamp_get(&ts);
247 t->next_elapse_monotonic = t->next_elapse_realtime = 0;
871d7de4
LP
248
249 LIST_FOREACH(value, v, t->values) {
250
251 if (v->disabled)
252 continue;
253
36697dc0
LP
254 if (v->base == TIMER_CALENDAR) {
255
256 r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
257 if (r < 0)
258 continue;
259
36697dc0
LP
260 if (!found_realtime)
261 t->next_elapse_realtime = v->next_elapse;
10717a1a 262 else
36697dc0 263 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
871d7de4 264
36697dc0 265 found_realtime = true;
871d7de4 266
36697dc0
LP
267 } else {
268 switch (v->base) {
871d7de4 269
36697dc0
LP
270 case TIMER_ACTIVE:
271 if (state_translation_table[t->state] == UNIT_ACTIVE)
272 base = UNIT(t)->inactive_exit_timestamp.monotonic;
273 else
274 base = ts.monotonic;
275 break;
871d7de4 276
36697dc0
LP
277 case TIMER_BOOT:
278 /* CLOCK_MONOTONIC equals the uptime on Linux */
279 base = 0;
280 break;
871d7de4 281
36697dc0
LP
282 case TIMER_STARTUP:
283 base = UNIT(t)->manager->userspace_timestamp.monotonic;
284 break;
871d7de4 285
36697dc0 286 case TIMER_UNIT_ACTIVE:
871d7de4 287
e41e1943
LP
288 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
289
290 if (base <= 0)
291 base = t->last_trigger_monotonic;
292
293 if (base <= 0)
36697dc0 294 continue;
871d7de4 295
36697dc0 296 break;
871d7de4 297
36697dc0 298 case TIMER_UNIT_INACTIVE:
871d7de4 299
e41e1943
LP
300 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
301
302 if (base <= 0)
303 base = t->last_trigger_monotonic;
304
305 if (base <= 0)
36697dc0 306 continue;
871d7de4 307
36697dc0 308 break;
871d7de4 309
36697dc0
LP
310 default:
311 assert_not_reached("Unknown timer base");
312 }
871d7de4 313
36697dc0
LP
314 v->next_elapse = base + v->value;
315
e41e1943
LP
316 if (!initial &&
317 v->next_elapse < ts.monotonic &&
318 (v->base == TIMER_ACTIVE || v->base == TIMER_BOOT || v->base == TIMER_STARTUP)) {
319 /* This is a one time trigger, disable it now */
36697dc0
LP
320 v->disabled = true;
321 continue;
322 }
323
324 if (!found_monotonic)
325 t->next_elapse_monotonic = v->next_elapse;
326 else
327 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
328
329 found_monotonic = true;
330 }
871d7de4
LP
331 }
332
36697dc0 333 if (!found_monotonic && !found_realtime) {
66870f90 334 log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
871d7de4
LP
335 timer_set_state(t, TIMER_ELAPSED);
336 return;
337 }
338
36697dc0
LP
339 if (found_monotonic) {
340 char buf[FORMAT_TIMESPAN_MAX];
66870f90 341 log_debug_unit(UNIT(t)->id,
1a512bf4 342 "%s: Monotonic timer elapses in %s.",
66870f90 343 UNIT(t)->id,
2fa4092c 344 format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0));
871d7de4 345
718db961
LP
346 if (t->monotonic_event_source) {
347 r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic);
348 if (r < 0)
349 goto fail;
350
351 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
352 } else
9f5eb56a 353 r = sd_event_add_monotonic(UNIT(t)->manager->event, t->next_elapse_monotonic, t->accuracy_usec, timer_dispatch, t, &t->monotonic_event_source);
718db961 354
36697dc0
LP
355 if (r < 0)
356 goto fail;
718db961
LP
357
358 } else if (t->monotonic_event_source) {
359 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
360
361 if (r < 0)
362 goto fail;
363 }
36697dc0
LP
364
365 if (found_realtime) {
366 char buf[FORMAT_TIMESTAMP_MAX];
66870f90 367 log_debug_unit(UNIT(t)->id,
1a512bf4 368 "%s: Realtime timer elapses at %s.",
66870f90
ZJS
369 UNIT(t)->id,
370 format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
36697dc0 371
718db961
LP
372 if (t->realtime_event_source) {
373 r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
374 if (r < 0)
375 goto fail;
376
377 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
378 } else
9f5eb56a 379 r = sd_event_add_realtime(UNIT(t)->manager->event, t->next_elapse_realtime, t->accuracy_usec, timer_dispatch, t, &t->realtime_event_source);
718db961 380
36697dc0
LP
381 if (r < 0)
382 goto fail;
718db961
LP
383
384 } else if (t->realtime_event_source) {
385 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
386
387 if (r < 0)
388 goto fail;
389 }
871d7de4
LP
390
391 timer_set_state(t, TIMER_WAITING);
392 return;
393
394fail:
66870f90
ZJS
395 log_warning_unit(UNIT(t)->id,
396 "%s failed to enter waiting state: %s",
397 UNIT(t)->id, strerror(-r));
067d72c9 398 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
399}
400
401static void timer_enter_running(Timer *t) {
718db961 402 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
871d7de4 403 int r;
398ef8ba 404
871d7de4
LP
405 assert(t);
406
ba3e67a7 407 /* Don't start job if we are supposed to go down */
31afa0a4 408 if (unit_stop_pending(UNIT(t)))
ba3e67a7
LP
409 return;
410
3ecaa09b
LP
411 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
412 JOB_REPLACE, true, &error, NULL);
36697dc0 413 if (r < 0)
871d7de4
LP
414 goto fail;
415
e41e1943
LP
416 t->last_trigger_monotonic = now(CLOCK_MONOTONIC);
417
871d7de4
LP
418 timer_set_state(t, TIMER_RUNNING);
419 return;
420
421fail:
66870f90
ZJS
422 log_warning_unit(UNIT(t)->id,
423 "%s failed to queue unit startup job: %s",
718db961 424 UNIT(t)->id, bus_error_message(&error, r));
067d72c9 425 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
426}
427
428static int timer_start(Unit *u) {
429 Timer *t = TIMER(u);
430
431 assert(t);
fdf20a31 432 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
01f78473 433
3ecaa09b 434 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
01f78473 435 return -ENOENT;
871d7de4 436
067d72c9 437 t->result = TIMER_SUCCESS;
871d7de4
LP
438 timer_enter_waiting(t, true);
439 return 0;
440}
441
442static int timer_stop(Unit *u) {
443 Timer *t = TIMER(u);
444
445 assert(t);
446 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
447
067d72c9 448 timer_enter_dead(t, TIMER_SUCCESS);
871d7de4
LP
449 return 0;
450}
451
452static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
453 Timer *t = TIMER(u);
454
455 assert(u);
456 assert(f);
457 assert(fds);
458
459 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
067d72c9 460 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
871d7de4
LP
461
462 return 0;
463}
464
465static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
466 Timer *t = TIMER(u);
467
468 assert(u);
469 assert(key);
470 assert(value);
471 assert(fds);
472
473 if (streq(key, "state")) {
474 TimerState state;
475
36697dc0
LP
476 state = timer_state_from_string(value);
477 if (state < 0)
66870f90 478 log_debug_unit(u->id, "Failed to parse state value %s", value);
871d7de4
LP
479 else
480 t->deserialized_state = state;
067d72c9
LP
481 } else if (streq(key, "result")) {
482 TimerResult f;
483
484 f = timer_result_from_string(value);
485 if (f < 0)
66870f90 486 log_debug_unit(u->id, "Failed to parse result value %s", value);
067d72c9
LP
487 else if (f != TIMER_SUCCESS)
488 t->result = f;
489
871d7de4 490 } else
66870f90 491 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
871d7de4
LP
492
493 return 0;
034c6ed7
LP
494}
495
44a6b1b6 496_pure_ static UnitActiveState timer_active_state(Unit *u) {
871d7de4
LP
497 assert(u);
498
499 return state_translation_table[TIMER(u)->state];
500}
501
44a6b1b6 502_pure_ static const char *timer_sub_state_to_string(Unit *u) {
871d7de4
LP
503 assert(u);
504
505 return timer_state_to_string(TIMER(u)->state);
506}
507
718db961
LP
508static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
509 Timer *t = TIMER(userdata);
871d7de4
LP
510
511 assert(t);
5cb5a6ff 512
871d7de4 513 if (t->state != TIMER_WAITING)
718db961 514 return 0;
5cb5a6ff 515
718db961 516 log_debug_unit(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id);
871d7de4 517 timer_enter_running(t);
718db961 518 return 0;
5cb5a6ff
LP
519}
520
3ecaa09b
LP
521static void timer_trigger_notify(Unit *u, Unit *other) {
522 Timer *t = TIMER(u);
523 TimerValue *v;
871d7de4 524
3ecaa09b
LP
525 assert(u);
526 assert(other);
871d7de4 527
3ecaa09b
LP
528 if (other->load_state != UNIT_LOADED)
529 return;
871d7de4 530
3ecaa09b
LP
531 /* Reenable all timers that depend on unit state */
532 LIST_FOREACH(value, v, t->values)
533 if (v->base == TIMER_UNIT_ACTIVE ||
534 v->base == TIMER_UNIT_INACTIVE)
535 v->disabled = false;
01f78473 536
3ecaa09b 537 switch (t->state) {
871d7de4 538
3ecaa09b
LP
539 case TIMER_WAITING:
540 case TIMER_ELAPSED:
871d7de4 541
3ecaa09b
LP
542 /* Recalculate sleep time */
543 timer_enter_waiting(t, false);
544 break;
871d7de4 545
3ecaa09b 546 case TIMER_RUNNING:
871d7de4 547
3ecaa09b
LP
548 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
549 log_debug_unit(UNIT(t)->id,
550 "%s got notified about unit deactivation.",
551 UNIT(t)->id);
871d7de4 552 timer_enter_waiting(t, false);
3ecaa09b
LP
553 }
554 break;
871d7de4 555
3ecaa09b
LP
556 case TIMER_DEAD:
557 case TIMER_FAILED:
558 break;
871d7de4 559
3ecaa09b
LP
560 default:
561 assert_not_reached("Unknown timer state");
871d7de4 562 }
871d7de4
LP
563}
564
fdf20a31 565static void timer_reset_failed(Unit *u) {
5632e374
LP
566 Timer *t = TIMER(u);
567
568 assert(t);
569
fdf20a31 570 if (t->state == TIMER_FAILED)
5632e374
LP
571 timer_set_state(t, TIMER_DEAD);
572
067d72c9 573 t->result = TIMER_SUCCESS;
5632e374
LP
574}
575
8742514c
LP
576static void timer_time_change(Unit *u) {
577 Timer *t = TIMER(u);
578
579 assert(u);
580
581 if (t->state != TIMER_WAITING)
582 return;
583
c5962bd1
MS
584 log_debug_unit(u->id,
585 "%s: time change, recalculating next elapse.", u->id);
8742514c
LP
586 timer_enter_waiting(t, false);
587}
588
871d7de4
LP
589static const char* const timer_state_table[_TIMER_STATE_MAX] = {
590 [TIMER_DEAD] = "dead",
591 [TIMER_WAITING] = "waiting",
592 [TIMER_RUNNING] = "running",
593 [TIMER_ELAPSED] = "elapsed",
fdf20a31 594 [TIMER_FAILED] = "failed"
871d7de4
LP
595};
596
597DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
598
599static const char* const timer_base_table[_TIMER_BASE_MAX] = {
03fae018
LP
600 [TIMER_ACTIVE] = "OnActiveSec",
601 [TIMER_BOOT] = "OnBootSec",
602 [TIMER_STARTUP] = "OnStartupSec",
603 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
36697dc0
LP
604 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
605 [TIMER_CALENDAR] = "OnCalendar"
871d7de4
LP
606};
607
608DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
609
067d72c9
LP
610static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
611 [TIMER_SUCCESS] = "success",
612 [TIMER_FAILURE_RESOURCES] = "resources"
613};
614
615DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
616
87f0e418 617const UnitVTable timer_vtable = {
7d17cfbc 618 .object_size = sizeof(Timer),
718db961 619
f975e971
LP
620 .sections =
621 "Unit\0"
622 "Timer\0"
623 "Install\0",
5cb5a6ff 624
871d7de4 625 .init = timer_init,
034c6ed7 626 .done = timer_done,
871d7de4
LP
627 .load = timer_load,
628
629 .coldplug = timer_coldplug,
630
631 .dump = timer_dump,
632
633 .start = timer_start,
634 .stop = timer_stop,
635
636 .serialize = timer_serialize,
637 .deserialize_item = timer_deserialize_item,
638
639 .active_state = timer_active_state,
640 .sub_state_to_string = timer_sub_state_to_string,
641
3ecaa09b
LP
642 .trigger_notify = timer_trigger_notify,
643
fdf20a31 644 .reset_failed = timer_reset_failed,
8742514c 645 .time_change = timer_time_change,
5632e374 646
c4e2ceae 647 .bus_interface = "org.freedesktop.systemd1.Timer",
718db961 648 .bus_vtable = bus_timer_vtable,
5cb5a6ff 649};