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