diff --git a/chrony.texi.in b/chrony.texi.in index 6a36b85..fbdff38 100644 --- a/chrony.texi.in +++ b/chrony.texi.in @@ -1160,6 +1160,7 @@ the configuration file is ignored. * maxslewrate directive:: Set maximum slew rate * maxupdateskew directive:: Stop bad estimates upsetting machine clock * minsamples directive:: Set minimum number of samples per source +* minsources directive:: Set minimum number of selectable sources to update clock * noclientlog directive:: Prevent chronyd from gathering data about clients * peer directive:: Specify an NTP peer * pidfile directive:: Specify the file where chronyd's pid is written @@ -2447,6 +2448,23 @@ The syntax is minsamples @end example @c }}} +@c {{{ minsources +@node minsources directive +@subsection minsources +The @code{minsources} directive sets the minimum number of sources that need +to be considered as selectable in the source selection algorithm before the +local clock is updated. The default value is 1. + +Setting this option to a larger number can be used to improve the reliability. +More sources will have to agree with each other and the clock will not be +updated when only one source (which could be serving wrong time) is reachable. + +The syntax is + +@example +minsources +@end example +@c }}} @c {{{ noclientlog @node noclientlog directive @subsection noclientlog diff --git a/conf.c b/conf.c index 26a15be..d14c53e 100644 --- a/conf.c +++ b/conf.c @@ -135,6 +135,9 @@ static double make_step_threshold = 0.0; /* Threshold for automatic RTC trimming */ static double rtc_autotrim_threshold = 0.0; +/* Minimum number of selectables sources required to update the clock */ +static int min_sources = 1; + /* Number of updates before offset checking, number of ignored updates before exiting and the maximum allowed offset */ static int max_offset_delay = -1; @@ -456,6 +459,8 @@ CNF_ParseLine(const char *filename, int number, char *line) parse_double(p, &max_update_skew); } else if (!strcasecmp(command, "minsamples")) { parse_int(p, &min_samples); + } else if (!strcasecmp(command, "minsources")) { + parse_int(p, &min_sources); } else if (!strcasecmp(command, "noclientlog")) { no_client_log = parse_null(p); } else if (!strcasecmp(command, "peer")) { @@ -1700,6 +1705,14 @@ CNF_GetMinSamples(void) /* ================================================== */ +int +CNF_GetMinSources(void) +{ + return min_sources; +} + +/* ================================================== */ + char * CNF_GetHwclockFile(void) { diff --git a/conf.h b/conf.h index 7bc7ec2..70db557 100644 --- a/conf.h +++ b/conf.h @@ -101,6 +101,8 @@ extern char *CNF_GetUser(void); extern int CNF_GetMaxSamples(void); extern int CNF_GetMinSamples(void); +extern int CNF_GetMinSources(void); + extern double CNF_GetRtcAutotrim(void); extern char *CNF_GetHwclockFile(void); diff --git a/sources.c b/sources.c index bac56cf..01ebdcd 100644 --- a/sources.c +++ b/sources.c @@ -74,6 +74,7 @@ typedef enum { SRC_STALE, /* Has older samples than others */ SRC_FALSETICKER, /* Doesn't agree with others */ SRC_JITTERY, /* Scatter worse than other's dispersion (not used) */ + SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */ SRC_NONPREFERRED, /* Others have prefer option */ SRC_WAITS_UPDATE, /* No updates, selection postponed */ SRC_DISTANT, /* Others have shorter root distance */ @@ -807,11 +808,13 @@ SRC_SelectSource(SRC_Instance updated_inst) n_sel_sources = j; #endif - if (n_sel_sources == 0) { + if (n_sel_sources == 0 || n_sel_sources < CNF_GetMinSources()) { if (selected_source_index != INVALID_SOURCE) { - log_selection_message("Can't synchronise: no selectable sources", NULL); + log_selection_message("Can't synchronise: %s selectable sources", + n_sel_sources ? "not enough" : "no"); selected_source_index = INVALID_SOURCE; } + mark_ok_sources(SRC_WAITS_SOURCES); return; } @@ -1225,6 +1228,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now) case SRC_JITTERY: report->state = RPT_JITTERY; break; + case SRC_WAITS_SOURCES: case SRC_NONPREFERRED: case SRC_WAITS_UPDATE: case SRC_DISTANT: