source: trunk/third/xntp/xntpd/ntp_leap.c @ 10832

Revision 10832, 7.2 KB checked in by brlewis, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r10831, which included commits to RCS files with non-trunk default branches.
Line 
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 */
41u_char leap_indicator;
42u_char leap_warning;
43u_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 */
48u_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 */
54extern 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 */
60u_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 */
74extern u_long current_time;
75
76
77/*
78 * Some statistics counters
79 */
80u_long leap_processcalls;       /* calls to leap_process */
81u_long leap_notclose;           /* leap found to be a long time from now */
82u_long leap_monthofleap;        /* in the month of a leap */
83u_long leap_dayofleap;          /* This is the day of the leap */
84u_long leap_hoursfromleap;      /* only 2 hours from leap */
85u_long leap_happened;           /* leap process saw the leap */
86
87/*
88 * Imported from the main module
89 */
90extern int debug;
91
92
93static void     setnexttimeout  P((u_long));
94
95/*
96 * init_leap - initialize the leap module's data.
97 */
98void
99init_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 */
118void
119leap_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 */
227static void
228setnexttimeout(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 */
244int
245leap_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*/
305int
306leap_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
Note: See TracBrowser for help on using the repository browser.