/src/systemd/src/core/dbus-timer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | | |
3 | | #include "alloc-util.h" |
4 | | #include "bus-util.h" |
5 | | #include "dbus-timer.h" |
6 | | #include "dbus-util.h" |
7 | | #include "strv.h" |
8 | | #include "timer.h" |
9 | | #include "unit.h" |
10 | | |
11 | | static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult); |
12 | | |
13 | | static int property_get_monotonic_timers( |
14 | | sd_bus *bus, |
15 | | const char *path, |
16 | | const char *interface, |
17 | | const char *property, |
18 | | sd_bus_message *reply, |
19 | | void *userdata, |
20 | 0 | sd_bus_error *error) { |
21 | 0 |
|
22 | 0 | Timer *t = userdata; |
23 | 0 | TimerValue *v; |
24 | 0 | int r; |
25 | 0 |
|
26 | 0 | assert(bus); |
27 | 0 | assert(reply); |
28 | 0 | assert(t); |
29 | 0 |
|
30 | 0 | r = sd_bus_message_open_container(reply, 'a', "(stt)"); |
31 | 0 | if (r < 0) |
32 | 0 | return r; |
33 | 0 | |
34 | 0 | LIST_FOREACH(value, v, t->values) { |
35 | 0 | _cleanup_free_ char *buf = NULL; |
36 | 0 | const char *s; |
37 | 0 | size_t l; |
38 | 0 |
|
39 | 0 | if (v->base == TIMER_CALENDAR) |
40 | 0 | continue; |
41 | 0 | |
42 | 0 | s = timer_base_to_string(v->base); |
43 | 0 | assert(endswith(s, "Sec")); |
44 | 0 |
|
45 | 0 | /* s/Sec/USec/ */ |
46 | 0 | l = strlen(s); |
47 | 0 | buf = new(char, l+2); |
48 | 0 | if (!buf) |
49 | 0 | return -ENOMEM; |
50 | 0 | |
51 | 0 | memcpy(buf, s, l-3); |
52 | 0 | memcpy(buf+l-3, "USec", 5); |
53 | 0 |
|
54 | 0 | r = sd_bus_message_append(reply, "(stt)", buf, v->value, v->next_elapse); |
55 | 0 | if (r < 0) |
56 | 0 | return r; |
57 | 0 | } |
58 | 0 |
|
59 | 0 | return sd_bus_message_close_container(reply); |
60 | 0 | } |
61 | | |
62 | | static int property_get_calendar_timers( |
63 | | sd_bus *bus, |
64 | | const char *path, |
65 | | const char *interface, |
66 | | const char *property, |
67 | | sd_bus_message *reply, |
68 | | void *userdata, |
69 | 0 | sd_bus_error *error) { |
70 | 0 |
|
71 | 0 | Timer *t = userdata; |
72 | 0 | TimerValue *v; |
73 | 0 | int r; |
74 | 0 |
|
75 | 0 | assert(bus); |
76 | 0 | assert(reply); |
77 | 0 | assert(t); |
78 | 0 |
|
79 | 0 | r = sd_bus_message_open_container(reply, 'a', "(sst)"); |
80 | 0 | if (r < 0) |
81 | 0 | return r; |
82 | 0 | |
83 | 0 | LIST_FOREACH(value, v, t->values) { |
84 | 0 | _cleanup_free_ char *buf = NULL; |
85 | 0 |
|
86 | 0 | if (v->base != TIMER_CALENDAR) |
87 | 0 | continue; |
88 | 0 | |
89 | 0 | r = calendar_spec_to_string(v->calendar_spec, &buf); |
90 | 0 | if (r < 0) |
91 | 0 | return r; |
92 | 0 | |
93 | 0 | r = sd_bus_message_append(reply, "(sst)", timer_base_to_string(v->base), buf, v->next_elapse); |
94 | 0 | if (r < 0) |
95 | 0 | return r; |
96 | 0 | } |
97 | 0 |
|
98 | 0 | return sd_bus_message_close_container(reply); |
99 | 0 | } |
100 | | |
101 | | static int property_get_next_elapse_monotonic( |
102 | | sd_bus *bus, |
103 | | const char *path, |
104 | | const char *interface, |
105 | | const char *property, |
106 | | sd_bus_message *reply, |
107 | | void *userdata, |
108 | 0 | sd_bus_error *error) { |
109 | 0 |
|
110 | 0 | Timer *t = userdata; |
111 | 0 |
|
112 | 0 | assert(bus); |
113 | 0 | assert(reply); |
114 | 0 | assert(t); |
115 | 0 |
|
116 | 0 | return sd_bus_message_append(reply, "t", |
117 | 0 | (uint64_t) usec_shift_clock(t->next_elapse_monotonic_or_boottime, |
118 | 0 | TIMER_MONOTONIC_CLOCK(t), CLOCK_MONOTONIC)); |
119 | 0 | } |
120 | | |
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), |
137 | | SD_BUS_VTABLE_END |
138 | | }; |
139 | | |
140 | | static int bus_timer_set_transient_property( |
141 | | Timer *t, |
142 | | const char *name, |
143 | | sd_bus_message *message, |
144 | | UnitWriteFlags flags, |
145 | | sd_bus_error *error) { |
146 | | |
147 | | Unit *u = UNIT(t); |
148 | | int r; |
149 | | |
150 | | assert(t); |
151 | | assert(name); |
152 | | assert(message); |
153 | | |
154 | | flags |= UNIT_PRIVATE; |
155 | | |
156 | | if (streq(name, "AccuracyUSec")) |
157 | | return bus_set_transient_usec(u, name, &t->accuracy_usec, message, flags, error); |
158 | | |
159 | | if (streq(name, "AccuracySec")) { |
160 | | log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead."); |
161 | | return bus_set_transient_usec(u, "AccuracyUSec", &t->accuracy_usec, message, flags, error); |
162 | | } |
163 | | |
164 | | if (streq(name, "RandomizedDelayUSec")) |
165 | | return bus_set_transient_usec(u, name, &t->random_usec, message, flags, error); |
166 | | |
167 | | if (streq(name, "WakeSystem")) |
168 | | return bus_set_transient_bool(u, name, &t->wake_system, message, flags, error); |
169 | | |
170 | | if (streq(name, "Persistent")) |
171 | | return bus_set_transient_bool(u, name, &t->persistent, message, flags, error); |
172 | | |
173 | | if (streq(name, "RemainAfterElapse")) |
174 | | return bus_set_transient_bool(u, name, &t->remain_after_elapse, message, flags, error); |
175 | | |
176 | | if (streq(name, "OnTimezoneChange")) |
177 | | return bus_set_transient_bool(u, name, &t->on_timezone_change, message, flags, error); |
178 | | |
179 | | if (streq(name, "OnClockChange")) |
180 | | return bus_set_transient_bool(u, name, &t->on_clock_change, message, flags, error); |
181 | | |
182 | | if (streq(name, "TimersMonotonic")) { |
183 | | const char *base_name; |
184 | | usec_t usec = 0; |
185 | | bool empty = true; |
186 | | |
187 | | r = sd_bus_message_enter_container(message, 'a', "(st)"); |
188 | | if (r < 0) |
189 | | return r; |
190 | | |
191 | | while ((r = sd_bus_message_read(message, "(st)", &base_name, &usec)) > 0) { |
192 | | TimerBase b; |
193 | | |
194 | | b = timer_base_from_string(base_name); |
195 | | if (b < 0 || b == TIMER_CALENDAR) |
196 | | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name); |
197 | | |
198 | | if (!UNIT_WRITE_FLAGS_NOOP(flags)) { |
199 | | char ts[FORMAT_TIMESPAN_MAX]; |
200 | | TimerValue *v; |
201 | | |
202 | | unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name, |
203 | | format_timespan(ts, sizeof(ts), usec, USEC_PER_MSEC)); |
204 | | |
205 | | v = new(TimerValue, 1); |
206 | | if (!v) |
207 | | return -ENOMEM; |
208 | | |
209 | | *v = (TimerValue) { |
210 | | .base = b, |
211 | | .value = usec, |
212 | | }; |
213 | | |
214 | | LIST_PREPEND(value, t->values, v); |
215 | | } |
216 | | |
217 | | empty = false; |
218 | | } |
219 | | if (r < 0) |
220 | | return r; |
221 | | |
222 | | r = sd_bus_message_exit_container(message); |
223 | | if (r < 0) |
224 | | return r; |
225 | | |
226 | | if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { |
227 | | timer_free_values(t); |
228 | | unit_write_setting(u, flags, name, "OnActiveSec="); |
229 | | } |
230 | | |
231 | | return 1; |
232 | | |
233 | | } else if (streq(name, "TimersCalendar")) { |
234 | | const char *base_name, *str; |
235 | | bool empty = true; |
236 | | |
237 | | r = sd_bus_message_enter_container(message, 'a', "(ss)"); |
238 | | if (r < 0) |
239 | | return r; |
240 | | |
241 | | while ((r = sd_bus_message_read(message, "(ss)", &base_name, &str)) > 0) { |
242 | | _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; |
243 | | TimerBase b; |
244 | | |
245 | | b = timer_base_from_string(base_name); |
246 | | if (b != TIMER_CALENDAR) |
247 | | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name); |
248 | | |
249 | | r = calendar_spec_from_string(str, &c); |
250 | | if (r == -EINVAL) |
251 | | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec: %s", str); |
252 | | if (r < 0) |
253 | | return r; |
254 | | |
255 | | if (!UNIT_WRITE_FLAGS_NOOP(flags)) { |
256 | | TimerValue *v; |
257 | | |
258 | | unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name, str); |
259 | | |
260 | | v = new(TimerValue, 1); |
261 | | if (!v) |
262 | | return -ENOMEM; |
263 | | |
264 | | *v = (TimerValue) { |
265 | | .base = b, |
266 | | .calendar_spec = TAKE_PTR(c), |
267 | | }; |
268 | | |
269 | | LIST_PREPEND(value, t->values, v); |
270 | | } |
271 | | |
272 | | empty = false; |
273 | | } |
274 | | if (r < 0) |
275 | | return r; |
276 | | |
277 | | r = sd_bus_message_exit_container(message); |
278 | | if (r < 0) |
279 | | return r; |
280 | | |
281 | | if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { |
282 | | timer_free_values(t); |
283 | | unit_write_setting(u, flags, name, "OnCalendar="); |
284 | | } |
285 | | |
286 | | return 1; |
287 | | |
288 | | } else if (STR_IN_SET(name, |
289 | | "OnActiveSec", |
290 | | "OnBootSec", |
291 | | "OnStartupSec", |
292 | | "OnUnitActiveSec", |
293 | | "OnUnitInactiveSec")) { |
294 | | |
295 | | TimerValue *v; |
296 | | TimerBase b = _TIMER_BASE_INVALID; |
297 | | usec_t usec = 0; |
298 | | |
299 | | log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name); |
300 | | |
301 | | b = timer_base_from_string(name); |
302 | | if (b < 0) |
303 | | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown timer base"); |
304 | | |
305 | | r = sd_bus_message_read(message, "t", &usec); |
306 | | if (r < 0) |
307 | | return r; |
308 | | |
309 | | if (!UNIT_WRITE_FLAGS_NOOP(flags)) { |
310 | | char time[FORMAT_TIMESPAN_MAX]; |
311 | | |
312 | | unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, |
313 | | format_timespan(time, sizeof(time), usec, USEC_PER_MSEC)); |
314 | | |
315 | | v = new(TimerValue, 1); |
316 | | if (!v) |
317 | | return -ENOMEM; |
318 | | |
319 | | *v = (TimerValue) { |
320 | | .base = b, |
321 | | .value = usec, |
322 | | }; |
323 | | |
324 | | LIST_PREPEND(value, t->values, v); |
325 | | } |
326 | | |
327 | | return 1; |
328 | | |
329 | | } else if (streq(name, "OnCalendar")) { |
330 | | |
331 | | TimerValue *v; |
332 | | _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; |
333 | | const char *str; |
334 | | |
335 | | log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name); |
336 | | |
337 | | r = sd_bus_message_read(message, "s", &str); |
338 | | if (r < 0) |
339 | | return r; |
340 | | |
341 | | if (!UNIT_WRITE_FLAGS_NOOP(flags)) { |
342 | | r = calendar_spec_from_string(str, &c); |
343 | | if (r == -EINVAL) |
344 | | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec"); |
345 | | if (r < 0) |
346 | | return r; |
347 | | |
348 | | unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, str); |
349 | | |
350 | | v = new(TimerValue, 1); |
351 | | if (!v) |
352 | | return -ENOMEM; |
353 | | |
354 | | *v = (TimerValue) { |
355 | | .base = TIMER_CALENDAR, |
356 | | .calendar_spec = TAKE_PTR(c), |
357 | | }; |
358 | | |
359 | | LIST_PREPEND(value, t->values, v); |
360 | | } |
361 | | |
362 | | return 1; |
363 | | } |
364 | | |
365 | | return 0; |
366 | | } |
367 | | |
368 | | int bus_timer_set_property( |
369 | | Unit *u, |
370 | | const char *name, |
371 | | sd_bus_message *message, |
372 | | UnitWriteFlags mode, |
373 | 0 | sd_bus_error *error) { |
374 | 0 |
|
375 | 0 | Timer *t = TIMER(u); |
376 | 0 |
|
377 | 0 | assert(t); |
378 | 0 | assert(name); |
379 | 0 | assert(message); |
380 | 0 |
|
381 | 0 | if (u->transient && u->load_state == UNIT_STUB) |
382 | 0 | return bus_timer_set_transient_property(t, name, message, mode, error); |
383 | 0 | |
384 | 0 | return 0; |
385 | 0 | } |