Code

config: Added support for specifying include filter patterns.
authorSebastian Harl <sh@tokkee.org>
Sat, 24 Nov 2012 09:26:20 +0000 (10:26 +0100)
committerSebastian Harl <sh@tokkee.org>
Sat, 24 Nov 2012 09:26:20 +0000 (10:26 +0100)
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
src/collectd.conf.pod
src/configfile.c

index 98395ed14228a484c886a3659031a30b6c333c85..f0f9cfb9af553b4013c2969e629e2282d5bd51d9 100644 (file)
@@ -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"
index 1c8b7a4f2153183c716f83accd0dc02e7389abbd..80fd31b13141013f34239de9cc975f6b2a6e5a3a 100644 (file)
@@ -109,7 +109,7 @@ interval, that setting will take precedence.
 
 =back
 
-=item B<Include> I<Path>
+=item B<Include> I<Path> [I<pattern>]
 
 If I<Path> points to a file, includes that file. If I<Path> 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<fnmatch> function is available on your system, a shell-like wildcard
+I<pattern> 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</etc/collectd.d>:
+
+  Include "/etc/collectd.d" "*.conf"
+
 If more than one files are included by a single B<Include> option, the files
 will be included in lexicographical order (as defined by the C<strcmp>
 function). Thus, you can e.E<nbsp>g. use numbered prefixes to specify the
index 5920c53129628c3691af816843aff70af8f55e5c..1a9e28a624fed56c302cdec73c30bc563ef89597 100644 (file)
 # include <wordexp.h>
 #endif /* HAVE_WORDEXP_H */
 
+#if HAVE_FNMATCH_H
+# include <fnmatch.h>
+#endif /* HAVE_FNMATCH_H */
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#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);