chiark / gitweb /
list: make LIST_FIND_TAIL work for empty lists
[elogind.git] / src / timesync / timesyncd.c
index 6c886972f8d3e35729f2f54e787826b1fb97c18e..fa9671da1dfd9c3b9558679049b8543fa1bde741 100644 (file)
@@ -32,6 +32,7 @@
 #include <sys/timerfd.h>
 #include <sys/timex.h>
 #include <sys/socket.h>
+#include <resolv.h>
 
 #include "missing.h"
 #include "util.h"
@@ -39,6 +40,8 @@
 #include "log.h"
 #include "socket-util.h"
 #include "list.h"
+#include "ratelimit.h"
+#include "strv.h"
 #include "sd-event.h"
 #include "sd-resolve.h"
 #include "sd-daemon.h"
  */
 #define OFFSET_1900_1970        2208988800UL
 
+#define RETRY_USEC (30*USEC_PER_SEC)
+#define RATELIMIT_INTERVAL_USEC (10*USEC_PER_SEC)
+#define RATELIMIT_BURST 10
+
 struct ntp_ts {
         be32_t sec;
         be32_t frac;
@@ -129,6 +136,8 @@ struct Manager {
 
         LIST_HEAD(ServerName, servers);
 
+        RateLimit ratelimit;
+
         /* peer */
         sd_resolve_query *resolve_query;
         sd_event_source *event_receive;
@@ -164,6 +173,9 @@ struct Manager {
         sd_event_source *event_clock_watch;
         int clock_watch_fd;
 
+        /* Retry connections */
+        sd_event_source *event_retry;
+
         /* Handle SIGINT/SIGTERM */
         sd_event_source *sigterm, *sigint;
 };
@@ -235,8 +247,10 @@ static int manager_send_request(Manager *m) {
         if (len == sizeof(ntpmsg)) {
                 m->pending = true;
                 log_debug("Sent NTP request to %s (%s)", pretty, m->current_server_name->string);
-        } else
+        } else {
                 log_debug("Sending NTP request to %s (%s) failed: %m", pretty, m->current_server_name->string);
+                return manager_connect(m);
+        }
 
         /* re-arm timer with incresing timeout, in case the packets never arrive back */
         if (m->retry_interval > 0) {
@@ -321,7 +335,7 @@ static int manager_clock_watch_setup(Manager *m) {
         assert(m);
 
         m->event_clock_watch = sd_event_source_unref(m->event_clock_watch);
-        m->clock_watch_fd = safe_close(m->clock_watch_fd);
+        safe_close(m->clock_watch_fd);
 
         m->clock_watch_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
         if (m->clock_watch_fd < 0) {
@@ -601,7 +615,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                 return manager_connect(m);
         }
 
-        if (NTP_FIELD_VERSION(ntpmsg.field) != 4) {
+        if (NTP_FIELD_VERSION(ntpmsg.field) != 4 && NTP_FIELD_VERSION(ntpmsg.field) != 3) {
                 log_debug("Response NTPv%d. Disconnecting.", NTP_FIELD_VERSION(ntpmsg.field));
                 return manager_connect(m);
         }
@@ -847,6 +861,14 @@ static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct ad
         return manager_begin(m);
 }
 
+static int manager_retry(sd_event_source *source, usec_t usec, void *userdata) {
+        Manager *m = userdata;
+
+        assert(m);
+
+        return manager_connect(m);
+}
+
 static int manager_connect(Manager *m) {
 
         struct addrinfo hints = {
@@ -859,6 +881,19 @@ static int manager_connect(Manager *m) {
 
         manager_disconnect(m);
 
+        m->event_retry = sd_event_source_unref(m->event_retry);
+        if (!ratelimit_test(&m->ratelimit)) {
+                log_debug("Slowing down attempts to contact servers.");
+
+                r = sd_event_add_time(m->event, &m->event_retry, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + RETRY_USEC, 0, manager_retry, m);
+                if (r < 0) {
+                        log_error("Failed to create retry timer: %s", strerror(-r));
+                        return r;
+                }
+
+                return 0;
+        }
+
         /* If we already are operating on some address, switch to the
          * next one. */
         if (m->current_server_address && m->current_server_address->addresses_next)
@@ -879,6 +914,10 @@ static int manager_connect(Manager *m) {
                         m->current_server_name = m->servers;
                 }
 
+                /* Tell the resolver to reread /etc/resolv.conf, in
+                 * case it changed. */
+                res_init();
+
                 r = sd_resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, m);
                 if (r < 0) {
                         log_error("Failed to create resolver: %s", strerror(-r));
@@ -896,7 +935,7 @@ static int manager_connect(Manager *m) {
 }
 
 static int manager_add_server(Manager *m, const char *server) {
-        ServerName *n;
+        ServerName *n, *tail;
 
         assert(m);
         assert(server);
@@ -911,7 +950,9 @@ static int manager_add_server(Manager *m, const char *server) {
                 return -ENOMEM;
         }
 
-        LIST_PREPEND(names, m->servers, n);
+        LIST_FIND_TAIL(names, m->servers, tail);
+        LIST_INSERT_AFTER(names, m->servers, tail, n);
+
         return 0;
 }
 
@@ -939,6 +980,8 @@ static int manager_new(Manager **ret) {
 
         m->server_socket = m->clock_watch_fd = -1;
 
+        RATELIMIT_INIT(m->ratelimit, RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST);
+
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -974,6 +1017,8 @@ static void manager_free(Manager *m) {
         sd_event_source_unref(m->sigint);
         sd_event_source_unref(m->sigterm);
 
+        sd_event_source_unref(m->event_retry);
+
         sd_resolve_unref(m->resolve);
         sd_event_unref(m->event);
 
@@ -982,6 +1027,7 @@ static void manager_free(Manager *m) {
 
 int main(int argc, char *argv[]) {
         _cleanup_manager_free_ Manager *m = NULL;
+        const char *x;
         int r;
 
         log_set_target(LOG_TARGET_AUTO);
@@ -998,10 +1044,12 @@ int main(int argc, char *argv[]) {
 
         sd_notify(false, "READY=1");
 
-        r = manager_add_server(m, "time1.google.com");
-        if (r < 0) {
-                log_error("Failed to add server: %s", strerror(-r));
-                goto out;
+        FOREACH_STRING(x, "time1.google.com", "time2.google.com", "time3.google.com", "time4.google.com", "0.fedora.pool.ntp.org") {
+                r = manager_add_server(m, x);
+                if (r < 0) {
+                        log_error("Failed to add server %s: %s", x, strerror(-r));
+                        goto out;
+                }
         }
 
         r = manager_connect(m);