/* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" #include "bus-get-properties.h" #include "dbus-timer.h" #include "dbus-util.h" #include "strv.h" #include "timer.h" #include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult); static int 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) { Timer *t = userdata; TimerValue *v; int r; assert(bus); assert(reply); assert(t); r = sd_bus_message_open_container(reply, 'a', "(stt)"); if (r < 0) return r; LIST_FOREACH(value, v, t->values) { _cleanup_free_ char *buf = NULL; const char *s; size_t l; if (v->base == TIMER_CALENDAR) continue; s = timer_base_to_string(v->base); assert(endswith(s, "Sec")); /* s/Sec/USec/ */ l = strlen(s); buf = new(char, l+2); if (!buf) return -ENOMEM; memcpy(buf, s, l-3); memcpy(buf+l-3, "USec", 5); r = sd_bus_message_append(reply, "(stt)", buf, v->value, v->next_elapse); if (r < 0) return r; } return sd_bus_message_close_container(reply); } static int 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) { Timer *t = userdata; TimerValue *v; int r; assert(bus); assert(reply); assert(t); r = sd_bus_message_open_container(reply, 'a', "(sst)"); if (r < 0) return r; LIST_FOREACH(value, v, t->values) { _cleanup_free_ char *buf = NULL; if (v->base != TIMER_CALENDAR) continue; r = calendar_spec_to_string(v->calendar_spec, &buf); if (r < 0) return r; r = sd_bus_message_append(reply, "(sst)", timer_base_to_string(v->base), buf, v->next_elapse); if (r < 0) return r; } return sd_bus_message_close_container(reply); } static int 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) { Timer *t = userdata; assert(bus); assert(reply); assert(t); return sd_bus_message_append(reply, "t", (uint64_t) usec_shift_clock(t->next_elapse_monotonic_or_boottime, TIMER_MONOTONIC_CLOCK(t), CLOCK_MONOTONIC)); } const sd_bus_vtable bus_timer_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_PROPERTY("OnClockChange", "b", bus_property_get_bool, offsetof(Timer, on_clock_change), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OnTimezoneChange", "b", bus_property_get_bool, offsetof(Timer, on_timezone_change), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("FixedRandomDelay", "b", bus_property_get_bool, offsetof(Timer, fixed_random_delay), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; static int timer_add_one_monotonic_spec( Timer *t, const char *name, TimerBase base, UnitWriteFlags flags, usec_t usec, sd_bus_error *error) { if (!UNIT_WRITE_FLAGS_NOOP(flags)) { char ts[FORMAT_TIMESPAN_MAX]; TimerValue *v; unit_write_settingf(UNIT(t), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", timer_base_to_string(base), format_timespan(ts, sizeof ts, usec, USEC_PER_MSEC)); v = new(TimerValue, 1); if (!v) return -ENOMEM; *v = (TimerValue) { .base = base, .value = usec, }; LIST_PREPEND(value, t->values, v); } return 1; } static int timer_add_one_calendar_spec( Timer *t, const char *name, TimerBase base, UnitWriteFlags flags, const char *str, sd_bus_error *error) { _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; int r; r = calendar_spec_from_string(str, &c); if (r == -EINVAL) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec"); if (r < 0) return r; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { unit_write_settingf(UNIT(t), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", timer_base_to_string(base), str); TimerValue *v = new(TimerValue, 1); if (!v) return -ENOMEM; *v = (TimerValue) { .base = base, .calendar_spec = TAKE_PTR(c), }; LIST_PREPEND(value, t->values, v); } return 1; }; static int bus_timer_set_transient_property( Timer *t, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) { Unit *u = UNIT(t); int r; assert(t); assert(name); assert(message); flags |= UNIT_PRIVATE; if (streq(name, "AccuracyUSec")) return bus_set_transient_usec(u, name, &t->accuracy_usec, message, flags, error); if (streq(name, "AccuracySec")) { log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead."); return bus_set_transient_usec(u, "AccuracyUSec", &t->accuracy_usec, message, flags, error); } if (streq(name, "RandomizedDelayUSec")) return bus_set_transient_usec(u, name, &t->random_usec, message, flags, error); if (streq(name, "FixedRandomDelay")) return bus_set_transient_bool(u, name, &t->fixed_random_delay, message, flags, error); if (streq(name, "WakeSystem")) return bus_set_transient_bool(u, name, &t->wake_system, message, flags, error); if (streq(name, "Persistent")) return bus_set_transient_bool(u, name, &t->persistent, message, flags, error); if (streq(name, "RemainAfterElapse")) return bus_set_transient_bool(u, name, &t->remain_after_elapse, message, flags, error); if (streq(name, "OnTimezoneChange")) return bus_set_transient_bool(u, name, &t->on_timezone_change, message, flags, error); if (streq(name, "OnClockChange")) return bus_set_transient_bool(u, name, &t->on_clock_change, message, flags, error); if (streq(name, "TimersMonotonic")) { const char *base_name; usec_t usec; bool empty = true; r = sd_bus_message_enter_container(message, 'a', "(st)"); if (r < 0) return r; while ((r = sd_bus_message_read(message, "(st)", &base_name, &usec)) > 0) { TimerBase b; b = timer_base_from_string(base_name); if (b < 0 || b == TIMER_CALENDAR) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name); r = timer_add_one_monotonic_spec(t, name, b, flags, usec, error); if (r < 0) return r; empty = false; } if (r < 0) return r; r = sd_bus_message_exit_container(message); if (r < 0) return r; if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { timer_free_values(t); unit_write_setting(u, flags, name, "OnActiveSec="); } return 1; } else if (streq(name, "TimersCalendar")) { const char *base_name, *str; bool empty = true; r = sd_bus_message_enter_container(message, 'a', "(ss)"); if (r < 0) return r; while ((r = sd_bus_message_read(message, "(ss)", &base_name, &str)) > 0) { TimerBase b; b = timer_base_from_string(base_name); if (b != TIMER_CALENDAR) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name); r = timer_add_one_calendar_spec(t, name, b, flags, str, error); if (r < 0) return r; empty = false; } if (r < 0) return r; r = sd_bus_message_exit_container(message); if (r < 0) return r; if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { timer_free_values(t); unit_write_setting(u, flags, name, "OnCalendar="); } return 1; } else if (STR_IN_SET(name, "OnActiveSec", "OnBootSec", "OnStartupSec", "OnUnitActiveSec", "OnUnitInactiveSec")) { TimerBase b; usec_t usec; log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name); b = timer_base_from_string(name); if (b < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown timer base"); r = sd_bus_message_read(message, "t", &usec); if (r < 0) return r; return timer_add_one_monotonic_spec(t, name, b, flags, usec, error); } else if (streq(name, "OnCalendar")) { const char *str; log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name); r = sd_bus_message_read(message, "s", &str); if (r < 0) return r; return timer_add_one_calendar_spec(t, name, TIMER_CALENDAR, flags, str, error); } return 0; } int bus_timer_set_property( Unit *u, const char *name, sd_bus_message *message, UnitWriteFlags mode, sd_bus_error *error) { Timer *t = TIMER(u); assert(t); assert(name); assert(message); if (u->transient && u->load_state == UNIT_STUB) return bus_timer_set_transient_property(t, name, message, mode, error); return 0; }