Code

qmail plugin: Made the counting of files more generalized.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 23 Aug 2008 12:53:41 +0000 (14:53 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 23 Aug 2008 12:53:41 +0000 (14:53 +0200)
src/qmail.c
src/types.db

index 063eee93cf5a8e895730eab0d871dd8c06ccd5e6..da7a1aea6596baf81cf452ed32089237c228b4dc 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/qmail.c
  * Copyright (C) 2008  Alessandro Iurlano
+ * Copyright (C) 2008  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
@@ -15,8 +16,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
- * Author:
+ * Authors:
  *   Alessandro Iurlano <alessandro.iurlano at gmail.com>
+ *   Florian octo Forster <octo at verplant.org>
  **/
 
 #include "collectd.h"
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <dirent.h>
+#include <fnmatch.h>
 
-#define DEFAULT_BASE_DIR "/var/qmail"
+struct fc_directory_conf_s
+{
+  char *path;
+  char *instance;
 
-static char *qmail_base_dir;
+  /* Data counters */
+  uint64_t files_num;
+  uint64_t files_size;
 
-static const char *config_keys[] =
-{
-  "QmailDir"
+  /* Selectors */
+  char *name;
+  int64_t mtime;
+  int64_t size;
+
+  /* Helper for the recursive functions */
+  time_t now;
 };
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+typedef struct fc_directory_conf_s fc_directory_conf_t;
 
-static void qmail_submit (const char *plugin_instance, gauge_t value)
+static fc_directory_conf_t **directories = NULL;
+static size_t directories_num = 0;
+
+static void fc_submit_dir (const fc_directory_conf_t *dir)
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
 
-  values[0].gauge = value;
+  values[0].gauge = (gauge_t) dir->files_num;
 
   vl.values = values;
   vl.values_len = STATIC_ARRAY_SIZE (values);
   vl.time = time (NULL);
   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-  sstrncpy (vl.type, "gauge", sizeof (vl.type));
   sstrncpy (vl.plugin, "qmail", sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+  sstrncpy (vl.plugin_instance, dir->instance, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, "files", sizeof (vl.type));
+
+  plugin_dispatch_values (&vl);
+
+  values[0].gauge = (gauge_t) dir->files_size;
+  sstrncpy (vl.type, "bytes", sizeof (vl.type));
 
   plugin_dispatch_values (&vl);
 } /* void qmail_submit */
 
-static int count_files_in_subtree (const char *path, int depth)
+/*
+ * Config:
+ * <Plugin qmail>
+ *   <Directory /path/to/dir>
+ *     Instance "foobar"
+ *     Name "*.conf"
+ *     MTime -3600
+ *     Size "+10M"
+ *   </Directory>
+ * </Plugin>
+ *
+ * Collect:
+ * - Number of files
+ * - Total size
+ */
+
+static int fc_config_set_instance (fc_directory_conf_t *dir, const char *str)
 {
-#if NAME_MAX < 1024
-# define DIRENT_BUFFER_SIZE (sizeof (struct dirent) + 1024 + 1)
-#else
-# define DIRENT_BUFFER_SIZE (sizeof (struct dirent) + NAME_MAX + 1)
-#endif
-
-  DIR *dh;
-  struct dirent *de;
-  char dirent_buffer[DIRENT_BUFFER_SIZE];
-  int status;
+  char buffer[1024];
+  char *ptr;
+  char *copy;
 
-  char **subdirs;
-  size_t subdirs_num;
+  strncpy (buffer, str, sizeof (buffer));
+  for (ptr = buffer; *ptr != 0; ptr++)
+    if (*ptr == '/')
+      *ptr = '-';
 
-  int count;
-  int i;
+  for (ptr = buffer; *ptr == '-'; ptr++)
+    /* do nothing */;
+
+  if (*ptr == 0)
+    return (-1);
+
+  copy = strdup (ptr);
+  if (copy == NULL)
+    return (-1);
+
+  sfree (dir->instance);
+  dir->instance = copy;
+
+  return (0);
+} /* int fc_config_set_instance */
+
+static int fc_config_add_dir_instance (fc_directory_conf_t *dir,
+    oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("qmail plugin: The `Instance' config option needs exactly one "
+        "string argument.");
+    return (-1);
+  }
+
+  return (fc_config_set_instance (dir, ci->values[0].value.string));
+} /* int fc_config_add_dir_instance */
+
+static int fc_config_add_dir_name (fc_directory_conf_t *dir,
+    oconfig_item_t *ci)
+{
+  char *temp;
 
-  dh = opendir (path);
-  if (dh == NULL)
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
   {
-    ERROR ("qmail plugin: opendir (%s) failed.", path);
+    WARNING ("qmail plugin: The `Name' config option needs exactly one "
+        "string argument.");
     return (-1);
   }
 
-  subdirs = NULL;
-  subdirs_num = 0;
+  temp = strdup (ci->values[0].value.string);
+  if (temp == NULL)
+  {
+    ERROR ("qmail plugin: strdup failed.");
+    return (-1);
+  }
+
+  sfree (dir->name);
+  dir->name = temp;
+
+  return (0);
+} /* int fc_config_add_dir_name */
+
+static int fc_config_add_dir_mtime (fc_directory_conf_t *dir,
+    oconfig_item_t *ci)
+{
+  char *endptr;
+  double temp;
 
-  count = 0;
-  while (42)
+  if ((ci->values_num != 1)
+      || ((ci->values[0].type != OCONFIG_TYPE_STRING)
+        && (ci->values[0].type != OCONFIG_TYPE_NUMBER)))
   {
-    char abs_path[PATH_MAX];
-    struct stat statbuf;
+    WARNING ("qmail plugin: The `MTime' config option needs exactly one "
+        "string or numeric argument.");
+    return (-1);
+  }
 
-    de = NULL;
-    status = readdir_r (dh, (struct dirent *) dirent_buffer, &de);
-    if (status != 0)
-    {
-      char errbuf[4096];
-      ERROR ("qmail plugin: readdir_r failed: %s",
-          sstrerror (errno, errbuf, sizeof (errbuf)));
-      closedir (dh);
+  if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
+  {
+    dir->mtime = (int64_t) ci->values[0].value.number;
+    return (0);
+  }
+
+  errno = 0;
+  endptr = NULL;
+  temp = strtod (ci->values[0].value.string, &endptr);
+  if ((errno != 0) || (endptr == NULL)
+      || (endptr == ci->values[0].value.string))
+  {
+    WARNING ("qmail plugin: Converting `%s' to a number failed.",
+        ci->values[0].value.string);
+    return (-1);
+  }
+
+  switch (*endptr)
+  {
+    case 0:
+    case 's':
+    case 'S':
+      break;
+
+    case 'm':
+    case 'M':
+      temp *= 60;
+      break;
+
+    case 'h':
+    case 'H':
+      temp *= 3600;
+      break;
+
+    case 'd':
+    case 'D':
+      temp *= 86400;
+      break;
+
+    case 'w':
+    case 'W':
+      temp *= 7 * 86400;
+      break;
+
+    case 'y':
+    case 'Y':
+      temp *= 31557600; /* == 365.25 * 86400 */
+      break;
+
+    default:
+      WARNING ("qmail plugin: Invalid suffix for `MTime': `%c'", *endptr);
       return (-1);
-    }
-    else if (de == NULL)
-    {
-      /* end of directory */
+  } /* switch (*endptr) */
+
+  dir->mtime = (int64_t) temp;
+
+  return (0);
+} /* int fc_config_add_dir_mtime */
+
+static int fc_config_add_dir_size (fc_directory_conf_t *dir,
+    oconfig_item_t *ci)
+{
+  char *endptr;
+  double temp;
+
+  if ((ci->values_num != 1)
+      || ((ci->values[0].type != OCONFIG_TYPE_STRING)
+        && (ci->values[0].type != OCONFIG_TYPE_NUMBER)))
+  {
+    WARNING ("qmail plugin: The `Size' config option needs exactly one "
+        "string or numeric argument.");
+    return (-1);
+  }
+
+  if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
+  {
+    dir->size = (int64_t) ci->values[0].value.number;
+    return (0);
+  }
+
+  errno = 0;
+  endptr = NULL;
+  temp = strtod (ci->values[0].value.string, &endptr);
+  if ((errno != 0) || (endptr == NULL)
+      || (endptr == ci->values[0].value.string))
+  {
+    WARNING ("qmail plugin: Converting `%s' to a number failed.",
+        ci->values[0].value.string);
+    return (-1);
+  }
+
+  switch (*endptr)
+  {
+    case 0:
+    case 'b':
+    case 'B':
       break;
-    }
 
-    if (de->d_name[0] == '.')
-      continue;
+    case 'k':
+    case 'K':
+      temp *= 1000.0;
+      break;
 
-    ssnprintf (abs_path, sizeof (abs_path), "%s/%s", path, de->d_name);
+    case 'm':
+    case 'M':
+      temp *= 1000.0 * 1000.0;
+      break;
 
-    status = lstat (abs_path, &statbuf);
-    if (status != 0)
-    {
-      ERROR ("qmail plugin: stat (%s) failed.", abs_path);
-      continue;
-    }
+    case 'g':
+    case 'G':
+      temp *= 1000.0 * 1000.0 * 1000.0;
+      break;
 
-    if (S_ISREG (statbuf.st_mode))
-    {
-      count++;
-    }
-    else if (S_ISDIR (statbuf.st_mode))
+    case 't':
+    case 'T':
+      temp *= 1000.0 * 1000.0 * 1000.0 * 1000.0;
+      break;
+
+    case 'p':
+    case 'P':
+      temp *= 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0;
+      break;
+
+    default:
+      WARNING ("qmail plugin: Invalid suffix for `Size': `%c'", *endptr);
+      return (-1);
+  } /* switch (*endptr) */
+
+  dir->size = (int64_t) temp;
+
+  return (0);
+} /* int fc_config_add_dir_size */
+
+static int fc_config_add_dir (oconfig_item_t *ci)
+{
+  fc_directory_conf_t *dir;
+  int status;
+  int i;
+
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("qmail plugin: `Directory' needs exactly one string argument.");
+    return (-1);
+  }
+
+  /* Initialize `dir' */
+  dir = (fc_directory_conf_t *) malloc (sizeof (*dir));
+  if (dir == NULL)
+  {
+    ERROR ("qmail plugin: mallow failed.");
+    return (-1);
+  }
+  memset (dir, 0, sizeof (*dir));
+
+  dir->path = strdup (ci->values[0].value.string);
+  if (dir->path == NULL)
+  {
+    ERROR ("qmail plugin: strdup failed.");
+    return (-1);
+  }
+
+  fc_config_set_instance (dir, dir->path);
+
+  dir->name = NULL;
+  dir->mtime = 0;
+  dir->size = 0;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Instance", option->key) == 0)
+      status = fc_config_add_dir_instance (dir, option);
+    else if (strcasecmp ("Name", option->key) == 0)
+      status = fc_config_add_dir_name (dir, option);
+    else if (strcasecmp ("MTime", option->key) == 0)
+      status = fc_config_add_dir_mtime (dir, option);
+    else if (strcasecmp ("Size", option->key) == 0)
+      status = fc_config_add_dir_size (dir, option);
+    else
     {
-      char **temp;
-
-      temp = (char **) realloc (subdirs, sizeof (char *) * (subdirs_num + 1));
-      if (temp == NULL)
-      {
-        ERROR ("qmail plugin: realloc failed.");
-        continue;
-      }
-      subdirs = temp;
-
-      subdirs[subdirs_num] = strdup (abs_path);
-      if (subdirs[subdirs_num] == NULL)
-      {
-        ERROR ("qmail plugin: strdup failed.");
-        continue;
-      }
-      subdirs_num++;
+      WARNING ("qmail plugin: fc_config_add_dir: "
+          "Option `%s' not allowed here.", option->key);
+      status = -1;
     }
-  }
 
-  closedir (dh);
-  dh = NULL;
+    if (status != 0)
+      break;
+  } /* for (ci->children) */
 
-  if (depth > 0)
+  if (status == 0)
   {
-    for (i = 0; i < subdirs_num; i++)
+    fc_directory_conf_t **temp;
+
+    temp = (fc_directory_conf_t **) realloc (directories,
+        sizeof (*directories) * directories_num);
+    if (temp == NULL)
+    {
+      ERROR ("qmail plugin: realloc failed.");
+      status = -1;
+    }
+    else
     {
-      status = count_files_in_subtree (subdirs[i], depth - 1);
-      if (status > 0)
-        count += status;
+      directories = temp;
+      directories[directories_num] = dir;
+      directories_num++;
     }
   }
 
-  for (i = 0; i < subdirs_num; i++)
+  if (status != 0)
   {
-    sfree (subdirs[i]);
+    sfree (dir->name);
+    sfree (dir->instance);
+    sfree (dir->path);
+    sfree (dir);
+    return (-1);
   }
-  sfree (subdirs);
 
-  return (count);
-} /* int count_files_in_subtree */
+  return (0);
+} /* int fc_config_add_dir */
 
-static int read_queue_length (const char *queue_name, const char *path)
+static int fc_config (oconfig_item_t *ci)
 {
-  int64_t num_files;
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    if (strcasecmp ("Directory", child->key) == 0)
+      fc_config_add_dir (child);
+    else
+    {
+      WARNING ("qmail plugin: Ignoring unknown config option `%s'.",
+          child->key);
+    }
+  } /* for (ci->children) */
+
+  return (0);
+} /* int qmail_config */
 
-  num_files = count_files_in_subtree (path, /* depth = */ 1);
-  if (num_files < 0)
+static int fc_init (void)
+{
+  if (directories_num < 1)
   {
-    ERROR ("qmail plugin: Counting files in `%s' failed.", path);
+    WARNING ("qmail plugin: No directories have been configured.");
     return (-1);
   }
 
-  qmail_submit (queue_name, (gauge_t) num_files);
   return (0);
-} /* int read_queue_length */
+} /* int fc_init */
 
-static int queue_len_read (void)
+static int fc_read_dir_callback (const char *dirname, const char *filename,
+    void *user_data)
 {
-  char path[4096];
-  int success;
+  fc_directory_conf_t *dir = user_data;
+  char abs_path[PATH_MAX];
+  struct stat statbuf;
   int status;
 
-  success = 0;
-  
-  ssnprintf (path, sizeof (path), "%s/queue/mess",
-      (qmail_base_dir != NULL)
-      ? qmail_base_dir
-      : DEFAULT_BASE_DIR);
+  if (dir == NULL)
+    return (-1);
 
-  status = read_queue_length ("messages", path);
-  if (status == 0)
-    success++;
+  ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, filename);
 
-  ssnprintf (path, sizeof (path), "%s/queue/todo",
-      (qmail_base_dir != NULL)
-      ? qmail_base_dir
-      : DEFAULT_BASE_DIR);
+  status = lstat (abs_path, &statbuf);
+  if (status != 0)
+  {
+    ERROR ("qmail plugin: stat (%s) failed.", abs_path);
+    return (-1);
+  }
 
-  status = read_queue_length ("todo", path);
-  if (status == 0)
-    success++;
+  if (S_ISDIR (statbuf.st_mode))
+  {
+    status = walk_directory (abs_path, fc_read_dir_callback, dir);
+    return (status);
+  }
+  else if (!S_ISREG (statbuf.st_mode))
+  {
+    return (0);
+  }
 
-  if (success > 0)
-    return 0;
-  return (-1);
-} /* int queue_len_read */
+  if (dir->name != NULL)
+  {
+    status = fnmatch (dir->name, filename, /* flags = */ 0);
+    if (status != 0)
+      return (0);
+  }
 
-static int qmail_config (const char *key, const char *val)
-{
-  if (strcasecmp ("QmailDir", key) == 0)
+  if (dir->mtime != 0)
   {
-    size_t qmail_base_dir_len;
+    time_t mtime = dir->now;
 
-    sfree (qmail_base_dir);
-    qmail_base_dir = strdup(val);
-    if (qmail_base_dir == NULL)
-    {
-      ERROR ("qmail plugin: strdup failed.");
-      return (1);
-    }
+    if (dir->mtime < 0)
+      mtime += dir->mtime;
+    else
+      mtime -= dir->mtime;
 
-    qmail_base_dir_len = strlen (qmail_base_dir);
-    while ((qmail_base_dir_len > 0)
-        && (qmail_base_dir[qmail_base_dir_len - 1] == '/'))
-    {
-      qmail_base_dir[qmail_base_dir_len - 1] = 0;
-      qmail_base_dir_len--;
-    }
+    DEBUG ("qmail plugin: Only collecting files that were touched %s %u.",
+        (dir->mtime < 0) ? "after" : "before",
+        (unsigned int) mtime);
 
-    if (qmail_base_dir_len == 0)
-    {
-      ERROR ("qmail plugin: QmailDir is invalid.");
-      sfree (qmail_base_dir);
-      qmail_base_dir = NULL;
-      return (1);
-    }
+    if (((dir->mtime < 0) && (statbuf.st_mtime < mtime))
+        || ((dir->mtime > 0) && (statbuf.st_mtime > mtime)))
+      return (0);
+  }
+
+  if (dir->size != 0)
+  {
+    off_t size;
+
+    if (dir->size < 0)
+      size = (off_t) ((-1) * dir->size);
+    else
+      size = (off_t) dir->size;
+
+    if (((dir->size < 0) && (statbuf.st_size > size))
+        || ((dir->size > 0) && (statbuf.st_size < size)))
+      return (0);
   }
-  else
+
+  dir->files_num++;
+  dir->files_size += (uint64_t) statbuf.st_size;
+
+  return (0);
+} /* int fc_read_dir_callback */
+
+static int fc_read_dir (fc_directory_conf_t *dir)
+{
+  int status;
+
+  dir->files_num = 0;
+  dir->files_size = 0;
+
+  if (dir->mtime != 0)
+    dir->now = time (NULL);
+
+  status = walk_directory (dir->path, fc_read_dir_callback, dir);
+  if (status != 0)
   {
+    WARNING ("qmail plugin: walk_directory (%s) failed.", dir->path);
     return (-1);
   }
 
+  fc_submit_dir (dir);
+
   return (0);
-} /* int qmail_config */
+} /* int fc_read_dir */
+
+static int fc_read (void)
+{
+  size_t i;
+
+  for (i = 0; i < directories_num; i++)
+    fc_read_dir (directories[i]);
+
+  return (0);
+} /* int fc_read */
 
 void module_register (void)
 {
-  plugin_register_config ("qmail", qmail_config,
-      config_keys, config_keys_num);
-  plugin_register_read ("qmail", queue_len_read);
+  plugin_register_complex_config ("qmail", fc_config);
+  plugin_register_init ("qmail", fc_init);
+  plugin_register_read ("qmail", fc_read);
 } /* void module_register */
 
 /*
index a31809dc2e4b6a2969301a891ccd129d72fca2d5..c8d2f8a1f233d8529b5cd2a85a4f7648f79254ba 100644 (file)
@@ -3,6 +3,7 @@ apache_connections      count:GAUGE:0:65535
 apache_requests                count:COUNTER:0:134217728
 apache_scoreboard      count:GAUGE:0:65535
 bitrate                        value:GAUGE:0:4294967295
+bytes                  value:GAUGE:0:U
 cache_result           value:COUNTER:0:4294967295
 cache_size             value:GAUGE:0:4294967295
 charge                 value:GAUGE:0:U
@@ -28,6 +29,7 @@ email_count           value:GAUGE:0:U
 email_size             value:GAUGE:0:U
 entropy                        entropy:GAUGE:0:4294967295
 fanspeed               value:GAUGE:0:U
+files                  value:GAUGE:0:U
 frequency              frequency:GAUGE:0:U
 frequency_offset       ppm:GAUGE:-1000000:1000000
 gauge                  value:GAUGE:U:U