1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "bus-get-properties.h"
5 #include "calendarspec.h"
6 #include "dbus-timer.h"
8 #include "string-util.h"
13 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result
, timer_result
, TimerResult
);
15 static int property_get_monotonic_timers(
18 const char *interface
,
20 sd_bus_message
*reply
,
22 sd_bus_error
*error
) {
24 Timer
*t
= ASSERT_PTR(userdata
);
30 r
= sd_bus_message_open_container(reply
, 'a', "(stt)");
34 LIST_FOREACH(value
, v
, t
->values
) {
35 _cleanup_free_
char *usec
= NULL
;
37 if (v
->base
== TIMER_CALENDAR
)
40 usec
= timer_base_to_usec_string(v
->base
);
44 r
= sd_bus_message_append(reply
, "(stt)", usec
, v
->value
, v
->next_elapse
);
49 return sd_bus_message_close_container(reply
);
52 static int property_get_calendar_timers(
55 const char *interface
,
57 sd_bus_message
*reply
,
59 sd_bus_error
*error
) {
61 Timer
*t
= ASSERT_PTR(userdata
);
67 r
= sd_bus_message_open_container(reply
, 'a', "(sst)");
71 LIST_FOREACH(value
, v
, t
->values
) {
72 _cleanup_free_
char *buf
= NULL
;
74 if (v
->base
!= TIMER_CALENDAR
)
77 r
= calendar_spec_to_string(v
->calendar_spec
, &buf
);
81 r
= sd_bus_message_append(reply
, "(sst)", timer_base_to_string(v
->base
), buf
, v
->next_elapse
);
86 return sd_bus_message_close_container(reply
);
89 static int property_get_next_elapse_monotonic(
92 const char *interface
,
94 sd_bus_message
*reply
,
96 sd_bus_error
*error
) {
98 Timer
*t
= ASSERT_PTR(userdata
);
103 return sd_bus_message_append(reply
, "t", timer_next_elapse_monotonic(t
));
106 const sd_bus_vtable bus_timer_vtable
[] = {
107 SD_BUS_VTABLE_START(0),
108 SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
109 SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
110 SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
111 SD_BUS_PROPERTY("OnClockChange", "b", bus_property_get_bool
, offsetof(Timer
, on_clock_change
), SD_BUS_VTABLE_PROPERTY_CONST
),
112 SD_BUS_PROPERTY("OnTimezoneChange", "b", bus_property_get_bool
, offsetof(Timer
, on_timezone_change
), SD_BUS_VTABLE_PROPERTY_CONST
),
113 SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec
, offsetof(Timer
, next_elapse_realtime
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
114 SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
115 BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer
, last_trigger
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
116 SD_BUS_PROPERTY("Result", "s", property_get_result
, offsetof(Timer
, result
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
117 SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec
, offsetof(Timer
, accuracy_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
118 SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec
, offsetof(Timer
, random_delay_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
119 SD_BUS_PROPERTY("RandomizedOffsetUSec", "t", bus_property_get_usec
, offsetof(Timer
, random_offset_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
120 SD_BUS_PROPERTY("FixedRandomDelay", "b", bus_property_get_bool
, offsetof(Timer
, fixed_random_delay
), SD_BUS_VTABLE_PROPERTY_CONST
),
121 SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool
, offsetof(Timer
, persistent
), SD_BUS_VTABLE_PROPERTY_CONST
),
122 SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool
, offsetof(Timer
, wake_system
), SD_BUS_VTABLE_PROPERTY_CONST
),
123 SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool
, offsetof(Timer
, remain_after_elapse
), SD_BUS_VTABLE_PROPERTY_CONST
),
124 SD_BUS_PROPERTY("DeferReactivation", "b", bus_property_get_bool
, offsetof(Timer
, defer_reactivation
), SD_BUS_VTABLE_PROPERTY_CONST
),
128 static int timer_add_one_monotonic_spec(
132 UnitWriteFlags flags
,
134 sd_bus_error
*error
) {
136 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
139 unit_write_settingf(UNIT(t
), flags
|UNIT_ESCAPE_SPECIFIERS
, name
,
141 timer_base_to_string(base
),
142 FORMAT_TIMESPAN(usec
, USEC_PER_MSEC
));
144 v
= new(TimerValue
, 1);
153 LIST_PREPEND(value
, t
->values
, v
);
159 static int timer_add_one_calendar_spec(
163 UnitWriteFlags flags
,
165 sd_bus_error
*error
) {
167 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
170 r
= calendar_spec_from_string(str
, &c
);
172 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid calendar spec");
176 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
177 unit_write_settingf(UNIT(t
), flags
|UNIT_ESCAPE_SPECIFIERS
, name
,
178 "%s=%s", timer_base_to_string(base
), str
);
180 TimerValue
*v
= new(TimerValue
, 1);
186 .calendar_spec
= TAKE_PTR(c
),
189 LIST_PREPEND(value
, t
->values
, v
);
195 static int bus_timer_set_transient_property(
198 sd_bus_message
*message
,
199 UnitWriteFlags flags
,
200 sd_bus_error
*error
) {
209 flags
|= UNIT_PRIVATE
;
211 if (streq(name
, "AccuracyUSec"))
212 return bus_set_transient_usec(u
, name
, &t
->accuracy_usec
, message
, flags
, error
);
214 if (streq(name
, "AccuracySec")) {
215 log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
216 return bus_set_transient_usec(u
, "AccuracyUSec", &t
->accuracy_usec
, message
, flags
, error
);
219 if (streq(name
, "RandomizedDelayUSec"))
220 return bus_set_transient_usec(u
, name
, &t
->random_delay_usec
, message
, flags
, error
);
222 if (streq(name
, "RandomizedOffsetUSec"))
223 return bus_set_transient_usec(u
, name
, &t
->random_offset_usec
, message
, flags
, error
);
225 if (streq(name
, "FixedRandomDelay"))
226 return bus_set_transient_bool(u
, name
, &t
->fixed_random_delay
, message
, flags
, error
);
228 if (streq(name
, "WakeSystem"))
229 return bus_set_transient_bool(u
, name
, &t
->wake_system
, message
, flags
, error
);
231 if (streq(name
, "Persistent"))
232 return bus_set_transient_bool(u
, name
, &t
->persistent
, message
, flags
, error
);
234 if (streq(name
, "RemainAfterElapse"))
235 return bus_set_transient_bool(u
, name
, &t
->remain_after_elapse
, message
, flags
, error
);
237 if (streq(name
, "OnTimezoneChange"))
238 return bus_set_transient_bool(u
, name
, &t
->on_timezone_change
, message
, flags
, error
);
240 if (streq(name
, "OnClockChange"))
241 return bus_set_transient_bool(u
, name
, &t
->on_clock_change
, message
, flags
, error
);
243 if (streq(name
, "DeferReactivation"))
244 return bus_set_transient_bool(u
, name
, &t
->defer_reactivation
, message
, flags
, error
);
246 if (streq(name
, "TimersMonotonic")) {
247 const char *base_name
;
251 r
= sd_bus_message_enter_container(message
, 'a', "(st)");
255 while ((r
= sd_bus_message_read(message
, "(st)", &base_name
, &usec
)) > 0) {
258 b
= timer_base_from_string(base_name
);
259 if (b
< 0 || b
== TIMER_CALENDAR
)
260 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
,
261 "Invalid timer base: %s", base_name
);
263 r
= timer_add_one_monotonic_spec(t
, name
, b
, flags
, usec
, error
);
272 r
= sd_bus_message_exit_container(message
);
276 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
277 timer_free_values(t
);
278 unit_write_setting(u
, flags
, name
, "OnActiveSec=");
283 } else if (streq(name
, "TimersCalendar")) {
284 const char *base_name
, *str
;
287 r
= sd_bus_message_enter_container(message
, 'a', "(ss)");
291 while ((r
= sd_bus_message_read(message
, "(ss)", &base_name
, &str
)) > 0) {
294 b
= timer_base_from_string(base_name
);
295 if (b
!= TIMER_CALENDAR
)
296 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
,
297 "Invalid timer base: %s", base_name
);
299 r
= timer_add_one_calendar_spec(t
, name
, b
, flags
, str
, error
);
308 r
= sd_bus_message_exit_container(message
);
312 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
313 timer_free_values(t
);
314 unit_write_setting(u
, flags
, name
, "OnCalendar=");
319 } else if (STR_IN_SET(name
,
324 "OnUnitInactiveSec")) {
329 log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name
);
331 b
= timer_base_from_string(name
);
333 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown timer base %s", name
);
335 r
= sd_bus_message_read(message
, "t", &usec
);
339 return timer_add_one_monotonic_spec(t
, name
, b
, flags
, usec
, error
);
341 } else if (streq(name
, "OnCalendar")) {
345 log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name
);
347 r
= sd_bus_message_read(message
, "s", &str
);
351 return timer_add_one_calendar_spec(t
, name
, TIMER_CALENDAR
, flags
, str
, error
);
357 int bus_timer_set_property(
360 sd_bus_message
*message
,
362 sd_bus_error
*error
) {
370 if (u
->transient
&& u
->load_state
== UNIT_STUB
)
371 return bus_timer_set_transient_property(t
, name
, message
, mode
, error
);