conf: add better support for fragmented configuration

Add a confdirs directive to include *.conf files from multiple
directories. If a file with the same name exists in multiple
directories, only the first one in the order of the specified
directories will be included.
This commit is contained in:
Miroslav Lichvar 2020-05-07 13:04:26 +02:00
parent 6901df5c18
commit 3470ab66f0
2 changed files with 106 additions and 12 deletions

81
conf.c
View file

@ -42,6 +42,11 @@
#include "cmdparse.h" #include "cmdparse.h"
#include "util.h" #include "util.h"
/* ================================================== */
#define MAX_LINE_LENGTH 2048
#define MAX_CONF_DIRS 10
/* ================================================== */ /* ================================================== */
/* Forward prototypes */ /* Forward prototypes */
@ -57,6 +62,7 @@ static void parse_bindaddress(char *);
static void parse_bindcmdaddress(char *); static void parse_bindcmdaddress(char *);
static void parse_broadcast(char *); static void parse_broadcast(char *);
static void parse_clientloglimit(char *); static void parse_clientloglimit(char *);
static void parse_confdirs(char *);
static void parse_fallbackdrift(char *); static void parse_fallbackdrift(char *);
static void parse_hwtimestamp(char *); static void parse_hwtimestamp(char *);
static void parse_include(char *); static void parse_include(char *);
@ -424,7 +430,7 @@ void
CNF_ReadFile(const char *filename) CNF_ReadFile(const char *filename)
{ {
FILE *in; FILE *in;
char line[2048]; char line[MAX_LINE_LENGTH];
int i; int i;
in = UTI_OpenFile(NULL, filename, NULL, 'R', 0); 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); &cmd_ratelimit_burst, &cmd_ratelimit_leak);
} else if (!strcasecmp(command, "combinelimit")) { } else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit); parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "confdirs")) {
parse_confdirs(p);
} else if (!strcasecmp(command, "corrtimeratio")) { } else if (!strcasecmp(command, "corrtimeratio")) {
parse_double(p, &correction_time_ratio); parse_double(p, &correction_time_ratio);
} else if (!strcasecmp(command, "deny")) { } 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 static void
parse_include(char *line) parse_include(char *line)
{ {

View file

@ -2121,6 +2121,32 @@ sendmail binary.
=== Miscellaneous === 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_]...:: [[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
This directive enables hardware timestamping of NTP packets sent to and This directive enables hardware timestamping of NTP packets sent to and
received from the specified network interface. The network interface controller received from the specified network interface. The network interface controller
@ -2208,17 +2234,6 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp * 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_:: [[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric 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 keys which are shared between NTP servers and clients, or peers, in order to