1 | /* |
---|
2 | * ntp_leap - maintain leap bits and take action when a leap occurs |
---|
3 | */ |
---|
4 | #ifdef HAVE_CONFIG_H |
---|
5 | #include <config.h> |
---|
6 | #endif |
---|
7 | |
---|
8 | #include <stdio.h> |
---|
9 | |
---|
10 | #include "ntpd.h" |
---|
11 | #include "ntp_stdlib.h" |
---|
12 | |
---|
13 | /* |
---|
14 | * This module is devoted to maintaining the leap bits and taking |
---|
15 | * action when a leap second occurs. It probably has bugs since |
---|
16 | * a leap second has never occurred to excercise the code. |
---|
17 | * |
---|
18 | * The code does two things when a leap second occurs. It first |
---|
19 | * steps the clock one second in the appropriate direction. It |
---|
20 | * then informs the reference clock code, if compiled in, that the |
---|
21 | * leap second has occurred so that any clocks which need to disable |
---|
22 | * themselves can do so. This is done within the first few seconds |
---|
23 | * after midnight, UTC. |
---|
24 | * |
---|
25 | * The code maintains two variables which may be written externally, |
---|
26 | * leap_warning and leap_indicator. Leap_warning can be written |
---|
27 | * any time in the month preceeding a leap second. 24 hours before |
---|
28 | * the leap is to occur, leap_warning's contents are copied to |
---|
29 | * leap_indicator. The latter is used by reference clocks to set |
---|
30 | * their leap bits. |
---|
31 | * |
---|
32 | * The module normally maintains a timer which is arranged to expire |
---|
33 | * just after 0000Z one day before the leap. On the day a leap might |
---|
34 | * occur the interrupt is aimed at 2200Z and every 5 minutes thereafter |
---|
35 | * until 1200Z to see if the leap bits appear. |
---|
36 | */ |
---|
37 | |
---|
38 | /* |
---|
39 | * The leap indicator and leap warning flags. Set by control messages |
---|
40 | */ |
---|
41 | u_char leap_indicator; |
---|
42 | u_char leap_warning; |
---|
43 | u_char leap_mask; /* set on day before a potential leap */ |
---|
44 | /* |
---|
45 | * Timer. The timer code imports this so it can call us prior to |
---|
46 | * calling out any pending transmits. |
---|
47 | */ |
---|
48 | u_long leap_timer; |
---|
49 | |
---|
50 | /* |
---|
51 | * We don't want to do anything drastic if the leap function is handled |
---|
52 | * by the kernel. |
---|
53 | */ |
---|
54 | extern int pll_control; /* set nonzero if kernel pll in uss */ |
---|
55 | |
---|
56 | /* |
---|
57 | * Internal leap bits. If we see leap bits set during the last |
---|
58 | * hour we set these. |
---|
59 | */ |
---|
60 | u_char leapbits; |
---|
61 | |
---|
62 | /* |
---|
63 | * Constants. |
---|
64 | */ |
---|
65 | #define OKAYTOSETWARNING (31*24*60*60) |
---|
66 | #define DAYBEFORE (24*60*60) |
---|
67 | #define TWOHOURSBEFORE (2*60*60) |
---|
68 | #define FIVEMINUTES (5*60) |
---|
69 | #define ONEMINUTE (60) |
---|
70 | |
---|
71 | /* |
---|
72 | * Imported from the timer module. |
---|
73 | */ |
---|
74 | extern u_long current_time; |
---|
75 | |
---|
76 | |
---|
77 | /* |
---|
78 | * Some statistics counters |
---|
79 | */ |
---|
80 | u_long leap_processcalls; /* calls to leap_process */ |
---|
81 | u_long leap_notclose; /* leap found to be a long time from now */ |
---|
82 | u_long leap_monthofleap; /* in the month of a leap */ |
---|
83 | u_long leap_dayofleap; /* This is the day of the leap */ |
---|
84 | u_long leap_hoursfromleap; /* only 2 hours from leap */ |
---|
85 | u_long leap_happened; /* leap process saw the leap */ |
---|
86 | |
---|
87 | /* |
---|
88 | * Imported from the main module |
---|
89 | */ |
---|
90 | extern int debug; |
---|
91 | |
---|
92 | |
---|
93 | static void setnexttimeout P((u_long)); |
---|
94 | |
---|
95 | /* |
---|
96 | * init_leap - initialize the leap module's data. |
---|
97 | */ |
---|
98 | void |
---|
99 | init_leap() |
---|
100 | { |
---|
101 | /* |
---|
102 | * Zero the indicators. Schedule an event for just after |
---|
103 | * initialization so we can sort things out. |
---|
104 | */ |
---|
105 | leap_indicator = leap_warning = leap_mask = 0; |
---|
106 | leap_timer = 1 << EVENT_TIMEOUT; |
---|
107 | leapbits = 0; |
---|
108 | |
---|
109 | leap_processcalls = leap_notclose = 0; |
---|
110 | leap_monthofleap = leap_dayofleap = 0; |
---|
111 | leap_hoursfromleap = leap_happened = 0; |
---|
112 | } |
---|
113 | |
---|
114 | |
---|
115 | /* |
---|
116 | * leap_process - process a leap event expiry and/or a system time step |
---|
117 | */ |
---|
118 | void |
---|
119 | leap_process() |
---|
120 | { |
---|
121 | u_long leapnext; |
---|
122 | u_long leaplast; |
---|
123 | l_fp ts; |
---|
124 | u_char bits; |
---|
125 | extern u_char sys_leap; |
---|
126 | |
---|
127 | leap_processcalls++; |
---|
128 | get_systime(&ts); |
---|
129 | calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext); |
---|
130 | |
---|
131 | /* |
---|
132 | * Figure out what to do based on how long to the next leap. |
---|
133 | */ |
---|
134 | if (leapnext > OKAYTOSETWARNING) { |
---|
135 | if (leaplast < ONEMINUTE) { |
---|
136 | /* |
---|
137 | * The golden moment! See if there's anything |
---|
138 | * to do. |
---|
139 | */ |
---|
140 | leap_happened++; |
---|
141 | bits = 0; |
---|
142 | leap_mask = 0; |
---|
143 | if (leap_indicator != 0) |
---|
144 | bits = leap_indicator; |
---|
145 | else if (leapbits != 0) |
---|
146 | bits = leapbits; |
---|
147 | |
---|
148 | if (bits != 0 && !pll_control) { |
---|
149 | l_fp tmp; |
---|
150 | |
---|
151 | /* |
---|
152 | * Step the clock 1 second in the proper |
---|
153 | * direction. |
---|
154 | */ |
---|
155 | if (bits == LEAP_DELSECOND) |
---|
156 | tmp.l_i = 1; |
---|
157 | else |
---|
158 | tmp.l_i = -1; |
---|
159 | tmp.l_uf = 0; |
---|
160 | |
---|
161 | step_systime(&tmp); |
---|
162 | NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT) /* conditional if clause for conditional syslog */ |
---|
163 | msyslog(LOG_NOTICE, |
---|
164 | #ifdef SLEWALWAYS |
---|
165 | "leap second occurred, slewed time %s 1 second", |
---|
166 | #else |
---|
167 | "leap second occurred, stepped time %s 1 second", |
---|
168 | #endif |
---|
169 | tmp.l_i > 0 ? "forward" : "back"); |
---|
170 | } |
---|
171 | } else { |
---|
172 | leap_notclose++; |
---|
173 | } |
---|
174 | leap_warning = 0; |
---|
175 | } else { |
---|
176 | if (leapnext > DAYBEFORE) |
---|
177 | leap_monthofleap++; |
---|
178 | else if (leapnext > TWOHOURSBEFORE) |
---|
179 | leap_dayofleap++; |
---|
180 | else |
---|
181 | leap_hoursfromleap++; |
---|
182 | } |
---|
183 | |
---|
184 | if (leapnext > DAYBEFORE) { |
---|
185 | leap_indicator = 0; |
---|
186 | leapbits = 0; |
---|
187 | /* |
---|
188 | * Berkeley's setitimer call does result in alarm |
---|
189 | * signal drift despite rumours to the contrary. |
---|
190 | * Schedule an event no more than 24 hours into |
---|
191 | * the future to allow the event time to be |
---|
192 | * recomputed. |
---|
193 | */ |
---|
194 | if ((leapnext - DAYBEFORE) >= DAYBEFORE) |
---|
195 | setnexttimeout((u_long)DAYBEFORE); |
---|
196 | else |
---|
197 | setnexttimeout(leapnext - DAYBEFORE); |
---|
198 | return; |
---|
199 | } |
---|
200 | |
---|
201 | /* |
---|
202 | * Here we're in the day of the leap. Set the leap indicator |
---|
203 | * bits from the warning, if necessary. |
---|
204 | */ |
---|
205 | if (leap_indicator == 0 && leap_warning != 0) |
---|
206 | leap_indicator = leap_warning; |
---|
207 | leap_mask = LEAP_NOTINSYNC; |
---|
208 | if (leapnext > TWOHOURSBEFORE) { |
---|
209 | leapbits = 0; |
---|
210 | setnexttimeout(leapnext - TWOHOURSBEFORE); |
---|
211 | return; |
---|
212 | } |
---|
213 | |
---|
214 | /* |
---|
215 | * Here we're in the final 2 hours. If sys_leap is set, set |
---|
216 | * leapbits to it. |
---|
217 | */ |
---|
218 | if (sys_leap == LEAP_ADDSECOND || sys_leap == LEAP_DELSECOND) |
---|
219 | leapbits = sys_leap; |
---|
220 | setnexttimeout((leapnext > FIVEMINUTES) ? FIVEMINUTES : leapnext); |
---|
221 | } |
---|
222 | |
---|
223 | |
---|
224 | /* |
---|
225 | * setnexttimeout - set the next leap alarm |
---|
226 | */ |
---|
227 | static void |
---|
228 | setnexttimeout(secs) |
---|
229 | u_long secs; |
---|
230 | { |
---|
231 | /* |
---|
232 | * We try to aim the time out at between 1 and 1+(1<<EVENT_TIMEOUT) |
---|
233 | * seconds after the desired time. |
---|
234 | */ |
---|
235 | leap_timer = (secs + 1 + (1<<EVENT_TIMEOUT) + current_time) |
---|
236 | & ~((1<<EVENT_TIMEOUT)-1); |
---|
237 | } |
---|
238 | |
---|
239 | |
---|
240 | /* |
---|
241 | * leap_setleap - set leap_indicator and/or leap_warning. Return failure |
---|
242 | * if we don't want to do it. |
---|
243 | */ |
---|
244 | int |
---|
245 | leap_setleap(indicator, warning) |
---|
246 | int indicator; |
---|
247 | int warning; |
---|
248 | { |
---|
249 | u_long leapnext; |
---|
250 | u_long leaplast; |
---|
251 | l_fp ts; |
---|
252 | int i; |
---|
253 | |
---|
254 | get_systime(&ts); |
---|
255 | calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext); |
---|
256 | |
---|
257 | i = 0; |
---|
258 | if (warning != ~0) |
---|
259 | if (leapnext > OKAYTOSETWARNING) |
---|
260 | i = 1; |
---|
261 | |
---|
262 | if (indicator != ~0) |
---|
263 | if (leapnext > DAYBEFORE) |
---|
264 | i = 1; |
---|
265 | |
---|
266 | if (i) { |
---|
267 | msyslog(LOG_ERR, |
---|
268 | "attempt to set leap bits at unlikely time of month"); |
---|
269 | return 0; |
---|
270 | } |
---|
271 | |
---|
272 | if (warning != ~0) |
---|
273 | leap_warning = warning; |
---|
274 | |
---|
275 | if (indicator != ~0) { |
---|
276 | if (indicator == LEAP_NOWARNING) { |
---|
277 | leap_warning = LEAP_NOWARNING; |
---|
278 | } |
---|
279 | leap_indicator = indicator; |
---|
280 | } |
---|
281 | return 1; |
---|
282 | } |
---|
283 | |
---|
284 | /* |
---|
285 | * leap_actual |
---|
286 | * |
---|
287 | * calculate leap value - pass arg through if no local |
---|
288 | * configuration. Otherwise ise local configuration |
---|
289 | * (only used to cope with broken time servers and |
---|
290 | * broken refclocks) |
---|
291 | * |
---|
292 | * Mapping of leap_indicator: |
---|
293 | * LEAP_NOWARNING |
---|
294 | * pass peer value to sys_leap - usual operation |
---|
295 | * LEAP_ADD/DEL_SECOND |
---|
296 | * pass LEAP_ADD/DEL_SECOND to sys_leap |
---|
297 | * LEAP_NOTINSYNC |
---|
298 | * pass LEAP_NOWARNING to sys_leap - effectively ignores leap |
---|
299 | */ |
---|
300 | /* there seems to be a bug in the IRIX 4 compiler which prevents |
---|
301 | u_char from beeing used in prototyped functions |
---|
302 | AIX also suffers from this. |
---|
303 | So give up and define it terms of int. |
---|
304 | */ |
---|
305 | int |
---|
306 | leap_actual(l) |
---|
307 | int l ; |
---|
308 | { |
---|
309 | if (leap_indicator != LEAP_NOWARNING) { |
---|
310 | if (leap_indicator == LEAP_NOTINSYNC) |
---|
311 | return LEAP_NOWARNING; |
---|
312 | else |
---|
313 | return leap_indicator; |
---|
314 | } else { |
---|
315 | return l; |
---|
316 | } |
---|
317 | } |
---|
318 | |
---|