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