* 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
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
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
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]
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 */
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 {
{"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},
/* ================================================== */
+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)
{
/* ================================================== */
+char *
+CNF_GetLeapSecTimezone(void)
+{
+ return leapsec_tz;
+}
+
+/* ================================================== */
+
void
CNF_GetLinuxHz(int *set, int *hz)
{
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);
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;
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);
/* ================================================== */
+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)
+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) {
double update_interval;
double elapsed;
double correction_rate;
- struct timeval now;
+ struct timeval now, raw_now;
assert(initialised);
}
}
- 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;
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);
are_we_synchronised = 0;
- update_leap_status(LEAP_Unsynchronised);
+ update_leap_status(LEAP_Unsynchronised, 0);
}
/* ================================================== */