diff --git a/chrony.texi b/chrony.texi index 9b75eff..b09da92 100644 --- a/chrony.texi +++ b/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] diff --git a/conf.c b/conf.c index bbd890c..460b42f 100644 --- a/conf.c +++ b/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) { diff --git a/conf.h b/conf.h index 7f0f6b3..15d24d7 100644 --- a/conf.h +++ b/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); diff --git a/reference.c b/reference.c index 7bf8ec7..0310d20 100644 --- a/reference.c +++ b/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); } /* ================================================== */