Add support for reading leap second data from tz database
leapsectz directive is used to set the name of the timezone in the system tz database which chronyd can use to find out when will the next leap second occur. It will periodically check if dates Jun 30 23:59:60 and Dec 31 23:59:60 are valid in that timezone. This is mainly useful with reference clocks which don't provide the leap second information. It is not necessary to restart chronyd if the tz database is updated with a new leap second at least 12 hours before the event.
This commit is contained in:
parent
f7c65a4b88
commit
2a5c045c3d
4 changed files with 128 additions and 13 deletions
30
chrony.texi
30
chrony.texi
|
@ -1183,6 +1183,7 @@ directives can occur in any order in the file.
|
|||
* include directive:: Include a configuration file
|
||||
* initstepslew directive:: Trim the system clock on boot-up.
|
||||
* keyfile directive:: Specify location of file containing keys
|
||||
* leapsectz directive:: Read leap second data from tz database
|
||||
* linux_hz directive:: Define a non-standard value of the kernel HZ constant
|
||||
* linux_freq_scale directive:: Define a non-standard value to compensate the kernel frequency bias
|
||||
* local directive:: Allow unsynchronised machine to act as server
|
||||
|
@ -1744,6 +1745,25 @@ prefix.
|
|||
|
||||
The ID for the chronyc authentication key is specified with the
|
||||
commandkey command (see earlier).
|
||||
@c }}}
|
||||
@c {{{ leapsectz
|
||||
@node leapsectz directive
|
||||
@subsection leapsectz
|
||||
This directive is used to set the name of the timezone in the system
|
||||
tz database which @code{chronyd} can use to find out when will the
|
||||
next leap second occur. It will periodically check if the times
|
||||
23:59:59 and 23:59:60 are valid on Jun 30 and Dec 31 in the timezone.
|
||||
This is mainly useful with reference clocks which don't provide the
|
||||
leap second information. It is not necessary to restart
|
||||
@code{chronyd} if the tz database is updated with a new leap second at
|
||||
least 12 hours before the event.
|
||||
|
||||
An example of the command is
|
||||
|
||||
@example
|
||||
leapsectz right/UTC
|
||||
@end example
|
||||
|
||||
@c }}}
|
||||
@c {{{ local
|
||||
@node local directive
|
||||
|
@ -1894,9 +1914,9 @@ expressed in UTC, not the local time zone.
|
|||
IP address of server/peer from which measurement comes [158.152.1.76]
|
||||
@item
|
||||
Leap status (@code{N} means normal, @code{+} means that the last minute
|
||||
of today has 61 seconds, @code{-} means that the last minute of the day
|
||||
has 59 seconds, @code{?} means the remote computer is not currently
|
||||
synchronised.) [N]
|
||||
of the current month has 61 seconds, @code{-} means that the last minute
|
||||
of the month has 59 seconds, @code{?} means the remote computer is not
|
||||
currently synchronised.) [N]
|
||||
@item
|
||||
Stratum of remote computer. [2]
|
||||
@item
|
||||
|
@ -2110,8 +2130,8 @@ Sequence number of driver poll within one polling interval for raw
|
|||
samples, or @code{-} for filtered samples. [7]
|
||||
@item
|
||||
Leap status (@code{N} means normal, @code{+} means that the last minute
|
||||
of today has 61 seconds, @code{-} means that the last minute of the day
|
||||
has 59 seconds). [N]
|
||||
of the current month has 61 seconds, @code{-} means that the last minute
|
||||
of the month has 59 seconds). [N]
|
||||
@item
|
||||
Flag indicating whether the sample comes from PPS source. (1 for yes,
|
||||
0 for no, or @code{-} for filtered sample). [1]
|
||||
|
|
23
conf.c
23
conf.c
|
@ -112,6 +112,7 @@ static void parse_sched_priority(const char *);
|
|||
static void parse_lockall(const char *);
|
||||
static void parse_tempcomp(const char *);
|
||||
static void parse_include(const char *);
|
||||
static void parse_leapsectz(const char *);
|
||||
|
||||
/* ================================================== */
|
||||
/* Configuration variables */
|
||||
|
@ -224,6 +225,9 @@ static double linux_freq_scale;
|
|||
static int sched_priority = 0;
|
||||
static int lock_memory = 0;
|
||||
|
||||
/* Name of a system timezone containing leap seconds occuring at midnight */
|
||||
static char *leapsec_tz = NULL;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
typedef struct {
|
||||
|
@ -276,6 +280,7 @@ static const Command commands[] = {
|
|||
{"reselectdist", 12, parse_reselectdist},
|
||||
{"stratumweight", 13, parse_stratumweight},
|
||||
{"include", 7, parse_include},
|
||||
{"leapsectz", 9, parse_leapsectz},
|
||||
{"linux_hz", 8, parse_linux_hz},
|
||||
{"linux_freq_scale", 16, parse_linux_freq_scale},
|
||||
{"sched_priority", 14, parse_sched_priority},
|
||||
|
@ -1282,6 +1287,16 @@ parse_include(const char *line)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
parse_leapsectz(const char *line)
|
||||
{
|
||||
/* This must allocate enough space! */
|
||||
leapsec_tz = MallocArray(char, 1 + strlen(line));
|
||||
sscanf(line, "%s", leapsec_tz);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
parse_linux_hz(const char *line)
|
||||
{
|
||||
|
@ -1707,6 +1722,14 @@ CNF_GetPidFile(void)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
char *
|
||||
CNF_GetLeapSecTimezone(void)
|
||||
{
|
||||
return leapsec_tz;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
CNF_GetLinuxHz(int *set, int *hz)
|
||||
{
|
||||
|
|
1
conf.h
1
conf.h
|
@ -69,6 +69,7 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
|
|||
extern void CNF_GetBindAddress(int family, IPAddr *addr);
|
||||
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
|
||||
extern char *CNF_GetPidFile(void);
|
||||
extern char *CNF_GetLeapSecTimezone(void);
|
||||
extern void CNF_GetLinuxHz(int *set, int *hz);
|
||||
extern void CNF_GetLinuxFreqScale(int *set, double *freq_scale);
|
||||
|
||||
|
|
87
reference.c
87
reference.c
|
@ -89,6 +89,11 @@ static double drift_file_age;
|
|||
|
||||
static void update_drift_file(double, double);
|
||||
|
||||
/* Name of a system timezone containing leap seconds occuring at midnight */
|
||||
static char *leap_tzname;
|
||||
static time_t last_tz_leap_check;
|
||||
static NTP_Leap tz_leap;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static LOG_FileID logfileid;
|
||||
|
@ -196,6 +201,8 @@ REF_Initialise(void)
|
|||
|
||||
enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
|
||||
|
||||
leap_tzname = CNF_GetLeapSecTimezone();
|
||||
|
||||
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
|
||||
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
|
||||
CNF_GetLogChange(&do_log_change, &log_change_threshold);
|
||||
|
@ -526,20 +533,82 @@ is_offset_ok(double offset)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
update_leap_status(NTP_Leap leap)
|
||||
static NTP_Leap
|
||||
get_tz_leap(time_t when)
|
||||
{
|
||||
struct tm stm;
|
||||
time_t t;
|
||||
char *tz_env, tz_orig[128];
|
||||
|
||||
/* Do this check at most twice a day */
|
||||
when = when / (12 * 3600) * (12 * 3600);
|
||||
if (last_tz_leap_check == when)
|
||||
return tz_leap;
|
||||
|
||||
last_tz_leap_check = when;
|
||||
tz_leap = LEAP_Normal;
|
||||
|
||||
stm = *gmtime(&when);
|
||||
|
||||
/* Check for leap second only in the latter half of June and December */
|
||||
if (stm.tm_mon == 5 && stm.tm_mday > 14)
|
||||
stm.tm_mday = 30;
|
||||
else if (stm.tm_mon == 11 && stm.tm_mday > 14)
|
||||
stm.tm_mday = 31;
|
||||
else
|
||||
return tz_leap;
|
||||
|
||||
/* Temporarily switch to the timezone containing leap seconds */
|
||||
tz_env = getenv("TZ");
|
||||
if (tz_env) {
|
||||
if (strlen(tz_env) >= sizeof (tz_orig))
|
||||
return tz_leap;
|
||||
strcpy(tz_orig, tz_env);
|
||||
}
|
||||
setenv("TZ", leap_tzname, 1);
|
||||
tzset();
|
||||
|
||||
/* Set the time to 23:59:60 and see how it overflows in mktime() */
|
||||
stm.tm_sec = 60;
|
||||
stm.tm_min = 59;
|
||||
stm.tm_hour = 23;
|
||||
|
||||
t = mktime(&stm);
|
||||
|
||||
if (tz_env)
|
||||
setenv("TZ", tz_orig, 1);
|
||||
else
|
||||
unsetenv("TZ");
|
||||
tzset();
|
||||
|
||||
if (t == -1)
|
||||
return tz_leap;
|
||||
|
||||
if (stm.tm_sec == 60)
|
||||
tz_leap = LEAP_InsertSecond;
|
||||
else if (stm.tm_sec == 1)
|
||||
tz_leap = LEAP_DeleteSecond;
|
||||
|
||||
return tz_leap;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
update_leap_status(NTP_Leap leap, time_t now)
|
||||
{
|
||||
time_t now;
|
||||
struct tm stm;
|
||||
int leap_sec;
|
||||
|
||||
leap_sec = 0;
|
||||
|
||||
if (leap_tzname && now && leap == LEAP_Normal)
|
||||
leap = get_tz_leap(now);
|
||||
|
||||
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
|
||||
/* Insert/delete leap second only on June 30 or December 31
|
||||
and in other months ignore the leap status completely */
|
||||
|
||||
now = time(NULL);
|
||||
stm = *gmtime(&now);
|
||||
|
||||
if (stm.tm_mon != 5 && stm.tm_mon != 11) {
|
||||
|
@ -600,7 +669,7 @@ REF_SetReference(int stratum,
|
|||
double update_interval;
|
||||
double elapsed;
|
||||
double correction_rate;
|
||||
struct timeval now;
|
||||
struct timeval now, raw_now;
|
||||
|
||||
assert(initialised);
|
||||
|
||||
|
@ -628,7 +697,9 @@ REF_SetReference(int stratum,
|
|||
}
|
||||
}
|
||||
|
||||
LCL_ReadCookedTime(&now, NULL);
|
||||
LCL_ReadRawTime(&raw_now);
|
||||
LCL_CookTime(&raw_now, &now, NULL);
|
||||
|
||||
UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time);
|
||||
our_offset = offset + elapsed * frequency;
|
||||
|
||||
|
@ -646,7 +717,7 @@ REF_SetReference(int stratum,
|
|||
our_root_delay = root_delay;
|
||||
our_root_dispersion = root_dispersion;
|
||||
|
||||
update_leap_status(leap);
|
||||
update_leap_status(leap, raw_now.tv_sec);
|
||||
|
||||
if (last_ref_update.tv_sec) {
|
||||
UTI_DiffTimevalsToDouble(&update_interval, &now, &last_ref_update);
|
||||
|
@ -827,7 +898,7 @@ REF_SetUnsynchronised(void)
|
|||
|
||||
are_we_synchronised = 0;
|
||||
|
||||
update_leap_status(LEAP_Unsynchronised);
|
||||
update_leap_status(LEAP_Unsynchronised, 0);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
Loading…
Reference in a new issue