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