]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/timer.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[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 "parse-util.h"
28 #include "special.h"
29 #include "string-util.h"
30 #include "timer.h"
31 #include "unit-name.h"
32 #include "unit.h"
33 #include "user-util.h"
34
35 static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
36 [TIMER_DEAD] = UNIT_INACTIVE,
37 [TIMER_WAITING] = UNIT_ACTIVE,
38 [TIMER_RUNNING] = UNIT_ACTIVE,
39 [TIMER_ELAPSED] = UNIT_ACTIVE,
40 [TIMER_FAILED] = UNIT_FAILED
41 };
42
43 static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
44
45 static void timer_init(Unit *u) {
46 Timer *t = TIMER(u);
47
48 assert(u);
49 assert(u->load_state == UNIT_STUB);
50
51 t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
52 t->next_elapse_realtime = USEC_INFINITY;
53 t->accuracy_usec = u->manager->default_timer_accuracy_usec;
54 }
55
56 void timer_free_values(Timer *t) {
57 TimerValue *v;
58
59 assert(t);
60
61 while ((v = t->values)) {
62 LIST_REMOVE(value, t->values, v);
63 calendar_spec_free(v->calendar_spec);
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), "Timer unit lacks value setting. Refusing.");
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 == MANAGER_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 == MANAGER_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_unit_error_errno(UNIT(t), 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), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
261
262 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
263 }
264
265 static void timer_enter_waiting(Timer *t, bool initial);
266
267 static int timer_coldplug(Unit *u) {
268 Timer *t = TIMER(u);
269
270 assert(t);
271 assert(t->state == TIMER_DEAD);
272
273 if (t->deserialized_state != t->state) {
274
275 if (t->deserialized_state == TIMER_WAITING)
276 timer_enter_waiting(t, false);
277 else
278 timer_set_state(t, t->deserialized_state);
279 }
280
281 return 0;
282 }
283
284 static void timer_enter_dead(Timer *t, TimerResult f) {
285 assert(t);
286
287 if (f != TIMER_SUCCESS)
288 t->result = f;
289
290 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
291 }
292
293 static usec_t monotonic_to_boottime(usec_t t) {
294 usec_t a, b;
295
296 if (t <= 0)
297 return 0;
298
299 a = now(CLOCK_BOOTTIME);
300 b = now(CLOCK_MONOTONIC);
301
302 if (t + a > b)
303 return t + a - b;
304 else
305 return 0;
306 }
307
308 static void timer_enter_waiting(Timer *t, bool initial) {
309 bool found_monotonic = false, found_realtime = false;
310 usec_t ts_realtime, ts_monotonic;
311 usec_t base = 0;
312 TimerValue *v;
313 int r;
314
315 /* If we shall wake the system we use the boottime clock
316 * rather than the monotonic clock. */
317
318 ts_realtime = now(CLOCK_REALTIME);
319 ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
320 t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
321
322 LIST_FOREACH(value, v, t->values) {
323
324 if (v->disabled)
325 continue;
326
327 if (v->base == TIMER_CALENDAR) {
328 usec_t b;
329
330 /* If we know the last time this was
331 * triggered, schedule the job based relative
332 * to that. If we don't just start from
333 * now. */
334
335 b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
336
337 r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
338 if (r < 0)
339 continue;
340
341 if (!found_realtime)
342 t->next_elapse_realtime = v->next_elapse;
343 else
344 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
345
346 found_realtime = true;
347
348 } else {
349 switch (v->base) {
350
351 case TIMER_ACTIVE:
352 if (state_translation_table[t->state] == UNIT_ACTIVE)
353 base = UNIT(t)->inactive_exit_timestamp.monotonic;
354 else
355 base = ts_monotonic;
356 break;
357
358 case TIMER_BOOT:
359 /* CLOCK_MONOTONIC equals the uptime on Linux */
360 base = 0;
361 break;
362
363 case TIMER_STARTUP:
364 base = UNIT(t)->manager->userspace_timestamp.monotonic;
365 break;
366
367 case TIMER_UNIT_ACTIVE:
368
369 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
370
371 if (base <= 0)
372 base = t->last_trigger.monotonic;
373
374 if (base <= 0)
375 continue;
376
377 break;
378
379 case TIMER_UNIT_INACTIVE:
380
381 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
382
383 if (base <= 0)
384 base = t->last_trigger.monotonic;
385
386 if (base <= 0)
387 continue;
388
389 break;
390
391 default:
392 assert_not_reached("Unknown timer base");
393 }
394
395 if (t->wake_system)
396 base = monotonic_to_boottime(base);
397
398 v->next_elapse = base + v->value;
399
400 if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
401 /* This is a one time trigger, disable it now */
402 v->disabled = true;
403 continue;
404 }
405
406 if (!found_monotonic)
407 t->next_elapse_monotonic_or_boottime = v->next_elapse;
408 else
409 t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
410
411 found_monotonic = true;
412 }
413 }
414
415 if (!found_monotonic && !found_realtime) {
416 log_unit_debug(UNIT(t), "Timer is elapsed.");
417 timer_set_state(t, TIMER_ELAPSED);
418 return;
419 }
420
421 if (found_monotonic) {
422 char buf[FORMAT_TIMESPAN_MAX];
423
424 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));
425
426 if (t->monotonic_event_source) {
427 r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
428 if (r < 0)
429 goto fail;
430
431 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
432 if (r < 0)
433 goto fail;
434 } else {
435
436 r = sd_event_add_time(
437 UNIT(t)->manager->event,
438 &t->monotonic_event_source,
439 t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
440 t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
441 timer_dispatch, t);
442 if (r < 0)
443 goto fail;
444
445 (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic");
446 }
447
448 } else if (t->monotonic_event_source) {
449
450 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
451 if (r < 0)
452 goto fail;
453 }
454
455 if (found_realtime) {
456 char buf[FORMAT_TIMESTAMP_MAX];
457 log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
458
459 if (t->realtime_event_source) {
460 r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
461 if (r < 0)
462 goto fail;
463
464 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
465 if (r < 0)
466 goto fail;
467 } else {
468 r = sd_event_add_time(
469 UNIT(t)->manager->event,
470 &t->realtime_event_source,
471 t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
472 t->next_elapse_realtime, t->accuracy_usec,
473 timer_dispatch, t);
474 if (r < 0)
475 goto fail;
476
477 (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime");
478 }
479
480 } else if (t->realtime_event_source) {
481
482 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
483 if (r < 0)
484 goto fail;
485 }
486
487 timer_set_state(t, TIMER_WAITING);
488 return;
489
490 fail:
491 log_unit_warning_errno(UNIT(t), r, "Failed to enter waiting state: %m");
492 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
493 }
494
495 static void timer_enter_running(Timer *t) {
496 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
497 int r;
498
499 assert(t);
500
501 /* Don't start job if we are supposed to go down */
502 if (unit_stop_pending(UNIT(t)))
503 return;
504
505 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
506 JOB_REPLACE, true, &error, NULL);
507 if (r < 0)
508 goto fail;
509
510 dual_timestamp_get(&t->last_trigger);
511
512 if (t->stamp_path)
513 touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
514
515 timer_set_state(t, TIMER_RUNNING);
516 return;
517
518 fail:
519 log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
520 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
521 }
522
523 static int timer_start(Unit *u) {
524 Timer *t = TIMER(u);
525 TimerValue *v;
526
527 assert(t);
528 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
529
530 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
531 return -ENOENT;
532
533 t->last_trigger = DUAL_TIMESTAMP_NULL;
534
535 /* Reenable all timers that depend on unit activation time */
536 LIST_FOREACH(value, v, t->values)
537 if (v->base == TIMER_ACTIVE)
538 v->disabled = false;
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 touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
550 }
551
552 t->result = TIMER_SUCCESS;
553 timer_enter_waiting(t, true);
554 return 1;
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 1;
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_unit_debug(u, "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_unit_debug(u, "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_unit_debug(u, "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_unit_debug(u, "Failed to parse last-trigger-monotonic value: %s", value);
622
623 } else
624 log_unit_debug(u, "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_unit_debug(UNIT(t), "Timer elapsed.");
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_unit_debug(UNIT(t), "Got notified about unit deactivation.");
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_unit_debug(u, "Time change, recalculating next elapse.");
716 timer_enter_waiting(t, false);
717 }
718
719 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
720 [TIMER_ACTIVE] = "OnActiveSec",
721 [TIMER_BOOT] = "OnBootSec",
722 [TIMER_STARTUP] = "OnStartupSec",
723 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
724 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
725 [TIMER_CALENDAR] = "OnCalendar"
726 };
727
728 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
729
730 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
731 [TIMER_SUCCESS] = "success",
732 [TIMER_FAILURE_RESOURCES] = "resources"
733 };
734
735 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
736
737 const UnitVTable timer_vtable = {
738 .object_size = sizeof(Timer),
739
740 .sections =
741 "Unit\0"
742 "Timer\0"
743 "Install\0",
744 .private_section = "Timer",
745
746 .init = timer_init,
747 .done = timer_done,
748 .load = timer_load,
749
750 .coldplug = timer_coldplug,
751
752 .dump = timer_dump,
753
754 .start = timer_start,
755 .stop = timer_stop,
756
757 .serialize = timer_serialize,
758 .deserialize_item = timer_deserialize_item,
759
760 .active_state = timer_active_state,
761 .sub_state_to_string = timer_sub_state_to_string,
762
763 .trigger_notify = timer_trigger_notify,
764
765 .reset_failed = timer_reset_failed,
766 .time_change = timer_time_change,
767
768 .bus_vtable = bus_timer_vtable,
769 .bus_set_property = bus_timer_set_property,
770
771 .can_transient = true,
772 };