]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Leap second support
authorMiroslav Lichvar <mlichvar@redhat.com>
Wed, 31 Dec 2008 08:59:20 +0000 (09:59 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Thu, 8 Jan 2009 12:33:15 +0000 (13:33 +0100)
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.

13 files changed:
README
chrony.texi
local.c
local.h
localp.h
reference.c
sources.c
sys_linux.c
sys_netbsd.c
sys_solaris.c
sys_sunos.c
wrap_adjtimex.c
wrap_adjtimex.h

diff --git a/README b/README
index 44649c3b7b4e2844a4c6bd79cd70c279e109e40a..40a51a2b216f2b17bf3f55b9a6937853a6b17d58 100644 (file)
--- a/README
+++ b/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?
 =================================
index 045f02cd6a5f1b3e37269b54b194520e3c3cfcf9..ce29f7aab7cd5dd947dd109e677e0abdd89ece93 100644 (file)
@@ -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
diff --git a/local.c b/local.c
index 905ce3f5ba173fa7be8fcc501f4c65aea1958229..5e63a0e9060d90209ff861523692cd0355bd9c15 100644 (file)
--- a/local.c
+++ b/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;
+}
+
+/* ================================================== */
diff --git a/local.h b/local.h
index 857d8b77af6792eede121db61a6d5eef46806f1f..cc0f4a28c7b407e778d924f3cda86d512fef3d4c 100644 (file)
--- a/local.h
+++ b/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 */
index 3e01a33e08f0ef9ff90cd09c3bb44f62c6620f51..0ccdd0c60e776605b844f76afa29063c054c3281 100644 (file)
--- a/localp.h
+++ b/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 */
index 829fa80869851de4a314fd35f993d98701b94e3f..b7313e9ab15d1a07d16ea0c960ed692cd9d7b7a3 100644 (file)
@@ -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);
 }
 
 /* ================================================== */
index 40cb7d2c90fdea8e6b56994c4702effb23f201c2..d90eb99727ab65beeea8459fe6e3ee00d237b72b 100644 (file)
--- a/sources.c
+++ b/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)) {
 
index 65eb563b8030793936ff75134e1778f9f049735c..d734b5e1ca155be793e32996c9fca673502b45fe 100644 (file)
@@ -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);
 }
 
 /* ================================================== */
index 3b275f39da88bec120b449721f8789cc77df2b2d..54ea02499274cc9fcbe56f36aa30526d5cd11a9d 100644 (file)
@@ -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 */);
 
 }
 
index e9192aa43034acf09590d0ac7a34b394e9a1a909..33b45c12ace44586ff7d5d98da6cd5d642e6cd22 100644 (file)
@@ -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 */
index 49ebf6740a4a4ca2cef08a4ce7e3d72b638bef19..ac3b25bb09f0185da50f0b66207be66afefffac3 100644 (file)
@@ -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 */
index 9571ec460026b93b9e07451c411bec1e0c3e32c6..3481e3c8eca8fe9eb813ad6bc601fca35337806a 100644 (file)
@@ -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
 
index 7e44c6a937ac54b63459f645d52e85ca8b999896..d7d59b16b8c63c9c18a695b182f273006293112e 100644 (file)
@@ -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 */