1275970Scy/* 2275970Scy * ntp_leapsec.h - leap second processing for NTPD 3275970Scy * 4275970Scy * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5275970Scy * The contents of 'html/copyright.html' apply. 6275970Scy * ---------------------------------------------------------------------- 7275970Scy * This is an attempt to get the leap second handling into a dedicated 8275970Scy * module to make the somewhat convoluted logic testable. 9275970Scy */ 10275970Scy 11275970Scy#ifndef NTP_LEAPSEC_H 12275970Scy#define NTP_LEAPSEC_H 13275970Scy 14275970Scystruct stat; 15275970Scy 16275970Scy 17275970Scy/* function pointer types. Note that 'fprintf' and 'getc' can be casted 18275970Scy * to the dumper resp. reader type, provided the auxiliary argument is a 19275970Scy * valid FILE pointer in hat case. 20275970Scy */ 21275970Scytypedef void (*leapsec_dumper)(void*, const char *fmt, ...); 22275970Scytypedef int (*leapsec_reader)(void*); 23275970Scy 24275970Scystruct leap_table; 25275970Scytypedef struct leap_table leap_table_t; 26275970Scy 27275970Scy/* Validate a stream containing a leap second file in the NIST / NTPD 28275970Scy * format that can also be loaded via 'leapsec_load()'. This uses 29275970Scy * the SHA1 hash and preprocessing as described in the NIST leapsecond 30275970Scy * file. 31275970Scy */ 32275970Scy#define LSVALID_GOODHASH 1 /* valid signature */ 33275970Scy#define LSVALID_NOHASH 0 /* no signature in file */ 34275970Scy#define LSVALID_BADHASH -1 /* signature mismatch */ 35275970Scy#define LSVALID_BADFORMAT -2 /* signature not parseable */ 36275970Scy 37275970Scyextern int leapsec_validate(leapsec_reader, void*); 38275970Scy 39275970Scy 40275970Scy/* Set/get electric mode 41275970Scy * Electric mode is defined as the operation mode where the system clock 42275970Scy * automagically manages the leap second, so we don't have to care about 43275970Scy * stepping the clock. (This should be the case with most systems, 44275970Scy * including the current implementation of the Win32 timekeeping.) 45275970Scy * 46275970Scy * The consequence of electric mode is that we do not 'see' the leap 47275970Scy * second, and no client actions are needed when crossing the leap era 48275970Scy * boundary. In manual (aka non-electric) mode the clock will simply 49275970Scy * step forward untill *we* (that is, this module) tells the client app 50275970Scy * to step at the right time. This needs a slightly different type of 51275970Scy * processing, so switching between those two modes should not be done 52275970Scy * too close to a leap second. The transition might be lost in that 53275970Scy * case. (The limit is actual 2 sec before transition.) 54275970Scy * 55275970Scy * OTOH, this is a system characteristic, so it's expected to be set 56275970Scy * properly somewhere after system start and retain the value. 57275970Scy * 58275970Scy * Simply querying the state or setting it to the same value as before 59275970Scy * does not have any unwanted side effects. You can query by giving a 60275970Scy * negative value for the switch. 61275970Scy */ 62275970Scyextern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on); 63275970Scy 64285169Scy/* Query result for a leap era. This is the minimal stateless 65285169Scy * information available for a time stamp in UTC. 66285169Scy */ 67285169Scystruct leap_era { 68285169Scy vint64 ebase; /* era base (UTC of start) */ 69285169Scy vint64 ttime; /* era end (UTC of next leap second) */ 70285169Scy int16_t taiof; /* offset to TAI in this era */ 71285169Scy}; 72285169Scytypedef struct leap_era leap_era_t; 73275970Scy 74275970Scy/* Query result for a leap second schedule 75285169Scy * 'ebase' is the nominal UTC time when the current leap era 76285169Scy * started. (Era base time) 77285169Scy * 'ttime' is the next transition point in full time scale. (Nominal UTC 78285169Scy * time when the next leap era starts.) 79275970Scy * 'ddist' is the distance to the transition, in clock seconds. 80275970Scy * This is the distance to the due time, which is different 81275970Scy * from the transition time if the mode is non-electric. 82275970Scy * Only valid if 'tai_diff' is not zero. 83285169Scy * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always 84285169Scy * valid. 85275970Scy * 'tai_diff' is the change in TAI offset after the next leap 86275970Scy * transition. Zero if nothing is pending or too far ahead. 87275970Scy * 'warped' is set only once, when the the leap second occurred between 88275970Scy * two queries. Always zero in electric mode. If non-zero, 89275970Scy * immediately step the clock. 90275970Scy * 'proximity' is a proximity warning. See definitions below. This is 91275970Scy * more useful than an absolute difference to the leap second. 92275970Scy * 'dynamic' != 0 if entry was requested by clock/peer 93285169Scy */ 94275970Scystruct leap_result { 95285169Scy vint64 ebase; 96275970Scy vint64 ttime; 97275970Scy uint32_t ddist; 98275970Scy int16_t tai_offs; 99275970Scy int16_t tai_diff; 100275970Scy int16_t warped; 101275970Scy uint8_t proximity; 102275970Scy uint8_t dynamic; 103275970Scy}; 104275970Scytypedef struct leap_result leap_result_t; 105275970Scy 106285169Scy/* The leap signature is used in two distinct circumstances, and it has 107285169Scy * slightly different content in these cases: 108285169Scy * - it is used to indictae the time range covered by the leap second 109285169Scy * table, and then it contains the last transition, TAI offset after 110285169Scy * the final transition, and the expiration time. 111285169Scy * - it is used to query data for AUTOKEY updates, and then it contains 112285169Scy * the *current* TAI offset, the *next* transition time and the 113285169Scy * expiration time of the table. 114285169Scy */ 115275970Scystruct leap_signature { 116275970Scy uint32_t etime; /* expiration time */ 117275970Scy uint32_t ttime; /* transition time */ 118275970Scy int16_t taiof; /* total offset to TAI */ 119275970Scy}; 120275970Scytypedef struct leap_signature leap_signature_t; 121275970Scy 122275970Scy 123285169Scy#ifdef LEAP_SMEAR 124285169Scy 125285169Scystruct leap_smear_info { 126285169Scy int enabled; /* not 0 if smearing is generally enabled */ 127285169Scy int in_progress; /* not 0 if smearing is in progress, i.e. the offset has been computed */ 128285169Scy int leap_occurred; /* not 0 if the leap second has already occurred, i.e., during the leap second */ 129285169Scy double doffset; /* the current smear offset as double */ 130285169Scy l_fp offset; /* the current smear offset */ 131285169Scy uint32_t t_offset; /* the current time for which a smear offset has been computed */ 132285169Scy long interval; /* smear interval, in [s], should be at least some hours */ 133285169Scy double intv_start; /* start time of the smear interval */ 134285169Scy double intv_end; /* end time of the smear interval */ 135285169Scy}; 136285169Scytypedef struct leap_smear_info leap_smear_info_t; 137285169Scy 138285169Scy#endif /* LEAP_SMEAR */ 139285169Scy 140285169Scy 141275970Scy#define LSPROX_NOWARN 0 /* clear radar screen */ 142275970Scy#define LSPROX_SCHEDULE 1 /* less than 1 month to target*/ 143275970Scy#define LSPROX_ANNOUNCE 2 /* less than 1 day to target */ 144275970Scy#define LSPROX_ALERT 3 /* less than 10 sec to target */ 145275970Scy 146275970Scy/* Get the current or alternate table pointer. Getting the alternate 147275970Scy * pointer will automatically copy the primary table, so it can be 148275970Scy * subsequently modified. 149275970Scy */ 150275970Scyextern leap_table_t *leapsec_get_table(int alternate); 151275970Scy 152275970Scy/* Set the current leap table. Accepts only return values from 153275970Scy * 'leapsec_get_table()', so it's hard to do something wrong. Returns 154275970Scy * TRUE if the current table is the requested one. 155275970Scy */ 156275970Scyextern int/*BOOL*/ leapsec_set_table(leap_table_t *); 157275970Scy 158275970Scy/* Clear all leap second data. Use it for init & cleanup */ 159275970Scyextern void leapsec_clear(leap_table_t*); 160275970Scy 161275970Scy/* Load a leap second file. If 'blimit' is set, do not store (but 162275970Scy * register with their TAI offset) leap entries before the build date. 163275970Scy * Update the leap signature data on the fly. 164275970Scy */ 165275970Scyextern int/*BOOL*/ leapsec_load(leap_table_t*, leapsec_reader, 166275970Scy void*, int blimit); 167275970Scy 168275970Scy/* Dump the current leap table in readable format, using the provided 169275970Scy * dump formatter function. 170275970Scy */ 171275970Scyextern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg); 172275970Scy 173275970Scy/* Read a leap second file from stream. This is a convenience wrapper 174275970Scy * around the generic load function, 'leapsec_load()'. 175275970Scy */ 176275970Scyextern int/*BOOL*/ leapsec_load_stream(FILE * fp, const char * fname, 177275970Scy int/*BOOL*/logall); 178275970Scy 179275970Scy/* Read a leap second file from file. It checks that the file exists and 180275970Scy * (if 'force' is not applied) the ctime/mtime has changed since the 181275970Scy * last load. If the file has to be loaded, either due to 'force' or 182275970Scy * changed time stamps, the 'stat()' results of the file are stored in 183275970Scy * '*sb' for the next cycle. Returns TRUE on successful load, FALSE 184275970Scy * otherwise. Uses 'leapsec_load_stream()' internally. 185275970Scy */ 186275970Scyextern int/*BOOL*/ leapsec_load_file(const char * fname, struct stat * sb, 187275970Scy int/*BOOL*/force, int/*BOOL*/logall); 188275970Scy 189275970Scy/* Get the current leap data signature. This consists of the last 190275970Scy * ransition, the table expiration, and the total TAI difference at the 191275970Scy * last transition. This is valid even if the leap transition itself was 192275970Scy * culled due to the build date limit. 193275970Scy */ 194275970Scyextern void leapsec_getsig(leap_signature_t * psig); 195275970Scy 196275970Scy/* Check if the leap table is expired at the given time. 197275970Scy */ 198275970Scyextern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot); 199275970Scy 200275970Scy/* Get the distance to expiration in days. 201275970Scy * Returns negative values if expired, zero if there are less than 24hrs 202275970Scy * left, and positive numbers otherwise. 203275970Scy */ 204275970Scyextern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot); 205275970Scy 206275970Scy/* Reset the current leap frame, so the next query will do proper table 207275970Scy * lookup from fresh. Suppresses a possible leap era transition detection 208275970Scy * for the next query. 209275970Scy */ 210275970Scyextern void leapsec_reset_frame(void); 211275970Scy 212285169Scy#if 0 /* currently unused -- possibly revived later */ 213275970Scy/* Given a transition time, the TAI offset valid after that and an 214275970Scy * expiration time, try to establish a system leap transition. Only 215275970Scy * works if the existing table is extended. On success, updates the 216275970Scy * signature data. 217275970Scy */ 218275970Scyextern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime, 219275970Scy const time_t * pivot); 220285169Scy#endif 221275970Scy 222275970Scy/* Take a time stamp and create a leap second frame for it. This will 223275970Scy * schedule a leap second for the beginning of the next month, midnight 224275970Scy * UTC. The 'insert' argument tells if a leap second is added (!=0) or 225275970Scy * removed (==0). We do not handle multiple inserts (yet?) 226275970Scy * 227275970Scy * Returns 1 if the insert worked, 0 otherwise. (It's not possible to 228275970Scy * insert a leap second into the current history -- only appending 229275970Scy * towards the future is allowed!) 230275970Scy * 231275970Scy * 'ntp_now' is subject to era unfolding. The entry is marked 232275970Scy * dynamic. The leap signature is NOT updated. 233275970Scy */ 234275970Scyextern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now, 235275970Scy const time_t * pivot); 236275970Scy 237275970Scy/* Take a time stamp and get the associated leap information. The time 238275970Scy * stamp is subject to era unfolding around the pivot or the current 239275970Scy * system time if pivot is NULL. Sets the information in '*qr' and 240275970Scy * returns TRUE if a leap second era boundary was crossed between the 241275970Scy * last and the current query. In that case, qr->warped contains the 242275970Scy * required clock stepping, which is always zero in electric mode. 243275970Scy */ 244285169Scyextern int/*BOOL*/ leapsec_query(leap_result_t * qr, uint32_t ntpts, 245275970Scy const time_t * pivot); 246275970Scy 247285169Scy/* For a given time stamp, fetch the data for the bracketing leap 248285169Scy * era. The time stamp is subject to NTP era unfolding. 249285169Scy */ 250285169Scyextern int/*BOOL*/ leapsec_query_era(leap_era_t * qr, uint32_t ntpts, 251285169Scy const time_t * pivot); 252285169Scy 253275970Scy/* Get the current leap frame info. Returns TRUE if the result contains 254275970Scy * useable data, FALSE if there is currently no leap second frame. 255275970Scy * This merely replicates some results from a previous query, but since 256275970Scy * it does not check the current time, only the following entries are 257275970Scy * meaningful: 258275970Scy * qr->ttime; 259275970Scy * qr->tai_offs; 260275970Scy * qr->tai_diff; 261275970Scy * qr->dynamic; 262275970Scy */ 263275970Scyextern int/*BOOL*/ leapsec_frame(leap_result_t *qr); 264275970Scy 265285169Scy 266285169Scy/* Process a AUTOKEY TAI offset information. This *might* augment the 267285169Scy * current leap data table with the given TAI offset. 268285169Scy * Returns TRUE if action was taken, FALSE otherwise. 269285169Scy */ 270285169Scyextern int/*BOOL*/ leapsec_autokey_tai(int tai_offset, uint32_t ntpnow, 271285169Scy const time_t * pivot); 272285169Scy 273285169Scy/* reset global state for unit tests */ 274285169Scyextern void leapsec_ut_pristine(void); 275285169Scy 276275970Scy#endif /* !defined(NTP_LEAPSEC_H) */ 277