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