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,
177358659Scy				       int/*BOOL*/logall, int/*BOOL*/vhash);
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,
187358659Scy				     int/*BOOL*/force, int/*BOOL*/logall,
188358659Scy				     int/*BOOL*/vhash);
189275970Scy
190275970Scy/* Get the current leap data signature. This consists of the last
191275970Scy * ransition, the table expiration, and the total TAI difference at the
192275970Scy * last transition. This is valid even if the leap transition itself was
193275970Scy * culled due to the build date limit.
194275970Scy */
195275970Scyextern void        leapsec_getsig(leap_signature_t * psig);
196275970Scy
197275970Scy/* Check if the leap table is expired at the given time.
198275970Scy */
199275970Scyextern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot);
200275970Scy
201275970Scy/* Get the distance to expiration in days.
202275970Scy * Returns negative values if expired, zero if there are less than 24hrs
203275970Scy * left, and positive numbers otherwise.
204275970Scy */
205275970Scyextern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot);
206275970Scy
207275970Scy/* Reset the current leap frame, so the next query will do proper table
208275970Scy * lookup from fresh. Suppresses a possible leap era transition detection
209275970Scy * for the next query.
210275970Scy */
211275970Scyextern void leapsec_reset_frame(void);
212275970Scy
213285169Scy#if 0 /* currently unused -- possibly revived later */
214275970Scy/* Given a transition time, the TAI offset valid after that and an
215275970Scy * expiration time, try to establish a system leap transition. Only
216275970Scy * works if the existing table is extended. On success, updates the
217275970Scy * signature data.
218275970Scy */
219275970Scyextern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime,
220275970Scy				   const time_t * pivot);
221285169Scy#endif
222275970Scy
223275970Scy/* Take a time stamp and create a leap second frame for it. This will
224275970Scy * schedule a leap second for the beginning of the next month, midnight
225275970Scy * UTC. The 'insert' argument tells if a leap second is added (!=0) or
226275970Scy * removed (==0). We do not handle multiple inserts (yet?)
227275970Scy *
228275970Scy * Returns 1 if the insert worked, 0 otherwise. (It's not possible to
229275970Scy * insert a leap second into the current history -- only appending
230275970Scy * towards the future is allowed!)
231275970Scy *
232275970Scy * 'ntp_now' is subject to era unfolding. The entry is marked
233275970Scy * dynamic. The leap signature is NOT updated.
234275970Scy */
235275970Scyextern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now,
236275970Scy				   const time_t * pivot);
237275970Scy
238275970Scy/* Take a time stamp and get the associated leap information. The time
239275970Scy * stamp is subject to era unfolding around the pivot or the current
240275970Scy * system time if pivot is NULL. Sets the information in '*qr' and
241275970Scy * returns TRUE if a leap second era boundary was crossed between the
242275970Scy * last and the current query. In that case, qr->warped contains the
243275970Scy * required clock stepping, which is always zero in electric mode.
244275970Scy */
245285169Scyextern int/*BOOL*/ leapsec_query(leap_result_t * qr, uint32_t ntpts,
246275970Scy				 const time_t * pivot);
247275970Scy
248285169Scy/* For a given time stamp, fetch the data for the bracketing leap
249285169Scy * era. The time stamp is subject to NTP era unfolding.
250285169Scy */
251285169Scyextern int/*BOOL*/ leapsec_query_era(leap_era_t * qr, uint32_t ntpts,
252285169Scy				     const time_t * pivot);
253285169Scy
254275970Scy/* Get the current leap frame info. Returns TRUE if the result contains
255275970Scy * useable data, FALSE if there is currently no leap second frame.
256275970Scy * This merely replicates some results from a previous query, but since
257275970Scy * it does not check the current time, only the following entries are
258275970Scy * meaningful:
259275970Scy *  qr->ttime;
260275970Scy *  qr->tai_offs;
261275970Scy *  qr->tai_diff;
262275970Scy *  qr->dynamic;
263275970Scy */
264275970Scyextern int/*BOOL*/ leapsec_frame(leap_result_t *qr);
265275970Scy
266285169Scy
267285169Scy/* Process a AUTOKEY TAI offset information. This *might* augment the
268285169Scy * current leap data table with the given TAI offset.
269285169Scy * Returns TRUE if action was taken, FALSE otherwise.
270285169Scy */
271285169Scyextern int/*BOOL*/ leapsec_autokey_tai(int tai_offset, uint32_t ntpnow,
272285169Scy				       const time_t * pivot);
273285169Scy
274285169Scy/* reset global state for unit tests */
275285169Scyextern void leapsec_ut_pristine(void);
276285169Scy
277275970Scy#endif /* !defined(NTP_LEAPSEC_H) */
278