1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
8 #include "alloc-util.h"
10 #include "dbus-timer.h"
11 #include "dbus-util.h"
16 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result
, timer_result
, TimerResult
);
18 static int property_get_monotonic_timers(
21 const char *interface
,
23 sd_bus_message
*reply
,
25 sd_bus_error
*error
) {
35 r
= sd_bus_message_open_container(reply
, 'a', "(stt)");
39 LIST_FOREACH(value
, v
, t
->values
) {
40 _cleanup_free_
char *buf
= NULL
;
44 if (v
->base
== TIMER_CALENDAR
)
47 s
= timer_base_to_string(v
->base
);
48 assert(endswith(s
, "Sec"));
57 memcpy(buf
+l
-3, "USec", 5);
59 r
= sd_bus_message_append(reply
, "(stt)", buf
, v
->value
, v
->next_elapse
);
64 return sd_bus_message_close_container(reply
);
67 static int property_get_calendar_timers(
70 const char *interface
,
72 sd_bus_message
*reply
,
74 sd_bus_error
*error
) {
84 r
= sd_bus_message_open_container(reply
, 'a', "(sst)");
88 LIST_FOREACH(value
, v
, t
->values
) {
89 _cleanup_free_
char *buf
= NULL
;
91 if (v
->base
!= TIMER_CALENDAR
)
94 r
= calendar_spec_to_string(v
->calendar_spec
, &buf
);
98 r
= sd_bus_message_append(reply
, "(sst)", timer_base_to_string(v
->base
), buf
, v
->next_elapse
);
103 return sd_bus_message_close_container(reply
);
106 static int property_get_next_elapse_monotonic(
109 const char *interface
,
110 const char *property
,
111 sd_bus_message
*reply
,
113 sd_bus_error
*error
) {
121 return sd_bus_message_append(reply
, "t",
122 (uint64_t) usec_shift_clock(t
->next_elapse_monotonic_or_boottime
,
123 TIMER_MONOTONIC_CLOCK(t
), CLOCK_MONOTONIC
));
126 const sd_bus_vtable bus_timer_vtable
[] = {
127 SD_BUS_VTABLE_START(0),
128 SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
129 SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
130 SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
),
131 SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec
, offsetof(Timer
, next_elapse_realtime
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
132 SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
133 BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer
, last_trigger
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
134 SD_BUS_PROPERTY("Result", "s", property_get_result
, offsetof(Timer
, result
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
135 SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec
, offsetof(Timer
, accuracy_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
136 SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec
, offsetof(Timer
, random_usec
), SD_BUS_VTABLE_PROPERTY_CONST
),
137 SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool
, offsetof(Timer
, persistent
), SD_BUS_VTABLE_PROPERTY_CONST
),
138 SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool
, offsetof(Timer
, wake_system
), SD_BUS_VTABLE_PROPERTY_CONST
),
139 SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool
, offsetof(Timer
, remain_after_elapse
), SD_BUS_VTABLE_PROPERTY_CONST
),
143 static int bus_timer_set_transient_property(
146 sd_bus_message
*message
,
147 UnitWriteFlags flags
,
148 sd_bus_error
*error
) {
157 flags
|= UNIT_PRIVATE
;
159 if (streq(name
, "AccuracyUSec"))
160 return bus_set_transient_usec(u
, name
, &t
->accuracy_usec
, message
, flags
, error
);
162 if (streq(name
, "AccuracySec")) {
163 log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
164 return bus_set_transient_usec(u
, "AccuracyUSec", &t
->accuracy_usec
, message
, flags
, error
);
167 if (streq(name
, "RandomizedDelayUSec"))
168 return bus_set_transient_usec(u
, name
, &t
->random_usec
, message
, flags
, error
);
170 if (streq(name
, "WakeSystem"))
171 return bus_set_transient_bool(u
, name
, &t
->wake_system
, message
, flags
, error
);
173 if (streq(name
, "Persistent"))
174 return bus_set_transient_bool(u
, name
, &t
->persistent
, message
, flags
, error
);
176 if (streq(name
, "RemainAfterElapse"))
177 return bus_set_transient_bool(u
, name
, &t
->remain_after_elapse
, message
, flags
, error
);
179 if (streq(name
, "TimersMonotonic")) {
180 const char *base_name
;
184 r
= sd_bus_message_enter_container(message
, 'a', "(st)");
188 while ((r
= sd_bus_message_read(message
, "(st)", &base_name
, &usec
)) > 0) {
191 b
= timer_base_from_string(base_name
);
192 if (b
< 0 || b
== TIMER_CALENDAR
)
193 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid timer base: %s", base_name
);
195 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
196 char ts
[FORMAT_TIMESPAN_MAX
];
199 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", base_name
,
200 format_timespan(ts
, sizeof(ts
), usec
, USEC_PER_MSEC
));
202 v
= new0(TimerValue
, 1);
209 LIST_PREPEND(value
, t
->values
, v
);
217 r
= sd_bus_message_exit_container(message
);
221 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
222 timer_free_values(t
);
223 unit_write_setting(u
, flags
, name
, "OnActiveSec=");
228 } else if (streq(name
, "TimersCalendar")) {
229 const char *base_name
, *str
;
232 r
= sd_bus_message_enter_container(message
, 'a', "(ss)");
236 while ((r
= sd_bus_message_read(message
, "(ss)", &base_name
, &str
)) > 0) {
237 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
240 b
= timer_base_from_string(base_name
);
241 if (b
!= TIMER_CALENDAR
)
242 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid timer base: %s", base_name
);
244 r
= calendar_spec_from_string(str
, &c
);
246 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid calendar spec: %s", str
);
250 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
253 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", base_name
, str
);
255 v
= new0(TimerValue
, 1);
260 v
->calendar_spec
= TAKE_PTR(c
);
262 LIST_PREPEND(value
, t
->values
, v
);
270 r
= sd_bus_message_exit_container(message
);
274 if (!UNIT_WRITE_FLAGS_NOOP(flags
) && empty
) {
275 timer_free_values(t
);
276 unit_write_setting(u
, flags
, name
, "OnCalendar=");
281 } else if (STR_IN_SET(name
,
286 "OnUnitInactiveSec")) {
289 TimerBase b
= _TIMER_BASE_INVALID
;
292 log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name
);
294 b
= timer_base_from_string(name
);
296 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown timer base");
298 r
= sd_bus_message_read(message
, "t", &usec
);
302 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
303 char time
[FORMAT_TIMESPAN_MAX
];
305 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", name
,
306 format_timespan(time
, sizeof(time
), usec
, USEC_PER_MSEC
));
308 v
= new0(TimerValue
, 1);
315 LIST_PREPEND(value
, t
->values
, v
);
320 } else if (streq(name
, "OnCalendar")) {
323 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
326 log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name
);
328 r
= sd_bus_message_read(message
, "s", &str
);
332 if (!UNIT_WRITE_FLAGS_NOOP(flags
)) {
333 r
= calendar_spec_from_string(str
, &c
);
335 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid calendar spec");
339 unit_write_settingf(u
, flags
|UNIT_ESCAPE_SPECIFIERS
, name
, "%s=%s", name
, str
);
341 v
= new0(TimerValue
, 1);
345 v
->base
= TIMER_CALENDAR
;
346 v
->calendar_spec
= TAKE_PTR(c
);
348 LIST_PREPEND(value
, t
->values
, v
);
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
);