From: Juergen Perlinger Date: Thu, 3 Oct 2013 12:55:29 +0000 (+0200) Subject: [Bug 2250] Cleanup & Win32 integration of the NTPD leapsec module X-Git-Tag: NTP_4_2_7P391~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b32b89a0ba924498f012a7e73e06484660258172;p=thirdparty%2Fntp.git [Bug 2250] Cleanup & Win32 integration of the NTPD leapsec module fixed handling of leap second removal trigger logic and windows port for leap seconds bk: 524d6941KGa3dlvVKJkuEjDJcVd_pQ --- diff --git a/include/ntpd.h b/include/ntpd.h index d62b5f0d5..d6f259c61 100644 --- a/include/ntpd.h +++ b/include/ntpd.h @@ -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; diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index b7e503a45..37a34f1cc 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -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; diff --git a/ntpd/ntp_leapsec.c b/ntpd/ntp_leapsec.c index 32171a090..458ae5d9e 100644 --- a/ntpd/ntp_leapsec.c +++ b/ntpd/ntp_leapsec.c @@ -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); } diff --git a/ntpd/ntp_leapsec.h b/ntpd/ntp_leapsec.h index fd76d9087..bea02d990 100644 --- a/ntpd/ntp_leapsec.h +++ b/ntpd/ntp_leapsec.h @@ -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) */ diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c index d61be3582..4a5ac45fc 100644 --- a/ntpd/ntp_proto.c +++ b/ntpd/ntp_proto.c @@ -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; diff --git a/ntpd/ntp_timer.c b/ntpd/ntp_timer.c index 14af57c3f..2c0780452 100644 --- a/ntpd/ntp_timer.c +++ b/ntpd/ntp_timer.c @@ -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; } diff --git a/ntpd/ntp_util.c b/ntpd/ntp_util.c index 1e95f0dad..a3e4f58e9 100644 --- a/ntpd/ntp_util.c +++ b/ntpd/ntp_util.c @@ -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); diff --git a/ports/winnt/ntpd/nt_clockstuff.c b/ports/winnt/ntpd/nt_clockstuff.c index faf9b3c75..052bfcd2d 100644 --- a/ports/winnt/ntpd/nt_clockstuff.c +++ b/ports/winnt/ntpd/nt_clockstuff.c @@ -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 diff --git a/ports/winnt/vs2005/ntpd.vcproj b/ports/winnt/vs2005/ntpd.vcproj index 07774c82d..005f287bc 100644 --- a/ports/winnt/vs2005/ntpd.vcproj +++ b/ports/winnt/vs2005/ntpd.vcproj @@ -384,6 +384,10 @@ /> + + @@ -821,6 +825,10 @@ RelativePath="..\..\..\include\ntp_io.h" > + + diff --git a/ports/winnt/vs2008/ntpd/ntpd.vcproj b/ports/winnt/vs2008/ntpd/ntpd.vcproj index b5c676078..3dfcfe4aa 100644 --- a/ports/winnt/vs2008/ntpd/ntpd.vcproj +++ b/ports/winnt/vs2008/ntpd/ntpd.vcproj @@ -286,6 +286,10 @@ RelativePath="..\..\ntpd\ntp_iocompletionport.c" > + + @@ -515,6 +519,10 @@ RelativePath="..\..\..\..\ntpd\ntp_keyword.h" > + + diff --git a/tests/ntpd/leapsec.cpp b/tests/ntpd/leapsec.cpp index 4596d8e84..e8abd28cb 100644 --- a/tests/ntpd/leapsec.cpp +++ b/tests/ntpd/leapsec.cpp @@ -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); } +