diff --git a/conf.c b/conf.c index b32e4ff..bfab31c 100644 --- a/conf.c +++ b/conf.c @@ -42,6 +42,11 @@ #include "cmdparse.h" #include "util.h" +/* ================================================== */ + +#define MAX_LINE_LENGTH 2048 +#define MAX_CONF_DIRS 10 + /* ================================================== */ /* Forward prototypes */ @@ -57,6 +62,7 @@ static void parse_bindaddress(char *); static void parse_bindcmdaddress(char *); static void parse_broadcast(char *); static void parse_clientloglimit(char *); +static void parse_confdirs(char *); static void parse_fallbackdrift(char *); static void parse_hwtimestamp(char *); static void parse_include(char *); @@ -424,7 +430,7 @@ void CNF_ReadFile(const char *filename) { FILE *in; - char line[2048]; + char line[MAX_LINE_LENGTH]; int i; in = UTI_OpenFile(NULL, filename, NULL, 'R', 0); @@ -486,6 +492,8 @@ CNF_ParseLine(const char *filename, int number, char *line) &cmd_ratelimit_burst, &cmd_ratelimit_leak); } else if (!strcasecmp(command, "combinelimit")) { parse_double(p, &combine_limit); + } else if (!strcasecmp(command, "confdirs")) { + parse_confdirs(p); } else if (!strcasecmp(command, "corrtimeratio")) { parse_double(p, &correction_time_ratio); } else if (!strcasecmp(command, "deny")) { @@ -1407,6 +1415,77 @@ parse_hwtimestamp(char *line) /* ================================================== */ +static const char * +get_basename(const char *path) +{ + const char *b = strrchr(path, '/'); + return b ? b + 1 : path; +} + +/* ================================================== */ + +static int +compare_basenames(const void *a, const void *b) +{ + return strcmp(get_basename(*(const char * const *)a), + get_basename(*(const char * const *)b)); +} + +/* ================================================== */ + +static void +parse_confdirs(char *line) +{ + char *dirs[MAX_CONF_DIRS], buf[MAX_LINE_LENGTH], *path; + size_t i, j, k, locations, n_dirs; + glob_t gl; + + n_dirs = UTI_SplitString(line, dirs, MAX_CONF_DIRS); + if (n_dirs < 1 || n_dirs > MAX_CONF_DIRS) { + command_parse_error(); + return; + } + + /* Get the paths of all config files in the specified directories */ + for (i = 0; i < n_dirs; i++) { + if (snprintf(buf, sizeof (buf), "%s/%s", dirs[i], "*.conf") >= sizeof (buf)) + assert(0); + if (glob(buf, GLOB_NOSORT | (i > 0 ? GLOB_APPEND : 0), NULL, &gl) != 0) + ; + } + + if (gl.gl_pathc > 0) { + /* Sort the paths by filenames */ + qsort(gl.gl_pathv, gl.gl_pathc, sizeof (gl.gl_pathv[0]), compare_basenames); + + for (i = 0; i < gl.gl_pathc; i += locations) { + /* Count directories containing files with this name */ + for (j = i + 1, locations = 1; j < gl.gl_pathc; j++, locations++) { + if (compare_basenames(&gl.gl_pathv[i], &gl.gl_pathv[j]) != 0) + break; + } + + /* Read the first file of this name in the order of the directive */ + for (j = 0; j < n_dirs; j++) { + for (k = 0; k < locations; k++) { + path = gl.gl_pathv[i + k]; + if (strncmp(path, dirs[j], strlen(dirs[j])) == 0 && + strlen(dirs[j]) + 1 + strlen(get_basename(path)) == strlen(path)) { + CNF_ReadFile(path); + break; + } + } + if (k < locations) + break; + } + } + } + + globfree(&gl); +} + +/* ================================================== */ + static void parse_include(char *line) { diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc index 462e98f..8687bc4 100644 --- a/doc/chrony.conf.adoc +++ b/doc/chrony.conf.adoc @@ -2121,6 +2121,32 @@ sendmail binary. === Miscellaneous +[[confdirs]]*confdirs* _directory_...:: +The *confdirs* directive includes configuration files with the _.conf_ suffix +from up to 10 directories. The files are included in the lexicographical order +(ignoring the names of the directories). If multiple directories contain a file +with the same name, only the first file in the order of the specified +directories will be included. This enables a fragmented configuration, where +existing fragments can be replaced by adding files to a different directory. ++ +An example of the directive is: ++ +---- +confdirs @SYSCONFDIR@/chrony.d /var/run/chrony.d /usr/lib/chrony.d +---- + +[[include]]*include* _pattern_:: +The *include* directive includes a configuration file, or multiple configuration +files if a wildcard pattern is specified. Unlike with the *confdirs* directive, +the full name of the files needs to be specified and at least one file is +required to exist. ++ +An example of the directive is: ++ +---- +include @SYSCONFDIR@/chrony.d/*.conf +---- + [[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...:: This directive enables hardware timestamping of NTP packets sent to and received from the specified network interface. The network interface controller @@ -2208,17 +2234,6 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9 hwtimestamp * ---- -[[include]]*include* _pattern_:: -The *include* directive includes a configuration file or multiple configuration -files if a wildcard pattern is specified. This can be useful when maintaining -configuration on multiple hosts to keep the differences in separate files. -+ -An example of the directive is: -+ ----- -include @SYSCONFDIR@/chrony.d/*.conf ----- - [[keyfile]]*keyfile* _file_:: This directive is used to specify the location of the file containing symmetric keys which are shared between NTP servers and clients, or peers, in order to