-#include "config.h"
+/**
+ * collectd-nagios - src/collectd-nagios.c
+ * 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
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if !defined(__GNUC__) || !__GNUC__
+# define __attribute__(x) /**/
+#endif
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
+#include <strings.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include "libcollectdclient/client.h"
+
/*
* This is copied directly from collectd.h. Make changes there!
*/
#define CON_NONE 0
#define CON_AVERAGE 1
#define CON_SUM 2
+#define CON_PERCENTAGE 3
struct range_s
{
static char **match_ds_g = NULL;
static int match_ds_num_g = 0;
-static int ignore_ds (const char *name)
+/* `strdup' is an XSI extension. I don't want to pull in all of XSI just for
+ * that, so here's an own implementation.. It's easy enough. The GCC attributes
+ * are supposed to get good performance.. -octo */
+__attribute__((malloc, nonnull (1)))
+static char *cn_strdup (const char *str) /* {{{ */
+{
+ size_t strsize;
+ char *ret;
+
+ strsize = strlen (str) + 1;
+ ret = (char *) malloc (strsize);
+ if (ret != NULL)
+ memcpy (ret, str, strsize);
+ return (ret);
+} /* }}} char *cn_strdup */
+
+static int filter_ds (size_t *values_num,
+ double **values, char ***values_names)
{
- int i;
+ gauge_t *new_values;
+ char **new_names;
+
+ size_t i;
if (match_ds_g == NULL)
- return (0);
+ return (RET_OKAY);
+
+ new_values = (gauge_t *)calloc (match_ds_num_g, sizeof (*new_values));
+ if (new_values == NULL)
+ {
+ fprintf (stderr, "malloc failed: %s\n", strerror (errno));
+ return (RET_UNKNOWN);
+ }
+
+ new_names = (char **)calloc (match_ds_num_g, sizeof (*new_names));
+ if (new_names == NULL)
+ {
+ fprintf (stderr, "malloc failed: %s\n", strerror (errno));
+ free (new_values);
+ return (RET_UNKNOWN);
+ }
- for (i = 0; i < match_ds_num_g; i++)
- if (strcasecmp (match_ds_g[i], name) == 0)
- return (0);
+ for (i = 0; i < (size_t) match_ds_num_g; i++)
+ {
+ size_t j;
+
+ /* match_ds_g keeps pointers into argv but the names will be freed */
+ new_names[i] = cn_strdup (match_ds_g[i]);
+ if (new_names[i] == NULL)
+ {
+ fprintf (stderr, "cn_strdup failed: %s\n", strerror (errno));
+ free (new_values);
+ for (j = 0; j < i; j++)
+ free (new_names[j]);
+ free (new_names);
+ return (RET_UNKNOWN);
+ }
+
+ for (j = 0; j < *values_num; j++)
+ if (strcasecmp (new_names[i], (*values_names)[j]) == 0)
+ break;
- return (1);
-} /* int ignore_ds */
+ if (j == *values_num)
+ {
+ printf ("ERROR: DS `%s' is not available.\n", new_names[i]);
+ free (new_values);
+ for (j = 0; j <= i; j++)
+ free (new_names[j]);
+ free (new_names);
+ return (RET_CRITICAL);
+ }
+
+ new_values[i] = (*values)[j];
+ }
+
+ free (*values);
+ for (i = 0; i < *values_num; i++)
+ free ((*values_names)[i]);
+ free (*values_names);
+
+ *values = new_values;
+ *values_names = new_names;
+ *values_num = match_ds_num_g;
+ return (RET_OKAY);
+} /* int filter_ds */
static void parse_range (char *string, range_t *range)
{
range->max = atof (max_ptr);
} /* void parse_range */
-int match_range (range_t *range, double value)
+static int match_range (range_t *range, double value)
{
int ret = 0;
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/%s\n", hostname_g, 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++;
-
- if (ignore_ds (key) != 0)
- continue;
-
- 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 */
+} /* int match_range */
static void usage (const char *name)
{
" -d <ds> Select the DS to examine. May be repeated to examine multiple\n"
" DSes. By default all DSes are used.\n"
" -g <consol> Method to use to consolidate several DSes.\n"
- " Valid arguments are `none', `average' and `sum'\n"
+ " See below for a list of valid arguments.\n"
" -H <host> Hostname to query the values for.\n"
" -c <range> Critical range\n"
" -w <range> Warning range\n"
" average: Calculate the average of all matching DSes and apply the\n"
" warning- and critical-ranges to the calculated average.\n"
" sum: Apply the ranges to the sum of all DSes.\n"
+ " percentage: Apply the ranges to the ratio (in percent) of the first value\n"
+ " and the sum of all values."
"\n", name);
exit (1);
} /* void usage */
-int do_check_con_none (int values_num, double *values, char **values_names)
+static int do_check_con_none (size_t values_num,
+ double *values, char **values_names)
{
- int i;
-
int num_critical = 0;
int num_warning = 0;
int num_okay = 0;
+ const char *status_str = "UNKNOWN";
+ int status_code = RET_UNKNOWN;
+ size_t i;
for (i = 0; i < values_num; i++)
{
num_okay++;
}
- printf ("%i critical, %i warning, %i okay",
+ if ((num_critical == 0) && (num_warning == 0) && (num_okay == 0))
+ {
+ printf ("WARNING: No defined values found\n");
+ return (RET_WARNING);
+ }
+ else if ((num_critical == 0) && (num_warning == 0))
+ {
+ status_str = "OKAY";
+ status_code = RET_OKAY;
+ }
+ else if (num_critical == 0)
+ {
+ status_str = "WARNING";
+ status_code = RET_WARNING;
+ }
+ else
+ {
+ status_str = "CRITICAL";
+ status_code = RET_CRITICAL;
+ }
+
+ printf ("%s: %i critical, %i warning, %i okay", status_str,
num_critical, num_warning, num_okay);
if (values_num > 0)
{
printf (" |");
for (i = 0; i < values_num; i++)
- printf (" %s=%lf;;;;", values_names[i], values[i]);
+ printf (" %s=%g;;;;", values_names[i], values[i]);
}
printf ("\n");
- if ((num_critical != 0) || (values_num == 0))
- return (RET_CRITICAL);
- else if (num_warning != 0)
- return (RET_WARNING);
-
- return (RET_OKAY);
+ return (status_code);
} /* int do_check_con_none */
-int do_check_con_average (int values_num, double *values, char **values_names)
+static int do_check_con_average (size_t values_num,
+ double *values, char **values_names)
{
- int i;
+ size_t i;
double total;
int total_num;
double average;
+ const char *status_str = "UNKNOWN";
+ int status_code = RET_UNKNOWN;
total = 0.0;
total_num = 0;
}
if (total_num == 0)
- average = NAN;
- else
- average = total / total_num;
- printf ("%lf average |", average);
- for (i = 0; i < values_num; i++)
- printf (" %s=%lf;;;;", values_names[i], values[i]);
-
- if (total_num == 0)
+ {
+ printf ("WARNING: No defined values found\n");
return (RET_WARNING);
+ }
- if (isnan (average)
- || match_range (&range_critical_g, average))
- return (RET_CRITICAL);
+ average = total / total_num;
+
+ if (match_range (&range_critical_g, average) != 0)
+ {
+ status_str = "CRITICAL";
+ status_code = RET_CRITICAL;
+ }
else if (match_range (&range_warning_g, average) != 0)
- return (RET_WARNING);
+ {
+ status_str = "WARNING";
+ status_code = RET_WARNING;
+ }
+ else
+ {
+ status_str = "OKAY";
+ status_code = RET_OKAY;
+ }
- return (RET_OKAY);
+ printf ("%s: %g average |", status_str, average);
+ for (i = 0; i < values_num; i++)
+ printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf ("\n");
+
+ return (status_code);
} /* int do_check_con_average */
-int do_check_con_sum (int values_num, double *values, char **values_names)
+static int do_check_con_sum (size_t values_num,
+ double *values, char **values_names)
{
- int i;
+ size_t i;
double total;
int total_num;
+ const char *status_str = "UNKNOWN";
+ int status_code = RET_UNKNOWN;
total = 0.0;
total_num = 0;
if (match_range (&range_critical_g, total) != 0)
{
- printf ("CRITICAL: Sum = %lf\n", total);
- return (RET_CRITICAL);
+ status_str = "CRITICAL";
+ status_code = RET_CRITICAL;
}
else if (match_range (&range_warning_g, total) != 0)
{
- printf ("WARNING: Sum = %lf\n", total);
- return (RET_WARNING);
+ status_str = "WARNING";
+ status_code = RET_WARNING;
}
else
{
- printf ("OKAY: Sum = %lf\n", total);
- return (RET_OKAY);
+ status_str = "OKAY";
+ status_code = RET_OKAY;
}
- return (RET_UNKNOWN);
+ printf ("%s: %g sum |", status_str, total);
+ for (i = 0; i < values_num; i++)
+ printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf ("\n");
+
+ return (status_code);
} /* int do_check_con_sum */
-int do_check (void)
+static int do_check_con_percentage (size_t values_num,
+ double *values, char **values_names)
{
- double *values;
+ size_t i;
+ double sum = 0.0;
+ double percentage;
+
+ const char *status_str = "UNKNOWN";
+ int status_code = RET_UNKNOWN;
+
+ if ((values_num < 1) || (isnan (values[0])))
+ {
+ printf ("WARNING: The first value is not defined\n");
+ return (RET_WARNING);
+ }
+
+ for (i = 0; i < values_num; i++)
+ if (!isnan (values[i]))
+ sum += values[i];
+
+ if (sum == 0.0)
+ {
+ printf ("WARNING: Values sum up to zero\n");
+ return (RET_WARNING);
+ }
+
+ percentage = 100.0 * values[0] / sum;
+
+ if (match_range (&range_critical_g, percentage) != 0)
+ {
+ status_str = "CRITICAL";
+ status_code = RET_CRITICAL;
+ }
+ else if (match_range (&range_warning_g, percentage) != 0)
+ {
+ status_str = "WARNING";
+ status_code = RET_WARNING;
+ }
+ else
+ {
+ status_str = "OKAY";
+ status_code = RET_OKAY;
+ }
+
+ printf ("%s: %lf percent |", status_str, percentage);
+ for (i = 0; i < values_num; i++)
+ printf (" %s=%lf;;;;", values_names[i], values[i]);
+ return (status_code);
+} /* int do_check_con_percentage */
+
+static int do_check (void)
+{
+ lcc_connection_t *connection;
+ gauge_t *values;
char **values_names;
- int values_num;
+ size_t values_num;
+ char address[1024];
+ char ident_str[1024];
+ lcc_identifier_t ident;
+ size_t i;
+ int status;
+
+ snprintf (address, sizeof (address), "unix:%s", socket_file_g);
+ address[sizeof (address) - 1] = 0;
- if (get_values (&values_num, &values, &values_names) != 0)
+ snprintf (ident_str, sizeof (ident_str), "%s/%s",
+ hostname_g, value_string_g);
+ ident_str[sizeof (ident_str) - 1] = 0;
+
+ connection = NULL;
+ status = lcc_connect (address, &connection);
+ if (status != 0)
+ {
+ printf ("ERROR: Connecting to daemon at %s failed.\n",
+ socket_file_g);
+ return (RET_CRITICAL);
+ }
+
+ memset (&ident, 0, sizeof (ident));
+ status = lcc_string_to_identifier (connection, &ident, ident_str);
+ if (status != 0)
{
- fputs ("ERROR: Cannot get values from daemon\n", stdout);
+ printf ("ERROR: Creating an identifier failed: %s.\n",
+ lcc_strerror (connection));
+ LCC_DESTROY (connection);
return (RET_CRITICAL);
}
+ status = lcc_getval (connection, &ident,
+ &values_num, &values, &values_names);
+ if (status != 0)
+ {
+ printf ("ERROR: Retrieving values from the daemon failed: %s.\n",
+ lcc_strerror (connection));
+ LCC_DESTROY (connection);
+ return (RET_CRITICAL);
+ }
+
+ LCC_DESTROY (connection);
+
+ status = filter_ds (&values_num, &values, &values_names);
+ if (status != RET_OKAY)
+ return (status);
+
+ status = RET_UNKNOWN;
if (consolitation_g == CON_NONE)
- return (do_check_con_none (values_num, values, values_names));
+ status = 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));
+ status = 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));
+ status = do_check_con_sum (values_num, values, values_names);
+ else if (consolitation_g == CON_PERCENTAGE)
+ status = do_check_con_percentage (values_num, values, values_names);
free (values);
- free (values_names); /* FIXME? */
+ if (values_names != NULL)
+ for (i = 0; i < values_num; i++)
+ free (values_names[i]);
+ free (values_names);
- return (RET_UNKNOWN);
-}
+ return (status);
+} /* int do_check */
int main (int argc, char **argv)
{
consolitation_g = CON_AVERAGE;
else if (strcasecmp (optarg, "sum") == 0)
consolitation_g = CON_SUM;
+ else if (strcasecmp (optarg, "percentage") == 0)
+ consolitation_g = CON_PERCENTAGE;
else
+ {
+ fprintf (stderr, "Unknown consolidation function `%s'.\n",
+ optarg);
usage (argv[0]);
+ }
break;
case 'd':
{
return (RET_UNKNOWN);
}
match_ds_g = tmp;
- match_ds_g[match_ds_num_g] = strdup (optarg);
+ match_ds_g[match_ds_num_g] = cn_strdup (optarg);
if (match_ds_g[match_ds_num_g] == NULL)
{
- fprintf (stderr, "strdup failed: %s\n",
+ fprintf (stderr, "cn_strdup failed: %s\n",
strerror (errno));
return (RET_UNKNOWN);
}
if ((socket_file_g == NULL) || (value_string_g == NULL)
|| (hostname_g == NULL))
+ {
+ fprintf (stderr, "Missing required arguments.\n");
usage (argv[0]);
+ }
return (do_check ());
} /* int main */