1 /* 2 * ntp_leapsec.h - leap second processing for NTPD 3 * 4 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5 * The contents of 'html/copyright.html' apply. 6 * ---------------------------------------------------------------------- 7 * This is an attempt to get the leap second handling into a dedicated 8 * module to make the somewhat convoluted logic testable. 9 */ 10 11 #ifndef NTP_LEAPSEC_H 12 #define NTP_LEAPSEC_H 13 14 struct stat; 15 16 17 /* function pointer types. Note that 'fprintf' and 'getc' can be casted 18 * to the dumper resp. reader type, provided the auxiliary argument is a 19 * valid FILE pointer in hat case. 20 */ 21 typedef void (*leapsec_dumper)(void*, const char *fmt, ...); 22 typedef int (*leapsec_reader)(void*); 23 24 struct leap_table; 25 typedef struct leap_table leap_table_t; 26 27 /* Validate a stream containing a leap second file in the NIST / NTPD 28 * format that can also be loaded via 'leapsec_load()'. This uses 29 * the SHA1 hash and preprocessing as described in the NIST leapsecond 30 * file. 31 */ 32 #define LSVALID_GOODHASH 1 /* valid signature */ 33 #define LSVALID_NOHASH 0 /* no signature in file */ 34 #define LSVALID_BADHASH -1 /* signature mismatch */ 35 #define LSVALID_BADFORMAT -2 /* signature not parseable */ 36 37 extern int leapsec_validate(leapsec_reader, void*); 38 39 40 /* Set/get electric mode 41 * Electric mode is defined as the operation mode where the system clock 42 * automagically manages the leap second, so we don't have to care about 43 * stepping the clock. (This should be the case with most systems, 44 * including the current implementation of the Win32 timekeeping.) 45 * 46 * The consequence of electric mode is that we do not 'see' the leap 47 * second, and no client actions are needed when crossing the leap era 48 * boundary. In manual (aka non-electric) mode the clock will simply 49 * step forward untill *we* (that is, this module) tells the client app 50 * to step at the right time. This needs a slightly different type of 51 * processing, so switching between those two modes should not be done 52 * too close to a leap second. The transition might be lost in that 53 * case. (The limit is actual 2 sec before transition.) 54 * 55 * OTOH, this is a system characteristic, so it's expected to be set 56 * properly somewhere after system start and retain the value. 57 * 58 * Simply querying the state or setting it to the same value as before 59 * does not have any unwanted side effects. You can query by giving a 60 * negative value for the switch. 61 */ 62 extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on); 63 64 65 /* Query result for a leap second schedule 66 * 'ttime' is the transition point in full time scale, but only if 67 * 'tai_diff' is not zero. Nominal UTC time when the next leap 68 * era starts. 69 * 'ddist' is the distance to the transition, in clock seconds. 70 * This is the distance to the due time, which is different 71 * from the transition time if the mode is non-electric. 72 * Only valid if 'tai_diff' is not zero. 73 * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always valid. 74 * 'tai_diff' is the change in TAI offset after the next leap 75 * transition. Zero if nothing is pending or too far ahead. 76 * 'warped' is set only once, when the the leap second occurred between 77 * two queries. Always zero in electric mode. If non-zero, 78 * immediately step the clock. 79 * 'proximity' is a proximity warning. See definitions below. This is 80 * more useful than an absolute difference to the leap second. 81 * 'dynamic' != 0 if entry was requested by clock/peer 82 */ 83 struct leap_result { 84 vint64 ttime; 85 uint32_t ddist; 86 int16_t tai_offs; 87 int16_t tai_diff; 88 int16_t warped; 89 uint8_t proximity; 90 uint8_t dynamic; 91 }; 92 typedef struct leap_result leap_result_t; 93 94 struct leap_signature { 95 uint32_t etime; /* expiration time */ 96 uint32_t ttime; /* transition time */ 97 int16_t taiof; /* total offset to TAI */ 98 }; 99 typedef struct leap_signature leap_signature_t; 100 101 102 #define LSPROX_NOWARN 0 /* clear radar screen */ 103 #define LSPROX_SCHEDULE 1 /* less than 1 month to target*/ 104 #define LSPROX_ANNOUNCE 2 /* less than 1 day to target */ 105 #define LSPROX_ALERT 3 /* less than 10 sec to target */ 106 107 /* Get the current or alternate table pointer. Getting the alternate 108 * pointer will automatically copy the primary table, so it can be 109 * subsequently modified. 110 */ 111 extern leap_table_t *leapsec_get_table(int alternate); 112 113 /* Set the current leap table. Accepts only return values from 114 * 'leapsec_get_table()', so it's hard to do something wrong. Returns 115 * TRUE if the current table is the requested one. 116 */ 117 extern int/*BOOL*/ leapsec_set_table(leap_table_t *); 118 119 /* Clear all leap second data. Use it for init & cleanup */ 120 extern void leapsec_clear(leap_table_t*); 121 122 /* Load a leap second file. If 'blimit' is set, do not store (but 123 * register with their TAI offset) leap entries before the build date. 124 * Update the leap signature data on the fly. 125 */ 126 extern int/*BOOL*/ leapsec_load(leap_table_t*, leapsec_reader, 127 void*, int blimit); 128 129 /* Dump the current leap table in readable format, using the provided 130 * dump formatter function. 131 */ 132 extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg); 133 134 /* Read a leap second file from stream. This is a convenience wrapper 135 * around the generic load function, 'leapsec_load()'. 136 */ 137 extern int/*BOOL*/ leapsec_load_stream(FILE * fp, const char * fname, 138 int/*BOOL*/logall); 139 140 /* Read a leap second file from file. It checks that the file exists and 141 * (if 'force' is not applied) the ctime/mtime has changed since the 142 * last load. If the file has to be loaded, either due to 'force' or 143 * changed time stamps, the 'stat()' results of the file are stored in 144 * '*sb' for the next cycle. Returns TRUE on successful load, FALSE 145 * otherwise. Uses 'leapsec_load_stream()' internally. 146 */ 147 extern int/*BOOL*/ leapsec_load_file(const char * fname, struct stat * sb, 148 int/*BOOL*/force, int/*BOOL*/logall); 149 150 /* Get the current leap data signature. This consists of the last 151 * ransition, the table expiration, and the total TAI difference at the 152 * last transition. This is valid even if the leap transition itself was 153 * culled due to the build date limit. 154 */ 155 extern void leapsec_getsig(leap_signature_t * psig); 156 157 /* Check if the leap table is expired at the given time. 158 */ 159 extern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot); 160 161 /* Get the distance to expiration in days. 162 * Returns negative values if expired, zero if there are less than 24hrs 163 * left, and positive numbers otherwise. 164 */ 165 extern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot); 166 167 /* Reset the current leap frame, so the next query will do proper table 168 * lookup from fresh. Suppresses a possible leap era transition detection 169 * for the next query. 170 */ 171 extern void leapsec_reset_frame(void); 172 173 /* Given a transition time, the TAI offset valid after that and an 174 * expiration time, try to establish a system leap transition. Only 175 * works if the existing table is extended. On success, updates the 176 * signature data. 177 */ 178 extern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime, 179 const time_t * pivot); 180 181 /* Take a time stamp and create a leap second frame for it. This will 182 * schedule a leap second for the beginning of the next month, midnight 183 * UTC. The 'insert' argument tells if a leap second is added (!=0) or 184 * removed (==0). We do not handle multiple inserts (yet?) 185 * 186 * Returns 1 if the insert worked, 0 otherwise. (It's not possible to 187 * insert a leap second into the current history -- only appending 188 * towards the future is allowed!) 189 * 190 * 'ntp_now' is subject to era unfolding. The entry is marked 191 * dynamic. The leap signature is NOT updated. 192 */ 193 extern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now, 194 const time_t * pivot); 195 196 /* Take a time stamp and get the associated leap information. The time 197 * stamp is subject to era unfolding around the pivot or the current 198 * system time if pivot is NULL. Sets the information in '*qr' and 199 * returns TRUE if a leap second era boundary was crossed between the 200 * last and the current query. In that case, qr->warped contains the 201 * required clock stepping, which is always zero in electric mode. 202 */ 203 extern int/*BOOL*/ leapsec_query(leap_result_t *qr, uint32_t ntpts, 204 const time_t * pivot); 205 206 /* Get the current leap frame info. Returns TRUE if the result contains 207 * useable data, FALSE if there is currently no leap second frame. 208 * This merely replicates some results from a previous query, but since 209 * it does not check the current time, only the following entries are 210 * meaningful: 211 * qr->ttime; 212 * qr->tai_offs; 213 * qr->tai_diff; 214 * qr->dynamic; 215 */ 216 extern int/*BOOL*/ leapsec_frame(leap_result_t *qr); 217 218 #endif /* !defined(NTP_LEAPSEC_H) */ 219