]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
reference: get TAI-UTC offset from leap second timezone
authorMiroslav Lichvar <mlichvar@redhat.com>
Thu, 29 Jun 2017 15:56:16 +0000 (17:56 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Fri, 30 Jun 2017 14:58:53 +0000 (16:58 +0200)
Use the timezone specified by the leapsectz directive to get the
current TAI-UTC offset and set the offset of the system clock in order
to provide correct TAI time to applications using ntp_adjtime(),
ntp_gettime(), or clock_gettime(CLOCK_TAI).

doc/chrony.conf.adoc
reference.c

index c9cd9a40ed14f39fdf6891ad08ef93a0027f0f10..ed81d406600074b6298e254c887eefd31e264b97 100644 (file)
@@ -830,15 +830,23 @@ clients to safely synchronise with multiple identically configured leap
 smearing servers.
 
 [[leapsectz]]*leapsectz* _timezone_::
-This 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 the times 23:59:59 and 23:59:60 are valid
-on Jun 30 and Dec 31 in the timezone. This typically works with the *right/UTC*
-timezone.
+This directive specifies a timezone in the system tz database which *chronyd*
+can use to determine when will the next leap second occur and what is the
+current offset between TAI and UTC. It will periodically check if 23:59:59 and
+23:59:60 are valid times in the timezone. This typically works with the
+_right/UTC_ timezone.
 +
-This directive is mainly useful with reference clocks which do not provide
-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.
+When a leap second is announced, the timezone needs to be updated at least 12
+hours before the leap second. It is not necessary to restart *chronyd*.
++
+This directive is useful with reference clocks and other time sources which do
+not announce leap seconds, or announce them too late for an NTP server to
+forward them to its own clients. Clients of leap smearing servers must not
+use this directive.
++
+It is also useful when the system clock is required to have correct TAI-UTC
+offset. Note that the offset is set only when leap seconds are handled by the
+kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*.
 +
 An example of the directive is:
 +
@@ -2325,7 +2333,7 @@ The amount of memory used for logging client accesses can be increased in order
 to enable clients to use the interleaved mode even when the server has a large
 number of clients, and better support rate limiting if it is enabled by the
 <<ratelimit,*ratelimit*>> directive. The system timezone database, if it is
-kept up to date and includes the *right/UTC* timezone, can be used as a
+kept up to date and includes the _right/UTC_ timezone, can be used as a
 reliable source to determine when a leap second will be applied to UTC. The
 *-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which
 *chronyd* will not be able to serve time to its clients when it needs to be
index aa151d1d51677a06a779dc0a18c5b83226ccb582..488e58c7f3878732ad86a1316bc2a7aeb67f8a54 100644 (file)
@@ -49,6 +49,7 @@ static int local_orphan;
 static double local_distance;
 static NTP_Leap our_leap_status;
 static int our_leap_sec;
+static int our_tai_offset;
 static int our_stratum;
 static uint32_t our_ref_id;
 static IPAddr our_ref_ip;
@@ -139,7 +140,7 @@ static double last_ref_update_interval;
 
 /* ================================================== */
 
-static NTP_Leap get_tz_leap(time_t when);
+static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
 static void update_leap_status(NTP_Leap leap, time_t now, int reset);
 
 /* ================================================== */
@@ -179,11 +180,13 @@ REF_Initialise(void)
   FILE *in;
   double file_freq_ppm, file_skew_ppm;
   double our_frequency_ppm;
+  int tai_offset;
 
   mode = REF_ModeNormal;
   are_we_synchronised = 0;
   our_leap_status = LEAP_Unsynchronised;
   our_leap_sec = 0;
+  our_tai_offset = 0;
   initialised = 1;
   our_root_dispersion = 1.0;
   our_root_delay = 1.0;
@@ -241,8 +244,8 @@ REF_Initialise(void)
   leap_tzname = CNF_GetLeapSecTimezone();
   if (leap_tzname) {
     /* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
-    if (get_tz_leap(1341014400) == LEAP_InsertSecond &&
-        get_tz_leap(1356912000) == LEAP_Normal) {
+    if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
+        get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
       LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
     } else {
       LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
@@ -613,14 +616,18 @@ is_leap_second_day(struct tm *stm) {
 /* ================================================== */
 
 static NTP_Leap
-get_tz_leap(time_t when)
+get_tz_leap(time_t when, int *tai_offset)
 {
   static time_t last_tz_leap_check;
   static NTP_Leap tz_leap;
+  static int tz_tai_offset;
+
   struct tm stm;
   time_t t;
   char *tz_env, tz_orig[128];
 
+  *tai_offset = tz_tai_offset;
+
   /* Do this check at most twice a day */
   when = when / (12 * 3600) * (12 * 3600);
   if (last_tz_leap_check == when)
@@ -628,12 +635,10 @@ get_tz_leap(time_t when)
 
   last_tz_leap_check = when;
   tz_leap = LEAP_Normal;
+  tz_tai_offset = 0;
 
   stm = *gmtime(&when);
 
-  if (!is_leap_second_day(&stm))
-    return tz_leap;
-
   /* Temporarily switch to the timezone containing leap seconds */
   tz_env = getenv("TZ");
   if (tz_env) {
@@ -644,6 +649,11 @@ get_tz_leap(time_t when)
   setenv("TZ", leap_tzname, 1);
   tzset();
 
+  /* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
+  t = mktime(&stm);
+  if (t != -1)
+    tz_tai_offset = t - when + 10;
+
   /* Set the time to 23:59:60 and see how it overflows in mktime() */
   stm.tm_sec = 60;
   stm.tm_min = 59;
@@ -665,6 +675,8 @@ get_tz_leap(time_t when)
   else if (stm.tm_sec == 1)
     tz_leap = LEAP_DeleteSecond;
 
+  *tai_offset = tz_tai_offset;
+
   return tz_leap;
 }
 
@@ -675,10 +687,13 @@ leap_end_timeout(void *arg)
 {
   leap_timeout_id = 0;
   leap_in_progress = 0;
+
+  if (our_tai_offset)
+    our_tai_offset += our_leap_sec;
   our_leap_sec = 0;
 
   if (leap_mode == REF_LeapModeSystem)
-    LCL_SetSystemLeap(our_leap_sec, 0);
+    LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
 
   if (our_leap_status == LEAP_InsertSecond ||
       our_leap_status == LEAP_DeleteSecond)
@@ -752,12 +767,17 @@ set_leap_timeout(time_t now)
 static void
 update_leap_status(NTP_Leap leap, time_t now, int reset)
 {
-  int leap_sec;
+  NTP_Leap tz_leap;
+  int leap_sec, tai_offset;
 
   leap_sec = 0;
+  tai_offset = 0;
 
-  if (leap_tzname && now && leap == LEAP_Normal)
-    leap = get_tz_leap(now);
+  if (leap_tzname && now) {
+    tz_leap = get_tz_leap(now, &tai_offset);
+    if (leap == LEAP_Normal)
+      leap = tz_leap;
+  }
 
   if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
     /* Check that leap second is allowed today */
@@ -773,12 +793,14 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
     }
   }
   
-  if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
+  if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
+      && !REF_IsLeapSecondClose()) {
     our_leap_sec = leap_sec;
+    our_tai_offset = tai_offset;
 
     switch (leap_mode) {
       case REF_LeapModeSystem:
-        LCL_SetSystemLeap(our_leap_sec, 0);
+        LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
         /* Fall through */
       case REF_LeapModeSlew:
       case REF_LeapModeStep: