]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Add support for reading leap second data from tz database
authorMiroslav Lichvar <mlichvar@redhat.com>
Thu, 23 Feb 2012 17:48:24 +0000 (18:48 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Fri, 24 Feb 2012 10:06:20 +0000 (11:06 +0100)
leapsectz directive is used to set the name of the timezone in the
system tz database which chronyd can use to find out when will the next
leap second occur.  It will periodically check if dates Jun 30 23:59:60
and Dec 31 23:59:60 are valid in that timezone. This is mainly useful
with reference clocks which don't provide the leap second information.
It is not necessary to restart chronyd if the tz database is updated
with a new leap second at least 12 hours before the event.

chrony.texi
conf.c
conf.h
reference.c

index 9b75eff6fefc04e5dfe750a499e4d9faf01fa5ae..b09da9266ff30475af763a2bbfe28438761e5b0c 100644 (file)
@@ -1183,6 +1183,7 @@ directives can occur in any order in the file.
 * include directive::           Include a configuration file
 * initstepslew directive::      Trim the system clock on boot-up.
 * keyfile directive::           Specify location of file containing keys
+* leapsectz directive::         Read leap second data from tz database
 * linux_hz directive::          Define a non-standard value of the kernel HZ constant
 * linux_freq_scale directive::  Define a non-standard value to compensate the kernel frequency bias
 * local directive::             Allow unsynchronised machine to act as server
@@ -1744,6 +1745,25 @@ prefix.
 
 The ID for the chronyc authentication key is specified with the
 commandkey command (see earlier).
+@c }}}
+@c {{{ leapsectz
+@node leapsectz directive
+@subsection leapsectz
+This directive is used to set the name of the timezone in the system
+tz database which @code{chronyd} can use to find out when will the
+next leap second occur.  It will periodically check if the times
+23:59:59 and 23:59:60 are valid on Jun 30 and Dec 31 in the timezone.
+This is mainly useful with reference clocks which don't provide the
+leap second information.  It is not necessary to restart
+@code{chronyd} if the tz database is updated with a new leap second at
+least 12 hours before the event.
+
+An example of the command is
+
+@example
+leapsectz right/UTC
+@end example
+
 @c }}}
 @c {{{ local
 @node local directive
@@ -1894,9 +1914,9 @@ expressed in UTC, not the local time zone.
 IP address of server/peer from which measurement comes [158.152.1.76]
 @item
 Leap status (@code{N} means normal, @code{+} means that the last minute
-of today has 61 seconds, @code{-} means that the last minute of the day
-has 59 seconds, @code{?} means the remote computer is not currently
-synchronised.) [N]
+of the current month has 61 seconds, @code{-} means that the last minute
+of the month has 59 seconds, @code{?} means the remote computer is not
+currently synchronised.) [N]
 @item
 Stratum of remote computer. [2]
 @item
@@ -2110,8 +2130,8 @@ Sequence number of driver poll within one polling interval for raw
 samples, or @code{-} for filtered samples. [7]
 @item
 Leap status (@code{N} means normal, @code{+} means that the last minute
-of today has 61 seconds, @code{-} means that the last minute of the day
-has 59 seconds). [N]
+of the current month has 61 seconds, @code{-} means that the last minute
+of the month has 59 seconds). [N]
 @item
 Flag indicating whether the sample comes from PPS source. (1 for yes,
 0 for no, or @code{-} for filtered sample). [1]
diff --git a/conf.c b/conf.c
index bbd890c417bb2a6953fced393b3890060efd55e2..460b42f2ba4c8f746fb9479e75f80f1bcc57713e 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -112,6 +112,7 @@ static void parse_sched_priority(const char *);
 static void parse_lockall(const char *);
 static void parse_tempcomp(const char *);
 static void parse_include(const char *);
+static void parse_leapsectz(const char *);
 
 /* ================================================== */
 /* Configuration variables */
@@ -224,6 +225,9 @@ static double linux_freq_scale;
 static int sched_priority = 0;
 static int lock_memory = 0;
 
+/* Name of a system timezone containing leap seconds occuring at midnight */
+static char *leapsec_tz = NULL;
+
 /* ================================================== */
 
 typedef struct {
@@ -276,6 +280,7 @@ static const Command commands[] = {
   {"reselectdist", 12, parse_reselectdist},
   {"stratumweight", 13, parse_stratumweight},
   {"include", 7, parse_include},
+  {"leapsectz", 9, parse_leapsectz},
   {"linux_hz", 8, parse_linux_hz},
   {"linux_freq_scale", 16, parse_linux_freq_scale},
   {"sched_priority", 14, parse_sched_priority},
@@ -1282,6 +1287,16 @@ parse_include(const char *line)
 
 /* ================================================== */
 
+static void
+parse_leapsectz(const char *line)
+{
+  /* This must allocate enough space! */
+  leapsec_tz = MallocArray(char, 1 + strlen(line));
+  sscanf(line, "%s", leapsec_tz);
+}
+
+/* ================================================== */
+
 static void
 parse_linux_hz(const char *line)
 {
@@ -1707,6 +1722,14 @@ CNF_GetPidFile(void)
 
 /* ================================================== */
 
+char *
+CNF_GetLeapSecTimezone(void)
+{
+  return leapsec_tz;
+}
+
+/* ================================================== */
+
 void
 CNF_GetLinuxHz(int *set, int *hz)
 {
diff --git a/conf.h b/conf.h
index 7f0f6b3e9f35ec126f8ea90bce8e33534ad2d4c1..15d24d7613133f4ce80c72e095b455d9e96dc093 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -69,6 +69,7 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
 extern void CNF_GetBindAddress(int family, IPAddr *addr);
 extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
 extern char *CNF_GetPidFile(void);
+extern char *CNF_GetLeapSecTimezone(void);
 extern void CNF_GetLinuxHz(int *set, int *hz);
 extern void CNF_GetLinuxFreqScale(int *set, double *freq_scale);
 
index 7bf8ec7c9ab1df4342917c6d2fbe8de0dcfe0460..0310d208bb3c7bda37aaae8c5a722d6891bd9b5c 100644 (file)
@@ -89,6 +89,11 @@ static double drift_file_age;
 
 static void update_drift_file(double, double);
 
+/* Name of a system timezone containing leap seconds occuring at midnight */
+static char *leap_tzname;
+static time_t last_tz_leap_check;
+static NTP_Leap tz_leap;
+
 /* ================================================== */
 
 static LOG_FileID logfileid;
@@ -196,6 +201,8 @@ REF_Initialise(void)
 
   enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
 
+  leap_tzname = CNF_GetLeapSecTimezone();
+
   CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
   CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
   CNF_GetLogChange(&do_log_change, &log_change_threshold);
@@ -526,20 +533,82 @@ is_offset_ok(double offset)
 
 /* ================================================== */
 
+static NTP_Leap
+get_tz_leap(time_t when)
+{
+  struct tm stm;
+  time_t t;
+  char *tz_env, tz_orig[128];
+
+  /* Do this check at most twice a day */
+  when = when / (12 * 3600) * (12 * 3600);
+  if (last_tz_leap_check == when)
+      return tz_leap;
+
+  last_tz_leap_check = when;
+  tz_leap = LEAP_Normal;
+
+  stm = *gmtime(&when);
+
+  /* Check for leap second only in the latter half of June and December */
+  if (stm.tm_mon == 5 && stm.tm_mday > 14)
+    stm.tm_mday = 30;
+  else if (stm.tm_mon == 11 && stm.tm_mday > 14)
+    stm.tm_mday = 31;
+  else
+    return tz_leap;
+
+  /* Temporarily switch to the timezone containing leap seconds */
+  tz_env = getenv("TZ");
+  if (tz_env) {
+    if (strlen(tz_env) >= sizeof (tz_orig))
+      return tz_leap;
+    strcpy(tz_orig, tz_env);
+  }
+  setenv("TZ", leap_tzname, 1);
+  tzset();
+
+  /* Set the time to 23:59:60 and see how it overflows in mktime() */
+  stm.tm_sec = 60;
+  stm.tm_min = 59;
+  stm.tm_hour = 23;
+
+  t = mktime(&stm);
+
+  if (tz_env)
+    setenv("TZ", tz_orig, 1);
+  else
+    unsetenv("TZ");
+  tzset();
+
+  if (t == -1)
+    return tz_leap;
+
+  if (stm.tm_sec == 60)
+    tz_leap = LEAP_InsertSecond;
+  else if (stm.tm_sec == 1)
+    tz_leap = LEAP_DeleteSecond;
+
+  return tz_leap;
+}
+
+/* ================================================== */
+
 static void
-update_leap_status(NTP_Leap leap)
+update_leap_status(NTP_Leap leap, time_t now)
 {
-  time_t now;
   struct tm stm;
   int leap_sec;
 
   leap_sec = 0;
 
+  if (leap_tzname && now && leap == LEAP_Normal)
+    leap = get_tz_leap(now);
+
   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) {
@@ -600,7 +669,7 @@ REF_SetReference(int stratum,
   double update_interval;
   double elapsed;
   double correction_rate;
-  struct timeval now;
+  struct timeval now, raw_now;
 
   assert(initialised);
 
@@ -628,7 +697,9 @@ REF_SetReference(int stratum,
     }
   }
     
-  LCL_ReadCookedTime(&now, NULL);
+  LCL_ReadRawTime(&raw_now);
+  LCL_CookTime(&raw_now, &now, NULL);
+
   UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time);
   our_offset = offset + elapsed * frequency;
 
@@ -646,7 +717,7 @@ REF_SetReference(int stratum,
   our_root_delay = root_delay;
   our_root_dispersion = root_dispersion;
 
-  update_leap_status(leap);
+  update_leap_status(leap, raw_now.tv_sec);
 
   if (last_ref_update.tv_sec) {
     UTI_DiffTimevalsToDouble(&update_interval, &now, &last_ref_update);
@@ -827,7 +898,7 @@ REF_SetUnsynchronised(void)
 
   are_we_synchronised = 0;
 
-  update_leap_status(LEAP_Unsynchronised);
+  update_leap_status(LEAP_Unsynchronised, 0);
 }
 
 /* ================================================== */