From dc7294ae3d2b76283b9967bf39810fd48e4bc772 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sat, 23 Aug 2008 14:53:41 +0200 Subject: [PATCH] qmail plugin: Made the counting of files more generalized. --- src/qmail.c | 586 ++++++++++++++++++++++++++++++++++++++------------- src/types.db | 2 + 2 files changed, 436 insertions(+), 152 deletions(-) diff --git a/src/qmail.c b/src/qmail.c index 063eee93..da7a1aea 100644 --- a/src/qmail.c +++ b/src/qmail.c @@ -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 + * Florian octo Forster **/ #include "collectd.h" @@ -27,235 +29,515 @@ #include #include #include +#include -#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: + * + * + * Instance "foobar" + * Name "*.conf" + * MTime -3600 + * Size "+10M" + * + * + * + * 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 */ /* diff --git a/src/types.db b/src/types.db index a31809dc..c8d2f8a1 100644 --- a/src/types.db +++ b/src/types.db @@ -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 -- 2.30.2