1290001Sglebius/* 2290001Sglebius * ntp_leapsec.h - leap second processing for NTPD 3290001Sglebius * 4290001Sglebius * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5290001Sglebius * The contents of 'html/copyright.html' apply. 6290001Sglebius * ---------------------------------------------------------------------- 7290001Sglebius * This is an attempt to get the leap second handling into a dedicated 8290001Sglebius * module to make the somewhat convoluted logic testable. 9290001Sglebius */ 10290001Sglebius 11290001Sglebius#ifndef NTP_LEAPSEC_H 12290001Sglebius#define NTP_LEAPSEC_H 13290001Sglebius 14290001Sglebiusstruct stat; 15290001Sglebius 16290001Sglebius 17290001Sglebius/* function pointer types. Note that 'fprintf' and 'getc' can be casted 18290001Sglebius * to the dumper resp. reader type, provided the auxiliary argument is a 19290001Sglebius * valid FILE pointer in hat case. 20290001Sglebius */ 21290001Sglebiustypedef void (*leapsec_dumper)(void*, const char *fmt, ...); 22290001Sglebiustypedef int (*leapsec_reader)(void*); 23290001Sglebius 24290001Sglebiusstruct leap_table; 25290001Sglebiustypedef struct leap_table leap_table_t; 26290001Sglebius 27290001Sglebius/* Validate a stream containing a leap second file in the NIST / NTPD 28290001Sglebius * format that can also be loaded via 'leapsec_load()'. This uses 29290001Sglebius * the SHA1 hash and preprocessing as described in the NIST leapsecond 30290001Sglebius * file. 31290001Sglebius */ 32290001Sglebius#define LSVALID_GOODHASH 1 /* valid signature */ 33290001Sglebius#define LSVALID_NOHASH 0 /* no signature in file */ 34290001Sglebius#define LSVALID_BADHASH -1 /* signature mismatch */ 35290001Sglebius#define LSVALID_BADFORMAT -2 /* signature not parseable */ 36290001Sglebius 37290001Sglebiusextern int leapsec_validate(leapsec_reader, void*); 38290001Sglebius 39290001Sglebius 40290001Sglebius/* Set/get electric mode 41290001Sglebius * Electric mode is defined as the operation mode where the system clock 42290001Sglebius * automagically manages the leap second, so we don't have to care about 43290001Sglebius * stepping the clock. (This should be the case with most systems, 44290001Sglebius * including the current implementation of the Win32 timekeeping.) 45290001Sglebius * 46290001Sglebius * The consequence of electric mode is that we do not 'see' the leap 47290001Sglebius * second, and no client actions are needed when crossing the leap era 48290001Sglebius * boundary. In manual (aka non-electric) mode the clock will simply 49290001Sglebius * step forward untill *we* (that is, this module) tells the client app 50290001Sglebius * to step at the right time. This needs a slightly different type of 51290001Sglebius * processing, so switching between those two modes should not be done 52290001Sglebius * too close to a leap second. The transition might be lost in that 53290001Sglebius * case. (The limit is actual 2 sec before transition.) 54290001Sglebius * 55290001Sglebius * OTOH, this is a system characteristic, so it's expected to be set 56290001Sglebius * properly somewhere after system start and retain the value. 57290001Sglebius * 58290001Sglebius * Simply querying the state or setting it to the same value as before 59290001Sglebius * does not have any unwanted side effects. You can query by giving a 60290001Sglebius * negative value for the switch. 61290001Sglebius */ 62290001Sglebiusextern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on); 63290001Sglebius 64290001Sglebius/* Query result for a leap era. This is the minimal stateless 65290001Sglebius * information available for a time stamp in UTC. 66290001Sglebius */ 67290001Sglebiusstruct leap_era { 68290001Sglebius vint64 ebase; /* era base (UTC of start) */ 69290001Sglebius vint64 ttime; /* era end (UTC of next leap second) */ 70290001Sglebius int16_t taiof; /* offset to TAI in this era */ 71290001Sglebius}; 72290001Sglebiustypedef struct leap_era leap_era_t; 73290001Sglebius 74290001Sglebius/* Query result for a leap second schedule 75290001Sglebius * 'ebase' is the nominal UTC time when the current leap era 76290001Sglebius * started. (Era base time) 77290001Sglebius * 'ttime' is the next transition point in full time scale. (Nominal UTC 78290001Sglebius * time when the next leap era starts.) 79290001Sglebius * 'ddist' is the distance to the transition, in clock seconds. 80290001Sglebius * This is the distance to the due time, which is different 81290001Sglebius * from the transition time if the mode is non-electric. 82290001Sglebius * Only valid if 'tai_diff' is not zero. 83290001Sglebius * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always 84290001Sglebius * valid. 85290001Sglebius * 'tai_diff' is the change in TAI offset after the next leap 86290001Sglebius * transition. Zero if nothing is pending or too far ahead. 87290001Sglebius * 'warped' is set only once, when the the leap second occurred between 88290001Sglebius * two queries. Always zero in electric mode. If non-zero, 89290001Sglebius * immediately step the clock. 90290001Sglebius * 'proximity' is a proximity warning. See definitions below. This is 91290001Sglebius * more useful than an absolute difference to the leap second. 92290001Sglebius * 'dynamic' != 0 if entry was requested by clock/peer 93290001Sglebius */ 94290001Sglebiusstruct leap_result { 95290001Sglebius vint64 ebase; 96290001Sglebius vint64 ttime; 97290001Sglebius uint32_t ddist; 98290001Sglebius int16_t tai_offs; 99290001Sglebius int16_t tai_diff; 100290001Sglebius int16_t warped; 101290001Sglebius uint8_t proximity; 102290001Sglebius uint8_t dynamic; 103290001Sglebius}; 104290001Sglebiustypedef struct leap_result leap_result_t; 105290001Sglebius 106290001Sglebius/* The leap signature is used in two distinct circumstances, and it has 107290001Sglebius * slightly different content in these cases: 108290001Sglebius * - it is used to indictae the time range covered by the leap second 109290001Sglebius * table, and then it contains the last transition, TAI offset after 110290001Sglebius * the final transition, and the expiration time. 111290001Sglebius * - it is used to query data for AUTOKEY updates, and then it contains 112290001Sglebius * the *current* TAI offset, the *next* transition time and the 113290001Sglebius * expiration time of the table. 114290001Sglebius */ 115290001Sglebiusstruct leap_signature { 116290001Sglebius uint32_t etime; /* expiration time */ 117290001Sglebius uint32_t ttime; /* transition time */ 118290001Sglebius int16_t taiof; /* total offset to TAI */ 119290001Sglebius}; 120290001Sglebiustypedef struct leap_signature leap_signature_t; 121290001Sglebius 122290001Sglebius 123290001Sglebius#ifdef LEAP_SMEAR 124290001Sglebius 125290001Sglebiusstruct leap_smear_info { 126290001Sglebius int enabled; /* not 0 if smearing is generally enabled */ 127290001Sglebius int in_progress; /* not 0 if smearing is in progress, i.e. the offset has been computed */ 128290001Sglebius int leap_occurred; /* not 0 if the leap second has already occurred, i.e., during the leap second */ 129290001Sglebius double doffset; /* the current smear offset as double */ 130290001Sglebius l_fp offset; /* the current smear offset */ 131290001Sglebius uint32_t t_offset; /* the current time for which a smear offset has been computed */ 132290001Sglebius long interval; /* smear interval, in [s], should be at least some hours */ 133290001Sglebius double intv_start; /* start time of the smear interval */ 134290001Sglebius double intv_end; /* end time of the smear interval */ 135290001Sglebius}; 136290001Sglebiustypedef struct leap_smear_info leap_smear_info_t; 137290001Sglebius 138290001Sglebius#endif /* LEAP_SMEAR */ 139290001Sglebius 140290001Sglebius 141290001Sglebius#define LSPROX_NOWARN 0 /* clear radar screen */ 142290001Sglebius#define LSPROX_SCHEDULE 1 /* less than 1 month to target*/ 143290001Sglebius#define LSPROX_ANNOUNCE 2 /* less than 1 day to target */ 144290001Sglebius#define LSPROX_ALERT 3 /* less than 10 sec to target */ 145290001Sglebius 146290001Sglebius/* Get the current or alternate table pointer. Getting the alternate 147290001Sglebius * pointer will automatically copy the primary table, so it can be 148290001Sglebius * subsequently modified. 149290001Sglebius */ 150290001Sglebiusextern leap_table_t *leapsec_get_table(int alternate); 151290001Sglebius 152290001Sglebius/* Set the current leap table. Accepts only return values from 153290001Sglebius * 'leapsec_get_table()', so it's hard to do something wrong. Returns 154290001Sglebius * TRUE if the current table is the requested one. 155290001Sglebius */ 156290001Sglebiusextern int/*BOOL*/ leapsec_set_table(leap_table_t *); 157290001Sglebius 158290001Sglebius/* Clear all leap second data. Use it for init & cleanup */ 159290001Sglebiusextern void leapsec_clear(leap_table_t*); 160290001Sglebius 161290001Sglebius/* Load a leap second file. If 'blimit' is set, do not store (but 162290001Sglebius * register with their TAI offset) leap entries before the build date. 163290001Sglebius * Update the leap signature data on the fly. 164290001Sglebius */ 165290001Sglebiusextern int/*BOOL*/ leapsec_load(leap_table_t*, leapsec_reader, 166290001Sglebius void*, int blimit); 167290001Sglebius 168290001Sglebius/* Dump the current leap table in readable format, using the provided 169290001Sglebius * dump formatter function. 170290001Sglebius */ 171290001Sglebiusextern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg); 172290001Sglebius 173290001Sglebius/* Read a leap second file from stream. This is a convenience wrapper 174290001Sglebius * around the generic load function, 'leapsec_load()'. 175290001Sglebius */ 176290001Sglebiusextern int/*BOOL*/ leapsec_load_stream(FILE * fp, const char * fname, 177290001Sglebius int/*BOOL*/logall); 178290001Sglebius 179290001Sglebius/* Read a leap second file from file. It checks that the file exists and 180290001Sglebius * (if 'force' is not applied) the ctime/mtime has changed since the 181290001Sglebius * last load. If the file has to be loaded, either due to 'force' or 182290001Sglebius * changed time stamps, the 'stat()' results of the file are stored in 183290001Sglebius * '*sb' for the next cycle. Returns TRUE on successful load, FALSE 184290001Sglebius * otherwise. Uses 'leapsec_load_stream()' internally. 185290001Sglebius */ 186290001Sglebiusextern int/*BOOL*/ leapsec_load_file(const char * fname, struct stat * sb, 187290001Sglebius int/*BOOL*/force, int/*BOOL*/logall); 188290001Sglebius 189290001Sglebius/* Get the current leap data signature. This consists of the last 190290001Sglebius * ransition, the table expiration, and the total TAI difference at the 191290001Sglebius * last transition. This is valid even if the leap transition itself was 192290001Sglebius * culled due to the build date limit. 193290001Sglebius */ 194290001Sglebiusextern void leapsec_getsig(leap_signature_t * psig); 195290001Sglebius 196290001Sglebius/* Check if the leap table is expired at the given time. 197290001Sglebius */ 198290001Sglebiusextern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot); 199290001Sglebius 200290001Sglebius/* Get the distance to expiration in days. 201290001Sglebius * Returns negative values if expired, zero if there are less than 24hrs 202290001Sglebius * left, and positive numbers otherwise. 203290001Sglebius */ 204290001Sglebiusextern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot); 205290001Sglebius 206290001Sglebius/* Reset the current leap frame, so the next query will do proper table 207290001Sglebius * lookup from fresh. Suppresses a possible leap era transition detection 208290001Sglebius * for the next query. 209290001Sglebius */ 210290001Sglebiusextern void leapsec_reset_frame(void); 211290001Sglebius 212290001Sglebius#if 0 /* currently unused -- possibly revived later */ 213290001Sglebius/* Given a transition time, the TAI offset valid after that and an 214290001Sglebius * expiration time, try to establish a system leap transition. Only 215290001Sglebius * works if the existing table is extended. On success, updates the 216290001Sglebius * signature data. 217290001Sglebius */ 218290001Sglebiusextern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime, 219290001Sglebius const time_t * pivot); 220290001Sglebius#endif 221290001Sglebius 222290001Sglebius/* Take a time stamp and create a leap second frame for it. This will 223290001Sglebius * schedule a leap second for the beginning of the next month, midnight 224290001Sglebius * UTC. The 'insert' argument tells if a leap second is added (!=0) or 225290001Sglebius * removed (==0). We do not handle multiple inserts (yet?) 226290001Sglebius * 227290001Sglebius * Returns 1 if the insert worked, 0 otherwise. (It's not possible to 228290001Sglebius * insert a leap second into the current history -- only appending 229290001Sglebius * towards the future is allowed!) 230290001Sglebius * 231290001Sglebius * 'ntp_now' is subject to era unfolding. The entry is marked 232290001Sglebius * dynamic. The leap signature is NOT updated. 233290001Sglebius */ 234290001Sglebiusextern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now, 235290001Sglebius const time_t * pivot); 236290001Sglebius 237290001Sglebius/* Take a time stamp and get the associated leap information. The time 238290001Sglebius * stamp is subject to era unfolding around the pivot or the current 239290001Sglebius * system time if pivot is NULL. Sets the information in '*qr' and 240290001Sglebius * returns TRUE if a leap second era boundary was crossed between the 241290001Sglebius * last and the current query. In that case, qr->warped contains the 242290001Sglebius * required clock stepping, which is always zero in electric mode. 243290001Sglebius */ 244290001Sglebiusextern int/*BOOL*/ leapsec_query(leap_result_t * qr, uint32_t ntpts, 245290001Sglebius const time_t * pivot); 246290001Sglebius 247290001Sglebius/* For a given time stamp, fetch the data for the bracketing leap 248290001Sglebius * era. The time stamp is subject to NTP era unfolding. 249290001Sglebius */ 250290001Sglebiusextern int/*BOOL*/ leapsec_query_era(leap_era_t * qr, uint32_t ntpts, 251290001Sglebius const time_t * pivot); 252290001Sglebius 253290001Sglebius/* Get the current leap frame info. Returns TRUE if the result contains 254290001Sglebius * useable data, FALSE if there is currently no leap second frame. 255290001Sglebius * This merely replicates some results from a previous query, but since 256290001Sglebius * it does not check the current time, only the following entries are 257290001Sglebius * meaningful: 258290001Sglebius * qr->ttime; 259290001Sglebius * qr->tai_offs; 260290001Sglebius * qr->tai_diff; 261290001Sglebius * qr->dynamic; 262290001Sglebius */ 263290001Sglebiusextern int/*BOOL*/ leapsec_frame(leap_result_t *qr); 264290001Sglebius 265290001Sglebius 266290001Sglebius/* Process a AUTOKEY TAI offset information. This *might* augment the 267290001Sglebius * current leap data table with the given TAI offset. 268290001Sglebius * Returns TRUE if action was taken, FALSE otherwise. 269290001Sglebius */ 270290001Sglebiusextern int/*BOOL*/ leapsec_autokey_tai(int tai_offset, uint32_t ntpnow, 271290001Sglebius const time_t * pivot); 272290001Sglebius 273290001Sglebius/* reset global state for unit tests */ 274290001Sglebiusextern void leapsec_ut_pristine(void); 275290001Sglebius 276290001Sglebius#endif /* !defined(NTP_LEAPSEC_H) */ 277