Home | History | Annotate | Line # | Download | only in ntpd
      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 (at) 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