-static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
- struct timex tmx = {};
- int r;
-
- assert(m);
-
- /*
- * For small deltas, tell the kernel to gradually adjust the system
- * clock to the NTP time, larger deltas are just directly set.
- *
- * Clear STA_UNSYNC, it will enable the kernel's 11-minute mode, which
- * syncs the system time periodically to the hardware clock.
- */
- if (fabs(offset) < NTP_MAX_ADJUST) {
- tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR;
- tmx.status = STA_PLL;
- tmx.offset = offset * NSEC_PER_SEC;
- tmx.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4;
- tmx.maxerror = 0;
- tmx.esterror = 0;
- log_debug(" adjust (slew): %+.3f sec\n", offset);
- } else {
- tmx.modes = ADJ_SETOFFSET | ADJ_NANO;
-
- /* ADJ_NANO uses nanoseconds in the microseconds field */
- tmx.time.tv_sec = (long)offset;
- tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC;
-
- /* the kernel expects -0.3s as {-1, 7000.000.000} */
- if (tmx.time.tv_usec < 0) {
- tmx.time.tv_sec -= 1;
- tmx.time.tv_usec += NSEC_PER_SEC;
- }
-
- m->jumped = true;
- log_debug(" adjust (jump): %+.3f sec\n", offset);
- }
-
- switch (leap_sec) {
- case 1:
- tmx.status |= STA_INS;
- break;
- case -1:
- tmx.status |= STA_DEL;
- break;
- }
-
- r = clock_adjtime(CLOCK_REALTIME, &tmx);
- if (r < 0)
- return r;
-
- m->drift_ppm = tmx.freq / 65536;
-
- log_debug(" status : %04i %s\n"
- " time now : %li.%03lli\n"
- " constant : %li\n"
- " offset : %+.3f sec\n"
- " freq offset : %+li (%i ppm)\n",
- tmx.status, tmx.status & STA_UNSYNC ? "" : "sync",
- tmx.time.tv_sec, tmx.time.tv_usec / NSEC_PER_MSEC,
- tmx.constant,
- (double)tmx.offset / NSEC_PER_SEC,
- tmx.freq, m->drift_ppm);
-
- return 0;
-}
-
-static bool manager_sample_spike_detection(Manager *m, double offset, double delay) {
- unsigned int i, idx_cur, idx_new, idx_min;
- double jitter;
- double j;
-
- assert(m);
-
- m->packet_count++;
-
- /* ignore initial sample */
- if (m->packet_count == 1)
- return false;
-
- /* store the current data in our samples array */
- idx_cur = m->samples_idx;
- idx_new = (idx_cur + 1) % ELEMENTSOF(m->samples);
- m->samples_idx = idx_new;
- m->samples[idx_new].offset = offset;
- m->samples[idx_new].delay = delay;
-
- /* calculate new jitter value from the RMS differences relative to the lowest delay sample */
- jitter = m->samples_jitter;
- for (idx_min = idx_cur, i = 0; i < ELEMENTSOF(m->samples); i++)
- if (m->samples[i].delay > 0 && m->samples[i].delay < m->samples[idx_min].delay)
- idx_min = i;
-
- j = 0;
- for (i = 0; i < ELEMENTSOF(m->samples); i++)
- j += square(m->samples[i].offset - m->samples[idx_min].offset);
- m->samples_jitter = sqrt(j / (ELEMENTSOF(m->samples) - 1));
-
- /* ignore samples when resyncing */
- if (m->poll_resync)
- return false;
-
- /* always accept offset if we are farther off than the round-trip delay */
- if (fabs(offset) > delay)
- return false;
-
- /* we need a few samples before looking at them */
- if (m->packet_count < 4)
- return false;
-
- /* do not accept anything worse than the maximum possible error of the best sample */
- if (fabs(offset) > m->samples[idx_min].delay)
- return true;
-
- /* compare the difference between the current offset to the previous offset and jitter */
- return fabs(offset - m->samples[idx_cur].offset) > 3 * jitter;
-}
-
-static void manager_adjust_poll(Manager *m, double offset, bool spike) {
- assert(m);
-
- if (m->poll_resync) {
- m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC;
- m->poll_resync = false;
- return;
- }
-
- /* set to minimal poll interval */
- if (!spike && fabs(offset) > NTP_ACCURACY_SEC) {
- m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC;
- return;
- }
-
- /* increase polling interval */
- if (fabs(offset) < NTP_ACCURACY_SEC * 0.25) {
- if (m->poll_interval_usec < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC)
- m->poll_interval_usec *= 2;
- return;
- }
-
- /* decrease polling interval */
- if (spike || fabs(offset) > NTP_ACCURACY_SEC * 0.75) {
- if (m->poll_interval_usec > NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC)
- m->poll_interval_usec /= 2;
- return;
- }
-}
-
-static bool sockaddr_equal(union sockaddr_union *a, union sockaddr_union *b) {
- assert(a);
- assert(b);
-
- if (a->sa.sa_family != b->sa.sa_family)
- return false;
-
- if (a->sa.sa_family == AF_INET)
- return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
-
- if (a->sa.sa_family == AF_INET6)
- return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
-
- return false;
-}
-
-static int manager_receive_response(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- _cleanup_free_ char *pretty = NULL;
- Manager *m = userdata;
- struct ntp_msg ntpmsg;
-
- struct iovec iov = {
- .iov_base = &ntpmsg,
- .iov_len = sizeof(ntpmsg),
- };
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(struct timeval))];
- } control;
- union sockaddr_union server_addr;
- struct msghdr msghdr = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- .msg_name = &server_addr,
- .msg_namelen = sizeof(server_addr),
- };
- struct cmsghdr *cmsg;
- struct timespec now_ts;
- struct timeval *recv_time;
- ssize_t len;
- double origin, receive, trans, dest;
- double delay, offset;
- bool spike;
- int leap_sec;