timedated: support multiple NTP services

Support for alternative NTP services was dropped by b72ddf0f4f.
This makes timedated re-support alternative NTP services.

Closes #8402. Also, fixes #1329.
This commit is contained in:
Yu Watanabe 2018-04-17 14:09:13 +09:00
parent e9e5ea880a
commit 5d280742b6

View file

@ -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;
unit_status_info_clear(u);
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));
}
r = sd_bus_message_read(reply, "s", &s);
if (r < 0)
return r;
c->can_ntp = true;
c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime");
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 (!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;
}
if (r < 0)
return r;
r = context_start_ntp(sd_bus_message_get_bus(m), error, enabled);
if (r < 0)
return r;
log_info("Set NTP to %sd", enable_disable(enable));
c->use_ntp = enabled;
log_info("Set NTP to %sd", enable_disable(enabled));
(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) {