/**
* 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
* 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 */
/*