diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2018-04-17 14:09:13 +0900 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2018-04-17 14:09:13 +0900 |
commit | 5d280742b645a69a19e7f9131adc0c95f5c7fa07 (patch) | |
tree | 1996df42c29e81fec577fb290b0a442b476775a5 /src/timedate | |
parent | e9e5ea880ae2256e36faf092ad72564facce025e (diff) | |
download | systemd-5d280742b645a69a19e7f9131adc0c95f5c7fa07.tar.gz |
timedated: support multiple NTP services
Support for alternative NTP services was dropped by b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12.
This makes timedated re-support alternative NTP services.
Closes #8402. Also, fixes #1329.
Diffstat (limited to 'src/timedate')
-rw-r--r-- | src/timedate/timedated.c | 383 |
1 files changed, 311 insertions, 72 deletions
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index b53bb5e49c..f479b0ab7f 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -21,28 +21,170 @@ #include "def.h" #include "fileio-label.h" #include "fs-util.h" +#include "hashmap.h" +#include "list.h" #include "path-util.h" #include "selinux-util.h" +#include "string-util.h" #include "strv.h" +#include "unit-def.h" +#include "unit-name.h" #include "user-util.h" #include "util.h" #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" +typedef struct UnitStatusInfo { + char *name; + char *load_state; + char *unit_file_state; + char *active_state; + + LIST_FIELDS(struct UnitStatusInfo, units); +} UnitStatusInfo; + typedef struct Context { char *zone; bool local_rtc; - bool can_ntp; - bool use_ntp; Hashmap *polkit_registry; + + LIST_HEAD(UnitStatusInfo, units); } Context; +static void unit_status_info_clear(UnitStatusInfo *p) { + assert(p); + + p->load_state = mfree(p->load_state); + p->unit_file_state = mfree(p->unit_file_state); + p->active_state = mfree(p->active_state); +} + +static void unit_status_info_free(UnitStatusInfo *p) { + assert(p); + + unit_status_info_clear(p); + free(p->name); + free(p); +} + static void context_free(Context *c) { + UnitStatusInfo *p; + assert(c); free(c->zone); bus_verify_polkit_async_registry_free(c->polkit_registry); + + while ((p = c->units)) { + LIST_REMOVE(units, c->units, p); + unit_status_info_free(p); + } +} + +static int context_add_ntp_service(Context *c, const char *s) { + _cleanup_free_ char *name = NULL; + UnitStatusInfo *u; + + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) + return -EINVAL; + + /* Do not add this if it is already listed */ + LIST_FOREACH(units, u, c->units) + if (streq(u->name, s)) + return 0; + + u = new0(UnitStatusInfo, 1); + if (!u) + return -ENOMEM; + + u->name = strdup(s); + if (!u->name) { + free(u); + return -ENOMEM; + } + + LIST_APPEND(units, c->units, u); + + return 0; +} + +static int context_parse_ntp_services(Context *c) { + const char *env, *p; + int r; + + assert(c); + + env = getenv("SYSTEMD_TIMEDATED_NTP_SERVICES"); + if (!env) { + r = context_add_ntp_service(c, "systemd-timesyncd.service"); + if (r < 0) + log_warning_errno(r, "Failed to add NTP service \"systemd-timesyncd.service\", ignoring: %m"); + + return 0; + } + + for (p = env;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, ":", 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_error("Invalid syntax, ignoring: %s", env); + break; + } + + r = context_add_ntp_service(c, word); + if (r < 0) + log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word); + } + + return 0; +} + +static int context_ntp_service_is_active(Context *c) { + UnitStatusInfo *info; + int count = 0; + + assert(c); + + /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */ + + LIST_FOREACH(units, info, c->units) + count += streq_ptr(info->active_state, "active"); + + return count; +} + +static int context_ntp_service_is_enabled(Context *c) { + UnitStatusInfo *info; + int count = 0; + + assert(c); + + /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */ + + LIST_FOREACH(units, info, c->units) + count += STRPTR_IN_SET(info->unit_file_state, "enabled", "enabled-runtime"); + + return count; +} + +static int context_ntp_service_exists(Context *c) { + UnitStatusInfo *info; + int count = 0; + + assert(c); + + /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */ + + LIST_FOREACH(units, info, c->units) + count += streq_ptr(info->load_state, "loaded"); + + return count; } static int context_read_data(Context *c) { @@ -158,81 +300,95 @@ static int context_write_data_local_rtc(Context *c) { return write_string_file_atomic_label("/etc/adjtime", w); } -static int context_read_ntp(Context *c, sd_bus *bus) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *s; +static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m) { + static const struct bus_properties_map map[] = { + { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) }, + { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) }, + { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) }, + {} + }; + static sd_bus_message *_m = NULL; + UnitStatusInfo *u; int r; assert(c); assert(bus); - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitFileState", - &error, - &reply, - "s", - "systemd-timesyncd.service"); + /* Suppress multiple call of context_update_ntp_status() within single DBus transaction. */ + if (m && m == _m) + return 0; - if (r < 0) { - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND) || - sd_bus_error_has_name(&error, "org.freedesktop.systemd1.LoadFailed") || - sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit")) - return 0; + _m = m; - return r; - } + LIST_FOREACH(units, u, c->units) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *path = NULL; - r = sd_bus_message_read(reply, "s", &s); - if (r < 0) - return r; + unit_status_info_clear(u); - c->can_ntp = true; - c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime"); + path = unit_dbus_path_from_name(u->name); + if (!path) + return -ENOMEM; + + r = bus_map_all_properties( + bus, + "org.freedesktop.systemd1", + path, + map, + BUS_MAP_STRDUP, + &error, + NULL, + u); + if (r < 0) + return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); + } return 0; } -static int context_start_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { +static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) { int r; + assert(u); assert(bus); assert(error); + /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */ + + if (streq(u->active_state, "active") == start) + return 0; + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - enabled ? "StartUnit" : "StopUnit", + start ? "StartUnit" : "StopUnit", error, NULL, "ss", - "systemd-timesyncd.service", + u->name, "replace"); - if (r < 0) { - if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) || - sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") || - sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) - return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported"); - + if (r < 0) return r; - } return 0; } -static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { +static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool enable) { int r; + assert(u); assert(bus); assert(error); - if (enabled) + /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */ + + if (streq(u->unit_file_state, "enabled") == enable) + return 0; + + if (enable) r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -242,7 +398,7 @@ static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { error, NULL, "asbb", 1, - "systemd-timesyncd.service", + u->name, false, true); else r = sd_bus_call_method( @@ -254,15 +410,10 @@ static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { error, NULL, "asb", 1, - "systemd-timesyncd.service", + u->name, false); - - if (r < 0) { - if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) - return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported"); - + if (r < 0) return r; - } r = sd_bus_call_method( bus, @@ -273,9 +424,8 @@ static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { error, NULL, NULL); - if (r < 0) - return r; - + if (r < 0) + return r; return 0; } @@ -332,6 +482,56 @@ static int property_get_ntp_sync( return sd_bus_message_append(reply, "b", ntp_synced()); } +static int property_get_can_ntp( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Context *c = userdata; + int r; + + assert(c); + assert(bus); + assert(property); + assert(reply); + assert(error); + + r = context_update_ntp_status(c, bus, reply); + if (r < 0) + return r; + + return sd_bus_message_append(reply, "b", context_ntp_service_exists(c) > 0); +} + +static int property_get_ntp( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Context *c = userdata; + int r; + + assert(c); + assert(bus); + assert(property); + assert(reply); + assert(error); + + r = context_update_ntp_status(c, bus, reply); + if (r < 0) + return r; + + return sd_bus_message_append(reply, "b", context_ntp_service_is_active(c) > 0); +} + static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = userdata; const char *z; @@ -495,18 +695,22 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error } static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int relative, interactive; + sd_bus *bus = sd_bus_message_get_bus(m); + int relative, interactive, r; Context *c = userdata; int64_t utc; struct timespec ts; usec_t start; struct tm* tm; - int r; assert(m); assert(c); - if (c->use_ntp) + r = context_update_ntp_status(c, bus, m); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to update context: %m"); + + if (context_ntp_service_is_active(c) > 0) return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled"); /* this only gets used if dbus does not provide a timestamp */ @@ -581,19 +785,25 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro } static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int enabled, interactive; + sd_bus *bus = sd_bus_message_get_bus(m); Context *c = userdata; - int r; + UnitStatusInfo *u; + int enable, interactive, q, r; assert(m); + assert(bus); assert(c); - r = sd_bus_message_read(m, "bb", &enabled, &interactive); + r = sd_bus_message_read(m, "bb", &enable, &interactive); if (r < 0) return r; - if ((bool)enabled == c->use_ntp) - return sd_bus_reply_method_return(m, NULL); + r = context_update_ntp_status(c, bus, m); + if (r < 0) + return r; + + if (context_ntp_service_exists(c) <= 0) + return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported"); r = bus_verify_polkit_async( m, @@ -609,18 +819,49 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error if (r == 0) return 1; - r = context_enable_ntp(sd_bus_message_get_bus(m), error, enabled); - if (r < 0) - return r; + if (!enable) + LIST_FOREACH(units, u, c->units) { + if (!streq(u->load_state, "loaded")) + continue; + + q = unit_enable_or_disable(u, bus, error, enable); + if (q < 0) + r = q; + + q = unit_start_or_stop(u, bus, error, enable); + if (q < 0) + r = q; + } + + else if (context_ntp_service_is_enabled(c) <= 0) + LIST_FOREACH(units, u, c->units) { + if (!streq(u->load_state, "loaded")) + continue; + + r = unit_enable_or_disable(u, bus, error, enable); + if (r < 0) + continue; + + r = unit_start_or_stop(u, bus, error, enable); + break; + } + + else if (context_ntp_service_is_active(c) <= 0) + LIST_FOREACH(units, u, c->units) { + if (!streq(u->load_state, "loaded") || + !streq(u->unit_file_state, "enabled")) + continue; + + r = unit_start_or_stop(u, bus, error, enable); + break; + } - r = context_start_ntp(sd_bus_message_get_bus(m), error, enabled); if (r < 0) return r; - c->use_ntp = enabled; - log_info("Set NTP to %sd", enable_disable(enabled)); + log_info("Set NTP to %sd", enable_disable(enable)); - (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL); + (void) sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL); return sd_bus_reply_method_return(m, NULL); } @@ -629,8 +870,8 @@ static const sd_bus_vtable timedate_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0), - SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0), + SD_BUS_PROPERTY("NTP", "b", property_get_ntp, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0), SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0), SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0), @@ -708,11 +949,9 @@ int main(int argc, char *argv[]) { goto finish; } - r = context_read_ntp(&context, bus); - if (r < 0) { - log_error_errno(r, "Failed to determine whether NTP is enabled: %m"); + r = context_parse_ntp_services(&context); + if (r < 0) goto finish; - } r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL); if (r < 0) { |