=======================
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?
=================================
@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
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;
/* ================================================== */
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;
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)();
}
/* ================================================== */
+
+void
+LCL_SetLeap(int leap)
+{
+ if (drv_set_leap) {
+ (drv_set_leap)(leap);
+ }
+
+ return;
+}
+
+/* ================================================== */
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 */
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
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 */
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 */
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;
void
REF_Finalise(void)
{
+ if (our_leap_sec) {
+ LCL_SetLeap(0);
+ }
+
if (logfile) {
fclose(logfile);
}
/* ================================================== */
+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,
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 */
}
are_we_synchronised = 0;
+
+ update_leap_status(LEAP_Unsynchronised);
}
/* ================================================== */
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)) {
/* ================================================== */
+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
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset,
- get_offset_correction, immediate_step);
+ get_offset_correction, immediate_step, set_leap);
}
/* ================================================== */
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 */);
}
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 */
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 */
#include "chrony_timex.h"
#include "wrap_adjtimex.h"
+/* Save leap status between calls */
+static int leap_status = 0;
+
int
TMX_SetTick(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);
}
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
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 */