Leap second support
Leap second status is accepted and forwarded to clients if majority of selectable sources agree. The actual insertion/deletion is supported only on Linux now.
This commit is contained in:
parent
0148ecaea0
commit
8f9c237010
13 changed files with 141 additions and 12 deletions
3
README
3
README
|
@ -90,8 +90,7 @@ What can chrony not do?
|
|||
=======================
|
||||
|
||||
Compared to the `reference' RFC1305 implementation xntpd, chronyd does
|
||||
not support hardware reference clocks, leap seconds or broadcast
|
||||
modes.
|
||||
not support hardware reference clocks or broadcast modes.
|
||||
|
||||
Where are new versions announced?
|
||||
=================================
|
||||
|
|
|
@ -213,8 +213,8 @@ interfaced at a later date.
|
|||
|
||||
@item
|
||||
@code{xntpd} supports effectively all of RFC1305, including broadcast /
|
||||
multicast clients, leap seconds, and extra encryption schemes for
|
||||
authenticating data packets.
|
||||
multicast clients and extra encryption schemes for authenticating
|
||||
data packets.
|
||||
|
||||
@item
|
||||
@code{xntpd} has been ported to more types of computer / operating
|
||||
|
|
17
local.c
17
local.c
|
@ -54,6 +54,7 @@ static lcl_AccrueOffsetDriver drv_accrue_offset;
|
|||
static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
|
||||
static lcl_OffsetCorrectionDriver drv_offset_convert;
|
||||
static lcl_ImmediateStepDriver drv_immediate_step;
|
||||
static lcl_SetLeapDriver drv_set_leap;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
|
@ -535,7 +536,8 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
|
|||
lcl_AccrueOffsetDriver accrue_offset,
|
||||
lcl_ApplyStepOffsetDriver apply_step_offset,
|
||||
lcl_OffsetCorrectionDriver offset_convert,
|
||||
lcl_ImmediateStepDriver immediate_step)
|
||||
lcl_ImmediateStepDriver immediate_step,
|
||||
lcl_SetLeapDriver set_leap)
|
||||
{
|
||||
drv_read_freq = read_freq;
|
||||
drv_set_freq = set_freq;
|
||||
|
@ -543,6 +545,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
|
|||
drv_apply_step_offset = apply_step_offset;
|
||||
drv_offset_convert = offset_convert;
|
||||
drv_immediate_step = immediate_step;
|
||||
drv_set_leap = set_leap;
|
||||
|
||||
current_freq_ppm = (*drv_read_freq)();
|
||||
|
||||
|
@ -572,3 +575,15 @@ LCL_MakeStep(void)
|
|||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LCL_SetLeap(int leap)
|
||||
{
|
||||
if (drv_set_leap) {
|
||||
(drv_set_leap)(leap);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
5
local.h
5
local.h
|
@ -181,4 +181,9 @@ extern void LCL_Finalise(void);
|
|||
to a timezone problem. */
|
||||
extern int LCL_MakeStep(void);
|
||||
|
||||
/* Routine to schedule a leap second. Leap second will be inserted
|
||||
at the end of the day if argument is positive, deleted if negative,
|
||||
and zero cancels scheduled leap second. */
|
||||
extern void LCL_SetLeap(int leap);
|
||||
|
||||
#endif /* GOT_LOCAL_H */
|
||||
|
|
6
localp.h
6
localp.h
|
@ -60,6 +60,9 @@ typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr);
|
|||
as an immediate step instead */
|
||||
typedef void (*lcl_ImmediateStepDriver)(void);
|
||||
|
||||
/* System driver to schedule leap second */
|
||||
typedef void (*lcl_SetLeapDriver)(int leap);
|
||||
|
||||
extern void lcl_InvokeDispersionNotifyHandlers(double dispersion);
|
||||
|
||||
extern void
|
||||
|
@ -68,6 +71,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
|
|||
lcl_AccrueOffsetDriver accrue_offset,
|
||||
lcl_ApplyStepOffsetDriver apply_step_offset,
|
||||
lcl_OffsetCorrectionDriver offset_convert,
|
||||
lcl_ImmediateStepDriver immediate_step_driver);
|
||||
lcl_ImmediateStepDriver immediate_step_driver,
|
||||
lcl_SetLeapDriver set_leap);
|
||||
|
||||
#endif /* GOT_LOCALP_H */
|
||||
|
|
51
reference.c
51
reference.c
|
@ -44,6 +44,7 @@ static int are_we_synchronised;
|
|||
static int enable_local_stratum;
|
||||
static int local_stratum;
|
||||
static NTP_Leap our_leap_status;
|
||||
static int our_leap_sec;
|
||||
static int our_stratum;
|
||||
static unsigned long our_ref_id;
|
||||
struct timeval our_ref_time; /* Stored relative to reference, NOT local time */
|
||||
|
@ -102,7 +103,8 @@ REF_Initialise(void)
|
|||
double our_frequency_ppm;
|
||||
|
||||
are_we_synchronised = 0;
|
||||
our_leap_status = LEAP_Normal;
|
||||
our_leap_status = LEAP_Unsynchronised;
|
||||
our_leap_sec = 0;
|
||||
initialised = 1;
|
||||
our_root_dispersion = 1.0;
|
||||
our_root_delay = 1.0;
|
||||
|
@ -176,6 +178,10 @@ REF_Initialise(void)
|
|||
void
|
||||
REF_Finalise(void)
|
||||
{
|
||||
if (our_leap_sec) {
|
||||
LCL_SetLeap(0);
|
||||
}
|
||||
|
||||
if (logfile) {
|
||||
fclose(logfile);
|
||||
}
|
||||
|
@ -311,6 +317,44 @@ maybe_log_offset(double offset)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
update_leap_status(NTP_Leap leap)
|
||||
{
|
||||
time_t now;
|
||||
struct tm stm;
|
||||
int leap_sec;
|
||||
|
||||
leap_sec = 0;
|
||||
|
||||
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
|
||||
/* Insert/delete leap second only on June 30 or December 31
|
||||
and in other months ignore the leap status completely */
|
||||
|
||||
now = time(NULL);
|
||||
stm = *gmtime(&now);
|
||||
|
||||
if (stm.tm_mon != 5 && stm.tm_mon != 11) {
|
||||
leap = LEAP_Normal;
|
||||
} else if ((stm.tm_mon == 5 && stm.tm_mday == 30) ||
|
||||
(stm.tm_mon == 11 && stm.tm_mday == 31)) {
|
||||
if (leap == LEAP_InsertSecond) {
|
||||
leap_sec = 1;
|
||||
} else {
|
||||
leap_sec = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (leap_sec != our_leap_sec) {
|
||||
LCL_SetLeap(leap_sec);
|
||||
our_leap_sec = leap_sec;
|
||||
}
|
||||
|
||||
our_leap_status = leap;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
|
||||
void
|
||||
REF_SetReference(int stratum,
|
||||
|
@ -356,13 +400,14 @@ REF_SetReference(int stratum,
|
|||
|
||||
are_we_synchronised = 1;
|
||||
our_stratum = stratum + 1;
|
||||
our_leap_status = leap;
|
||||
our_ref_id = ref_id;
|
||||
our_ref_time = *ref_time;
|
||||
our_offset = offset;
|
||||
our_root_delay = root_delay;
|
||||
our_root_dispersion = root_dispersion;
|
||||
|
||||
update_leap_status(leap);
|
||||
|
||||
/* Eliminate updates that are based on totally unreliable frequency
|
||||
information */
|
||||
|
||||
|
@ -517,6 +562,8 @@ REF_SetUnsynchronised(void)
|
|||
}
|
||||
|
||||
are_we_synchronised = 0;
|
||||
|
||||
update_leap_status(LEAP_Unsynchronised);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
17
sources.c
17
sources.c
|
@ -687,6 +687,23 @@ SRC_SelectSource(unsigned long match_addr)
|
|||
total_root_dispersion = (src_accrued_dispersion +
|
||||
sources[selected_source_index]->sel_info.root_dispersion);
|
||||
|
||||
/* Accept leap second status if more than half of selectable sources agree */
|
||||
|
||||
for (i=j1=j2=0; i<n_sel_sources; i++) {
|
||||
index = sel_sources[i];
|
||||
if (sources[index]->leap_status == LEAP_InsertSecond) {
|
||||
j1++;
|
||||
} else if (sources[index]->leap_status == LEAP_DeleteSecond) {
|
||||
j2++;
|
||||
}
|
||||
}
|
||||
|
||||
if (j1 > n_sel_sources / 2) {
|
||||
leap_status = LEAP_InsertSecond;
|
||||
} else if (j2 > n_sel_sources / 2) {
|
||||
leap_status = LEAP_DeleteSecond;
|
||||
}
|
||||
|
||||
if ((match_addr == 0) ||
|
||||
(match_addr == sources[selected_source_index]->ref_id)) {
|
||||
|
||||
|
|
17
sys_linux.c
17
sys_linux.c
|
@ -592,6 +592,21 @@ immediate_step(void)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
set_leap(int leap)
|
||||
{
|
||||
if (TMX_SetLeap(leap) < 0) {
|
||||
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
|
||||
}
|
||||
|
||||
LOG(LOGS_INFO, LOGF_SysLinux, "System clock status set to %s leap second",
|
||||
leap ? (leap > 0 ? "insert" : "delete") : "not insert/delete");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Estimate the value of HZ given the value of txc.tick that chronyd finds when
|
||||
* it starts. The only credible values are 100 (Linux/x86) or powers of 2.
|
||||
* Also, the bounds checking inside the kernel's adjtimex system call enforces
|
||||
|
@ -813,7 +828,7 @@ SYS_Linux_Initialise(void)
|
|||
|
||||
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
|
||||
accrue_offset, apply_step_offset,
|
||||
get_offset_correction, immediate_step);
|
||||
get_offset_correction, immediate_step, set_leap);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
|
@ -308,7 +308,8 @@ SYS_NetBSD_Initialise(void)
|
|||
|
||||
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
|
||||
accrue_offset, apply_step_offset,
|
||||
get_offset_correction, NULL /* immediate_step */);
|
||||
get_offset_correction, NULL /* immediate_step */,
|
||||
NULL /* set_leap */);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -444,7 +444,8 @@ SYS_Solaris_Initialise(void)
|
|||
|
||||
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
|
||||
accrue_offset, apply_step_offset,
|
||||
get_offset_correction, NULL /* immediate_step */);
|
||||
get_offset_correction, NULL /* immediate_step */,
|
||||
NULL /* set_leap */);
|
||||
|
||||
/* Turn off the kernel switch that keeps the system clock in step
|
||||
with the non-volatile clock */
|
||||
|
|
|
@ -395,7 +395,8 @@ SYS_SunOS_Initialise(void)
|
|||
|
||||
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
|
||||
accrue_offset, apply_step_offset,
|
||||
get_offset_correction, NULL /* immediate_step */);
|
||||
get_offset_correction, NULL /* immediate_step */,
|
||||
NULL /* set_leap */);
|
||||
|
||||
/* Turn off the kernel switch that keeps the system clock in step
|
||||
with the non-volatile clock */
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#include "chrony_timex.h"
|
||||
#include "wrap_adjtimex.h"
|
||||
|
||||
/* Save leap status between calls */
|
||||
static int leap_status = 0;
|
||||
|
||||
int
|
||||
TMX_SetTick(long tick)
|
||||
{
|
||||
|
@ -73,6 +76,7 @@ TMX_SetFrequency(double freq, long tick)
|
|||
txc.tick = tick;
|
||||
txc.status = STA_UNSYNC; /* Prevent any of the FLL/PLL stuff coming
|
||||
up */
|
||||
txc.status |= leap_status; /* Preserve leap bits */
|
||||
|
||||
return adjtimex(&txc);
|
||||
}
|
||||
|
@ -144,5 +148,24 @@ TMX_ReadCurrentParams(struct tmx_params *params)
|
|||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
TMX_SetLeap(int leap)
|
||||
{
|
||||
struct timex txc;
|
||||
|
||||
if (leap > 0) {
|
||||
leap_status = STA_INS;
|
||||
} else if (leap < 0) {
|
||||
leap_status = STA_DEL;
|
||||
} else {
|
||||
leap_status = 0;
|
||||
}
|
||||
|
||||
txc.modes = ADJ_STATUS;
|
||||
txc.status = STA_UNSYNC | leap_status;
|
||||
|
||||
return adjtimex(&txc);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ int TMX_SetFrequency(double freq, long tick);
|
|||
int TMX_GetFrequency(double *freq);
|
||||
int TMX_GetOffsetLeft(long *offset);
|
||||
int TMX_ReadCurrentParams(struct tmx_params *params);
|
||||
int TMX_SetLeap(int leap);
|
||||
|
||||
#endif /* GOT_WRAP_ADJTIMEX_H */
|
||||
|
||||
|
|
Loading…
Reference in a new issue