From 76910471b1d2b1d5f43d49286bf1f9a03a060f1c Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Sat, 24 Nov 2012 10:26:20 +0100 Subject: [PATCH] config: Added support for specifying include filter patterns. An optional second argument may now be passed to the "Include" configuration option. If specified (and if the fnmatch() function was available at build time), only files matching this pattern will be included. For example, the following will include all files matching "*.conf" in any subdirectory of /etc/collectd.d/: Include "/etc/collectd.d" "*.conf" This is useful, e.g. for distributions in order to include a possibly empty directory in the default configuration including all subdirectories but also making it possible to ship further documents like README files. --- configure.in | 4 +-- src/collectd.conf.pod | 11 +++++++- src/configfile.c | 65 +++++++++++++++++++++++++++++++++---------- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/configure.in b/configure.in index 98395ed1..f0f9cfb9 100644 --- a/configure.in +++ b/configure.in @@ -128,7 +128,7 @@ AC_HEADER_SYS_WAIT AC_HEADER_DIRENT AC_HEADER_STDBOOL -AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h) +AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h fnmatch.h libgen.h) # For ping library AC_CHECK_HEADERS(netinet/in_systm.h, [], [], @@ -298,7 +298,7 @@ else fi # For hddtemp module -AC_CHECK_HEADERS(linux/major.h libgen.h) +AC_CHECK_HEADERS(linux/major.h) # For md module (Linux only) if test "x$ac_system" = "xLinux" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 1c8b7a4f..80fd31b1 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -109,7 +109,7 @@ interval, that setting will take precedence. =back -=item B I +=item B I [I] If I points to a file, includes that file. If I points to a directory, recursively includes all files within that directory and its @@ -119,6 +119,15 @@ use statements like the following: Include "/etc/collectd.d/*.conf" +If the C function is available on your system, a shell-like wildcard +I may be specified to filter which files to include. This may be used +in combination with recursively including a directory to easily be able to +arbitrarily mix configuration files and other documents (e.g. README files). +The following statement is similar to the example above but includes all files +matching C<*.conf> in any subdirectory of C: + + Include "/etc/collectd.d" "*.conf" + If more than one files are included by a single B option, the files will be included in lexicographical order (as defined by the C function). Thus, you can e.Eg. use numbered prefixes to specify the diff --git a/src/configfile.c b/src/configfile.c index 5920c531..1a9e28a6 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -35,6 +35,14 @@ # include #endif /* HAVE_WORDEXP_H */ +#if HAVE_FNMATCH_H +# include +#endif /* HAVE_FNMATCH_H */ + +#if HAVE_LIBGEN_H +# include +#endif /* HAVE_LIBGEN_H */ + #define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str)) /* @@ -535,7 +543,8 @@ static int cf_ci_append_children (oconfig_item_t *dst, oconfig_item_t *src) } /* int cf_ci_append_children */ #define CF_MAX_DEPTH 8 -static oconfig_item_t *cf_read_generic (const char *path, int depth); +static oconfig_item_t *cf_read_generic (const char *path, + const char *pattern, int depth); static int cf_include_all (oconfig_item_t *root, int depth) { @@ -555,14 +564,18 @@ static int cf_include_all (oconfig_item_t *root, int depth) old = root->children + i; - if ((old->values_num != 1) - || (old->values[0].type != OCONFIG_TYPE_STRING)) + if ((old->values_num < 1) || (old->values_num > 2) + || (old->values[0].type != OCONFIG_TYPE_STRING) + || ((old->values_num == 2) + && (old->values[1].type != OCONFIG_TYPE_STRING))) { - ERROR ("configfile: `Include' needs exactly one string argument."); + ERROR ("configfile: `Include' needs exactly one or two string argument."); continue; } - new = cf_read_generic (old->values[0].value.string, depth + 1); + new = cf_read_generic (old->values[0].value.string, + (old->values_num == 2) ? old->values[1].value.string : NULL, + depth + 1); if (new == NULL) continue; @@ -579,12 +592,31 @@ static int cf_include_all (oconfig_item_t *root, int depth) return (0); } /* int cf_include_all */ -static oconfig_item_t *cf_read_file (const char *file, int depth) +static oconfig_item_t *cf_read_file (const char *file, + const char *pattern, int depth) { oconfig_item_t *root; assert (depth < CF_MAX_DEPTH); + if (pattern != NULL) { +#if HAVE_FNMATCH_H && HAVE_LIBGEN_H + char *tmp = sstrdup (file); + char *filename = basename (tmp); + + if ((filename != NULL) && (fnmatch (pattern, filename, 0) != 0)) { + free (tmp); + return (NULL); + } + + free (tmp); +#else + ERROR ("configfile: Cannot apply pattern filter '%s' " + "to file '%s': functions basename() and / or " + "fnmatch() not available.", pattern, file); +#endif /* HAVE_FNMATCH_H && HAVE_LIBGEN_H */ + } + root = oconfig_parse_file (file); if (root == NULL) { @@ -602,7 +634,8 @@ static int cf_compare_string (const void *p1, const void *p2) return strcmp (*(const char **) p1, *(const char **) p2); } -static oconfig_item_t *cf_read_dir (const char *dir, int depth) +static oconfig_item_t *cf_read_dir (const char *dir, + const char *pattern, int depth) { oconfig_item_t *root = NULL; DIR *dh; @@ -677,7 +710,7 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth) oconfig_item_t *temp; char *name = filenames[i]; - temp = cf_read_generic (name, depth); + temp = cf_read_generic (name, pattern, depth); if (temp == NULL) { /* An error should already have been reported. */ @@ -708,7 +741,8 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth) * simpler function is used which does not do any such expansion. */ #if HAVE_WORDEXP_H -static oconfig_item_t *cf_read_generic (const char *path, int depth) +static oconfig_item_t *cf_read_generic (const char *path, + const char *pattern, int depth) { oconfig_item_t *root = NULL; int status; @@ -761,9 +795,9 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth) } if (S_ISREG (statbuf.st_mode)) - temp = cf_read_file (path_ptr, depth); + temp = cf_read_file (path_ptr, pattern, depth); else if (S_ISDIR (statbuf.st_mode)) - temp = cf_read_dir (path_ptr, depth); + temp = cf_read_dir (path_ptr, pattern, depth); else { WARNING ("configfile: %s is neither a file nor a " @@ -794,7 +828,8 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth) /* #endif HAVE_WORDEXP_H */ #else /* if !HAVE_WORDEXP_H */ -static oconfig_item_t *cf_read_generic (const char *path, int depth) +static oconfig_item_t *cf_read_generic (const char *path, + const char *pattern, int depth) { struct stat statbuf; int status; @@ -817,9 +852,9 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth) } if (S_ISREG (statbuf.st_mode)) - return (cf_read_file (path, depth)); + return (cf_read_file (path, pattern, depth)); else if (S_ISDIR (statbuf.st_mode)) - return (cf_read_dir (path, depth)); + return (cf_read_dir (path, pattern, depth)); ERROR ("configfile: %s is neither a file nor a directory.", path); return (NULL); @@ -993,7 +1028,7 @@ int cf_read (char *filename) oconfig_item_t *conf; int i; - conf = cf_read_generic (filename, 0 /* depth */); + conf = cf_read_generic (filename, /* pattern = */ NULL, /* depth = */ 0); if (conf == NULL) { ERROR ("Unable to read config file %s.", filename); -- 2.30.2