gnunet-svn
[Top][All Lists]
Advanced

[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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]