Code

collectd-nagios: Added a small program to perform Nagios-compatible checks on values.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 28 Jan 2007 17:14:39 +0000 (18:14 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 28 Jan 2007 17:14:39 +0000 (18:14 +0100)
src/Makefile.am
src/collectd-nagios.c [new file with mode: 0644]

index d9d90ef14271286c35e69bfc0de308fe58d347af..1efdb1aba5ce25d8e7f12f7799c975682ffdb14b 100644 (file)
@@ -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 (file)
index 0000000..21e877e
--- /dev/null
@@ -0,0 +1,431 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/*
+ * 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 <math.h>
+#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 <socket>   Path to collectd's UNIX-socket\n"
+                       " -n <v_spec>   Value specification to get from collectd\n"
+                       " -c <range>    Critical range\n"
+                       " -w <range>    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 */