1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2010 Lennart Poettering
6 #include "alloc-util.h"
8 #include "dbus-timer.h"
14 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result
, timer_result
, TimerResult
);
16 static int property_get_monotonic_timers(
19 const char *interface
,
21 sd_bus_message
*reply
,
23 sd_bus_error
*error
) {
33 r
= sd_bus_message_open_container(reply
, 'a', "(stt)");
37 LIST_FOREACH(value
, v
, t
->values
) {
38 _cleanup_free_
char *buf
= NULL
;
42 if (v
->base
== TIMER_CALENDAR
)
45 s
= timer_base_to_string(v
->base
);
46 assert(endswith(s
, "Sec"));
55 memcpy(buf
+l
-3, "USec", 5);
57 r
= sd_bus_message_append(reply
, "(stt)", buf
, v
->value
, v
->next_elapse
);
62 return sd_bus_message_close_container(reply
);
65 static int property_get_calendar_timers(
68 const char *interface
,
70 sd_bus_message
*reply
,
72 sd_bus_error
*error
) {
82 r
= sd_bus_message_open_container(reply
, 'a', "(sst)");
86 LIST_FOREACH(value
, v
, t
->values
) {
87 _cleanup_free_
char *buf
= NULL
;
89 if (v
->base
!= TIMER_CALENDAR
)
92 r
= calendar_spec_to_string(v
->calendar_spec
, &buf
);
96 r
= sd_bus_message_append(reply
, "(sst)", timer_base_to_string(v
->base
), buf
, v
->next_elapse
);
101 return sd_bus_message_close_container(reply
);
104 static int property_get_next_elapse_monotonic(
107 const char *interface
,
108 const char *property
,
109 sd_bus_message
*reply
,
111 sd_bus_error
*error
) {
119 return sd_bus_message_append(reply
, "t",
120 (uint64_t) usec_shift_clock(t
->next_elapse_monotonic_or_boottime
,
121 TIMER_MONOTONIC_CLOCK(t
), CLOCK_MONOTONIC
));
124 const sd_bus_vtable bus_timer_vtable
[] = {
125 SD_BUS_VTABLE_START(0),
126 SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
127 SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
128 SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
129 SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec
, offsetof(Timer
, next_elapse_realtime
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
130 SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
131 BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer
, last_trigger
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
132 SD_BUS_PROPERTY("Result", "s", property_get_result
, offsetof(Timer
, result
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
133 SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec
, offsetof(Timer
, accuracy_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
134 SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec
, offsetof(Timer
, random_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
135 SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool
, offsetof(Timer
, persistent
), SD_BUS_VTABLE_PROPERTY_CONST
),
136 SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool
, offsetof(Timer
, wake_system
), SD_BUS_VTABLE_PROPERTY_CONST
),
137 SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool
, offsetof(Timer
, remain_after_elapse
), SD_BUS_VTABLE_PROPERTY_CONST
),
141 static int bus_timer_set_transient_property(
144 sd_bus_message
*message
,
145 UnitWriteFlags flags
,
146 sd_bus_error
*error
) {
155 flags
|= UNIT_PRIVATE
;
157 if (streq(name
, "AccuracyUSec"))
158 return bus_set_transient_usec(u
, name
, &t
->accuracy_usec
, message
, flags
, error
);
160 if (streq(name
, "AccuracySec")) {
161 log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
162 return bus_set_transient_usec(u
, "AccuracyUSec", &t
->accuracy_usec
, message
, flags
, error
);
165 if (streq(name
, "RandomizedDelayUSec"))
166 return bus_set_transient_usec(u
, name
, &t
->random_usec
, message
, flags
, error
);
168 if (streq(name
, "WakeSystem"))
169 return bus_set_transient_bool(u
, name
, &t
->wake_system
, message
, flags
, error
);
171 if (streq(name
, "Persistent"))
172 return bus_set_transient_bool(u
, name
, &t
->persistent
, message
, flags
, error
);
174 if (streq(name
, "RemainAfterElapse"))
175 return bus_set_transient_bool(u
, name
, &t
->remain_after_elapse
, message
, flags
, error
);
177 if (streq(name
, "TimersMonotonic")) {
178 const char *base_name
;
182 r
= sd_bus_message_enter_container(message
, 'a', "(st)");
186 while ((r
= sd_bus_message_read(message
, "(st)", &base_name
, &usec
)) > 0) {
189 b
= timer_base_from_string(base_name
);
190 if (b
< 0 || b
== TIMER_CALENDAR
)
191 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid timer base: %s", base_name
);
193 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
194 char ts
[FORMAT_TIMESPAN_MAX
];
197 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", base_name
,
198 format_timespan(ts
, sizeof(ts
), usec
, USEC_PER_MSEC
));
200 v
= new0(TimerValue
, 1);
207 LIST_PREPEND(value
, t
->values
, v
);
215 r
= sd_bus_message_exit_container(message
);
219 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
220 timer_free_values(t
);
221 unit_write_setting(u
, flags
, name
, "OnActiveSec=");
226 } else if (streq(name
, "TimersCalendar")) {
227 const char *base_name
, *str
;
230 r
= sd_bus_message_enter_container(message
, 'a', "(ss)");
234 while ((r
= sd_bus_message_read(message
, "(ss)", &base_name
, &str
)) > 0) {
235 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
238 b
= timer_base_from_string(base_name
);
239 if (b
!= TIMER_CALENDAR
)
240 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid timer base: %s", base_name
);
242 r
= calendar_spec_from_string(str
, &c
);
244 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid calendar spec: %s", str
);
248 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
251 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", base_name
, str
);
253 v
= new0(TimerValue
, 1);
258 v
->calendar_spec
= TAKE_PTR(c
);
260 LIST_PREPEND(value
, t
->values
, v
);
268 r
= sd_bus_message_exit_container(message
);
272 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
273 timer_free_values(t
);
274 unit_write_setting(u
, flags
, name
, "OnCalendar=");
279 } else if (STR_IN_SET(name
,
284 "OnUnitInactiveSec")) {
287 TimerBase b
= _TIMER_BASE_INVALID
;
290 log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name
);
292 b
= timer_base_from_string(name
);
294 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown timer base");
296 r
= sd_bus_message_read(message
, "t", &usec
);
300 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
301 char time
[FORMAT_TIMESPAN_MAX
];
303 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", name
,
304 format_timespan(time
, sizeof(time
), usec
, USEC_PER_MSEC
));
306 v
= new0(TimerValue
, 1);
313 LIST_PREPEND(value
, t
->values
, v
);
318 } else if (streq(name
, "OnCalendar")) {
321 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
324 log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name
);
326 r
= sd_bus_message_read(message
, "s", &str
);
330 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
331 r
= calendar_spec_from_string(str
, &c
);
333 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid calendar spec");
337 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", name
, str
);
339 v
= new0(TimerValue
, 1);
343 v
->base
= TIMER_CALENDAR
;
344 v
->calendar_spec
= TAKE_PTR(c
);
346 LIST_PREPEND(value
, t
->values
, v
);
355 int bus_timer_set_property(
358 sd_bus_message
*message
,
360 sd_bus_error
*error
) {
368 if (u
->transient
&& u
->load_state
== UNIT_STUB
)
369 return bus_timer_set_transient_property(t
, name
, message
, mode
, error
);