/* * check_continuity.c - periodically check local clock deltas * Copyright (c) 2013 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "config.h" #include <time.h> #include <event2/event.h> #include "src/conf.h" #include "src/tlsdate.h" #include "src/util.h" /* Returns < 0 on error, * 0 on sync'd, * and > 0 on desync'd. * Old delta is in |delta|. |delta| is overwritten * if >= 0 is returned. * * This event catches any sort of real-time clock jump. A jump is observed * when settimeofday() or adjtimex() is called, or if the RTC misbehaves on * return from suspend. If a jump is detected between a cycle-oriented clock * (MONOTONIC_RAW) and a potentially RTC managed clock (REALTIME), then a * network resynchronization will be required. To avoid requiring this on * every resume-from-suspend, a larger delta represents the largest time jump * allowed before needing a resync. * * Note, CLOCK_BOOTTIME does not resolve this on platforms without a persistent * clock because the RTC still determines the time considered "suspend time". */ int check_continuity (time_t *delta) { time_t new_delta; struct timespec monotonic, real; if (clock_gettime (CLOCK_REALTIME, &real) < 0) return -1; if (clock_gettime (CLOCK_MONOTONIC_RAW, &monotonic) < 0) return -1; new_delta = real.tv_sec - monotonic.tv_sec; if (*delta) { /* The allowed delta matches the interval for now. */ static const time_t kDelta = CONTINUITY_INTERVAL; if (new_delta < *delta - kDelta || new_delta > *delta + kDelta) { *delta = new_delta; return 1; } } /* First delta after de-sync. */ *delta = new_delta; return 0; } /* Sets up a wake event just in case there has not been a wake event * recently enough to catch clock desynchronization. This does not * invalidate the time like the action_invalidate_time event. */ int setup_event_timer_continuity (struct state *state) { struct event *event; struct timeval interval = { state->opts.continuity_interval, 0 }; event = event_new (state->base, -1, EV_TIMEOUT|EV_PERSIST, action_kickoff_time_sync, state); if (!event) { error ("Failed to create interval event"); return 1; } event_priority_set (event, PRI_WAKE); return event_add (event, &interval); }