From 945716bdf1f707e8e245f4914297c8a79839c073 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 6 Dec 2023 17:51:34 +0100 Subject: [PATCH] confd: refactor ntp client setup Move from everything in a single /etc/chrony.conf to a split up with configuration and server snippets. The latter comes in the form of configured (static) and DHCP client (dynamic) server setup. To accomodate this new scheme we need to detect when serves are removed from the configuration, so not only have the whole change_ntp() been refactored, it has been extended with a new pass. Signed-off-by: Joachim Wiberg --- board/common/rootfs/etc/chrony/chrony.conf | 27 +++++ board/common/rootfs/etc/chrony/conf.d/.empty | 0 .../common/rootfs/etc/chrony/sources.d/.empty | 0 board/common/rootfs/etc/default/chronyd | 1 + .../common/rootfs/etc/tmpfiles.d/chrony.conf | 4 +- src/confd/src/ietf-system.c | 111 ++++++++++-------- 6 files changed, 90 insertions(+), 53 deletions(-) create mode 100644 board/common/rootfs/etc/chrony/chrony.conf create mode 100644 board/common/rootfs/etc/chrony/conf.d/.empty create mode 100644 board/common/rootfs/etc/chrony/sources.d/.empty create mode 100644 board/common/rootfs/etc/default/chronyd diff --git a/board/common/rootfs/etc/chrony/chrony.conf b/board/common/rootfs/etc/chrony/chrony.conf new file mode 100644 index 00000000..9db97f64 --- /dev/null +++ b/board/common/rootfs/etc/chrony/chrony.conf @@ -0,0 +1,27 @@ +# This file defines default behavior and a skeleton for including config +# snippets from both confd, i.e., ietf-system.yang, and DHCP clients. +# Default values taken from Debian /etc/chrony/chrony.conf + +# *.conf when acting as an NTP server +confdir /etc/chrony/conf.d + +# *.sources from DHCP clients, e.g., /run/chrony-dhcp/$ifname.sources: +# server 192.0.2.1 iburst +sourcedir /run/chrony/dhcp-sources.d + +# *.sources for NTP servers generated by confd:ietf-system.c +sourcedir /etc/chrony/sources.d + +# Where to store the system clock rate information across reboots. +driftfile /var/lib/chrony/chrony.drift + +# Stop bad estimates upsetting machine clock. +maxupdateskew 100.0 + +# Enables kernel synchronisation (every 11 minutes) of the real-time +# clock. Note that it can’t be used along with the 'rtcfile' directive. +rtcsync + +# Step the system clock instead of slewing it if the adjustment is +# larger than one second, but only in the first three clock updates. +makestep 1 3 diff --git a/board/common/rootfs/etc/chrony/conf.d/.empty b/board/common/rootfs/etc/chrony/conf.d/.empty new file mode 100644 index 00000000..e69de29b diff --git a/board/common/rootfs/etc/chrony/sources.d/.empty b/board/common/rootfs/etc/chrony/sources.d/.empty new file mode 100644 index 00000000..e69de29b diff --git a/board/common/rootfs/etc/default/chronyd b/board/common/rootfs/etc/default/chronyd new file mode 100644 index 00000000..30c0373e --- /dev/null +++ b/board/common/rootfs/etc/default/chronyd @@ -0,0 +1 @@ +CHRONY_ARGS="-f /etc/chrony/chrony.conf" diff --git a/board/common/rootfs/etc/tmpfiles.d/chrony.conf b/board/common/rootfs/etc/tmpfiles.d/chrony.conf index 27735426..09ddbeaa 100644 --- a/board/common/rootfs/etc/tmpfiles.d/chrony.conf +++ b/board/common/rootfs/etc/tmpfiles.d/chrony.conf @@ -1 +1,3 @@ -d /var/lib/chrony 0755 chrony chrony +d /var/lib/chrony 0750 chrony chrony +d /run/chrony 0750 chrony chrony +d /run/chrony/dhcp-sources.d 0755 chrony chrony diff --git a/src/confd/src/ietf-system.c b/src/confd/src/ietf-system.c index 362fd649..e50b07c6 100644 --- a/src/confd/src/ietf-system.c +++ b/src/confd/src/ietf-system.c @@ -361,19 +361,14 @@ static int change_clock(sr_session_ctx_t *session, uint32_t sub_id, const char * return SR_ERR_OK; } -#define CHRONY_CONF "/etc/chrony.conf" -#define CHRONY_PREV CHRONY_CONF "-" -#define CHRONY_NEXT CHRONY_CONF "+" - static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *module, const char *xpath, sr_event_t event, unsigned request_id, void *priv) { - const char *fn = CHRONY_NEXT; - int valid = -1; + sr_change_iter_t *iter = NULL; + int rc, err = SR_ERR_OK; + int changes = 0; sr_val_t *val; size_t cnt; - FILE *fp; - int rc; switch (event) { case SR_EV_ENABLED: /* first time, on register. */ @@ -382,26 +377,20 @@ static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *mo break; case SR_EV_ABORT: /* User abort, or other plugin failed */ - (void)remove(CHRONY_NEXT); return SR_ERR_OK; case SR_EV_DONE: - /* Check if passed validation in previous event */ - if (access(CHRONY_NEXT, F_OK)) - return SR_ERR_OK; - - (void)remove(CHRONY_PREV); - (void)rename(CHRONY_CONF, CHRONY_PREV); - (void)rename(CHRONY_NEXT, CHRONY_CONF); if (!srx_enabled(session, XPATH_BASE_"/ntp/enabled")) { + systemf("rm -rf /etc/chrony/conf.d/* /etc/chrony/sources.d/*"); systemf("initctl -nbq disable chronyd"); return SR_ERR_OK; } - /* - * If chrony is alrady enabled we tell Finit it's been - * modified , so Finit restarts it, otherwise enable it. - */ - systemf("initctl -nbq touch chronyd"); + + if (fexist("/run/chrony/.changes")) { + systemf("chronyc reload sources >/dev/null"); + erase("/run/chrony/.changes"); + } + systemf("initctl -nbq enable chronyd"); return SR_ERR_OK; @@ -409,30 +398,57 @@ static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *mo return SR_ERR_OK; } - rc = sr_get_items(session, XPATH_BASE_"/ntp/server", 0, 0, &val, &cnt); - if (rc) { - (void)remove(CHRONY_NEXT); - return rc; + /* + * First remove .sources files for all deleted servers + */ + sr_get_changes_iter(session, XPATH_BASE_"/ntp/server[name=*]/name", &iter); + if (iter) { + sr_change_oper_t op; + sr_val_t *old, *new; + + while (!sr_get_change_next(session, iter, &op, &old, &new)) { + char *name; + + if (op != SR_OP_DELETED) + continue; + + name = sr_val_to_str(old); + DEBUG("Removing NTP server %s\n", name); + erasef("/etc/chrony/sources.d/%s.sources", name); + free(name); + changes++; + } + + sr_free_change_iter(iter); } - fp = fopen(fn, "w"); - if (!fp) { - ERROR("failed updating %s: %s", fn, strerror(errno)); - sr_free_values(val, cnt); - return SR_ERR_SYS; + /* + * Then add or recreate any new or modified sources + */ + rc = sr_get_items(session, XPATH_BASE_"/ntp/server", 0, 0, &val, &cnt); + if (rc) { + return SR_ERR_OK; } for (size_t i = 0; i < cnt; i++) { const char *xpath = val[i].xpath; - char *type, *ptr; + char *type, *ptr, *name; int server = 0; + FILE *fp; - /* - * Handle empty startup-config on SR_EV_ENABLED, - * prevents subscribe failure due to false invalid. - */ - if (i == 0) - valid = 0; + name = srx_get_str(session, "%s/name", xpath); + if (!name) { + ERROR("no name for xpath %s", xpath); + continue; + } + + fp = fopenf("w", "/etc/chrony/sources.d/%s.sources", name); + if (!fp) { + ERROR("failed saving /etc/chrony/sources.d/%s.sources: %s", + name, strerror(errno)); + free(name); + continue; + } /* Get /ietf-system:system/ntp/server[name='foo'] */ ptr = srx_get_str(session, "%s/udp/address", xpath); @@ -456,26 +472,17 @@ static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *mo fprintf(fp, " iburst"); if (srx_enabled(session, "%s/prefer", xpath) > 0) fprintf(fp, " prefer"); - - fprintf(fp, "\n"); - valid++; } + fprintf(fp, "\n"); + fclose(fp); + changes++; } sr_free_values(val, cnt); - fprintf(fp, "driftfile /var/lib/chrony/drift\n"); - fprintf(fp, "makestep 1.0 3\n"); - fprintf(fp, "maxupdateskew 100.0\n"); - fprintf(fp, "dumpdir /var/lib/chrony\n"); - fprintf(fp, "rtcfile /var/lib/chrony/rtc\n"); - fclose(fp); + if (changes) + touch("/run/chrony/.changes"); - if (!valid) { - (void)remove(fn); - return SR_ERR_VALIDATION_FAILED; - } - - return SR_ERR_OK; + return err; } #define RESOLV_CONF "/etc/resolv.conf.head"