1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "alloc-util.h"
4 #include "bus-get-properties.h"
5 #include "dbus-timer.h"
11 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result
, timer_result
, TimerResult
);
13 static int property_get_monotonic_timers(
16 const char *interface
,
18 sd_bus_message
*reply
,
20 sd_bus_error
*error
) {
30 r
= sd_bus_message_open_container(reply
, 'a', "(stt)");
34 LIST_FOREACH(value
, v
, t
->values
) {
35 _cleanup_free_
char *buf
= NULL
;
39 if (v
->base
== TIMER_CALENDAR
)
42 s
= timer_base_to_string(v
->base
);
43 assert(endswith(s
, "Sec"));
52 memcpy(buf
+l
-3, "USec", 5);
54 r
= sd_bus_message_append(reply
, "(stt)", buf
, v
->value
, v
->next_elapse
);
59 return sd_bus_message_close_container(reply
);
62 static int property_get_calendar_timers(
65 const char *interface
,
67 sd_bus_message
*reply
,
69 sd_bus_error
*error
) {
79 r
= sd_bus_message_open_container(reply
, 'a', "(sst)");
83 LIST_FOREACH(value
, v
, t
->values
) {
84 _cleanup_free_
char *buf
= NULL
;
86 if (v
->base
!= TIMER_CALENDAR
)
89 r
= calendar_spec_to_string(v
->calendar_spec
, &buf
);
93 r
= sd_bus_message_append(reply
, "(sst)", timer_base_to_string(v
->base
), buf
, v
->next_elapse
);
98 return sd_bus_message_close_container(reply
);
101 static int property_get_next_elapse_monotonic(
104 const char *interface
,
105 const char *property
,
106 sd_bus_message
*reply
,
108 sd_bus_error
*error
) {
116 return sd_bus_message_append(reply
, "t",
117 (uint64_t) usec_shift_clock(t
->next_elapse_monotonic_or_boottime
,
118 TIMER_MONOTONIC_CLOCK(t
), CLOCK_MONOTONIC
));
121 const sd_bus_vtable bus_timer_vtable
[] = {
122 SD_BUS_VTABLE_START(0),
123 SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
124 SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
125 SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
126 SD_BUS_PROPERTY("OnClockChange", "b", bus_property_get_bool
, offsetof(Timer
, on_clock_change
), SD_BUS_VTABLE_PROPERTY_CONST
),
127 SD_BUS_PROPERTY("OnTimezoneChange", "b", bus_property_get_bool
, offsetof(Timer
, on_timezone_change
), SD_BUS_VTABLE_PROPERTY_CONST
),
128 SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec
, offsetof(Timer
, next_elapse_realtime
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
129 SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
130 BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer
, last_trigger
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
131 SD_BUS_PROPERTY("Result", "s", property_get_result
, offsetof(Timer
, result
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
132 SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec
, offsetof(Timer
, accuracy_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
133 SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec
, offsetof(Timer
, random_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
134 SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool
, offsetof(Timer
, persistent
), SD_BUS_VTABLE_PROPERTY_CONST
),
135 SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool
, offsetof(Timer
, wake_system
), SD_BUS_VTABLE_PROPERTY_CONST
),
136 SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool
, offsetof(Timer
, remain_after_elapse
), SD_BUS_VTABLE_PROPERTY_CONST
),
140 static int timer_add_one_monotonic_spec(
144 UnitWriteFlags flags
,
146 sd_bus_error
*error
) {
148 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
149 char ts
[FORMAT_TIMESPAN_MAX
];
152 unit_write_settingf(UNIT(t
), flags
|UNIT_ESCAPE_SPECIFIERS
, name
,
154 timer_base_to_string(base
),
155 format_timespan(ts
, sizeof ts
, usec
, USEC_PER_MSEC
));
157 v
= new(TimerValue
, 1);
166 LIST_PREPEND(value
, t
->values
, v
);
172 static int timer_add_one_calendar_spec(
176 UnitWriteFlags flags
,
178 sd_bus_error
*error
) {
180 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
183 r
= calendar_spec_from_string(str
, &c
);
185 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid calendar spec");
189 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
190 unit_write_settingf(UNIT(t
), flags
|UNIT_ESCAPE_SPECIFIERS
, name
,
191 "%s=%s", timer_base_to_string(base
), str
);
193 TimerValue
*v
= new(TimerValue
, 1);
199 .calendar_spec
= TAKE_PTR(c
),
202 LIST_PREPEND(value
, t
->values
, v
);
208 static int bus_timer_set_transient_property(
211 sd_bus_message
*message
,
212 UnitWriteFlags flags
,
213 sd_bus_error
*error
) {
222 flags
|= UNIT_PRIVATE
;
224 if (streq(name
, "AccuracyUSec"))
225 return bus_set_transient_usec(u
, name
, &t
->accuracy_usec
, message
, flags
, error
);
227 if (streq(name
, "AccuracySec")) {
228 log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
229 return bus_set_transient_usec(u
, "AccuracyUSec", &t
->accuracy_usec
, message
, flags
, error
);
232 if (streq(name
, "RandomizedDelayUSec"))
233 return bus_set_transient_usec(u
, name
, &t
->random_usec
, message
, flags
, error
);
235 if (streq(name
, "WakeSystem"))
236 return bus_set_transient_bool(u
, name
, &t
->wake_system
, message
, flags
, error
);
238 if (streq(name
, "Persistent"))
239 return bus_set_transient_bool(u
, name
, &t
->persistent
, message
, flags
, error
);
241 if (streq(name
, "RemainAfterElapse"))
242 return bus_set_transient_bool(u
, name
, &t
->remain_after_elapse
, message
, flags
, error
);
244 if (streq(name
, "OnTimezoneChange"))
245 return bus_set_transient_bool(u
, name
, &t
->on_timezone_change
, message
, flags
, error
);
247 if (streq(name
, "OnClockChange"))
248 return bus_set_transient_bool(u
, name
, &t
->on_clock_change
, message
, flags
, error
);
250 if (streq(name
, "TimersMonotonic")) {
251 const char *base_name
;
255 r
= sd_bus_message_enter_container(message
, 'a', "(st)");
259 while ((r
= sd_bus_message_read(message
, "(st)", &base_name
, &usec
)) > 0) {
262 b
= timer_base_from_string(base_name
);
263 if (b
< 0 || b
== TIMER_CALENDAR
)
264 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
,
265 "Invalid timer base: %s", base_name
);
267 r
= timer_add_one_monotonic_spec(t
, name
, b
, flags
, usec
, error
);
276 r
= sd_bus_message_exit_container(message
);
280 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
281 timer_free_values(t
);
282 unit_write_setting(u
, flags
, name
, "OnActiveSec=");
287 } else if (streq(name
, "TimersCalendar")) {
288 const char *base_name
, *str
;
291 r
= sd_bus_message_enter_container(message
, 'a', "(ss)");
295 while ((r
= sd_bus_message_read(message
, "(ss)", &base_name
, &str
)) > 0) {
298 b
= timer_base_from_string(base_name
);
299 if (b
!= TIMER_CALENDAR
)
300 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
,
301 "Invalid timer base: %s", base_name
);
303 r
= timer_add_one_calendar_spec(t
, name
, b
, flags
, str
, error
);
312 r
= sd_bus_message_exit_container(message
);
316 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
317 timer_free_values(t
);
318 unit_write_setting(u
, flags
, name
, "OnCalendar=");
323 } else if (STR_IN_SET(name
,
328 "OnUnitInactiveSec")) {
333 log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name
);
335 b
= timer_base_from_string(name
);
337 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown timer base");
339 r
= sd_bus_message_read(message
, "t", &usec
);
343 return timer_add_one_monotonic_spec(t
, name
, b
, flags
, usec
, error
);
345 } else if (streq(name
, "OnCalendar")) {
349 log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name
);
351 r
= sd_bus_message_read(message
, "s", &str
);
355 return timer_add_one_calendar_spec(t
, name
, TIMER_CALENDAR
, flags
, str
, error
);
361 int bus_timer_set_property(
364 sd_bus_message
*message
,
366 sd_bus_error
*error
) {
374 if (u
->transient
&& u
->load_state
== UNIT_STUB
)
375 return bus_timer_set_transient_property(t
, name
, message
, mode
, error
);