]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 2250] Cleanup & Win32 integration of the NTPD leapsec module
authorJuergen Perlinger <perlinger@ntp.org>
Thu, 3 Oct 2013 12:55:29 +0000 (14:55 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Thu, 3 Oct 2013 12:55:29 +0000 (14:55 +0200)
fixed handling of leap second removal
trigger logic and windows port for leap seconds

bk: 524d6941KGa3dlvVKJkuEjDJcVd_pQ

include/ntpd.h
ntpd/ntp_crypto.c
ntpd/ntp_leapsec.c
ntpd/ntp_leapsec.h
ntpd/ntp_proto.c
ntpd/ntp_timer.c
ntpd/ntp_util.c
ports/winnt/ntpd/nt_clockstuff.c
ports/winnt/vs2005/ntpd.vcproj
ports/winnt/vs2008/ntpd/ntpd.vcproj
tests/ntpd/leapsec.cpp

index d62b5f0d557a47d5e73e8e8ae405cb3498af320a..d6f259c611cc79a23266752bd2961b9a89738f64 100644 (file)
@@ -223,6 +223,7 @@ extern      void    process_packet  (struct peer *, struct pkt *, u_int);
 extern void    clock_select    (void);
 
 extern u_long  leapsec;        /* seconds to next leap (proximity class) */
+extern  int     leapdif;        /* TAI difference step at next leap second*/
 extern int     sys_orphan;
 extern double  sys_mindisp;
 extern double  sys_maxdist;
index b7e503a4577532ca3bbdeaf106c63f13bdec3d7b..37a34f1cc86f02a2bc32ef8d32922a88a7b87cc6 100644 (file)
@@ -953,9 +953,9 @@ crypto_recv(
                         * than the stored ones, install the new leap
                         * values and recompute the signatures.
                         */
-                       if (leapsec_add_fix(ntohl(ep->pkt[1]),
+                       if (leapsec_add_fix(ntohl(ep->pkt[0]),
+                                           ntohl(ep->pkt[1]),
                                            ntohl(ep->pkt[2]),
-                                           ntohl(ep->pkt[0]),
                                            NULL))
                        {
                                leap_signature_t lsig;
index 32171a090d72badf79ec27b3ecb6b2956532a89f..458ae5d9eda00a9951d35e9fc7f364a5a578e6d2 100644 (file)
@@ -50,17 +50,16 @@ strtouv64(
        int     sig, num;
        const u_char *src;
        
-       num = 0;
+       num = sig = 0;
        src = (const u_char*)begp;
-       while (*src && isspace(*src))
+       while (isspace(*src))
                src++;
 
        if (*src == '-') {
                src++;
                sig = 1;
-       } else {
-               sig = 0;
-               src += (*src == '+');
+       } else  if (*src == '+') {
+               src++;
        }
 
        if (base == 0) {
@@ -84,11 +83,11 @@ strtouv64(
        memset(&res, 0, sizeof(res));
        while (*src) {
                if (isdigit(*src))
-                       digit = (u_char)*src - '0';
+                       digit = *src - '0';
                else if (isupper(*src))
-                       digit = (u_char)*src - 'A' + 10;
+                       digit = *src - 'A' + 10;
                else if (islower(*src))
-                       digit = (u_char)*src - 'a' + 10;
+                       digit = *src - 'a' + 10;
                else
                        break;
                if (digit >= base)
@@ -267,22 +266,22 @@ ntpcal_date_to_ntp64(
 struct leap_info {
        vint64  ttime;  /* transition time (after the step, ntp scale)  */
        u_int32 stime;  /* schedule limit (a month before transition)   */
-       short   total;  /* accumulated leap seconds from that point     */
+       short   taiof;  /* TAI offset on and after the transition       */
        u_short 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       */
-       int32   total;  /* total leaps before first entry */
-       vint64  when;   /* begin of next leap era         */
-       vint64  ttime;  /* nominal transition time        */
-       vint64  stime;  /* announce leapsec 1 month ahead */
-       vint64  base;   /* base of this leap era          */
-       short   this_tai;       /* current TAI offset     */
-       short   next_tai;       /* TAI offset after 'when'*/
-       short   dynls;  /* next leap is dynamic           */
+       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)      */
 };
 typedef struct leap_head leap_head_t;
 
@@ -300,6 +299,7 @@ static int/*BOOL*/  _electric;
 static int    add_range(leap_table_t*, const leap_info_t*);
 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 void   reset_times(leap_table_t*);
@@ -399,8 +399,8 @@ leapsec_load(
        int            use_build_limit)
 {
        char   *cp, *ep, linebuf[50];
-       vint64 tt, limit;
-       int    al;
+       vint64 ttime, limit;
+       long   taiof;
        struct calendar build;
 
        leapsec_clear(pt);
@@ -410,33 +410,34 @@ leapsec_load(
                memset(&limit, 0, sizeof(limit));
 
        while (get_line(func, farg, linebuf, sizeof(linebuf))) {
-               cp = skipws(linebuf);
+               cp = linebuf;
                if (*cp == '#') {
                        cp++;
                        if (*cp == '@' || *cp == '$') {
                                cp = skipws(cp+1);
                                pt->head.expire = strtouv64(cp, &ep, 10);
-                               if (ep == cp || *ep > ' ')
+                               if (parsefail(cp, ep))
                                        goto fail_read;
                                pt->lsig.etime = pt->head.expire.D_s.lo;
                        }                   
-               } else if (isdigit(*cp)) {
-                       tt = strtouv64(cp, &ep, 10);
-                       if (ep == cp || *ep > ' ')
+               } else if (isdigit((u_char)*cp)) {
+                       ttime = strtouv64(cp, &ep, 10);
+                       if (parsefail(cp, ep))
                                goto fail_read;
                        cp = skipws(ep);
-                       al = strtol(cp, &ep, 10);
-                       if (ep == cp || *ep > ' ')
+                       taiof = strtol(cp, &ep, 10);
+                       if (   parsefail(cp, ep)
+                           || taiof > SHRT_MAX || taiof < SHRT_MIN)
                                goto fail_read;
-                       cp = skipws(ep);
-                       if (ucmpv64(&tt, &limit) >= 0) {
-                               if (!leapsec_raw(pt, &tt, al, FALSE))
+                       if (ucmpv64(&ttime, &limit) >= 0) {
+                               if (!leapsec_raw(pt, &ttime,
+                                                taiof, FALSE))
                                        goto fail_insn;
                        } else {
-                               pt->head.total = al;
+                               pt->head.base_tai = (short)taiof;
                        }
-                       pt->lsig.ttime = tt.D_s.lo;
-                       pt->lsig.taiof = al;
+                       pt->lsig.ttime = ttime.D_s.lo;
+                       pt->lsig.taiof = (short)taiof;
                }
        }
        return TRUE;
@@ -477,7 +478,7 @@ leapsec_dump(
                        ttb.year, ttb.month, ttb.monthday,
                        "-*"[pt->info[idx].dynls != 0],
                        atb.year, atb.month, atb.monthday,
-                       pt->info[idx].total);
+                       pt->info[idx].taiof);
        }
 }
 
@@ -492,8 +493,8 @@ leapsec_query(
        const time_t *  pivot)
 {
        leap_table_t *   pt;
-       vint64           ts64, last;
-       u_int32          when32;
+       vint64           ts64, last, next;
+       u_int32          due32;
        int              fired;
 
        /* preset things we use later on... */
@@ -502,21 +503,33 @@ leapsec_query(
        pt    = leapsec_get_table(FALSE);
        memset(qr, 0, sizeof(leap_result_t));
 
-       if (ucmpv64(&ts64, &pt->head.base) < 0) {
-               /* Ooops? Clock step backward? Oh, well... */ 
+       if (ucmpv64(&ts64, &pt->head.ebase) < 0) {
+               /* Most likely after leap frame reset. Could also be a
+                * backstep of the system clock. Anyway, get the new
+                * leap era frame.
+                */
                reload_limits(pt, &ts64);
-       } else if (ucmpv64(&ts64, &pt->head.when) >= 0) {
+       } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0)        {
                /* Boundary crossed in forward direction. This might
                 * indicate a leap transition, so we prepare for that
                 * case.
+                *
+                * Some operations below are actually NOPs in electric
+                * mode, but having only one code path that works for
+                * both modes is easier to maintain.
                 */
                last = pt->head.ttime;
-               qr->warped = last.D_s.lo - pt->head.when.D_s.lo;
-               reload_limits(pt, &ts64);
-               if (ucmpv64(&pt->head.base, &last) == 0)
-                       fired = TRUE;
-               else
+               qr->warped = (short)(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;
+               if (fired) {
+                       ts64 = next;
+                       ts32 = next.D_s.lo;
+               } else {
                        qr->warped = 0;
+               }
        }
 
        qr->tai_offs = pt->head.this_tai;
@@ -526,26 +539,47 @@ leapsec_query(
                return fired;
 
        /* now start to collect the remaing data */
-       when32 = pt->head.when.D_s.lo;
+       due32 = pt->head.dtime.D_s.lo;
 
        qr->tai_diff  = pt->head.next_tai - pt->head.this_tai;
-       qr->when      = pt->head.when;
-       qr->dist      = when32 - ts32;
+       qr->ttime     = pt->head.ttime;
+       qr->ddist     = due32 - ts32;
        qr->dynamic   = pt->head.dynls;
        qr->proximity = LSPROX_SCHEDULE;
 
        /* if not in the last day before transition, we're done. */
-       if (!betweenu32(when32 - SECSPERDAY, ts32, when32))
+       if (!betweenu32(due32 - SECSPERDAY, ts32, due32))
                return fired;
        
        qr->proximity = LSPROX_ANNOUNCE;
-       if (!betweenu32(when32 - 10, ts32, when32))
+       if (!betweenu32(due32 - 10, ts32, due32))
                return fired;
 
+       /* The last 10s before the transition. Prepare for action! */
        qr->proximity = LSPROX_ALERT;
        return fired;
 }
 
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_frame(
+        leap_result_t *qr)
+{
+       const leap_table_t * pt;
+
+        memset(qr, 0, sizeof(leap_result_t));
+       pt = leapsec_get_table(FALSE);
+       if (ucmpv64(&pt->head.ttime, &pt->head.stime) <= 0)
+                return FALSE;
+
+       qr->tai_offs = pt->head.this_tai;
+       qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
+       qr->ttime    = pt->head.ttime;
+       qr->dynamic  = pt->head.dynls;
+
+        return TRUE;
+}
+
 /* ------------------------------------------------------------------ */
 /* Reset the current leap frame */
 void
@@ -590,9 +624,9 @@ leapsec_expired(
 /* ------------------------------------------------------------------ */
 int/*BOOL*/
 leapsec_add_fix(
+       int            total,
        u_int32        ttime,
        u_int32        etime,
-       int            total,
        const time_t * pivot)
 {
        time_t         tpiv;
@@ -608,14 +642,13 @@ leapsec_add_fix(
        tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
        pt   = leapsec_get_table(TRUE);
 
-       if (ucmpv64(&et64, &pt->head.expire) <= 0)
-               return FALSE;
-       if ( ! leapsec_raw(pt, &tt64, total, FALSE))
+       if (   ucmpv64(&et64, &pt->head.expire) <= 0
+          || !leapsec_raw(pt, &tt64, total, FALSE) )
                return FALSE;
 
        pt->lsig.etime = etime;
        pt->lsig.ttime = ttime;
-       pt->lsig.taiof = total;
+       pt->lsig.taiof = (short)total;
 
        pt->head.expire = et64;
 
@@ -625,8 +658,8 @@ leapsec_add_fix(
 /* ------------------------------------------------------------------ */
 int/*BOOL*/
 leapsec_add_dyn(
-       u_int32        ntpnow,
        int            insert,
+       u_int32        ntpnow,
        const time_t * pivot )
 {
        leap_table_t * pt;
@@ -645,17 +678,18 @@ leapsec_add_dyn(
 /* [internal] Reset / init the time window in the leap processor to
  * force reload on next query. Since a leap transition cannot take place
  * at an odd second, the value chosen avoids spurious leap transition
- * triggers. Making all three times equal forces a reload.
+ * triggers. Making all three times equal forces a reload. Using the
+ * maximum value for unsigned 64 bits makes finding the next leap frame
+ * a bit easier.
  */
 static void
 reset_times(
        leap_table_t * pt)
 {
-       pt->head.base.D_s.hi = 0;
-       pt->head.base.D_s.lo = 1;
-       pt->head.stime = pt->head.base;
-       pt->head.ttime = pt->head.base;
-       pt->head.when  = pt->head.base;
+       memset(&pt->head.ebase, 0xFF, sizeof(vint64));
+       pt->head.stime = pt->head.ebase;
+       pt->head.ttime = pt->head.ebase;
+       pt->head.dtime = pt->head.ebase;
 }
 
 /* [internal] Add raw data to the table, removing old entries on the
@@ -670,8 +704,8 @@ add_range(
         * entry. But remember the accumulated leap seconds!
         */
        if (pt->head.size >= MAX_HIST) {
-               pt->head.size  = MAX_HIST - 1;
-               pt->head.total = pt->info[pt->head.size].total;
+               pt->head.size     = MAX_HIST - 1;
+               pt->head.base_tai = pt->info[pt->head.size].taiof;
        }
 
        /* make room in lower end and insert item */
@@ -706,15 +740,22 @@ get_line(
        size_t         size)
 {
        int   ch;
-       char *ptr = buff;
+       char *ptr;
+       
+       /* if we cannot even store the delimiter, declare failure */
+       if (buff == NULL || size == 0)
+               return NULL;
 
+       ptr = buff;
        while (EOF != (ch = (*func)(farg)) && '\n' != ch)
                if (size > 1) {
                        size--;
                        *ptr++ = (char)ch;
                }
-       if (size)
-               *ptr = '\0';
+       /* discard trailing whitespace */
+       while (ptr != buff && isspace((u_char)ptr[-1]))
+               ptr--;
+       *ptr = '\0';
        return (ptr == buff && ch == EOF) ? NULL : buff;
 }
 
@@ -723,12 +764,21 @@ static char *
 skipws(
        const char *ptr)
 {
-       const u_char * src;
+       while (isspace((u_char)*ptr))
+               ptr++;
+       return (char*)noconst(ptr);
+}
 
-       src = (const u_char*)ptr;
-       while (isspace(*src))
-               src++;
-       return (char*)noconst(src);
+/* [internal] check if a strtoXYZ ended at EOL or whistespace and
+ * converted something at all. Return TRUE if something went wrong.
+ */
+static int/*BOOL*/
+parsefail(
+       const char * cp,
+       const char * ep)
+{
+       return (cp == ep)
+           || (*ep && *ep != '#' && !isspace((u_char)*ep));
 }
 
 /* [internal] reload the table limits around the given time stamp. This
@@ -762,23 +812,23 @@ reload_limits(
         * empty -- no undefined condition must arise from this code.
         */
        if (idx >= pt->head.size) {
-               memset(&pt->head.base, 0x00, sizeof(vint64));
-               pt->head.this_tai = pt->head.total;
+               memset(&pt->head.ebase, 0x00, sizeof(vint64));
+               pt->head.this_tai = pt->head.base_tai;
        } else {
-               pt->head.base     = pt->info[idx].ttime;
-               pt->head.this_tai = pt->info[idx].total;
+               pt->head.ebase    = pt->info[idx].ttime;
+               pt->head.this_tai = pt->info[idx].taiof;
        }
        if (--idx >= 0) {
-               pt->head.next_tai = pt->info[idx].total;
+               pt->head.next_tai = pt->info[idx].taiof;
                pt->head.dynls    = pt->info[idx].dynls;
                pt->head.ttime    = pt->info[idx].ttime;
 
-               if ( ! _electric)
-                       pt->head.when = addv64i32(
+               if (_electric)
+                       pt->head.dtime = pt->head.ttime;
+                else
+                       pt->head.dtime = addv64i32(
                                &pt->head.ttime,
                                pt->head.next_tai - pt->head.this_tai);
-               else
-                       pt->head.when = pt->head.ttime;
                
                pt->head.stime = subv64u32(
                        &pt->head.ttime, pt->info[idx].stime);
@@ -786,7 +836,7 @@ reload_limits(
        } else {
                memset(&pt->head.ttime, 0xFF, sizeof(vint64));
                pt->head.stime    = pt->head.ttime;
-               pt->head.when     = pt->head.ttime;
+               pt->head.dtime    = pt->head.ttime;
                pt->head.next_tai = pt->head.this_tai;
                pt->head.dynls    = 0;
        }
@@ -842,7 +892,7 @@ leapsec_add(
 
        li.ttime = ttime;
        li.stime = ttime.D_s.lo - stime.D_s.lo;
-       li.total = (pt->head.size ? pt->info[0].total : pt->head.total)
+       li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai)
                 + (insert ? 1 : -1);
        li.dynls = 1;
        return add_range(pt, &li);
@@ -857,7 +907,7 @@ int/*BOOL*/
 leapsec_raw(
        leap_table_t * pt,
        const vint64 * ttime,
-       int            total,
+       int            taiof,
        int            dynls)
 {
        vint64          stime;
@@ -880,7 +930,7 @@ leapsec_raw(
        stime    = ntpcal_date_to_ntp64(&fts);
        li.ttime = *ttime;
        li.stime = ttime->D_s.lo - stime.D_s.lo;
-       li.total = (short)total;
+       li.taiof = (short)taiof;
        li.dynls = (dynls != 0);
        return add_range(pt, &li);
 }
index fd76d90870162f2f6c1851ec23a4f4f828cf0536..bea02d99095e9bee352f8b50b296f0c2dee434ea 100644 (file)
@@ -38,7 +38,8 @@ typedef struct leap_table leap_table_t;
  * step forward untill *we* (that is, this module) tells the client app
  * to step at the right time. This needs a slightly different type of
  * processing, so switching between those two modes should not be done
- * close to a leap second. The transition might be lost in that case.
+ * too close to a leap second. The transition might be lost in that
+ * case. (The limit is actual 2 sec before transition.)
  *
  * OTOH, this is a system characteristic, so it's expected to be set
  * properly somewhere after system start and retain the value.
@@ -51,23 +52,26 @@ extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on);
 
 
 /* Query result for a leap second schedule
- * 'when' tells the transition point in full time scale, but only if
- *     'tai_diff' is not zero. 
- * 'dist' is the distance to the transition, in clock seconds.
- *     Only valid if 'tai_diff' not zero. To get the true (elapsed
- *     time) difference, add 'tai_diff' to that value.
- * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI.
+ * 'ttime' is the transition point in full time scale, but only if
+ *     'tai_diff' is not zero. Nominal UTC time when the next leap
+ *      era starts.
+ * '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.
+ * '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.
+ *     transition. Zero if nothing is pending or too far ahead.
  * 'warped' is set only once, when the the leap second occurred between
- *     two queries.
- * 'proximity' is a proximity warning. See definitions below. This might
- *     be more useful than an absolute difference to the leap second.
+ *     two queries. Always zero in electric mode. If non-zero,
+ *      immediately step the clock.
+ * 'proximity' is a proximity warning. See definitions below. This is
+ *     more useful than an absolute difference to the leap second.
  * 'dynamic' != 0 if entry was requested by clock/peer
  */ 
 struct leap_result {
-       vint64          when;
-       u_int32         dist;
+       vint64          ttime;
+       u_int32         ddist;
        short           tai_offs;
        short           tai_diff;
        short           warped;
@@ -94,11 +98,12 @@ typedef struct leap_signature leap_signature_t;
  * subsequently modified.
  */
 extern leap_table_t *leapsec_get_table(int alternate);
+
 /* Set the current leap table. Accepts only return values from
  * 'leapsec_get_table()', so it's hard to do something wrong. Returns
- * TRUE if the table was exchanged.
+ * TRUE if the current table is the requested one.
  */
-extern int/*BOOL*/ leapsec_set_table(leap_table_t*);
+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*);
@@ -111,22 +116,36 @@ extern int/*BOOL*/ leapsec_is_expired(leap_table_t*, u_int32 when,
 
 /* 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.
  */
 extern int/*BOOL*/ leapsec_load(leap_table_t*, leapsec_reader,
                                void*, int blimit);
 
-
 /* Dump the current leap table in readable format, using the provided
  * dump formatter function.
  */
 extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg);
 
-/* Glue to integrate this easier into existing code */
-extern int/*BOOL*/ leapsec_load_file(FILE*, int blimit);
+/* Read a leap second file. This is a convenience wrapper around the
+ * generic load function, 'leapsec_load()'.
+ */
+extern int/*BOOL*/ leapsec_load_file(FILE * fp, int blimit);
+
+/* Get the current leap data signature. This consists of the last
+ * ransition, the table expiration, and the total TAI difference at the
+ * last transition. This is valid even if the leap transition itself was
+ * culled due to the build date limit.
+ */
 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);
 
-/* Reset the current leap frame */
+/* Reset the current leap frame, so the next query will do proper table
+ * lookup from fresh. Suppresses a possible leap era transition detection
+ * for the next query.
+ */
 extern void leapsec_reset_frame(void);
 
 /* Given a transition time, the TAI offset valid after that and an
@@ -134,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(u_int32 ttime, u_int32 etime, int total,
+extern int/*BOOL*/ leapsec_add_fix(int offset, u_int32 ttime, u_int32 etime,
                                   const time_t * pivot);
 
 /* Take a time stamp and create a leap second frame for it. This will
@@ -149,17 +168,29 @@ extern int/*BOOL*/ leapsec_add_fix(u_int32 ttime, u_int32 etime, int total,
  * 'ntp_now' is subject to era unfolding. The entry is marked
  * dynamic. The leap signature is NOT updated.
  */
-extern int/*BOOL*/ leapsec_add_dyn(u_int32 ntp_now, int insert,
+extern int/*BOOL*/ leapsec_add_dyn(int insert, u_int32 ntp_now,
                                   const time_t * pivot);
 
 /* Take a time stamp and get the associated leap information. The time
  * stamp is subject to era unfolding around the pivot or the current
  * system time if pivot is NULL. Sets the information in '*qr' and
  * returns TRUE if a leap second era boundary was crossed between the
- * last and the current query. In that case, qr.warped contains the
- * required clock stepping. (Which is zero in electric mode...)
+ * 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,
                                 const time_t * pivot);
 
+/* Get the current leap frame info. Returns TRUE if the result contains
+ * useable data, FALSE if there is currently no leap second frame.
+ * This merely replicates some results from a previous query, but since
+ * it does not check the current time, only the following entries are
+ * meaningful:
+ *  qr->ttime;
+ *  qr->tai_offs;
+ *  qr->tai_diff;
+ *  qr->dynamic;
+ */
+extern int/*BOOL*/ leapsec_frame(leap_result_t *qr);
+
 #endif /* !defined(NTP_LEAPSEC_H) */
index d61be35828d7be1ffcf4365b85ecefdc265f840e..4a5ac45fce0d5a1d018a3b38521e81954733f52e 100644 (file)
@@ -1905,7 +1905,7 @@ clock_update(
                sys_rootdisp = 0;
                L_CLR(&sys_reftime);
                sys_jitter = LOGTOD(sys_precision);
-               leapsec = 0;
+               leapsec_reset_frame();
                break;
 
        /*
@@ -1950,12 +1950,12 @@ clock_update(
                        if (leap_vote_ins > leap_vote_del
                            && leap_vote_ins > sys_survivors / 2) {
                                get_systime(&now);
-                               leapsec_add_dyn(now.l_ui, TRUE, NULL);
+                               leapsec_add_dyn(TRUE, now.l_ui, NULL);
                        }
                        if (leap_vote_del > leap_vote_ins
                            && leap_vote_del > sys_survivors / 2) {
                                get_systime(&now);
-                               leapsec_add_dyn(now.l_ui, FALSE, NULL);
+                               leapsec_add_dyn(FALSE, now.l_ui, NULL);
                        }
                }
                break;
index 14af57c3f9cf2695317017a132ca76c1be7d22d2..2c0780452124c443682ded2462f693b20e70aa9b 100644 (file)
@@ -40,7 +40,7 @@
 #define        TC_ERR  (-1)
 #endif
 
-static void check_leapsec(u_int32, const time_t*);
+static void check_leapsec(u_int32, const time_t*, int/*BOOL*/);
 
 /*
  * These routines provide support for the event timer.  The timer is
@@ -67,7 +67,8 @@ static        u_long adjust_timer;    /* second timer */
 static u_long stats_timer;     /* stats timer */
 static u_long huffpuff_timer;  /* huff-n'-puff timer */
 static u_long worker_idle_timer;/* next check for idle intres */
-u_long leapsec;                /* leapseconds countdown */
+u_long leapsec;                /* seconds to next leap (proximity class) */
+int     leapdif;                /* TAI difference step at next leap second*/
 u_long orphwait;               /* orphan wait time */
 #ifdef AUTOKEY
 static u_long revoke_timer;    /* keys revoke timer */
@@ -361,8 +362,18 @@ timer(void)
         * Leapseconds. Get time and defer to worker if either something
         * is imminent or every 8th second.
         */
-       if (leapsec > LSPROX_NOWARN || 0 == (now.l_ui & 7)) {
-               check_leapsec(now.l_ui, &tnow);
+       if (leapsec > LSPROX_NOWARN || 0 == (current_time & 7))
+               check_leapsec(now.l_ui, &tnow,
+                                (sys_leap == LEAP_NOTINSYNC));
+        if (sys_leap != LEAP_NOTINSYNC) {
+                if (leapsec >= LSPROX_ANNOUNCE && leapdif) {
+                       if (leapdif > 0)
+                               sys_leap = LEAP_ADDSECOND;
+                       else
+                               sys_leap = LEAP_DELSECOND;
+                } else {
+                        sys_leap = LEAP_NOWARNING;
+                }
        }
 
        /*
@@ -494,25 +505,24 @@ timer_clr_stats(void)
 
 static void
 check_leapsec(
-       u_int32        now ,
-       const time_t * tpiv)
+       u_int32        now  ,
+       const time_t * tpiv ,
+        int/*BOOL*/    reset)
 {
        leap_result_t lsdata;
        u_int32       lsprox;
        
-#if defined(SYS_WINNT)
-       leapsec_electric(1); /* WinNT port has its own leap second handling */
-#elif defined(KERNEL_PLL)
+#ifndef SYS_WINNT  /* WinNT port has its own leap second handling */
+# ifdef KERNEL_PLL
        leapsec_electric(pll_control && kern_enable);
-#else
+# else
        leapsec_electric(0);
-#endif
-       
-       if (sys_leap == LEAP_NOTINSYNC) {
+# endif
+#endif 
+       if (reset)      {
                lsprox = LSPROX_NOWARN;
                leapsec_reset_frame();
                memset(&lsdata, 0, sizeof(lsdata));
-       
        } else if (leapsec_query(&lsdata, now, tpiv)) {
                /* Full hit. Eventually step the clock, but always
                 * announce the leap event has happened.
@@ -538,10 +548,13 @@ check_leapsec(
         * to piping hot in one step. If things are already that wobbly,
         * we let the normal clock correction take over, even if a jump
         * is involved.
-        */
+         * Also make sure the alarming events are edge-triggered, that is,
+         * ceated only when the threshold is crossed.
+         */
        if (  (leapsec > 0 || lsprox < LSPROX_ALERT)
            && leapsec < lsprox                     ) {
-               if (lsprox >= LSPROX_SCHEDULE) {
+               if (  leapsec < LSPROX_SCHEDULE
+                   && lsprox >= LSPROX_SCHEDULE) {
                        if (lsdata.dynamic)
                                report_event(PEVNT_ARMED, sys_peer, NULL);
                        else
@@ -550,20 +563,15 @@ check_leapsec(
                leapsec = lsprox;
        }
        if (leapsec > lsprox) {
-               if (lsprox < LSPROX_SCHEDULE) {
+               if (  leapsec >= LSPROX_SCHEDULE
+                   && lsprox   < LSPROX_SCHEDULE) {
                        report_event(EVNT_DISARMED, NULL, NULL);
                }
                leapsec = lsprox;
        }
 
-       if (sys_leap != LEAP_NOTINSYNC) {
-               if (leapsec < LSPROX_ANNOUNCE)
-                       lsdata.tai_diff = 0;
-               if (lsdata.tai_diff > 0)
-                       sys_leap = LEAP_ADDSECOND;
-               else if (lsdata.tai_diff < 0)
-                       sys_leap = LEAP_DELSECOND;
-               else
-                       sys_leap = LEAP_NOWARNING;
-       }
+        if (leapsec >= LSPROX_SCHEDULE)
+                leapdif = lsdata.tai_diff;
+        else
+                leapdif = 0;
 }
index 1e95f0dadc5dd931c7ef11b73e1b396256df3115..a3e4f58e9316efff5cbcd32c7ce2d7d7442a9b28 100644 (file)
@@ -870,7 +870,6 @@ check_leap_file(
        FILE *fp;
        struct stat *sp1 = &leapseconds_file_sb1;
        struct stat *sp2 = &leapseconds_file_sb2;
-       leap_table_t * plt;
 
        if (leapseconds_file) {
                if ((fp = fopen(leapseconds_file, "r")) == NULL) {
@@ -889,13 +888,10 @@ check_leap_file(
                if (   (sp1->st_mtime != sp2->st_mtime)
                    || (sp1->st_ctime != sp2->st_ctime)) {
                        leapseconds_file_sb1 = leapseconds_file_sb2;
-                       plt = leapsec_get_table(TRUE);
-                       if (!leapsec_load(plt, (leapsec_reader)getc, fp, TRUE)) {
+                       if (!leapsec_load_file(fp, TRUE)) {
                                msyslog(LOG_ERR,
                                    "format error leapseconds file %s",
                                    leapseconds_file);
-                       } else {
-                               leapsec_set_table(plt);
                        }
                }
                fclose(fp);
index faf9b3c750975956354316a8cbd459988504f72e..052bfcd2d92bb2e7e95989ec11f583d0eab7388a 100644 (file)
@@ -40,6 +40,7 @@
 #include "ntp_unixtime.h"
 #include "ntp_timer.h"
 #include "ntp_assert.h"
+#include "ntp_leapsec.h"
 #include "clockstuff.h"
 #include "ntservice.h"
 #include "ntpd.h"
@@ -465,6 +466,9 @@ adj_systime(
        double now
        )
 {
+        /* ntp time scale origin as ticks since 1601-01-01 */
+        static const ULONGLONG HNS_JAN_1900 = 94354848000000000ull;
+
        static double   adjtime_carry;
        double          dtemp;
        u_char          isneg;
@@ -473,6 +477,7 @@ adj_systime(
        SYSTEMTIME      st;
        ULONGLONG       this_perf_count;
        FT_ULL          curr_ft;
+        leap_result_t   lsi;
 
        /*
         * Add the residual from the previous adjustment to the new
@@ -527,57 +532,46 @@ adj_systime(
        dtemp -= TimeAdjustment * ppm_per_adjust_unit;  
 
 
-       /*
-        * If a leap second is pending for the end of the month,
-        * determine the UTC time stamp when the insertion must take
-        * place 
+       /* If a piping-hot close leap second is pending for the end
+         * of this day, determine the UTC time stamp when the transition
+         * must take place. (Calculated in the current leap era!) 
         */
-       if (0 < leapsec && leapsec < 31 * DAY) {
-               if (0 == ls_ft.ull) {  /* time stamp has not yet been computed */
-                       GetSystemTime(&st);
-
-                       /*
-                        * Accept leap announcement only 1 month in advance,
-                        * for end of March, June, September, or December.
-                        */
-                       if (0 == (st.wMonth % 3)) {
-                               /*
-                                * The comparison time stamp is computed according 
-                                * to 0:00h UTC of the following day 
-                                */
-                               if (++st.wMonth > 12) {
-                                       st.wMonth -= 12;
-                                       st.wYear++;
-                               }
-                               
-                               st.wDay = 1;
-                               st.wHour = 0;
-                               st.wMinute = 0;
-                               st.wSecond = 0;
-                               st.wMilliseconds = 0;
-
-                               SystemTimeToFileTime(&st, &ls_ft.ft);
-                               msyslog(LOG_NOTICE,
-                                       "Detected positive leap second announcement "
-                                       "for %04d-%02d-%02d %02d:%02d:%02d UTC",
-                                       st.wYear, st.wMonth, st.wDay,
-                                       st.wHour, st.wMinute, st.wSecond);
-                       }
-               }
-       } else {
-               if (ls_ft.ull != 0) {  /* Leap second has been armed before */
-                       /*
-                        * Disarm leap second only if the leap second
-                        * is not already in progress.
-                        */
-                       if (0 == ls_time_adjustment) {
-                               ls_ft.ull = 0;
-                               msyslog(LOG_NOTICE, "Leap second announcement disarmed");
-                       }
+       if (leapsec >= LSPROX_ALERT) {
+                if (0 == ls_ft.ull && leapsec_frame(&lsi)) {
+                        if (lsi.tai_diff > 0) {
+                                /* A leap second insert is scheduled at the end
+                                 * of the day. Since we have not yet computed the
+                                 * time stamp, do it now. Signal electric mode
+                                 * for this insert.
+                                 */
+                                ls_ft.ull = lsi.ttime.Q_s * HECTONANOSECONDS
+                                          + HNS_JAN_1900;
+                                FileTimeToSystemTime(&ls_ft.ft, &st);
+                               msyslog(LOG_NOTICE,
+                                       "Detected positive leap second announcement "
+                                       "for %04d-%02d-%02d %02d:%02d:%02d UTC",
+                                       st.wYear, st.wMonth, st.wDay,
+                                       st.wHour, st.wMinute, st.wSecond);
+                                leapsec_electric(TRUE);
+                        } else if (lsi.tai_diff < 0) {
+                                /* Do not handle negative leap seconds here. If this
+                                 * happens, let the system step.
+                                 */
+                                leapsec_electric(FALSE);
+                        }
+                }
+        } else {
+                /* The leap second announcement is gone. Happens primarily after
+                 * the leap transition, but can also be due to a clock step.
+                 * Disarm the leap second, but only if there is one scheduled
+                 * and not currently in progress!
+                 */
+               if (ls_ft.ull != 0 && ls_time_adjustment == 0) {
+                       ls_ft.ull = 0;
+                       msyslog(LOG_NOTICE, "Leap second announcement disarmed");
                }
        }
 
-
        /*
         * If the time stamp for the next leap second has been set
         * then check if the leap second must be handled
@@ -696,6 +690,7 @@ init_winnt_time(void)
 #pragma warning(pop)
 
        init_small_adjustment();
+        leapsec_electric(TRUE);
 
        /*
         * Get privileges needed for fiddling with the clock
index 07774c82d9b6c866d384be7da551f2a82ab6abd8..005f287bcd5bdde7a9c6d60857b5b0420480b347 100644 (file)
                                        />
                                </FileConfiguration>
                        </File>
+                       <File
+                               RelativePath="..\..\..\ntpd\ntp_leapsec.c"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\..\ntpd\ntp_loopfilter.c"
                                >
                                RelativePath="..\..\..\include\ntp_io.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\..\ntpd\ntp_leapsec.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\include\ntp_iocompletionport.h"
                                >
index b5c676078b839865ff17f31d3afb4498466a3247..3dfcfe4aa0f0d7b7a22a4e50a0ed462c599d3d8d 100644 (file)
                                RelativePath="..\..\ntpd\ntp_iocompletionport.c"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\..\..\ntpd\ntp_leapsec.c"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\..\..\ntpd\ntp_loopfilter.c"
                                >
                                RelativePath="..\..\..\..\ntpd\ntp_keyword.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\..\..\ntpd\ntp_leapsec.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\..\..\include\ntp_libopts.h"
                                >
index 4596d8e843cb8ff910c9e34a2be6364d6e87bcb4..e8abd28cbae213e86b074c6023a5510cea5d6121 100644 (file)
@@ -69,6 +69,40 @@ static const char leap2 [] =
     "2950473600        28      # 1 Jul 1993\n"
     "#\n";
 
+// Faked table with a leap second removal at 2009 
+static const char leap3 [] =
+    "#\n"
+    "#@        3610569600\n"
+    "#\n"
+    "2272060800 10     # 1 Jan 1972\n"
+    "2287785600        11      # 1 Jul 1972\n"
+    "2303683200        12      # 1 Jan 1973\n"
+    "2335219200        13      # 1 Jan 1974\n"
+    "2366755200        14      # 1 Jan 1975\n"
+    "2398291200        15      # 1 Jan 1976\n"
+    "2429913600        16      # 1 Jan 1977\n"
+    "2461449600        17      # 1 Jan 1978\n"
+    "2492985600        18      # 1 Jan 1979\n"
+    "2524521600        19      # 1 Jan 1980\n"
+    "2571782400        20      # 1 Jul 1981\n"
+    "2603318400        21      # 1 Jul 1982\n"
+    "2634854400        22      # 1 Jul 1983\n"
+    "2698012800        23      # 1 Jul 1985\n"
+    "2776982400        24      # 1 Jan 1988\n"
+    "2840140800        25      # 1 Jan 1990\n"
+    "2871676800        26      # 1 Jan 1991\n"
+    "2918937600        27      # 1 Jul 1992\n"
+    "2950473600        28      # 1 Jul 1993\n"
+    "2982009600        29      # 1 Jul 1994\n"
+    "3029443200        30      # 1 Jan 1996\n"
+    "3076704000        31      # 1 Jul 1997\n"
+    "3124137600        32      # 1 Jan 1999\n"
+    "3345062400        33      # 1 Jan 2006\n"
+    "3439756800        32      # 1 Jan 2009\n"
+    "3550089600        33      # 1 Jul 2012\n"
+    "#\n";
+
+
 static u_int32 lsec2009 = 3439756800u; // 1 Jan 2009, 00:00:00 utc
 static u_int32 lsec2012 = 3550089600u; // 1 Jul 2012, 00:00:00 utc
 
@@ -160,6 +194,7 @@ void leapsecTest::TearDown()
     ntpcal_set_timefunc(NULL);
 }
 
+// test number parser
 TEST_F(leapsecTest, ParseVUI64) {
        vint64 act, exp;
        const char *sp;
@@ -187,6 +222,7 @@ TEST_F(leapsecTest, ParseVUI64) {
        EXPECT_EQ(*ep, '\0');
 }
 
+// test table selection
 TEST_F(leapsecTest, tableSelect) {
        leap_table_t *pt1, *pt2, *pt3, *pt4;
 
@@ -220,7 +256,8 @@ TEST_F(leapsecTest, tableSelect) {
        EXPECT_NE(pt2, pt3);
 }
 
-TEST_F(leapsecTest, loadFileOk) {
+// load file & checl expiration
+TEST_F(leapsecTest, loadFileExpire) {
        const char *cp = leap1;
        int rc;
        leap_table_t * pt = leapsec_get_table(0);
@@ -230,22 +267,11 @@ TEST_F(leapsecTest, loadFileOk) {
        EXPECT_EQ(1, rc);
        rc = leapsec_expired(3439756800, NULL);
        EXPECT_EQ(0, rc);
-       //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
-}
-
-TEST_F(leapsecTest, loadFileExpire) {
-       const char *cp = leap1;
-       int rc;
-       leap_table_t * pt = leapsec_get_table(0);
-
-       rc =   leapsec_load(pt, stringreader, &cp, FALSE)
-           && leapsec_set_table(pt);
-       EXPECT_EQ(1, rc);
        rc = leapsec_expired(3610569601, NULL);
        EXPECT_EQ(1, rc);
 }
 
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -60days
 TEST_F(leapsecTest, ls2009faraway) {
        int            rc;
        leap_result_t  qr;
@@ -258,11 +284,10 @@ TEST_F(leapsecTest, ls2009faraway) {
        EXPECT_EQ(FALSE, rc);
        EXPECT_EQ(33, qr.tai_offs);
        EXPECT_EQ(0,  qr.tai_diff);
-       EXPECT_EQ(0,  qr.dist);
        EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
 }
 
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -1week
 TEST_F(leapsecTest, ls2009weekaway) {
        int            rc;
        leap_result_t  qr;
@@ -275,11 +300,10 @@ TEST_F(leapsecTest, ls2009weekaway) {
        EXPECT_EQ(FALSE, rc);
        EXPECT_EQ(33, qr.tai_offs);
        EXPECT_EQ(1,  qr.tai_diff);
-       EXPECT_EQ(7*SECSPERDAY,  qr.dist);
        EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
 }
 
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -1hr
 TEST_F(leapsecTest, ls2009houraway) {
        int            rc;
        leap_result_t  qr;
@@ -292,11 +316,10 @@ TEST_F(leapsecTest, ls2009houraway) {
        EXPECT_EQ(FALSE, rc);
        EXPECT_EQ(33, qr.tai_offs);
        EXPECT_EQ(1,  qr.tai_diff);
-       EXPECT_EQ(SECSPERHR,  qr.dist);
        EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
 }
 
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -1sec
 TEST_F(leapsecTest, ls2009secaway) {
        int            rc;
        leap_result_t  qr;
@@ -309,11 +332,10 @@ TEST_F(leapsecTest, ls2009secaway) {
        EXPECT_EQ(FALSE, rc);
        EXPECT_EQ(33, qr.tai_offs);
        EXPECT_EQ(1,  qr.tai_diff);
-       EXPECT_EQ(1,  qr.dist);
        EXPECT_EQ(LSPROX_ALERT, qr.proximity);
 }
 
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump to leap second at 2009.01.01
 TEST_F(leapsecTest, ls2009onspot) {
        int            rc;
        leap_result_t  qr;
@@ -326,51 +348,10 @@ TEST_F(leapsecTest, ls2009onspot) {
        EXPECT_EQ(FALSE, rc);
        EXPECT_EQ(34, qr.tai_offs);
        EXPECT_EQ(0,  qr.tai_diff);
-       EXPECT_EQ(0,  qr.dist);
        EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
 }
 
-/* test handling of the leap second at 2009.01.01 */
-TEST_F(leapsecTest, ls2009sequenceElectric) {
-       int            rc;
-       leap_result_t  qr;
-
-       rc = setup_load_table(leap1);
-       EXPECT_EQ(1, rc);
-
-       rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
-       EXPECT_EQ(FALSE, rc);
-       EXPECT_EQ(0,             qr.warped   );
-       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
-
-       rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
-       EXPECT_EQ(FALSE, rc);
-       EXPECT_EQ(0,               qr.warped   );
-       EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
-
-       rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
-       EXPECT_EQ(FALSE, rc);
-       EXPECT_EQ(0,               qr.warped   );
-       EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
-
-       rc = leapsec_query(&qr, lsec2009 - 1, NULL);
-       EXPECT_EQ(FALSE, rc);
-       EXPECT_EQ(0,               qr.warped   );
-       EXPECT_EQ(LSPROX_ALERT,    qr.proximity);
-
-       rc = leapsec_query(&qr, lsec2009, NULL);
-       EXPECT_EQ(TRUE, rc);
-       EXPECT_EQ(0,             qr.warped   );
-       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
-
-       // second call, same time frame: no trigger!
-       rc = leapsec_query(&qr, lsec2009, NULL);
-       EXPECT_EQ(FALSE, rc);
-       EXPECT_EQ(0,             qr.warped   );
-       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
-}
-
-/* test handling of the leap second at 2009.01.01 */
+// test handling of the leap second at 2009.01.01 without table
 TEST_F(leapsecTest, ls2009nodata) {
        int            rc;
        leap_result_t  qr;
@@ -383,10 +364,10 @@ TEST_F(leapsecTest, ls2009nodata) {
        EXPECT_EQ(FALSE, rc);
        EXPECT_EQ(0,  qr.tai_offs);
        EXPECT_EQ(0,  qr.tai_diff);
-       EXPECT_EQ(0,  qr.dist);
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
 }
 
-/* test handling of the leap second at 2009.01.01 */
+// test handling of the leap second at 2009.01.01 with culled data
 TEST_F(leapsecTest, ls2009limdata) {
        int            rc;
        leap_result_t  qr;
@@ -399,10 +380,11 @@ TEST_F(leapsecTest, ls2009limdata) {
        EXPECT_EQ(FALSE, rc);
        EXPECT_EQ(35, qr.tai_offs);
        EXPECT_EQ(0,  qr.tai_diff);
-       EXPECT_EQ(0,  qr.dist);
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
 }
 
-TEST_F(leapsecTest, addManual) {
+// add dynamic leap second (like from peer/clock)
+TEST_F(leapsecTest, addDynamic) {
        int            rc;
        leap_result_t  qr;
 
@@ -422,15 +404,16 @@ TEST_F(leapsecTest, addManual) {
 
        leap_table_t * pt = leapsec_get_table(0);
        for (int idx=1; insns[idx]; ++idx) {
-           rc = leapsec_add_dyn(insns[idx] - 20*SECSPERDAY - 100, 1, NULL);
+           rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
                EXPECT_EQ(TRUE, rc);
        }
        // try to slip in a previous entry
-       rc = leapsec_add_dyn(insns[0] - 20*SECSPERDAY - 100, 1, NULL);
+       rc = leapsec_add_dyn(TRUE, insns[0] - 20*SECSPERDAY - 100, NULL);
        EXPECT_EQ(FALSE, rc);
        //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
 }
 
+// add fixed leap seconds (like from network packet)
 TEST_F(leapsecTest, addFixed) {
        int            rc;
        leap_result_t  qr;
@@ -452,31 +435,208 @@ TEST_F(leapsecTest, addFixed) {
        leap_table_t * pt = leapsec_get_table(0);
        // try to get in BAD time stamps...
        for (int idx=0; insns[idx].tt; ++idx) {
-           rc = leapsec_add_fix(insns[idx].tt - 20*SECSPERDAY - 100,
-                                insns[idx].tt + SECSPERDAY,
-                                insns[idx].of,
-                                NULL);
+           rc = leapsec_add_fix(
+               insns[idx].of,
+               insns[idx].tt - 20*SECSPERDAY - 100,
+               insns[idx].tt + SECSPERDAY,
+               NULL);
                EXPECT_EQ(FALSE, rc);
        }
        // no do it right
        for (int idx=0; insns[idx].tt; ++idx) {
-               rc = leapsec_add_fix(insns[idx].tt,
-                                insns[idx].tt + SECSPERDAY,
-                                insns[idx].of,
-                                NULL);
+               rc = leapsec_add_fix(
+                   insns[idx].of,
+                   insns[idx].tt,
+                   insns[idx].tt + SECSPERDAY,
+                   NULL);
                EXPECT_EQ(TRUE, rc);
        }
        // try to slip in a previous entry
-       rc = leapsec_add_fix(insns[0].tt,
-                            insns[0].tt + SECSPERDAY,
-                            insns[0].of,
-                            NULL);
+       rc = leapsec_add_fix(
+           insns[0].of,
+           insns[0].tt,
+           insns[0].tt + SECSPERDAY,
+           NULL);
        EXPECT_EQ(FALSE, rc);
        //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
 }
 
-/* test handling of the leap second at 2012.07.01 */
-TEST_F(leapsecTest, ls2012sequenceElectric) {
+// =====================================================================
+// SEQUENCE TESTS
+// =====================================================================
+
+// leap second insert at 2009.01.01, electric mode
+TEST_F(leapsecTest, ls2009seqInsElectric) {
+       int            rc;
+       leap_result_t  qr;
+
+       rc = setup_load_table(leap1);
+       EXPECT_EQ(1, rc);
+       leapsec_electric(1);
+
+       rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ALERT,    qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009, NULL);
+       EXPECT_EQ(TRUE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       // second call, same time frame: no trigger!
+       rc = leapsec_query(&qr, lsec2009, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+// leap second insert at 2009.01.01, dumb mode
+TEST_F(leapsecTest, ls2009seqInsDumb) {
+       int            rc;
+       leap_result_t  qr;
+
+       rc = setup_load_table(leap1);
+       EXPECT_EQ(1, rc);
+       leapsec_electric(0);
+
+       rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ALERT,    qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ALERT,    qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009+1, NULL);
+       EXPECT_EQ(TRUE, rc);
+       EXPECT_EQ(-1,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       // second call, same time frame: no trigger!
+       rc = leapsec_query(&qr, lsec2009, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+
+// fake leap second remove at 2009.01.01, electric mode
+TEST_F(leapsecTest, ls2009seqDelElectric) {
+       int            rc;
+       leap_result_t  qr;
+
+       rc = setup_load_table(leap3);
+       EXPECT_EQ(1, rc);
+       leapsec_electric(1);
+
+       rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ALERT,    qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009, NULL);
+       EXPECT_EQ(TRUE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       // second call, same time frame: no trigger!
+       rc = leapsec_query(&qr, lsec2009, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+// fake leap second remove at 2009.01.01. dumb mode
+TEST_F(leapsecTest, ls2009seqDelDumb) {
+       int            rc;
+       leap_result_t  qr;
+
+       rc = setup_load_table(leap3);
+       EXPECT_EQ(1, rc);
+       leapsec_electric(0);
+
+       rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 2, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,               qr.warped   );
+       EXPECT_EQ(LSPROX_ALERT,    qr.proximity);
+
+       rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+       EXPECT_EQ(TRUE, rc);
+       EXPECT_EQ(1,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+       // second call, same time frame: no trigger!
+       rc = leapsec_query(&qr, lsec2009, NULL);
+       EXPECT_EQ(FALSE, rc);
+       EXPECT_EQ(0,             qr.warped   );
+       EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+// leap second insert at 2012.07.01, electric mode
+TEST_F(leapsecTest, ls2012seqInsElectric) {
        int            rc;
        leap_result_t  qr;
 
@@ -515,8 +675,8 @@ TEST_F(leapsecTest, ls2012sequenceElectric) {
        EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
 }
 
-/* test handling of the leap second at 2012.07.01 */
-TEST_F(leapsecTest, ls2012sequenceDumb) {
+// leap second insert at 2012.07.01, dumb mode
+TEST_F(leapsecTest, ls2012seqInsDumb) {
        int            rc;
        leap_result_t  qr;
 
@@ -566,3 +726,4 @@ TEST_F(leapsecTest, ls2012sequenceDumb) {
        EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
 }
 
+