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