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