/*
* 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);
}