reference: get TAI-UTC offset from leap second timezone
Use the timezone specified by the leapsectz directive to get the current TAI-UTC offset and set the offset of the system clock in order to provide correct TAI time to applications using ntp_adjtime(), ntp_gettime(), or clock_gettime(CLOCK_TAI).
This commit is contained in:
parent
cde0a20307
commit
29b0ad894c
2 changed files with 52 additions and 22 deletions
|
@ -830,15 +830,23 @@ clients to safely synchronise with multiple identically configured leap
|
||||||
smearing servers.
|
smearing servers.
|
||||||
|
|
||||||
[[leapsectz]]*leapsectz* _timezone_::
|
[[leapsectz]]*leapsectz* _timezone_::
|
||||||
This directive is used to set the name of the timezone in the system tz
|
This directive specifies a timezone in the system tz database which *chronyd*
|
||||||
database which *chronyd* can use to find out when will the next leap second
|
can use to determine when will the next leap second occur and what is the
|
||||||
occur. It will periodically check if the times 23:59:59 and 23:59:60 are valid
|
current offset between TAI and UTC. It will periodically check if 23:59:59 and
|
||||||
on Jun 30 and Dec 31 in the timezone. This typically works with the *right/UTC*
|
23:59:60 are valid times in the timezone. This typically works with the
|
||||||
timezone.
|
_right/UTC_ timezone.
|
||||||
+
|
+
|
||||||
This directive is mainly useful with reference clocks which do not provide
|
When a leap second is announced, the timezone needs to be updated at least 12
|
||||||
leap second information. It is not necessary to restart *chronyd* if the tz
|
hours before the leap second. It is not necessary to restart *chronyd*.
|
||||||
database is updated with a new leap second at least 12 hours before the event.
|
+
|
||||||
|
This directive is useful with reference clocks and other time sources which do
|
||||||
|
not announce leap seconds, or announce them too late for an NTP server to
|
||||||
|
forward them to its own clients. Clients of leap smearing servers must not
|
||||||
|
use this directive.
|
||||||
|
+
|
||||||
|
It is also useful when the system clock is required to have correct TAI-UTC
|
||||||
|
offset. Note that the offset is set only when leap seconds are handled by the
|
||||||
|
kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*.
|
||||||
+
|
+
|
||||||
An example of the directive is:
|
An example of the directive is:
|
||||||
+
|
+
|
||||||
|
@ -2325,7 +2333,7 @@ The amount of memory used for logging client accesses can be increased in order
|
||||||
to enable clients to use the interleaved mode even when the server has a large
|
to enable clients to use the interleaved mode even when the server has a large
|
||||||
number of clients, and better support rate limiting if it is enabled by the
|
number of clients, and better support rate limiting if it is enabled by the
|
||||||
<<ratelimit,*ratelimit*>> directive. The system timezone database, if it is
|
<<ratelimit,*ratelimit*>> directive. The system timezone database, if it is
|
||||||
kept up to date and includes the *right/UTC* timezone, can be used as a
|
kept up to date and includes the _right/UTC_ timezone, can be used as a
|
||||||
reliable source to determine when a leap second will be applied to UTC. The
|
reliable source to determine when a leap second will be applied to UTC. The
|
||||||
*-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which
|
*-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which
|
||||||
*chronyd* will not be able to serve time to its clients when it needs to be
|
*chronyd* will not be able to serve time to its clients when it needs to be
|
||||||
|
|
48
reference.c
48
reference.c
|
@ -49,6 +49,7 @@ static int local_orphan;
|
||||||
static double local_distance;
|
static double local_distance;
|
||||||
static NTP_Leap our_leap_status;
|
static NTP_Leap our_leap_status;
|
||||||
static int our_leap_sec;
|
static int our_leap_sec;
|
||||||
|
static int our_tai_offset;
|
||||||
static int our_stratum;
|
static int our_stratum;
|
||||||
static uint32_t our_ref_id;
|
static uint32_t our_ref_id;
|
||||||
static IPAddr our_ref_ip;
|
static IPAddr our_ref_ip;
|
||||||
|
@ -139,7 +140,7 @@ static double last_ref_update_interval;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static NTP_Leap get_tz_leap(time_t when);
|
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
|
||||||
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
|
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
@ -179,11 +180,13 @@ REF_Initialise(void)
|
||||||
FILE *in;
|
FILE *in;
|
||||||
double file_freq_ppm, file_skew_ppm;
|
double file_freq_ppm, file_skew_ppm;
|
||||||
double our_frequency_ppm;
|
double our_frequency_ppm;
|
||||||
|
int tai_offset;
|
||||||
|
|
||||||
mode = REF_ModeNormal;
|
mode = REF_ModeNormal;
|
||||||
are_we_synchronised = 0;
|
are_we_synchronised = 0;
|
||||||
our_leap_status = LEAP_Unsynchronised;
|
our_leap_status = LEAP_Unsynchronised;
|
||||||
our_leap_sec = 0;
|
our_leap_sec = 0;
|
||||||
|
our_tai_offset = 0;
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
our_root_dispersion = 1.0;
|
our_root_dispersion = 1.0;
|
||||||
our_root_delay = 1.0;
|
our_root_delay = 1.0;
|
||||||
|
@ -241,8 +244,8 @@ REF_Initialise(void)
|
||||||
leap_tzname = CNF_GetLeapSecTimezone();
|
leap_tzname = CNF_GetLeapSecTimezone();
|
||||||
if (leap_tzname) {
|
if (leap_tzname) {
|
||||||
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
|
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
|
||||||
if (get_tz_leap(1341014400) == LEAP_InsertSecond &&
|
if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
|
||||||
get_tz_leap(1356912000) == LEAP_Normal) {
|
get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
|
||||||
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
|
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
|
||||||
} else {
|
} else {
|
||||||
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
|
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
|
||||||
|
@ -613,14 +616,18 @@ is_leap_second_day(struct tm *stm) {
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static NTP_Leap
|
static NTP_Leap
|
||||||
get_tz_leap(time_t when)
|
get_tz_leap(time_t when, int *tai_offset)
|
||||||
{
|
{
|
||||||
static time_t last_tz_leap_check;
|
static time_t last_tz_leap_check;
|
||||||
static NTP_Leap tz_leap;
|
static NTP_Leap tz_leap;
|
||||||
|
static int tz_tai_offset;
|
||||||
|
|
||||||
struct tm stm;
|
struct tm stm;
|
||||||
time_t t;
|
time_t t;
|
||||||
char *tz_env, tz_orig[128];
|
char *tz_env, tz_orig[128];
|
||||||
|
|
||||||
|
*tai_offset = tz_tai_offset;
|
||||||
|
|
||||||
/* Do this check at most twice a day */
|
/* Do this check at most twice a day */
|
||||||
when = when / (12 * 3600) * (12 * 3600);
|
when = when / (12 * 3600) * (12 * 3600);
|
||||||
if (last_tz_leap_check == when)
|
if (last_tz_leap_check == when)
|
||||||
|
@ -628,12 +635,10 @@ get_tz_leap(time_t when)
|
||||||
|
|
||||||
last_tz_leap_check = when;
|
last_tz_leap_check = when;
|
||||||
tz_leap = LEAP_Normal;
|
tz_leap = LEAP_Normal;
|
||||||
|
tz_tai_offset = 0;
|
||||||
|
|
||||||
stm = *gmtime(&when);
|
stm = *gmtime(&when);
|
||||||
|
|
||||||
if (!is_leap_second_day(&stm))
|
|
||||||
return tz_leap;
|
|
||||||
|
|
||||||
/* Temporarily switch to the timezone containing leap seconds */
|
/* Temporarily switch to the timezone containing leap seconds */
|
||||||
tz_env = getenv("TZ");
|
tz_env = getenv("TZ");
|
||||||
if (tz_env) {
|
if (tz_env) {
|
||||||
|
@ -644,6 +649,11 @@ get_tz_leap(time_t when)
|
||||||
setenv("TZ", leap_tzname, 1);
|
setenv("TZ", leap_tzname, 1);
|
||||||
tzset();
|
tzset();
|
||||||
|
|
||||||
|
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
|
||||||
|
t = mktime(&stm);
|
||||||
|
if (t != -1)
|
||||||
|
tz_tai_offset = t - when + 10;
|
||||||
|
|
||||||
/* Set the time to 23:59:60 and see how it overflows in mktime() */
|
/* Set the time to 23:59:60 and see how it overflows in mktime() */
|
||||||
stm.tm_sec = 60;
|
stm.tm_sec = 60;
|
||||||
stm.tm_min = 59;
|
stm.tm_min = 59;
|
||||||
|
@ -665,6 +675,8 @@ get_tz_leap(time_t when)
|
||||||
else if (stm.tm_sec == 1)
|
else if (stm.tm_sec == 1)
|
||||||
tz_leap = LEAP_DeleteSecond;
|
tz_leap = LEAP_DeleteSecond;
|
||||||
|
|
||||||
|
*tai_offset = tz_tai_offset;
|
||||||
|
|
||||||
return tz_leap;
|
return tz_leap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,10 +687,13 @@ leap_end_timeout(void *arg)
|
||||||
{
|
{
|
||||||
leap_timeout_id = 0;
|
leap_timeout_id = 0;
|
||||||
leap_in_progress = 0;
|
leap_in_progress = 0;
|
||||||
|
|
||||||
|
if (our_tai_offset)
|
||||||
|
our_tai_offset += our_leap_sec;
|
||||||
our_leap_sec = 0;
|
our_leap_sec = 0;
|
||||||
|
|
||||||
if (leap_mode == REF_LeapModeSystem)
|
if (leap_mode == REF_LeapModeSystem)
|
||||||
LCL_SetSystemLeap(our_leap_sec, 0);
|
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
|
||||||
|
|
||||||
if (our_leap_status == LEAP_InsertSecond ||
|
if (our_leap_status == LEAP_InsertSecond ||
|
||||||
our_leap_status == LEAP_DeleteSecond)
|
our_leap_status == LEAP_DeleteSecond)
|
||||||
|
@ -752,12 +767,17 @@ set_leap_timeout(time_t now)
|
||||||
static void
|
static void
|
||||||
update_leap_status(NTP_Leap leap, time_t now, int reset)
|
update_leap_status(NTP_Leap leap, time_t now, int reset)
|
||||||
{
|
{
|
||||||
int leap_sec;
|
NTP_Leap tz_leap;
|
||||||
|
int leap_sec, tai_offset;
|
||||||
|
|
||||||
leap_sec = 0;
|
leap_sec = 0;
|
||||||
|
tai_offset = 0;
|
||||||
|
|
||||||
if (leap_tzname && now && leap == LEAP_Normal)
|
if (leap_tzname && now) {
|
||||||
leap = get_tz_leap(now);
|
tz_leap = get_tz_leap(now, &tai_offset);
|
||||||
|
if (leap == LEAP_Normal)
|
||||||
|
leap = tz_leap;
|
||||||
|
}
|
||||||
|
|
||||||
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
|
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
|
||||||
/* Check that leap second is allowed today */
|
/* Check that leap second is allowed today */
|
||||||
|
@ -773,12 +793,14 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
|
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
|
||||||
|
&& !REF_IsLeapSecondClose()) {
|
||||||
our_leap_sec = leap_sec;
|
our_leap_sec = leap_sec;
|
||||||
|
our_tai_offset = tai_offset;
|
||||||
|
|
||||||
switch (leap_mode) {
|
switch (leap_mode) {
|
||||||
case REF_LeapModeSystem:
|
case REF_LeapModeSystem:
|
||||||
LCL_SetSystemLeap(our_leap_sec, 0);
|
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
|
||||||
/* Fall through */
|
/* Fall through */
|
||||||
case REF_LeapModeSlew:
|
case REF_LeapModeSlew:
|
||||||
case REF_LeapModeStep:
|
case REF_LeapModeStep:
|
||||||
|
|
Loading…
Reference in a new issue