12b15cb3dSCy Schubert /* 22b15cb3dSCy Schubert * ntp_leapsec.h - leap second processing for NTPD 32b15cb3dSCy Schubert * 42b15cb3dSCy Schubert * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 52b15cb3dSCy Schubert * The contents of 'html/copyright.html' apply. 62b15cb3dSCy Schubert * ---------------------------------------------------------------------- 72b15cb3dSCy Schubert * This is an attempt to get the leap second handling into a dedicated 82b15cb3dSCy Schubert * module to make the somewhat convoluted logic testable. 92b15cb3dSCy Schubert */ 102b15cb3dSCy Schubert 112b15cb3dSCy Schubert #ifndef NTP_LEAPSEC_H 122b15cb3dSCy Schubert #define NTP_LEAPSEC_H 132b15cb3dSCy Schubert 142b15cb3dSCy Schubert struct stat; 152b15cb3dSCy Schubert 162b15cb3dSCy Schubert 172b15cb3dSCy Schubert /* function pointer types. Note that 'fprintf' and 'getc' can be casted 182b15cb3dSCy Schubert * to the dumper resp. reader type, provided the auxiliary argument is a 192b15cb3dSCy Schubert * valid FILE pointer in hat case. 202b15cb3dSCy Schubert */ 212b15cb3dSCy Schubert typedef void (*leapsec_dumper)(void*, const char *fmt, ...); 222b15cb3dSCy Schubert typedef int (*leapsec_reader)(void*); 232b15cb3dSCy Schubert 242b15cb3dSCy Schubert struct leap_table; 252b15cb3dSCy Schubert typedef struct leap_table leap_table_t; 262b15cb3dSCy Schubert 272b15cb3dSCy Schubert /* Validate a stream containing a leap second file in the NIST / NTPD 282b15cb3dSCy Schubert * format that can also be loaded via 'leapsec_load()'. This uses 292b15cb3dSCy Schubert * the SHA1 hash and preprocessing as described in the NIST leapsecond 302b15cb3dSCy Schubert * file. 312b15cb3dSCy Schubert */ 322b15cb3dSCy Schubert #define LSVALID_GOODHASH 1 /* valid signature */ 332b15cb3dSCy Schubert #define LSVALID_NOHASH 0 /* no signature in file */ 342b15cb3dSCy Schubert #define LSVALID_BADHASH -1 /* signature mismatch */ 352b15cb3dSCy Schubert #define LSVALID_BADFORMAT -2 /* signature not parseable */ 362b15cb3dSCy Schubert 372b15cb3dSCy Schubert extern int leapsec_validate(leapsec_reader, void*); 382b15cb3dSCy Schubert 392b15cb3dSCy Schubert 402b15cb3dSCy Schubert /* Set/get electric mode 412b15cb3dSCy Schubert * Electric mode is defined as the operation mode where the system clock 422b15cb3dSCy Schubert * automagically manages the leap second, so we don't have to care about 432b15cb3dSCy Schubert * stepping the clock. (This should be the case with most systems, 442b15cb3dSCy Schubert * including the current implementation of the Win32 timekeeping.) 452b15cb3dSCy Schubert * 462b15cb3dSCy Schubert * The consequence of electric mode is that we do not 'see' the leap 472b15cb3dSCy Schubert * second, and no client actions are needed when crossing the leap era 482b15cb3dSCy Schubert * boundary. In manual (aka non-electric) mode the clock will simply 492b15cb3dSCy Schubert * step forward untill *we* (that is, this module) tells the client app 502b15cb3dSCy Schubert * to step at the right time. This needs a slightly different type of 512b15cb3dSCy Schubert * processing, so switching between those two modes should not be done 522b15cb3dSCy Schubert * too close to a leap second. The transition might be lost in that 532b15cb3dSCy Schubert * case. (The limit is actual 2 sec before transition.) 542b15cb3dSCy Schubert * 552b15cb3dSCy Schubert * OTOH, this is a system characteristic, so it's expected to be set 562b15cb3dSCy Schubert * properly somewhere after system start and retain the value. 572b15cb3dSCy Schubert * 582b15cb3dSCy Schubert * Simply querying the state or setting it to the same value as before 592b15cb3dSCy Schubert * does not have any unwanted side effects. You can query by giving a 602b15cb3dSCy Schubert * negative value for the switch. 612b15cb3dSCy Schubert */ 622b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on); 632b15cb3dSCy Schubert 64276da39aSCy Schubert /* Query result for a leap era. This is the minimal stateless 65276da39aSCy Schubert * information available for a time stamp in UTC. 66276da39aSCy Schubert */ 67276da39aSCy Schubert struct leap_era { 68276da39aSCy Schubert vint64 ebase; /* era base (UTC of start) */ 69276da39aSCy Schubert vint64 ttime; /* era end (UTC of next leap second) */ 70276da39aSCy Schubert int16_t taiof; /* offset to TAI in this era */ 71276da39aSCy Schubert }; 72276da39aSCy Schubert typedef struct leap_era leap_era_t; 732b15cb3dSCy Schubert 742b15cb3dSCy Schubert /* Query result for a leap second schedule 75276da39aSCy Schubert * 'ebase' is the nominal UTC time when the current leap era 76276da39aSCy Schubert * started. (Era base time) 77276da39aSCy Schubert * 'ttime' is the next transition point in full time scale. (Nominal UTC 78276da39aSCy Schubert * time when the next leap era starts.) 792b15cb3dSCy Schubert * 'ddist' is the distance to the transition, in clock seconds. 802b15cb3dSCy Schubert * This is the distance to the due time, which is different 812b15cb3dSCy Schubert * from the transition time if the mode is non-electric. 822b15cb3dSCy Schubert * Only valid if 'tai_diff' is not zero. 83276da39aSCy Schubert * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always 84276da39aSCy Schubert * valid. 852b15cb3dSCy Schubert * 'tai_diff' is the change in TAI offset after the next leap 862b15cb3dSCy Schubert * transition. Zero if nothing is pending or too far ahead. 872b15cb3dSCy Schubert * 'warped' is set only once, when the the leap second occurred between 882b15cb3dSCy Schubert * two queries. Always zero in electric mode. If non-zero, 892b15cb3dSCy Schubert * immediately step the clock. 902b15cb3dSCy Schubert * 'proximity' is a proximity warning. See definitions below. This is 912b15cb3dSCy Schubert * more useful than an absolute difference to the leap second. 922b15cb3dSCy Schubert * 'dynamic' != 0 if entry was requested by clock/peer 932b15cb3dSCy Schubert */ 942b15cb3dSCy Schubert struct leap_result { 95276da39aSCy Schubert vint64 ebase; 962b15cb3dSCy Schubert vint64 ttime; 972b15cb3dSCy Schubert uint32_t ddist; 982b15cb3dSCy Schubert int16_t tai_offs; 992b15cb3dSCy Schubert int16_t tai_diff; 1002b15cb3dSCy Schubert int16_t warped; 1012b15cb3dSCy Schubert uint8_t proximity; 1022b15cb3dSCy Schubert uint8_t dynamic; 1032b15cb3dSCy Schubert }; 1042b15cb3dSCy Schubert typedef struct leap_result leap_result_t; 1052b15cb3dSCy Schubert 106276da39aSCy Schubert /* The leap signature is used in two distinct circumstances, and it has 107276da39aSCy Schubert * slightly different content in these cases: 108276da39aSCy Schubert * - it is used to indictae the time range covered by the leap second 109276da39aSCy Schubert * table, and then it contains the last transition, TAI offset after 110276da39aSCy Schubert * the final transition, and the expiration time. 111276da39aSCy Schubert * - it is used to query data for AUTOKEY updates, and then it contains 112276da39aSCy Schubert * the *current* TAI offset, the *next* transition time and the 113276da39aSCy Schubert * expiration time of the table. 114276da39aSCy Schubert */ 1152b15cb3dSCy Schubert struct leap_signature { 1162b15cb3dSCy Schubert uint32_t etime; /* expiration time */ 1172b15cb3dSCy Schubert uint32_t ttime; /* transition time */ 1182b15cb3dSCy Schubert int16_t taiof; /* total offset to TAI */ 1192b15cb3dSCy Schubert }; 1202b15cb3dSCy Schubert typedef struct leap_signature leap_signature_t; 1212b15cb3dSCy Schubert 1222b15cb3dSCy Schubert 123276da39aSCy Schubert #ifdef LEAP_SMEAR 124276da39aSCy Schubert 125276da39aSCy Schubert struct leap_smear_info { 126276da39aSCy Schubert int enabled; /* not 0 if smearing is generally enabled */ 127276da39aSCy Schubert int in_progress; /* not 0 if smearing is in progress, i.e. the offset has been computed */ 128276da39aSCy Schubert int leap_occurred; /* not 0 if the leap second has already occurred, i.e., during the leap second */ 129276da39aSCy Schubert double doffset; /* the current smear offset as double */ 130276da39aSCy Schubert l_fp offset; /* the current smear offset */ 131276da39aSCy Schubert uint32_t t_offset; /* the current time for which a smear offset has been computed */ 132276da39aSCy Schubert long interval; /* smear interval, in [s], should be at least some hours */ 133276da39aSCy Schubert double intv_start; /* start time of the smear interval */ 134276da39aSCy Schubert double intv_end; /* end time of the smear interval */ 135276da39aSCy Schubert }; 136276da39aSCy Schubert typedef struct leap_smear_info leap_smear_info_t; 137276da39aSCy Schubert 138276da39aSCy Schubert #endif /* LEAP_SMEAR */ 139276da39aSCy Schubert 140276da39aSCy Schubert 1412b15cb3dSCy Schubert #define LSPROX_NOWARN 0 /* clear radar screen */ 1422b15cb3dSCy Schubert #define LSPROX_SCHEDULE 1 /* less than 1 month to target*/ 1432b15cb3dSCy Schubert #define LSPROX_ANNOUNCE 2 /* less than 1 day to target */ 1442b15cb3dSCy Schubert #define LSPROX_ALERT 3 /* less than 10 sec to target */ 1452b15cb3dSCy Schubert 1462b15cb3dSCy Schubert /* Get the current or alternate table pointer. Getting the alternate 1472b15cb3dSCy Schubert * pointer will automatically copy the primary table, so it can be 1482b15cb3dSCy Schubert * subsequently modified. 1492b15cb3dSCy Schubert */ 1502b15cb3dSCy Schubert extern leap_table_t *leapsec_get_table(int alternate); 1512b15cb3dSCy Schubert 1522b15cb3dSCy Schubert /* Set the current leap table. Accepts only return values from 1532b15cb3dSCy Schubert * 'leapsec_get_table()', so it's hard to do something wrong. Returns 1542b15cb3dSCy Schubert * TRUE if the current table is the requested one. 1552b15cb3dSCy Schubert */ 1562b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_set_table(leap_table_t *); 1572b15cb3dSCy Schubert 1582b15cb3dSCy Schubert /* Clear all leap second data. Use it for init & cleanup */ 1592b15cb3dSCy Schubert extern void leapsec_clear(leap_table_t*); 1602b15cb3dSCy Schubert 1612b15cb3dSCy Schubert /* Load a leap second file. If 'blimit' is set, do not store (but 1622b15cb3dSCy Schubert * register with their TAI offset) leap entries before the build date. 1632b15cb3dSCy Schubert * Update the leap signature data on the fly. 1642b15cb3dSCy Schubert */ 1652b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_load(leap_table_t*, leapsec_reader, 1662b15cb3dSCy Schubert void*, int blimit); 1672b15cb3dSCy Schubert 1682b15cb3dSCy Schubert /* Dump the current leap table in readable format, using the provided 1692b15cb3dSCy Schubert * dump formatter function. 1702b15cb3dSCy Schubert */ 1712b15cb3dSCy Schubert extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg); 1722b15cb3dSCy Schubert 1732b15cb3dSCy Schubert /* Read a leap second file from stream. This is a convenience wrapper 1742b15cb3dSCy Schubert * around the generic load function, 'leapsec_load()'. 1752b15cb3dSCy Schubert */ 1762b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_load_stream(FILE * fp, const char * fname, 177*2d4e511cSCy Schubert int/*BOOL*/logall, int/*BOOL*/vhash); 1782b15cb3dSCy Schubert 1792b15cb3dSCy Schubert /* Read a leap second file from file. It checks that the file exists and 1802b15cb3dSCy Schubert * (if 'force' is not applied) the ctime/mtime has changed since the 1812b15cb3dSCy Schubert * last load. If the file has to be loaded, either due to 'force' or 1822b15cb3dSCy Schubert * changed time stamps, the 'stat()' results of the file are stored in 1832b15cb3dSCy Schubert * '*sb' for the next cycle. Returns TRUE on successful load, FALSE 1842b15cb3dSCy Schubert * otherwise. Uses 'leapsec_load_stream()' internally. 1852b15cb3dSCy Schubert */ 1862b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_load_file(const char * fname, struct stat * sb, 187*2d4e511cSCy Schubert int/*BOOL*/force, int/*BOOL*/logall, 188*2d4e511cSCy Schubert int/*BOOL*/vhash); 1892b15cb3dSCy Schubert 1902b15cb3dSCy Schubert /* Get the current leap data signature. This consists of the last 1912b15cb3dSCy Schubert * ransition, the table expiration, and the total TAI difference at the 1922b15cb3dSCy Schubert * last transition. This is valid even if the leap transition itself was 1932b15cb3dSCy Schubert * culled due to the build date limit. 1942b15cb3dSCy Schubert */ 1952b15cb3dSCy Schubert extern void leapsec_getsig(leap_signature_t * psig); 1962b15cb3dSCy Schubert 1972b15cb3dSCy Schubert /* Check if the leap table is expired at the given time. 1982b15cb3dSCy Schubert */ 1992b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot); 2002b15cb3dSCy Schubert 2012b15cb3dSCy Schubert /* Get the distance to expiration in days. 2022b15cb3dSCy Schubert * Returns negative values if expired, zero if there are less than 24hrs 2032b15cb3dSCy Schubert * left, and positive numbers otherwise. 2042b15cb3dSCy Schubert */ 2052b15cb3dSCy Schubert extern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot); 2062b15cb3dSCy Schubert 2072b15cb3dSCy Schubert /* Reset the current leap frame, so the next query will do proper table 2082b15cb3dSCy Schubert * lookup from fresh. Suppresses a possible leap era transition detection 2092b15cb3dSCy Schubert * for the next query. 2102b15cb3dSCy Schubert */ 2112b15cb3dSCy Schubert extern void leapsec_reset_frame(void); 2122b15cb3dSCy Schubert 213276da39aSCy Schubert #if 0 /* currently unused -- possibly revived later */ 2142b15cb3dSCy Schubert /* Given a transition time, the TAI offset valid after that and an 2152b15cb3dSCy Schubert * expiration time, try to establish a system leap transition. Only 2162b15cb3dSCy Schubert * works if the existing table is extended. On success, updates the 2172b15cb3dSCy Schubert * signature data. 2182b15cb3dSCy Schubert */ 2192b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime, 2202b15cb3dSCy Schubert const time_t * pivot); 221276da39aSCy Schubert #endif 2222b15cb3dSCy Schubert 2232b15cb3dSCy Schubert /* Take a time stamp and create a leap second frame for it. This will 2242b15cb3dSCy Schubert * schedule a leap second for the beginning of the next month, midnight 2252b15cb3dSCy Schubert * UTC. The 'insert' argument tells if a leap second is added (!=0) or 2262b15cb3dSCy Schubert * removed (==0). We do not handle multiple inserts (yet?) 2272b15cb3dSCy Schubert * 2282b15cb3dSCy Schubert * Returns 1 if the insert worked, 0 otherwise. (It's not possible to 2292b15cb3dSCy Schubert * insert a leap second into the current history -- only appending 2302b15cb3dSCy Schubert * towards the future is allowed!) 2312b15cb3dSCy Schubert * 2322b15cb3dSCy Schubert * 'ntp_now' is subject to era unfolding. The entry is marked 2332b15cb3dSCy Schubert * dynamic. The leap signature is NOT updated. 2342b15cb3dSCy Schubert */ 2352b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now, 2362b15cb3dSCy Schubert const time_t * pivot); 2372b15cb3dSCy Schubert 2382b15cb3dSCy Schubert /* Take a time stamp and get the associated leap information. The time 2392b15cb3dSCy Schubert * stamp is subject to era unfolding around the pivot or the current 2402b15cb3dSCy Schubert * system time if pivot is NULL. Sets the information in '*qr' and 2412b15cb3dSCy Schubert * returns TRUE if a leap second era boundary was crossed between the 2422b15cb3dSCy Schubert * last and the current query. In that case, qr->warped contains the 2432b15cb3dSCy Schubert * required clock stepping, which is always zero in electric mode. 2442b15cb3dSCy Schubert */ 2452b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_query(leap_result_t * qr, uint32_t ntpts, 2462b15cb3dSCy Schubert const time_t * pivot); 2472b15cb3dSCy Schubert 248276da39aSCy Schubert /* For a given time stamp, fetch the data for the bracketing leap 249276da39aSCy Schubert * era. The time stamp is subject to NTP era unfolding. 250276da39aSCy Schubert */ 251276da39aSCy Schubert extern int/*BOOL*/ leapsec_query_era(leap_era_t * qr, uint32_t ntpts, 252276da39aSCy Schubert const time_t * pivot); 253276da39aSCy Schubert 2542b15cb3dSCy Schubert /* Get the current leap frame info. Returns TRUE if the result contains 2552b15cb3dSCy Schubert * useable data, FALSE if there is currently no leap second frame. 2562b15cb3dSCy Schubert * This merely replicates some results from a previous query, but since 2572b15cb3dSCy Schubert * it does not check the current time, only the following entries are 2582b15cb3dSCy Schubert * meaningful: 2592b15cb3dSCy Schubert * qr->ttime; 2602b15cb3dSCy Schubert * qr->tai_offs; 2612b15cb3dSCy Schubert * qr->tai_diff; 2622b15cb3dSCy Schubert * qr->dynamic; 2632b15cb3dSCy Schubert */ 2642b15cb3dSCy Schubert extern int/*BOOL*/ leapsec_frame(leap_result_t *qr); 2652b15cb3dSCy Schubert 266276da39aSCy Schubert 267276da39aSCy Schubert /* Process a AUTOKEY TAI offset information. This *might* augment the 268276da39aSCy Schubert * current leap data table with the given TAI offset. 269276da39aSCy Schubert * Returns TRUE if action was taken, FALSE otherwise. 270276da39aSCy Schubert */ 271276da39aSCy Schubert extern int/*BOOL*/ leapsec_autokey_tai(int tai_offset, uint32_t ntpnow, 272276da39aSCy Schubert const time_t * pivot); 273276da39aSCy Schubert 274276da39aSCy Schubert /* reset global state for unit tests */ 275276da39aSCy Schubert extern void leapsec_ut_pristine(void); 276276da39aSCy Schubert 2772b15cb3dSCy Schubert #endif /* !defined(NTP_LEAPSEC_H) */ 278