/**
* collectd-nagios - src/collectd-nagios.c
- * Copyright (C) 2008 Florian octo Forster
+ * Copyright (C) 2008-2010 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.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
*
- * 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.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
*
- * 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
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
-/* Set to C99 and POSIX code */
-#ifndef _ISOC99_SOURCE
-# define _ISOC99_SOURCE
-#endif
-#ifndef _POSIX_SOURCE
-# define _POSIX_SOURCE
-#endif
-#ifndef _POSIX_C_SOURCE
-# define _POSIX_C_SOURCE 200112L
-#endif
-#ifndef _REENTRANT
-# define _REENTRANT
-#endif
-
-/* Disable non-standard extensions */
-#ifdef _BSD_SOURCE
-# undef _BSD_SOURCE
-#endif
-#ifdef _SVID_SOURCE
-# undef _SVID_SOURCE
-#endif
-#ifdef _GNU_SOURCE
-# undef _GNU_SOURCE
+#if HAVE_CONFIG_H
+# include "config.h"
#endif
#if !defined(__GNUC__) || !__GNUC__
# define __attribute__(x) /**/
#endif
-#include "config.h"
-
#include <stdlib.h>
#include <unistd.h>
#include <stdio.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!
- */
#if NAN_STATIC_DEFAULT
# include <math.h>
/* #endif NAN_STATIC_DEFAULT*/
# ifndef isnan
# define isnan(f) ((f) != (f))
# endif /* !defined(isnan) */
+# ifndef isfinite
+# define isfinite(f) (((f) - (f)) == 0.0)
+# endif
+# ifndef isinf
+# define isinf(f) (!isfinite(f) && !isnan(f))
+# endif
#endif /* NAN_ZERO_ZERO */
+#include "libcollectdclient/collectd/client.h"
+
#define RET_OKAY 0
#define RET_WARNING 1
#define RET_CRITICAL 2
static range_t range_critical_g;
static range_t range_warning_g;
static int consolitation_g = CON_NONE;
+static _Bool nan_is_error_g = 0;
static char **match_ds_g = NULL;
-static int match_ds_num_g = 0;
+static size_t match_ds_num_g = 0;
/* `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
new_values = (gauge_t *)calloc (match_ds_num_g, sizeof (*new_values));
if (new_values == NULL)
{
- fprintf (stderr, "malloc failed: %s\n", strerror (errno));
+ fprintf (stderr, "calloc 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));
+ fprintf (stderr, "calloc failed: %s\n", strerror (errno));
free (new_values);
return (RET_UNKNOWN);
}
return (((ret - range->invert) == 0) ? 0 : 1);
} /* int match_range */
+__attribute__((noreturn))
static void usage (const char *name)
{
fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> <-H hostname> [options]\n"
" -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"
+ " -m Treat \"Not a Number\" (NaN) as critical (default: warning)\n"
"\n"
"Consolidation functions:\n"
" none: Apply the warning- and critical-ranges to each data-source\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 */
+static int do_listval (lcc_connection_t *connection)
+{
+ lcc_identifier_t *ret_ident = NULL;
+ size_t ret_ident_num = 0;
+
+ char *hostname = NULL;
+
+ int status;
+ size_t i;
+
+ status = lcc_listval (connection, &ret_ident, &ret_ident_num);
+ if (status != 0) {
+ printf ("UNKNOWN: %s\n", lcc_strerror (connection));
+ if (ret_ident != NULL)
+ free (ret_ident);
+ return (RET_UNKNOWN);
+ }
+
+ status = lcc_sort_identifiers (connection, ret_ident, ret_ident_num);
+ if (status != 0) {
+ printf ("UNKNOWN: %s\n", lcc_strerror (connection));
+ if (ret_ident != NULL)
+ free (ret_ident);
+ return (RET_UNKNOWN);
+ }
+
+ for (i = 0; i < ret_ident_num; ++i) {
+ char id[1024];
+
+ if ((hostname_g != NULL) && (strcasecmp (hostname_g, ret_ident[i].host)))
+ continue;
+
+ if ((hostname == NULL) || strcasecmp (hostname, ret_ident[i].host))
+ {
+ free (hostname);
+ hostname = strdup (ret_ident[i].host);
+ printf ("Host: %s\n", hostname);
+ }
+
+ /* empty hostname; not to be printed again */
+ ret_ident[i].host[0] = '\0';
+
+ status = lcc_identifier_to_string (connection,
+ id, sizeof (id), ret_ident + i);
+ if (status != 0) {
+ printf ("ERROR: listval: Failed to convert returned "
+ "identifier to a string: %s\n",
+ lcc_strerror (connection));
+ free (hostname);
+ hostname = NULL;
+ continue;
+ }
+
+ /* skip over the (empty) hostname and following '/' */
+ printf ("\t%s\n", id + 1);
+ }
+
+ free (ret_ident);
+ free (hostname);
+ return (RET_OKAY);
+} /* int do_listval */
+
static int do_check_con_none (size_t values_num,
double *values, char **values_names)
{
int num_okay = 0;
const char *status_str = "UNKNOWN";
int status_code = RET_UNKNOWN;
- int i;
+ size_t i;
for (i = 0; i < values_num; i++)
{
if (isnan (values[i]))
- num_warning++;
+ {
+ if (nan_is_error_g)
+ num_critical++;
+ else
+ num_warning++;
+ }
else if (match_range (&range_critical_g, values[i]) != 0)
num_critical++;
else if (match_range (&range_warning_g, values[i]) != 0)
{
printf (" |");
for (i = 0; i < values_num; i++)
- printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf (" %s=%f;;;;", values_names[i], values[i]);
}
printf ("\n");
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;
total_num = 0;
for (i = 0; i < values_num; i++)
{
- if (!isnan (values[i]))
+ if (isnan (values[i]))
{
- total += values[i];
- total_num++;
+ if (!nan_is_error_g)
+ continue;
+
+ printf ("CRITICAL: Data source \"%s\" is NaN\n",
+ values_names[i]);
+ return (RET_CRITICAL);
}
+
+ total += values[i];
+ total_num++;
}
if (total_num == 0)
printf ("%s: %g average |", status_str, average);
for (i = 0; i < values_num; i++)
- printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf (" %s=%f;;;;", values_names[i], values[i]);
printf ("\n");
return (status_code);
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";
total_num = 0;
for (i = 0; i < values_num; i++)
{
- if (!isnan (values[i]))
+ if (isnan (values[i]))
{
- total += values[i];
- total_num++;
+ if (!nan_is_error_g)
+ continue;
+
+ printf ("CRITICAL: Data source \"%s\" is NaN\n",
+ values_names[i]);
+ return (RET_CRITICAL);
}
+
+ total += values[i];
+ total_num++;
}
if (total_num == 0)
printf ("%s: %g sum |", status_str, total);
for (i = 0; i < values_num; i++)
- printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf (" %s=%f;;;;", values_names[i], values[i]);
printf ("\n");
return (status_code);
static int do_check_con_percentage (size_t values_num,
double *values, char **values_names)
{
- int i;
+ size_t i;
double sum = 0.0;
double percentage;
}
for (i = 0; i < values_num; i++)
- if (!isnan (values[i]))
- sum += values[i];
+ {
+ if (isnan (values[i]))
+ {
+ if (!nan_is_error_g)
+ continue;
+
+ printf ("CRITICAL: Data source \"%s\" is NaN\n",
+ values_names[i]);
+ return (RET_CRITICAL);
+ }
+
+ sum += values[i];
+ }
if (sum == 0.0)
{
return (status_code);
} /* int do_check_con_percentage */
-static int do_check (void)
+static int do_check (lcc_connection_t *connection)
{
- lcc_connection_t *connection;
gauge_t *values;
char **values_names;
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;
-
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)
int main (int argc, char **argv)
{
+ char address[1024];
+ lcc_connection_t *connection;
+
+ int status;
+
range_critical_g.min = NAN;
range_critical_g.max = NAN;
range_critical_g.invert = 0;
{
int c;
- c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
+ c = getopt (argc, argv, "w:c:s:n:H:g:d:hm");
if (c < 0)
break;
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':
{
match_ds_num_g++;
break;
}
+ case 'm':
+ nan_is_error_g = 1;
+ break;
default:
usage (argv[0]);
} /* switch (c) */
}
if ((socket_file_g == NULL) || (value_string_g == NULL)
- || (hostname_g == NULL))
+ || ((hostname_g == NULL) && (strcasecmp (value_string_g, "LIST"))))
+ {
+ fprintf (stderr, "Missing required arguments.\n");
usage (argv[0]);
+ }
+
+ snprintf (address, sizeof (address), "unix:%s", socket_file_g);
+ address[sizeof (address) - 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);
+ }
+
+ if (0 == strcasecmp (value_string_g, "LIST"))
+ return (do_listval (connection));
- return (do_check ());
+ return (do_check (connection));
} /* int main */