src/utils_fbhash.c: Implementation of a file-backed hash.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 25 Apr 2009 13:16:01 +0000 (15:16 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 25 Apr 2009 13:16:01 +0000 (15:16 +0200)
This is going to be used for password files on the server.

src/utils_fbhash.c [new file with mode: 0644]
src/utils_fbhash.h [new file with mode: 0644]

diff --git a/src/utils_fbhash.c b/src/utils_fbhash.c
new file mode 100644 (file)
index 0000000..d20b7e3
--- /dev/null
@@ -0,0 +1,270 @@
+/**
+ * collectd - src/utils_fbhash.c
+ * Copyright (C) 2009  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+
+#include <pthread.h>
+
+#include "utils_fbhash.h"
+#include "utils_avltree.h"
+
+struct fbhash_s
+{
+  char *filename;
+  time_t mtime;
+
+  pthread_mutex_t lock;
+  c_avl_tree_t *tree;
+};
+
+/* 
+ * Private functions
+ */
+static void fbh_free_tree (c_avl_tree_t *tree) /* {{{ */
+{
+  int status;
+
+  if (tree == NULL)
+    return;
+
+  while (42)
+  {
+    char *key = NULL;
+    char *value = NULL;
+
+    status = c_avl_pick (tree, (void *) &key, (void *) &value);
+    if (status != 0)
+      break;
+
+    free (key);
+    free (value);
+  }
+
+  c_avl_destroy (tree);
+} /* }}} void fbh_free_tree */
+
+static int fbh_read_file (fbhash_t *h) /* {{{ */
+{
+  FILE *fh;
+  char buffer[4096];
+  struct flock fl;
+  c_avl_tree_t *tree;
+  int status;
+
+  fh = fopen (h->filename, "r");
+  if (fh == NULL)
+    return (-1);
+
+  memset (&fl, 0, sizeof (fl));
+  fl.l_type = F_RDLCK;
+  fl.l_whence = SEEK_SET;
+  fl.l_start = 0;
+  fl.l_len = 0; /* == entire file */
+  /* TODO: Lock file? -> fcntl */
+
+  status = fcntl (fileno (fh), F_SETLK, &fl);
+  if (status != 0)
+  {
+    fclose (fh);
+    return (-1);
+  }
+
+  tree = c_avl_create ((void *) strcmp);
+  if (tree == NULL)
+  {
+    fclose (fh);
+    return (-1);
+  }
+
+  /* Read `fh' into `tree' */
+  while (fgets (buffer, sizeof (buffer), fh) != NULL) /* {{{ */
+  {
+    size_t len;
+    char *key;
+    char *value;
+
+    char *key_copy;
+    char *value_copy;
+
+    buffer[sizeof (buffer) - 1] = 0;
+    len = strlen (buffer);
+
+    /* Remove trailing newline characters. */
+    while ((len > 0)
+        && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
+    {
+      len--;
+      buffer[len] = 0;
+    }
+
+    /* Seek first non-space character */
+    key = buffer;
+    while ((*key != 0) && isspace ((int) *key))
+      key++;
+
+    /* Skip empty lines and comments */
+    if ((key[0] == 0) || (key[0] == '#'))
+      continue;
+
+    /* Seek first colon */
+    value = strchr (key, ':');
+    if (value == NULL)
+      continue;
+
+    /* Null-terminate `key'. */
+    *value = 0;
+    value++;
+
+    /* Skip leading whitespace */
+    while ((*value != 0) && isspace ((int) *value))
+      value++;
+
+    /* Skip lines without value */
+    if (value[0] == 0)
+      continue;
+
+    key_copy = strdup (key);
+    value_copy = strdup (value);
+
+    if ((key_copy == NULL) || (value_copy == NULL))
+    {
+      free (key_copy);
+      free (value_copy);
+      continue;
+    }
+
+    status = c_avl_insert (tree, key_copy, value_copy);
+    if (status != 0)
+    {
+      free (key_copy);
+      free (value_copy);
+      continue;
+    }
+
+    DEBUG ("utils_fbhash: fbh_read_file: key = %s; value = %s;",
+        key, value);
+  } /* }}} while (fgets) */
+
+  fclose (fh);
+
+  fbh_free_tree (h->tree);
+  h->tree = tree;
+
+  return (0);
+} /* }}} int fbh_read_file */
+
+static int fbh_check_file (fbhash_t *h) /* {{{ */
+{
+  struct stat statbuf;
+  int status;
+
+  memset (&statbuf, 0, sizeof (statbuf));
+
+  status = stat (h->filename, &statbuf);
+  if (status != 0)
+    return (-1);
+
+  if (h->mtime >= statbuf.st_mtime)
+    return (0);
+
+  status = fbh_read_file (h);
+  if (status == 0)
+    h->mtime = statbuf.st_mtime;
+
+  return (status);
+} /* }}} int fbh_check_file */
+
+/* 
+ * Public functions
+ */
+fbhash_t *fbh_create (const char *file) /* {{{ */
+{
+  fbhash_t *h;
+  int status;
+
+  if (file == NULL)
+    return (NULL);
+
+  h = malloc (sizeof (*h));
+  if (h == NULL)
+    return (NULL);
+  memset (h, 0, sizeof (*h));
+
+  h->filename = strdup (file);
+  if (h->filename == NULL)
+  {
+    free (h);
+    return (NULL);
+  }
+
+  h->mtime = 0;
+  pthread_mutex_init (&h->lock, /* attr = */ NULL);
+
+  status = fbh_check_file (h);
+  if (status != 0)
+  {
+    fbh_destroy (h);
+    return (NULL);
+  }
+
+  return (h);
+} /* }}} fbhash_t *fbh_create */
+
+void fbh_destroy (fbhash_t *h) /* {{{ */
+{
+  if (h == NULL)
+    return;
+
+  free (h->filename);
+  fbh_free_tree (h->tree);
+} /* }}} void fbh_destroy */
+
+char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
+{
+  char *value;
+  char *value_copy;
+  int status;
+
+  if ((h == NULL) || (key == NULL))
+    return (NULL);
+
+  value = NULL;
+  value_copy = NULL;
+
+  pthread_mutex_lock (&h->lock);
+
+  /* TODO: Checking this everytime may be a bit much..? */
+  fbh_check_file (h);
+
+  status = c_avl_get (h->tree, key, (void *) &value);
+  if (status == 0)
+  {
+    assert (value != NULL);
+    value_copy = strdup (value);
+  }
+
+  pthread_mutex_unlock (&h->lock);
+
+  return (value_copy);
+} /* }}} char *fbh_get */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_fbhash.h b/src/utils_fbhash.h
new file mode 100644 (file)
index 0000000..0a0305e
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * collectd - src/utils_fbhash.h
+ * Copyright (C) 2009  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#ifndef UTILS_FBHASH_H
+#define UTILS_FBHASH_H 1
+
+/*
+ * File-backed hash
+ *
+ * This module reads a file of the form
+ *   key: value
+ * into a hash, which can then be queried. The file is given to `fbh_create',
+ * the hash is queried using `fbh_get'. If the file is changed during runtime,
+ * it will automatically be re-read.
+ */
+
+struct fbhash_s;
+typedef struct fbhash_s fbhash_t;
+
+fbhash_t *fbh_create (const char *file);
+void fbh_destroy (fbhash_t *h);
+
+/* Returns the value as a newly allocated `char *'. It's the caller's
+ * responsibility to free this memory. */
+char *fbh_get (fbhash_t *h, const char *key);
+
+#endif /* UTILS_FBHASH_H */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */