From 76c0a54209a25f62b1fe16d9b00b41dde5725ae0 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sun, 28 Jan 2007 18:14:39 +0100 Subject: [PATCH] collectd-nagios: Added a small program to perform Nagios-compatible checks on values. --- src/Makefile.am | 3 + src/collectd-nagios.c | 431 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 434 insertions(+) create mode 100644 src/collectd-nagios.c diff --git a/src/Makefile.am b/src/Makefile.am index d9d90ef1..1efdb1ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,7 @@ AM_CFLAGS = -Wall -Werror endif sbin_PROGRAMS = collectd +bin_PROGRAMS = collectd-nagios collectd_SOURCES = collectd.c collectd.h \ utils_debug.c utils_debug.h \ @@ -61,6 +62,8 @@ endif collectd_LDADD = $(LIBLTDL) libconfig/libconfig.la "-dlopen" self collectd_DEPENDENCIES = $(LIBLTDL) libconfig/libconfig.la +collectd_nagios_SOURCES = collectd-nagios.c + pkglib_LTLIBRARIES = if BUILD_MODULE_APACHE diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c new file mode 100644 index 00000000..21e877e1 --- /dev/null +++ b/src/collectd-nagios.c @@ -0,0 +1,431 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * This weird macro cascade forces the glibc to define `NAN'. I don't know + * another way to solve this, so more intelligent solutions are welcome. -octo + */ +#ifndef __USE_ISOC99 +# define DISABLE__USE_ISOC99 1 +# define __USE_ISOC99 1 +#endif +#include +#ifdef DISABLE__USE_ISOC99 +# undef DISABLE__USE_ISOC99 +# undef __USE_ISOC99 +#endif + +#define RET_OKAY 0 +#define RET_WARNING 1 +#define RET_CRITICAL 2 +#define RET_UNKNOWN 3 + +#define CON_NONE 0 +#define CON_AVERAGE 1 +#define CON_SUM 2 + +struct range_s +{ + double min; + double max; + int invert; +}; +typedef struct range_s range_t; + +extern char *optarg; +extern int optind, opterr, optopt; + +static char *socket_file_g = NULL; +static char *value_string_g = NULL; +static range_t range_critical_g; +static range_t range_warning_g; +static int consolitation_g = CON_NONE; + +static void parse_range (char *string, range_t *range) +{ + char *min_ptr; + char *max_ptr; + + if (*string == '@') + { + range->invert = 1; + string++; + } + + max_ptr = strchr (string, ':'); + if (max_ptr == NULL) + { + min_ptr = NULL; + max_ptr = string; + } + else + { + min_ptr = string; + *max_ptr = '\0'; + max_ptr++; + } + + assert (max_ptr != NULL); + + /* `10' == `0:10' */ + if (min_ptr == NULL) + range->min = 0.0; + /* :10 == ~:10 == -inf:10 */ + else if ((*min_ptr == '\0') || (*min_ptr == '~')) + range->min = NAN; + else + range->min = atof (min_ptr); + + if ((*max_ptr == '\0') || (*max_ptr == '~')) + range->max = NAN; + else + range->max = atof (max_ptr); +} /* void parse_range */ + +int match_range (range_t *range, double value) +{ + int ret = 0; + + if ((range->min != NAN) && (range->min > value)) + ret = 1; + if ((range->max != NAN) && (range->max < value)) + ret = 1; + + return (((ret - range->invert) == 0) ? 0 : 1); +} + +static int get_values (int *ret_values_num, double **ret_values, + char ***ret_values_names) +{ + struct sockaddr_un sa; + int status; + int fd; + FILE *fh; + char buffer[4096]; + + int values_num; + double *values; + char **values_names; + + int i; + + fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + { + fprintf (stderr, "socket failed: %s\n", + strerror (errno)); + return (-1); + } + + memset (&sa, '\0', sizeof (sa)); + sa.sun_family = AF_UNIX; + strncpy (sa.sun_path, socket_file_g, + sizeof (sa.sun_path) - 1); + + status = connect (fd, (struct sockaddr *) &sa, sizeof (sa)); + if (status != 0) + { + fprintf (stderr, "connect failed: %s\n", + strerror (errno)); + return (-1); + } + + fh = fdopen (fd, "r+"); + if (fh == NULL) + { + fprintf (stderr, "fdopen failed: %s\n", + strerror (errno)); + close (fd); + return (-1); + } + + fprintf (fh, "GETVAL %s\n", value_string_g); + fflush (fh); + + if (fgets (buffer, sizeof (buffer), fh) == NULL) + { + fprintf (stderr, "fgets failed: %s\n", + strerror (errno)); + close (fd); + return (-1); + } + close (fd); fd = -1; + + values_num = atoi (buffer); + if (values_num < 1) + return (-1); + + values = (double *) malloc (values_num * sizeof (double)); + if (values == NULL) + { + fprintf (stderr, "malloc failed: %s\n", + strerror (errno)); + return (-1); + } + + values_names = (char **) malloc (values_num * sizeof (char *)); + if (values_names == NULL) + { + fprintf (stderr, "malloc failed: %s\n", + strerror (errno)); + free (values); + return (-1); + } + + { + char *ptr = strchr (buffer, ' ') + 1; + char *key; + char *value; + + i = 0; + while ((key = strtok (ptr, " \t")) != NULL) + { + ptr = NULL; + value = strchr (key, '='); + if (value == NULL) + continue; + *value = '\0'; value++; + + values_names[i] = strdup (key); + values[i] = atof (value); + + i++; + if (i >= values_num) + break; + } + values_num = i; + } + + *ret_values_num = values_num; + *ret_values = values; + *ret_values_names = values_names; + + return (0); +} /* int get_values */ + +static void usage (const char *name) +{ + fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> [options]\n" + "\n" + "Valid options are:\n" + " -s Path to collectd's UNIX-socket\n" + " -n Value specification to get from collectd\n" + " -c Critical range\n" + " -w Range for critical values\n", + name); + exit (1); +} /* void usage */ + +int do_check_con_none (int values_num, double *values, char **values_names) +{ + int i; + + int num_critical = 0; + int num_warning = 0; + int num_okay = 0; + + for (i = 0; i < values_num; i++) + { + if (values[i] == NAN) + num_warning++; + else if (match_range (&range_critical_g, values[i]) != 0) + num_critical++; + else if (match_range (&range_warning_g, values[i]) != 0) + num_warning++; + else + num_okay++; + } + + if ((num_critical != 0) || (values_num == 0)) + { + printf ("CRITICAL: %i critical, %i warning, %i okay\n", + num_critical, num_warning, num_okay); + return (RET_CRITICAL); + } + else if (num_warning != 0) + { + printf ("WARNING: %i warning, %i okay\n", + num_warning, num_okay); + return (RET_WARNING); + } + else + { + printf ("OKAY: %i okay\n", num_okay); + return (RET_OKAY); + } + + return (RET_UNKNOWN); +} /* int do_check_con_none */ + +int do_check_con_average (int values_num, double *values, char **values_names) +{ + int i; + double total; + int total_num; + + total = 0.0; + total_num = 0; + for (i = 0; i < values_num; i++) + { + if (values[i] != NAN) + { + total += values[i]; + total_num++; + } + } + + if (total_num == 0) + { + printf ("WARNING: No defined values found\n"); + return (RET_WARNING); + } + + if (match_range (&range_critical_g, total / total_num) != 0) + { + printf ("CRITICAL: Average = %lf\n", + (double) (total / total_num)); + return (RET_CRITICAL); + } + else if (match_range (&range_warning_g, total / total_num) != 0) + { + printf ("WARNING: Average = %lf\n", + (double) (total / total_num)); + return (RET_WARNING); + } + else + { + printf ("OKAY: Average = %lf\n", + (double) (total / total_num)); + return (RET_OKAY); + } + + return (RET_UNKNOWN); +} /* int do_check_con_average */ + +int do_check_con_sum (int values_num, double *values, char **values_names) +{ + int i; + double total; + int total_num; + + total = 0.0; + total_num = 0; + for (i = 0; i < values_num; i++) + { + if (values[i] != NAN) + { + total += values[i]; + total_num++; + } + } + + if (total_num == 0) + { + printf ("WARNING: No defined values found\n"); + return (RET_WARNING); + } + + if (match_range (&range_critical_g, total) != 0) + { + printf ("CRITICAL: Sum = %lf\n", total); + return (RET_CRITICAL); + } + else if (match_range (&range_warning_g, total) != 0) + { + printf ("WARNING: Sum = %lf\n", total); + return (RET_WARNING); + } + else + { + printf ("OKAY: Sum = %lf\n", total); + return (RET_OKAY); + } + + return (RET_UNKNOWN); +} /* int do_check_con_sum */ + +int do_check (void) +{ + double *values; + char **values_names; + int values_num; + int i; + + if (get_values (&values_num, &values, &values_names) != 0) + { + fputs ("ERROR: Cannot get values from daemon\n", stdout); + return (RET_CRITICAL); + } + + for (i = 0; i < values_num; i++) + printf ("%s=%lf\n", values_names[i], values[i]); + + if (consolitation_g == CON_NONE) + return (do_check_con_none (values_num, values, values_names)); + else if (consolitation_g == CON_AVERAGE) + return (do_check_con_average (values_num, values, values_names)); + else if (consolitation_g == CON_SUM) + return (do_check_con_sum (values_num, values, values_names)); + + return (RET_UNKNOWN); +} + +int main (int argc, char **argv) +{ + range_critical_g.min = NAN; + range_critical_g.max = NAN; + range_critical_g.invert = 0; + + range_warning_g.min = NAN; + range_warning_g.max = NAN; + range_warning_g.invert = 0; + + while (42) + { + int c; + + c = getopt (argc, argv, "w:c:s:n:g:h"); + if (c < 0) + break; + + switch (c) + { + case 'c': + parse_range (optarg, &range_critical_g); + break; + case 'w': + parse_range (optarg, &range_warning_g); + break; + case 's': + socket_file_g = optarg; + break; + case 'n': + value_string_g = optarg; + break; + case 'g': + if (strcasecmp (optarg, "none") == 0) + consolitation_g = CON_NONE; + else if (strcasecmp (optarg, "average") == 0) + consolitation_g = CON_AVERAGE; + else if (strcasecmp (optarg, "sum") == 0) + consolitation_g = CON_SUM; + else + usage (argv[0]); + break; + default: + usage (argv[0]); + } /* switch (c) */ + } + + if ((socket_file_g == NULL) || (value_string_g == NULL)) + usage (argv[0]); + + return (do_check ()); +} /* int main */ -- 2.11.0