[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnunet] 01/02: implement config inline globbing
From: |
gnunet |
Subject: |
[gnunet] 01/02: implement config inline globbing |
Date: |
Wed, 28 Jul 2021 15:30:59 +0200 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository gnunet.
commit 7615d46b09275383bd244a0ef1d94b3a77559b88
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Jul 28 14:31:38 2021 +0200
implement config inline globbing
---
src/include/gnunet_disk_lib.h | 16 +++
src/util/configuration.c | 259 ++++++++++++++++++++++++++++++++++--------
src/util/disk.c | 139 ++++++++++++++++++++++-
3 files changed, 363 insertions(+), 51 deletions(-)
diff --git a/src/include/gnunet_disk_lib.h b/src/include/gnunet_disk_lib.h
index 3805039fc..df5d3ba21 100644
--- a/src/include/gnunet_disk_lib.h
+++ b/src/include/gnunet_disk_lib.h
@@ -654,6 +654,22 @@ GNUNET_DISK_directory_scan (const char *dir_name,
GNUNET_FileNameCallback callback,
void *callback_cls);
+/**
+ * Find all files matching a glob pattern.
+ *
+ * Currently, the glob_pattern only supports asterisks in the last
+ * path component.
+ *
+ * @param glob_patterb the glob pattern to search for
+ * @param callback the method to call for each file
+ * @param callback_cls closure for @a callback
+ * @return the number of files found, -1 on error
+ */
+int
+GNUNET_DISK_glob (const char *glob_pattern,
+ GNUNET_FileNameCallback callback,
+ void *callback_cls);
+
/**
* Create the directory structure for storing
diff --git a/src/util/configuration.c b/src/util/configuration.c
index 4a1af10d3..da9cdb924 100644
--- a/src/util/configuration.c
+++ b/src/util/configuration.c
@@ -230,6 +230,120 @@ GNUNET_CONFIGURATION_parse_and_run (const char *filename,
return ret;
}
+struct InlineGlobClosure
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+};
+
+/**
+ * Function called with a filename.
+ *
+ * @param cls closure
+ * @param filename complete filename (absolute path)
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+inline_glob_cb (void *cls,
+ const char *filename)
+{
+ struct InlineGlobClosure *igc = cls;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Reading globbed config file '%s'\n",
+ filename);
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (igc->cfg,
+ filename))
+ {
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+/**
+ * Handle an inline directive.
+ *
+ * @returns #GNUNET_SYSERR on error, #GNUNET_OK otherwise
+ */
+enum GNUNET_GenericReturnValue
+handle_inline (struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *path_or_glob,
+ bool path_is_glob,
+ const char *restrict_section,
+ const char *source_filename)
+{
+ char *inline_path;
+
+ /* We support the section restriction only for non-globs */
+ GNUNET_assert (! (path_is_glob && (NULL != restrict_section)));
+
+ if (NULL == source_filename)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Refusing to parse inline configurations, "
+ "not allowed without source filename!\n");
+ return GNUNET_SYSERR;
+ }
+ if ('/' == *path_or_glob)
+ inline_path = GNUNET_strdup (path_or_glob);
+ else
+ {
+ /* We compute the canonical, absolute path first,
+ so that relative imports resolve properly with symlinked
+ config files. */
+ char *source_realpath;
+ char *endsep;
+
+ source_realpath = realpath (source_filename,
+ NULL);
+ if (NULL == source_realpath)
+ {
+ /* Couldn't even resolve path of base dir. */
+ GNUNET_break (0);
+ /* failed to parse included config */
+ return GNUNET_SYSERR;
+ }
+ endsep = strrchr (source_realpath, '/');
+ GNUNET_assert (NULL != endsep);
+ *endsep = '\0';
+ GNUNET_asprintf (&inline_path,
+ "%s/%s",
+ source_realpath,
+ path_or_glob);
+ free (source_realpath);
+ }
+ if (path_is_glob)
+ {
+ int nret;
+ struct InlineGlobClosure igc = {
+ .cfg = cfg,
+ };
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "processing config glob '%s'\n",
+ inline_path);
+
+ nret = GNUNET_DISK_glob (inline_path, inline_glob_cb, &igc);
+ if (-1 == nret)
+ {
+ GNUNET_free (inline_path);
+ return GNUNET_SYSERR;
+ }
+ }
+ else if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (cfg,
+ inline_path))
+ {
+ GNUNET_free (inline_path);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (inline_path);
+ return GNUNET_OK;
+}
+
enum GNUNET_GenericReturnValue
GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg,
@@ -237,28 +351,27 @@ GNUNET_CONFIGURATION_deserialize (struct
GNUNET_CONFIGURATION_Handle *cfg,
size_t size,
const char *source_filename)
{
- char *line;
- char *line_orig;
size_t line_size;
- char *pos;
unsigned int nr;
size_t r_bytes;
size_t to_read;
- size_t i;
- int emptyline;
enum GNUNET_GenericReturnValue ret;
char *section;
char *eq;
char *tag;
char *value;
+ char *line_orig = NULL;
ret = GNUNET_OK;
- section = GNUNET_strdup ("");
+ section = NULL;
nr = 0;
r_bytes = 0;
- line_orig = NULL;
while (r_bytes < size)
{
+ char *pos;
+ char *line;
+ bool emptyline;
+
GNUNET_free (line_orig);
/* fgets-like behaviour on buffer */
to_read = size - r_bytes;
@@ -280,7 +393,7 @@ GNUNET_CONFIGURATION_deserialize (struct
GNUNET_CONFIGURATION_Handle *cfg,
nr++;
/* tabs and '\r' are whitespace */
emptyline = GNUNET_YES;
- for (i = 0; i < line_size; i++)
+ for (size_t i = 0; i < line_size; i++)
{
if (line[i] == '\t')
line[i] = ' ';
@@ -294,7 +407,7 @@ GNUNET_CONFIGURATION_deserialize (struct
GNUNET_CONFIGURATION_Handle *cfg,
continue;
/* remove tailing whitespace */
- for (i = line_size - 1;
+ for (size_t i = line_size - 1;
(i >= 1) && (isspace ((unsigned char) line[i]));
i--)
line[i] = '\0';
@@ -308,60 +421,103 @@ GNUNET_CONFIGURATION_deserialize (struct
GNUNET_CONFIGURATION_Handle *cfg,
('%' == line[0]) )
continue;
- /* handle special "@INLINE@" directive */
- if (0 == strncasecmp (line,
- "@INLINE@ ",
- strlen ("@INLINE@ ")))
+ /* Handle special directives. */
+ if ('@' == line[0])
{
- char *inline_path;
+ char *end = strchr (line + 1, '@');
+ char *directive;
+ enum GNUNET_GenericReturnValue directive_ret;
- if (NULL == source_filename)
+ if (NULL == end)
{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Refusing to parse @INLINE@ configurations, "
- "not allowed without source filename!\n");
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Bad directive in line %u\n"),
+ nr);
ret = GNUNET_SYSERR;
break;
}
- /* FIXME: also trim space and end of line comment? */
- value = &line[strlen ("@INLINE@ ")];
- if ('/' == *value)
- inline_path = GNUNET_strdup (value);
- else
+ *end = '\0';
+ directive = line + 1;
+
+ if (0 == strcasecmp (directive, "INLINE"))
{
- /* We compute the canonical, absolute path first,
- so that relative imports resolve properly with symlinked
- config files. */
- char *source_realpath;
- char *endsep;
-
- source_realpath = realpath (source_filename,
- NULL);
- if (NULL == source_realpath)
+ const char *path = end + 1;
+
+ /* Skip space before path */
+ for (; isspace (*path); path++)
+ ;
+
+ directive_ret = handle_inline (cfg,
+ path,
+ false,
+ NULL,
+ source_filename);
+ }
+ else if (0 == strcasecmp (directive, "INLINE-MATCHING"))
+ {
+ const char *path = end + 1;
+
+ /* Skip space before path */
+ for (; isspace (*path); path++)
+ ;
+
+ directive_ret = handle_inline (cfg,
+ path,
+ true,
+ NULL,
+ source_filename);
+ }
+ else if (0 == strcasecmp (directive, "INLINE-SECRET"))
+ {
+ const char *secname = end + 1;
+ const char *path;
+ const char *secname_end;
+
+ /* Skip space before secname */
+ for (; isspace (*secname); secname++)
+ ;
+
+ secname_end = strchr (secname, ' ');
+
+ if (NULL == secname_end)
{
- /* Couldn't even resolve path of base dir. */
- GNUNET_break (0);
- ret = GNUNET_SYSERR; /* failed to parse included config */
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Bad inline-secret directive in line %u\n"),
+ nr);
+ ret = GNUNET_SYSERR;
break;
}
- endsep = strrchr (source_realpath, '/');
- GNUNET_assert (NULL != endsep);
- *endsep = '\0';
- GNUNET_asprintf (&inline_path,
- "%s/%s",
- source_realpath,
- value);
- free (source_realpath);
+ secname_end = '\0';
+ path = secname_end + 1;
+
+ /* Skip space before path */
+ for (; isspace (*path); path++)
+ ;
+
+ directive_ret = handle_inline (cfg,
+ path,
+ false,
+ secname,
+ source_filename);
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Unknown or malformed directive '%s' in line %u\n"),
+ directive,
+ nr);
+ ret = GNUNET_SYSERR;
+ break;
}
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_parse (cfg,
- inline_path))
+ if (GNUNET_OK != directive_ret)
{
- GNUNET_free (inline_path);
- ret = GNUNET_SYSERR; /* failed to parse included config */
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Bad directive '%s' in line %u\n"),
+ directive,
+ nr);
+ ret = GNUNET_SYSERR;
break;
}
- GNUNET_free (inline_path);
continue;
}
if (('[' == line[0]) && (']' == line[line_size - 1]))
@@ -375,10 +531,13 @@ GNUNET_CONFIGURATION_deserialize (struct
GNUNET_CONFIGURATION_Handle *cfg,
}
if (NULL != (eq = strchr (line, '=')))
{
+ size_t i;
+
/* tag = value */
tag = GNUNET_strndup (line, eq - line);
/* remove tailing whitespace */
- for (i = strlen (tag) - 1; (i >= 1) && (isspace ((unsigned char)
tag[i]));
+ for (i = strlen (tag) - 1;
+ (i >= 1) && (isspace ((unsigned char) tag[i]));
i--)
tag[i] = '\0';
diff --git a/src/util/disk.c b/src/util/disk.c
index 3bafe311d..1b909f13e 100644
--- a/src/util/disk.c
+++ b/src/util/disk.c
@@ -882,6 +882,143 @@ GNUNET_DISK_directory_scan (const char *dir_name,
return count;
}
+/**
+ * Check for a simple wildcard match.
+ * Only asterisks are allowed.
+ * Asterisks match everything, including slashes.
+ *
+ * @param pattern pattern with wildcards
+ * @param str string to match against
+ * @returns true on match, false otherwise
+ */
+static bool
+glob_match (const char *pattern, const char *str)
+{
+ /* Position in the input string */
+ const char *str_pos = str;
+ /* Position in the pattern */
+ const char *pat_pos = pattern;
+ /* Backtrack position in string */
+ const char *str_bt = NULL;
+ /* Backtrack position in pattern */
+ const char *pat_bt = NULL;
+
+ for (;;)
+ {
+ if (*pat_pos == '*')
+ {
+ str_bt = str_pos;
+ pat_bt = pat_pos++;
+ }
+ else if (*pat_pos == *str_pos)
+ {
+ if ('\0' == *pat_pos)
+ return true;
+ str_pos++;
+ pat_pos++;
+ }
+ else
+ {
+ if (NULL == str_bt)
+ return false;
+ /* Backtrack to match one more
+ character as part of the asterisk. */
+ str_pos = str_bt + 1;
+ if ('\0' == *str_pos)
+ return false;
+ pat_pos = pat_bt;
+ }
+ }
+}
+
+struct GlobClosure
+{
+ const char *glob;
+ GNUNET_FileNameCallback cb;
+ void *cls;
+};
+
+/**
+ * Function called with a filename.
+ *
+ * @param cls closure
+ * @param filename complete filename (absolute path)
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static enum GNUNET_GenericReturnValue
+glob_cb (void *cls,
+ const char *filename)
+{
+ struct GlobClosure *gc = cls;
+ const char *fn;
+
+ fn = strrchr (filename, DIR_SEPARATOR);
+ fn = (NULL == fn) ? filename : (fn + 1);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "checking glob '%s' against '%s'\n",
+ gc->glob,
+ fn);
+
+ if (glob_match (gc->glob, fn))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "found glob match '%s'\n",
+ filename);
+ gc->cb (gc->cls, filename);
+ }
+ return GNUNET_OK;
+}
+
+
+int
+GNUNET_DISK_glob (const char *glob_pattern,
+ GNUNET_FileNameCallback callback,
+ void *callback_cls)
+{
+ char *mypat = GNUNET_strdup (glob_pattern);
+ char *sep;
+ int ret;
+
+ sep = strrchr (mypat, DIR_SEPARATOR);
+ if (NULL == sep)
+ {
+ GNUNET_free (mypat);
+ return -1;
+ }
+
+ *sep = '\0';
+
+ if (NULL != strchr (mypat, '*'))
+ {
+ GNUNET_free (mypat);
+ GNUNET_break (0);
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ "glob pattern may only contain '*' in the final path component\n");
+ return -1;
+ }
+
+ {
+ struct GlobClosure gc = {
+ .glob = sep + 1,
+ .cb = callback,
+ .cls = callback_cls,
+ };
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "scanning directory '%s' for glob matches on '%s'\n",
+ mypat,
+ gc.glob);
+ ret = GNUNET_DISK_directory_scan (mypat,
+ glob_cb,
+ &gc
+ );
+ }
+ GNUNET_free (mypat);
+ return ret;
+}
+
/**
* Function that removes the given directory by calling
@@ -997,7 +1134,7 @@ GNUNET_DISK_file_copy (const char *src,
GNUNET_DISK_file_close (in);
GNUNET_DISK_file_close (out);
return GNUNET_OK;
-FAIL:
+ FAIL:
GNUNET_free (buf);
GNUNET_DISK_file_close (in);
GNUNET_DISK_file_close (out);
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.