]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[bug 2326] use time-to-live of leap second table for better diagnostics
authorJuergen Perlinger <perlinger@ntp.org>
Sun, 17 Nov 2013 13:36:40 +0000 (14:36 +0100)
committerJuergen Perlinger <perlinger@ntp.org>
Sun, 17 Nov 2013 13:36:40 +0000 (14:36 +0100)
bk: 5288c668LdoAGMxz9Y-YBQrHfm8WJA

ntpd/ntp_leapsec.c
ntpd/ntp_leapsec.h
ntpd/ntp_util.c
tests/ntpd/leapsec.cpp

index 458ae5d9eda00a9951d35e9fc7f364a5a578e6d2..22e2bd9c78c0614c3da9c7ba56c05d9907bf07b0 100644 (file)
@@ -100,12 +100,12 @@ strtouv64(
                 * multiplication. Slow but portable.
                 */ 
                {
-                       u_int32 accu;
-                       accu       = (u_int32)res.W_s.ll * base;
-                       res.W_s.ll = (u_short)accu;
+                       uint32_t accu;
+                       accu       = (uint32_t)res.W_s.ll * base;
+                       res.W_s.ll = (uint16_t)accu;
                        accu       = (accu >> 16)
-                                  + (u_int32)res.W_s.lh * base;
-                       res.W_s.lh = (u_short)accu;
+                                  + (uint32_t)res.W_s.lh * base;
+                       res.W_s.lh = (uint16_t)accu;
                        /* the upper bits can be done in one step: */
                        res.D_s.hi = res.D_s.hi * base + (accu >> 16);
                }
@@ -162,10 +162,44 @@ int ucmpv64(
        return res;
 }
 
+#if 0
+static vint64
+addv64(
+    const vint64 *lhs,
+    const vint64 *rhs)
+{
+       vint64 res;
+
+#if defined(HAVE_INT64)
+       res.Q_s = lhs->Q_s + rhs->Q_s;
+#else
+       res = *lhs;
+       M_ADD(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo);
+#endif
+       return res;
+}
+#endif
+
+static vint64
+subv64(
+    const vint64 *lhs,
+    const vint64 *rhs)
+{
+       vint64 res;
+
+#if defined(HAVE_INT64)
+       res.Q_s = lhs->Q_s - rhs->Q_s;
+#else
+       res = *lhs;
+       M_SUB(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo);
+#endif
+       return res;
+}
+
 static vint64
 addv64i32(
        const vint64 * lhs,
-       int32          rhs)
+       int32_t        rhs)
 {
        vint64 res;
 
@@ -182,7 +216,7 @@ addv64i32(
 static vint64
 subv64i32(
        const vint64 * lhs,
-       int32          rhs)
+       int32_t        rhs)
 {
        vint64 res;
 
@@ -200,7 +234,7 @@ subv64i32(
 static vint64
 addv64u32(
        const vint64 * lhs,
-       u_int32        rhs)
+       uint32_t       rhs)
 {
        vint64 res;
 
@@ -217,7 +251,7 @@ addv64u32(
 static vint64
 subv64u32(
        const vint64 * lhs,
-       u_int32        rhs)
+       uint32_t       rhs)
 {
        vint64 res;
 
@@ -264,24 +298,25 @@ ntpcal_date_to_ntp64(
 #define MAX_HIST 10    /* history of leap seconds */
 
 struct leap_info {
-       vint64  ttime;  /* transition time (after the step, ntp scale)  */
-       u_int32 stime;  /* schedule limit (a month before transition)   */
-       short   taiof;  /* TAI offset on and after the transition       */
-       u_short dynls;  /* dynamic: inserted on peer/clock request      */
+       vint64   ttime; /* transition time (after the step, ntp scale) */
+       uint32_t stime; /* schedule limit (a month before transition)  */
+       int16_t  taiof; /* TAI offset on and after the transition      */
+       uint8_t  dynls; /* dynamic: inserted on peer/clock request     */
 };
 typedef struct leap_info leap_info_t;
 
 struct leap_head {
-       vint64  expire; /* table expiration time                       */
-       u_short size;   /* number of infos in table                    */
-       short   base_tai;       /* total leaps before first entry      */
-       short   this_tai;       /* current TAI offset                  */
-       short   next_tai;       /* TAI offset after 'when'             */
-       vint64  dtime;  /* due time (current era end)                  */
-       vint64  ttime;  /* nominal transition time (next era start)    */
-       vint64  stime;  /* schedule time (when we take notice)         */
-       vint64  ebase;  /* base time of this leap era                  */
-       short   dynls;  /* next leap is dynamic (by peer request)      */
+       vint64   update; /* time of information update                 */
+       vint64   expire; /* table expiration time                      */
+       uint16_t size;   /* number of infos in table                   */
+       int16_t  base_tai;      /* total leaps before first entry      */
+       int16_t  this_tai;      /* current TAI offset                  */
+       int16_t  next_tai;      /* TAI offset after 'when'             */
+       vint64   dtime;  /* due time (current era end)                 */
+       vint64   ttime;  /* nominal transition time (next era start)   */
+       vint64   stime;  /* schedule time (when we take notice)        */
+       vint64   ebase;  /* base time of this leap era                 */
+       uint8_t  dynls;  /* next leap is dynamic (by peer request)     */
 };
 typedef struct leap_head leap_head_t;
 
@@ -301,7 +336,7 @@ static char * get_line(leapsec_reader, void*, char*, size_t);
 static char * skipws(const char*);
 static int    parsefail(const char * cp, const char * ep);
 static void   reload_limits(leap_table_t*, const vint64*);
-static int    betweenu32(u_int32, u_int32, u_int32);
+static int    betweenu32(uint32_t, uint32_t, uint32_t);
 static void   reset_times(leap_table_t*);
 static int    leapsec_add(leap_table_t*, const vint64*, int);
 static int    leapsec_raw(leap_table_t*, const vint64 *, int, int);
@@ -373,21 +408,6 @@ leapsec_clear(
        reset_times(pt);
 }
 
-/* ---------------------------------------------------------------------
- * Check if expired at a given time
- */
-int/*BOOL*/
-leapsec_is_expired(
-       leap_table_t * pt  ,
-       u_int32        when,
-       const time_t * tpiv)
-{
-       vint64 limit;
-
-       limit = ntpcal_ntp_to_ntp(when, tpiv);
-       return ucmpv64(&limit, &pt->head.expire) >= 0;
-}
-
 /* ---------------------------------------------------------------------
  * Load a leap second file and check expiration on the go
  */
@@ -413,12 +433,17 @@ leapsec_load(
                cp = linebuf;
                if (*cp == '#') {
                        cp++;
-                       if (*cp == '@' || *cp == '$') {
+                       if (*cp == '@') {
                                cp = skipws(cp+1);
                                pt->head.expire = strtouv64(cp, &ep, 10);
                                if (parsefail(cp, ep))
                                        goto fail_read;
                                pt->lsig.etime = pt->head.expire.D_s.lo;
+                       } else if (*cp == '$') {
+                               cp = skipws(cp+1);
+                               pt->head.update = strtouv64(cp, &ep, 10);
+                               if (parsefail(cp, ep))
+                                       goto fail_read;
                        }                   
                } else if (isdigit((u_char)*cp)) {
                        ttime = strtouv64(cp, &ep, 10);
@@ -434,10 +459,10 @@ leapsec_load(
                                                 taiof, FALSE))
                                        goto fail_insn;
                        } else {
-                               pt->head.base_tai = (short)taiof;
+                               pt->head.base_tai = (int16_t)taiof;
                        }
                        pt->lsig.ttime = ttime.D_s.lo;
-                       pt->lsig.taiof = (short)taiof;
+                       pt->lsig.taiof = (int16_t)taiof;
                }
        }
        return TRUE;
@@ -489,12 +514,12 @@ leapsec_dump(
 int/*BOOL*/
 leapsec_query(
        leap_result_t * qr   ,
-       u_int32         ts32 ,
+       uint32_t        ts32 ,
        const time_t *  pivot)
 {
        leap_table_t *   pt;
        vint64           ts64, last, next;
-       u_int32          due32;
+       uint32_t         due32;
        int              fired;
 
        /* preset things we use later on... */
@@ -519,8 +544,8 @@ leapsec_query(
                 * both modes is easier to maintain.
                 */
                last = pt->head.ttime;
-               qr->warped = (short)(last.D_s.lo -
-                                        pt->head.dtime.D_s.lo);
+               qr->warped = (int16_t)(last.D_s.lo -
+                                      pt->head.dtime.D_s.lo);
                next = addv64i32(&ts64, qr->warped);
                reload_limits(pt, &next);
                fired = ucmpv64(&pt->head.ebase, &last) == 0;
@@ -615,18 +640,38 @@ leapsec_getsig(
 /* ------------------------------------------------------------------ */
 int/*BOOL*/
 leapsec_expired(
-       u_int32        when,
+       uint32_t       when,
        const time_t * tpiv)
 {
-       return leapsec_is_expired(leapsec_get_table(FALSE), when, tpiv);
+       const leap_table_t * pt;
+       vint64 limit;
+
+       pt = leapsec_get_table(FALSE);
+       limit = ntpcal_ntp_to_ntp(when, tpiv);
+       return ucmpv64(&limit, &pt->head.expire) >= 0;
+}
+
+/* ------------------------------------------------------------------ */
+int32_t
+leapsec_daystolive(
+       uint32_t       when,
+       const time_t * tpiv)
+{
+       const leap_table_t * pt;
+       vint64 limit;
+
+       pt = leapsec_get_table(FALSE);
+       limit = ntpcal_ntp_to_ntp(when, tpiv);
+       limit = subv64(&pt->head.expire, &limit);
+       return ntpcal_daysplit(&limit).hi;
 }
 
 /* ------------------------------------------------------------------ */
 int/*BOOL*/
 leapsec_add_fix(
        int            total,
-       u_int32        ttime,
-       u_int32        etime,
+       uint32_t       ttime,
+       uint32_t       etime,
        const time_t * pivot)
 {
        time_t         tpiv;
@@ -648,7 +693,7 @@ leapsec_add_fix(
 
        pt->lsig.etime = etime;
        pt->lsig.ttime = ttime;
-       pt->lsig.taiof = (short)total;
+       pt->lsig.taiof = (int16_t)total;
 
        pt->head.expire = et64;
 
@@ -659,7 +704,7 @@ leapsec_add_fix(
 int/*BOOL*/
 leapsec_add_dyn(
        int            insert,
-       u_int32        ntpnow,
+       uint32_t       ntpnow,
        const time_t * pivot )
 {
        leap_table_t * pt;
@@ -930,7 +975,7 @@ leapsec_raw(
        stime    = ntpcal_date_to_ntp64(&fts);
        li.ttime = *ttime;
        li.stime = ttime->D_s.lo - stime.D_s.lo;
-       li.taiof = (short)taiof;
+       li.taiof = (int16_t)taiof;
        li.dynls = (dynls != 0);
        return add_range(pt, &li);
 }
@@ -941,9 +986,9 @@ leapsec_raw(
  */
 static int/*BOOL*/
 betweenu32(
-       u_int32 lo,
-       u_int32 x,
-       u_int32 hi)
+       uint32_t lo,
+       uint32_t x,
+       uint32_t hi)
 {
        int rc;
        if (lo <= hi)
index bea02d99095e9bee352f8b50b296f0c2dee434ea..f2442c224bca757a96a4cc941ce56b3f1c33d4ac 100644 (file)
@@ -58,7 +58,7 @@ extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on);
  * 'ddist' is the distance to the transition, in clock seconds.
  *      This is the distance to the due time, which is different
  *      from the transition time if the mode is non-electric.
- *     Only valid if 'tai_diff' not zero.
+ *     Only valid if 'tai_diff' is not zero.
  * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always valid.
  * 'tai_diff' is the change in TAI offset after the next leap
  *     transition. Zero if nothing is pending or too far ahead.
@@ -70,20 +70,20 @@ extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on);
  * 'dynamic' != 0 if entry was requested by clock/peer
  */ 
 struct leap_result {
-       vint64          ttime;
-       u_int32         ddist;
-       short           tai_offs;
-       short           tai_diff;
-       short           warped;
-       u_short         proximity;
-       short           dynamic;
+       vint64   ttime;
+       uint32_t ddist;
+       int16_t  tai_offs;
+       int16_t  tai_diff;
+       int16_t  warped;
+       uint8_t  proximity;
+       uint8_t  dynamic;
 };
 typedef struct leap_result leap_result_t;
 
 struct leap_signature {
-       u_int32 etime;  /* expiration time      */
-       u_int32 ttime;  /* transition time      */
-       short   taiof;  /* total offset to TAI  */
+       uint32_t etime; /* expiration time      */
+       uint32_t ttime; /* transition time      */
+       int16_t  taiof; /* total offset to TAI  */
 };
 typedef struct leap_signature leap_signature_t;
 
@@ -93,7 +93,7 @@ typedef struct leap_signature leap_signature_t;
 #define LSPROX_ANNOUNCE        2       /* less than 1 day to target  */
 #define LSPROX_ALERT   3       /* less than 10 sec to target */
 
-/* Get the current or alternate table ponter. Getting the alternate
+/* Get the current or alternate table pointer. Getting the alternate
  * pointer will automatically copy the primary table, so it can be
  * subsequently modified.
  */
@@ -108,12 +108,6 @@ extern int/*BOOL*/ leapsec_set_table(leap_table_t *);
 /* Clear all leap second data. Use it for init & cleanup */
 extern void leapsec_clear(leap_table_t*);
 
-/* Check if a table is expired at a given point in time. 'when' is
- * subject to NTP era unfolding.
- */
-extern int/*BOOL*/ leapsec_is_expired(leap_table_t*, u_int32 when, 
-                                     const time_t * pivot);
-
 /* Load a leap second file. If 'blimit' is set, do not store (but
  * register with their TAI offset) leap entries before the build date.
  * Update the leap signature data on the fly.
@@ -140,7 +134,13 @@ extern void        leapsec_getsig(leap_signature_t * psig);
 
 /* Check if the leap table is expired at the given time.
  */
-extern int/*BOOL*/ leapsec_expired(u_int32 when, const time_t * pivot);
+extern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot);
+
+/* Get the distance to expiration in days.
+ * Returns negative values if expired, zero if there are less than 24hrs
+ * left, and positive numbers otherwise.
+ */
+extern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot);
 
 /* Reset the current leap frame, so the next query will do proper table
  * lookup from fresh. Suppresses a possible leap era transition detection
@@ -153,7 +153,7 @@ extern void leapsec_reset_frame(void);
  * works if the existing table is extended. On success, updates the
  * signature data.
  */
-extern int/*BOOL*/ leapsec_add_fix(int offset, u_int32 ttime, u_int32 etime,
+extern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime,
                                   const time_t * pivot);
 
 /* Take a time stamp and create a leap second frame for it. This will
@@ -168,7 +168,7 @@ extern int/*BOOL*/ leapsec_add_fix(int offset, u_int32 ttime, u_int32 etime,
  * 'ntp_now' is subject to era unfolding. The entry is marked
  * dynamic. The leap signature is NOT updated.
  */
-extern int/*BOOL*/ leapsec_add_dyn(int insert, u_int32 ntp_now,
+extern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now,
                                   const time_t * pivot);
 
 /* Take a time stamp and get the associated leap information. The time
@@ -178,7 +178,7 @@ extern int/*BOOL*/ leapsec_add_dyn(int insert, u_int32 ntp_now,
  * last and the current query. In that case, qr->warped contains the
  * required clock stepping, which is always zero in electric mode.
  */
-extern int/*BOOL*/ leapsec_query(leap_result_t *qr, u_int32 ntpts,
+extern int/*BOOL*/ leapsec_query(leap_result_t *qr, uint32_t ntpts,
                                 const time_t * pivot);
 
 /* Get the current leap frame info. Returns TRUE if the result contains
index 9f538c1de9611d91819d6cab1df33d3977e6e428..185f4b0e7c41173e5007db9a8368e0306fb29803 100644 (file)
@@ -864,8 +864,8 @@ record_timing_stats(
  *
  * Returns:
  *     -1 if there was a problem,
- *      0 if the leapfile has expired
- *     >0 # of days until the leapfile expires
+ *      0 if the leapfile has expired or less than 24hrs remaining TTL
+ *     >0 # of full days until the leapfile expires
  */
 int
 check_leap_file(
@@ -875,37 +875,39 @@ check_leap_file(
        FILE *fp;
        struct stat *sp1 = &leapseconds_file_sb1;
        struct stat *sp2 = &leapseconds_file_sb2;
-       int rc;
+       int          rc  = INT_MAX; /* assume not expired for long a time */
+       l_fp         now;
+
 
        if (leapseconds_file) {
+               get_systime(&now);
                if ((fp = fopen(leapseconds_file, "r")) == NULL) {
                        msyslog(LOG_ERR,
-                           "check_leap_file: fopen(%s): %m",
-                           leapseconds_file);
-                       return -1;
-               }
-               if (fstat(fileno(fp), &leapseconds_file_sb2)) {
+                               "check_leap_file: fopen(%s): %m",
+                               leapseconds_file);
+                       rc = -1;
+               } else if (fstat(fileno(fp), &leapseconds_file_sb2)) {
                        msyslog(LOG_ERR,
-                           "check_leap_file: stat(%s): %m",
-                           leapseconds_file);
-                       fclose(fp);
-                       return -1;
-               }
-               if (   (sp1->st_mtime != sp2->st_mtime)
-                   || (sp1->st_ctime != sp2->st_ctime)) {
+                               "check_leap_file: stat(%s): %m",
+                               leapseconds_file);
+                       rc = -1;
+               } else if (  (sp1->st_mtime != sp2->st_mtime)
+                         || (sp1->st_ctime != sp2->st_ctime)) {
                        leapseconds_file_sb1 = leapseconds_file_sb2;
                        if (!leapsec_load_file(fp, TRUE)) {
                                msyslog(LOG_ERR,
-                                   "format error leapseconds file %s",
-                                   leapseconds_file);
+                                       "format error leapseconds file %s",
+                                       leapseconds_file);
                                rc = -1;
-                       } else {
-                               rc = 1; /* XXX: 0 or days til expire */
                        }
-               } else {
-                       rc = 0; /* XXX: 0 or days til expire */
                }
-               fclose(fp);
+               if (rc >= 0) {
+                       rc = leapsec_daystolive(now.l_ui, NULL);
+                       if (rc < 0)
+                               rc = 0;
+               }
+               if (fp != NULL)
+                       fclose(fp);
        }
 
        return rc;
index e8abd28cbae213e86b074c6023a5510cea5d6121..2fc528e7e03dc7ccbe4a60e89a8f7d57a6008998 100644 (file)
@@ -256,7 +256,7 @@ TEST_F(leapsecTest, tableSelect) {
        EXPECT_NE(pt2, pt3);
 }
 
-// load file & checl expiration
+// load file & check expiration
 TEST_F(leapsecTest, loadFileExpire) {
        const char *cp = leap1;
        int rc;
@@ -271,6 +271,33 @@ TEST_F(leapsecTest, loadFileExpire) {
        EXPECT_EQ(1, rc);
 }
 
+// load file & check time-to-live
+TEST_F(leapsecTest, loadFileTTL) {
+       const char *cp = leap1;
+       int rc;
+       leap_table_t * pt = leapsec_get_table(0);
+       time_t         pivot = 0x70000000;
+
+       const uint32_t limit = 3610569600u;
+
+       rc =   leapsec_load(pt, stringreader, &cp, FALSE)
+           && leapsec_set_table(pt);
+       ASSERT_EQ(1, rc);
+
+       // exactly 1 day to live
+       rc = leapsec_daystolive(limit - 86400, &pivot);
+       EXPECT_EQ( 1, rc);      
+       // less than 1 day to live
+       rc = leapsec_daystolive(limit - 86399, &pivot);
+       EXPECT_EQ( 0, rc);      
+       // hit expiration exactly
+       rc = leapsec_daystolive(limit, &pivot);
+       EXPECT_EQ( 0, rc);      
+       // expired since 1 sec
+       rc = leapsec_daystolive(limit + 1, &pivot);
+       EXPECT_EQ(-1, rc);      
+}
+
 // ad-hoc jump: leap second at 2009.01.01 -60days
 TEST_F(leapsecTest, ls2009faraway) {
        int            rc;