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