321 lines
7.3 KiB
C
321 lines
7.3 KiB
C
/* $NetBSD: ntp_leap.c,v 1.2 1998/01/09 06:06:38 perry Exp $ */
|
|
|
|
/*
|
|
* ntp_leap - maintain leap bits and take action when a leap occurs
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "ntpd.h"
|
|
#include "ntp_stdlib.h"
|
|
|
|
/*
|
|
* This module is devoted to maintaining the leap bits and taking
|
|
* action when a leap second occurs. It probably has bugs since
|
|
* a leap second has never occurred to excercise the code.
|
|
*
|
|
* The code does two things when a leap second occurs. It first
|
|
* steps the clock one second in the appropriate direction. It
|
|
* then informs the reference clock code, if compiled in, that the
|
|
* leap second has occurred so that any clocks which need to disable
|
|
* themselves can do so. This is done within the first few seconds
|
|
* after midnight, UTC.
|
|
*
|
|
* The code maintains two variables which may be written externally,
|
|
* leap_warning and leap_indicator. Leap_warning can be written
|
|
* any time in the month preceeding a leap second. 24 hours before
|
|
* the leap is to occur, leap_warning's contents are copied to
|
|
* leap_indicator. The latter is used by reference clocks to set
|
|
* their leap bits.
|
|
*
|
|
* The module normally maintains a timer which is arranged to expire
|
|
* just after 0000Z one day before the leap. On the day a leap might
|
|
* occur the interrupt is aimed at 2200Z and every 5 minutes thereafter
|
|
* until 1200Z to see if the leap bits appear.
|
|
*/
|
|
|
|
/*
|
|
* The leap indicator and leap warning flags. Set by control messages
|
|
*/
|
|
u_char leap_indicator;
|
|
u_char leap_warning;
|
|
u_char leap_mask; /* set on day before a potential leap */
|
|
/*
|
|
* Timer. The timer code imports this so it can call us prior to
|
|
* calling out any pending transmits.
|
|
*/
|
|
u_long leap_timer;
|
|
|
|
/*
|
|
* We don't want to do anything drastic if the leap function is handled
|
|
* by the kernel.
|
|
*/
|
|
extern int pll_control; /* set nonzero if kernel pll in uss */
|
|
|
|
/*
|
|
* Internal leap bits. If we see leap bits set during the last
|
|
* hour we set these.
|
|
*/
|
|
u_char leapbits;
|
|
|
|
/*
|
|
* Constants.
|
|
*/
|
|
#define OKAYTOSETWARNING (31*24*60*60)
|
|
#define DAYBEFORE (24*60*60)
|
|
#define TWOHOURSBEFORE (2*60*60)
|
|
#define FIVEMINUTES (5*60)
|
|
#define ONEMINUTE (60)
|
|
|
|
/*
|
|
* Imported from the timer module.
|
|
*/
|
|
extern u_long current_time;
|
|
|
|
|
|
/*
|
|
* Some statistics counters
|
|
*/
|
|
u_long leap_processcalls; /* calls to leap_process */
|
|
u_long leap_notclose; /* leap found to be a long time from now */
|
|
u_long leap_monthofleap; /* in the month of a leap */
|
|
u_long leap_dayofleap; /* This is the day of the leap */
|
|
u_long leap_hoursfromleap; /* only 2 hours from leap */
|
|
u_long leap_happened; /* leap process saw the leap */
|
|
|
|
/*
|
|
* Imported from the main module
|
|
*/
|
|
extern int debug;
|
|
|
|
|
|
static void setnexttimeout P((u_long));
|
|
|
|
/*
|
|
* init_leap - initialize the leap module's data.
|
|
*/
|
|
void
|
|
init_leap()
|
|
{
|
|
/*
|
|
* Zero the indicators. Schedule an event for just after
|
|
* initialization so we can sort things out.
|
|
*/
|
|
leap_indicator = leap_warning = leap_mask = 0;
|
|
leap_timer = 1 << EVENT_TIMEOUT;
|
|
leapbits = 0;
|
|
|
|
leap_processcalls = leap_notclose = 0;
|
|
leap_monthofleap = leap_dayofleap = 0;
|
|
leap_hoursfromleap = leap_happened = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* leap_process - process a leap event expiry and/or a system time step
|
|
*/
|
|
void
|
|
leap_process()
|
|
{
|
|
u_long leapnext;
|
|
u_long leaplast;
|
|
l_fp ts;
|
|
u_char bits;
|
|
extern u_char sys_leap;
|
|
|
|
leap_processcalls++;
|
|
get_systime(&ts);
|
|
calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
|
|
|
|
/*
|
|
* Figure out what to do based on how long to the next leap.
|
|
*/
|
|
if (leapnext > OKAYTOSETWARNING) {
|
|
if (leaplast < ONEMINUTE) {
|
|
/*
|
|
* The golden moment! See if there's anything
|
|
* to do.
|
|
*/
|
|
leap_happened++;
|
|
bits = 0;
|
|
leap_mask = 0;
|
|
if (leap_indicator != 0)
|
|
bits = leap_indicator;
|
|
else if (leapbits != 0)
|
|
bits = leapbits;
|
|
|
|
if (bits != 0 && !pll_control) {
|
|
l_fp tmp;
|
|
|
|
/*
|
|
* Step the clock 1 second in the proper
|
|
* direction.
|
|
*/
|
|
if (bits == LEAP_DELSECOND)
|
|
tmp.l_i = 1;
|
|
else
|
|
tmp.l_i = -1;
|
|
tmp.l_uf = 0;
|
|
|
|
step_systime(&tmp);
|
|
NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT) /* conditional if clause for conditional syslog */
|
|
msyslog(LOG_NOTICE,
|
|
#ifdef SLEWALWAYS
|
|
"leap second occurred, slewed time %s 1 second",
|
|
#else
|
|
"leap second occurred, stepped time %s 1 second",
|
|
#endif
|
|
tmp.l_i > 0 ? "forward" : "back");
|
|
}
|
|
} else {
|
|
leap_notclose++;
|
|
}
|
|
leap_warning = 0;
|
|
} else {
|
|
if (leapnext > DAYBEFORE)
|
|
leap_monthofleap++;
|
|
else if (leapnext > TWOHOURSBEFORE)
|
|
leap_dayofleap++;
|
|
else
|
|
leap_hoursfromleap++;
|
|
}
|
|
|
|
if (leapnext > DAYBEFORE) {
|
|
leap_indicator = 0;
|
|
leapbits = 0;
|
|
/*
|
|
* Berkeley's setitimer call does result in alarm
|
|
* signal drift despite rumours to the contrary.
|
|
* Schedule an event no more than 24 hours into
|
|
* the future to allow the event time to be
|
|
* recomputed.
|
|
*/
|
|
if ((leapnext - DAYBEFORE) >= DAYBEFORE)
|
|
setnexttimeout((u_long)DAYBEFORE);
|
|
else
|
|
setnexttimeout(leapnext - DAYBEFORE);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Here we're in the day of the leap. Set the leap indicator
|
|
* bits from the warning, if necessary.
|
|
*/
|
|
if (leap_indicator == 0 && leap_warning != 0)
|
|
leap_indicator = leap_warning;
|
|
leap_mask = LEAP_NOTINSYNC;
|
|
if (leapnext > TWOHOURSBEFORE) {
|
|
leapbits = 0;
|
|
setnexttimeout(leapnext - TWOHOURSBEFORE);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Here we're in the final 2 hours. If sys_leap is set, set
|
|
* leapbits to it.
|
|
*/
|
|
if (sys_leap == LEAP_ADDSECOND || sys_leap == LEAP_DELSECOND)
|
|
leapbits = sys_leap;
|
|
setnexttimeout((leapnext > FIVEMINUTES) ? FIVEMINUTES : leapnext);
|
|
}
|
|
|
|
|
|
/*
|
|
* setnexttimeout - set the next leap alarm
|
|
*/
|
|
static void
|
|
setnexttimeout(secs)
|
|
u_long secs;
|
|
{
|
|
/*
|
|
* We try to aim the time out at between 1 and 1+(1<<EVENT_TIMEOUT)
|
|
* seconds after the desired time.
|
|
*/
|
|
leap_timer = (secs + 1 + (1<<EVENT_TIMEOUT) + current_time)
|
|
& ~((1<<EVENT_TIMEOUT)-1);
|
|
}
|
|
|
|
|
|
/*
|
|
* leap_setleap - set leap_indicator and/or leap_warning. Return failure
|
|
* if we don't want to do it.
|
|
*/
|
|
int
|
|
leap_setleap(indicator, warning)
|
|
int indicator;
|
|
int warning;
|
|
{
|
|
u_long leapnext;
|
|
u_long leaplast;
|
|
l_fp ts;
|
|
int i;
|
|
|
|
get_systime(&ts);
|
|
calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
|
|
|
|
i = 0;
|
|
if (warning != ~0)
|
|
if (leapnext > OKAYTOSETWARNING)
|
|
i = 1;
|
|
|
|
if (indicator != ~0)
|
|
if (leapnext > DAYBEFORE)
|
|
i = 1;
|
|
|
|
if (i) {
|
|
msyslog(LOG_ERR,
|
|
"attempt to set leap bits at unlikely time of month");
|
|
return 0;
|
|
}
|
|
|
|
if (warning != ~0)
|
|
leap_warning = warning;
|
|
|
|
if (indicator != ~0) {
|
|
if (indicator == LEAP_NOWARNING) {
|
|
leap_warning = LEAP_NOWARNING;
|
|
}
|
|
leap_indicator = indicator;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* leap_actual
|
|
*
|
|
* calculate leap value - pass arg through if no local
|
|
* configuration. Otherwise ise local configuration
|
|
* (only used to cope with broken time servers and
|
|
* broken refclocks)
|
|
*
|
|
* Mapping of leap_indicator:
|
|
* LEAP_NOWARNING
|
|
* pass peer value to sys_leap - usual operation
|
|
* LEAP_ADD/DEL_SECOND
|
|
* pass LEAP_ADD/DEL_SECOND to sys_leap
|
|
* LEAP_NOTINSYNC
|
|
* pass LEAP_NOWARNING to sys_leap - effectively ignores leap
|
|
*/
|
|
/* there seems to be a bug in the IRIX 4 compiler which prevents
|
|
u_char from beeing used in prototyped functions
|
|
AIX also suffers from this.
|
|
So give up and define it terms of int.
|
|
*/
|
|
int
|
|
leap_actual(l)
|
|
int l ;
|
|
{
|
|
if (leap_indicator != LEAP_NOWARNING) {
|
|
if (leap_indicator == LEAP_NOTINSYNC)
|
|
return LEAP_NOWARNING;
|
|
else
|
|
return leap_indicator;
|
|
} else {
|
|
return l;
|
|
}
|
|
}
|
|
|