From b86e5f88e547b20ffdfe8d730161cbaf2fef67d5 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Thu, 7 Apr 2011 17:24:23 +0200 Subject: [PATCH] Initial version of the 'check_dbi' plugin. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This plugin connects to an SQL database using libdbi, thus supporting all database backends supported by libdbi. It will then issue the specified SQL query and check the result (the numeric value of the first column of the first row to be precise) against the specified warning/critical ranges. The performance data includes the connection time (µs-resolution as provided by gettimeofday()) and the query result. --- .gitignore | 1 + REQUIREMENTS | 4 + configure.in | 13 ++ plugins/Makefile.am | 4 +- plugins/check_dbi.c | 516 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 537 insertions(+), 1 deletion(-) create mode 100644 plugins/check_dbi.c diff --git a/.gitignore b/.gitignore index 7def6f8..e12b6fd 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,7 @@ NP-VERSION-FILE /plugins/check_by_ssh /plugins/check_clamd /plugins/check_cluster +/plugins/check_dbi /plugins/check_dig /plugins/check_disk /plugins/check_dns diff --git a/REQUIREMENTS b/REQUIREMENTS index fd41ded..9f2eec0 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -46,6 +46,10 @@ check_pqsql: - Requires the PostgreSQL libraries available from http://www.postgresql.org/ +check_dbi: + - Requires the DBI libraries available from + http://libdbi.sourceforge.net/ + check_radius: - Requires the radiusclient-ng library available from: http://developer.berlios.de/projects/radiusclient-ng/ diff --git a/configure.in b/configure.in index bdd4431..69ffc9d 100644 --- a/configure.in +++ b/configure.in @@ -252,6 +252,19 @@ fi LIBS="$_SAVEDLIBS" CPPFLAGS="$_SAVEDCPPFLAGS" +dnl Check for DBI libraries +_SAVEDLIBS="$LIBS" +AC_CHECK_LIB(dbi,dbi_initialize) +if test "$ac_cv_lib_dbi_dbi_initialize" = "yes"; then + EXTRAS="$EXTRAS check_dbi" + DBILIBS="-ldbi" + AC_SUBST(DBILIBS) +else + AC_MSG_WARN([Skipping dbi plugin]) + AC_MSG_WARN([install DBI libs to compile this plugin (see REQUIREMENTS).]) +fi +LIBS="$_SAVEDLIBS" + dnl Check for radius libraries _SAVEDLIBS="$LIBS" AC_CHECK_LIB(radiusclient,rc_read_config) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 36a28b0..4781e0f 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -37,7 +37,7 @@ check_tcp_programs = check_ftp check_imap check_nntp check_pop \ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ check_swap check_fping check_ldap check_game check_dig \ check_nagios check_by_ssh check_dns check_nt check_ide_smart \ - check_procs check_mysql_query check_apt + check_procs check_mysql_query check_apt check_dbi EXTRA_DIST = t tests utils.c netutils.c sslutils.c popen.c utils.h netutils.h \ popen.h common.h runcmd.c runcmd.h @@ -64,6 +64,7 @@ test-debug: check_apt_LDADD = $(BASEOBJS) runcmd.o check_cluster_LDADD = $(BASEOBJS) +check_dbi_LDADD = $(NETLIBS) $(DBILIBS) check_dig_LDADD = $(NETLIBS) runcmd.o check_disk_LDADD = $(BASEOBJS) popen.o check_dns_LDADD = $(NETLIBS) runcmd.o @@ -109,6 +110,7 @@ urlize_LDADD = $(BASEOBJS) popen.o check_apt_DEPENDENCIES = check_apt.c $(BASEOBJS) runcmd.o $(DEPLIBS) check_cluster_DEPENDENCIES = check_cluster.c $(BASEOBJS) $(DEPLIBS) +check_dbi_DEPENDENCIES = check_dbi.c $(NETOBJS) $(DEPLIBS) check_dig_DEPENDENCIES = check_dig.c $(NETOBJS) runcmd.o $(DEPLIBS) check_disk_DEPENDENCIES = check_disk.c $(BASEOBJS) popen.o $(DEPLIBS) check_dns_DEPENDENCIES = check_dns.c $(NETOBJS) runcmd.o $(DEPLIBS) diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c new file mode 100644 index 0000000..b52602c --- /dev/null +++ b/plugins/check_dbi.c @@ -0,0 +1,516 @@ +/***************************************************************************** +* +* Nagios check_dbi plugin +* +* License: GPL +* Copyright (c) 2011 Nagios Plugins Development Team +* Author: Sebastian 'tokkee' Harl +* +* Description: +* +* This file contains the check_dbi plugin +* +* Runs an arbitrary SQL command and checks the result. +* +* +* 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, either version 3 of the License, or +* (at your option) any later version. +* +* 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, see . +* +* +*****************************************************************************/ + +const char *progname = "check_dbi"; +const char *copyright = "2011"; +const char *email = "nagiosplug-devel@lists.sourceforge.net"; + +#include "common.h" +#include "utils.h" + +#include "netutils.h" + +#include + +#include + +typedef struct { + char *key; + char *value; +} driver_option_t; + +char *host = NULL; +int verbose = 0; + +char *warning_range = NULL; +char *critical_range = NULL; +thresholds *query_thresholds = NULL; + +char *np_dbi_driver = NULL; +driver_option_t *np_dbi_options = NULL; +int np_dbi_options_num = 0; +char *np_dbi_database = NULL; +char *np_dbi_query = NULL; + +int process_arguments (int, char **); +int validate_arguments (void); +void print_usage (void); +void print_help (void); + +void np_dbi_print_error (dbi_conn, char *, ...); + +int do_query (dbi_conn, double *); + +int +main (int argc, char **argv) +{ + int status = STATE_UNKNOWN; + + dbi_driver driver; + dbi_conn conn; + + struct timeval start_timeval, end_timeval; + double elapsed_time; + + double query_val = 0.0; + + int i; + + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + /* Parse extra opts if any */ + argv = np_extra_opts (&argc, argv, progname); + + if (process_arguments (argc, argv) == ERROR) + usage4 (_("Could not parse arguments")); + + /* Set signal handling and alarm */ + if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { + usage4 (_("Cannot catch SIGALRM")); + } + alarm (timeout_interval); + + if (verbose > 2) + printf ("Initializing DBI\n"); + + if (dbi_initialize (NULL) < 0) { + printf ("UNKNOWN - failed to initialize DBI.\n"); + return STATE_UNKNOWN; + } + + if (verbose) + printf ("Opening DBI driver '%s'\n", np_dbi_driver); + + driver = dbi_driver_open (np_dbi_driver); + if (! driver) { + printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", + np_dbi_driver); + + printf ("Known drivers:\n"); + for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) { + printf (" - %s\n", dbi_driver_get_name (driver)); + } + return STATE_UNKNOWN; + } + + /* make a connection to the database */ + gettimeofday (&start_timeval, NULL); + + conn = dbi_conn_open (driver); + if (! conn) { + printf ("UNKNOWN - failed top open connection object.\n"); + dbi_conn_close (conn); + return STATE_UNKNOWN; + } + + for (i = 0; i < np_dbi_options_num; ++i) { + const char *opt; + + if (verbose > 1) + printf ("Setting DBI driver option '%s' to '%s'\n", + np_dbi_options[i].key, np_dbi_options[i].value); + + if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value)) + continue; + /* else: status != 0 */ + + np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'", + np_dbi_options[i].key, np_dbi_options[i].value); + printf ("Known driver options:\n"); + + for (opt = dbi_conn_get_option_list (conn, NULL); opt; + opt = dbi_conn_get_option_list (conn, opt)) { + printf (" - %s\n", opt); + } + dbi_conn_close (conn); + return STATE_UNKNOWN; + } + + if (host) { + if (verbose > 1) + printf ("Setting DBI driver option 'host' to '%s'\n", host); + dbi_conn_set_option (conn, "host", host); + } + + if (verbose) { + const char *dbname, *host; + + dbname = dbi_conn_get_option (conn, "dbname"); + host = dbi_conn_get_option (conn, "host"); + + if (! dbname) + dbname = ""; + if (! host) + host = ""; + + printf ("Connecting to database '%s' at host '%s'\n", + dbname, host); + } + + if (dbi_conn_connect (conn) < 0) { + np_dbi_print_error (conn, "UNKOWN - failed to connect to database"); + return STATE_UNKNOWN; + } + + gettimeofday (&end_timeval, NULL); + while (start_timeval.tv_usec > end_timeval.tv_usec) { + --end_timeval.tv_sec; + end_timeval.tv_usec += 1000000; + } + elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) + + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0; + + if (verbose) + printf("Time elapsed: %f\n", elapsed_time); + + /* select a database */ + if (np_dbi_database) { + if (verbose > 1) + printf ("Selecting database '%s'\n", np_dbi_database); + + if (dbi_conn_select_db (conn, np_dbi_database)) { + np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'", + np_dbi_database); + return STATE_UNKNOWN; + } + } + + /* execute query */ + status = do_query (conn, &query_val); + if (status != STATE_OK) + /* do_query prints an error message in this case */ + return status; + + status = get_status (query_val, query_thresholds); + + if (verbose) + printf("Closing connection\n"); + dbi_conn_close (conn); + + printf ("%s - connection time: %fs, '%s' returned %f", + state_text (status), elapsed_time, np_dbi_query, query_val); + printf (" | conntime=%fs;;;0 query=%f;%s;%s;0\n", elapsed_time, query_val, + warning_range ? warning_range : "", critical_range ? critical_range : ""); + return status; +} + +/* process command-line arguments */ +int +process_arguments (int argc, char **argv) +{ + int c; + + int option = 0; + static struct option longopts[] = { + STD_LONG_OPTS, + + {"driver", required_argument, 0, 'd'}, + {"option", required_argument, 0, 'o'}, + {"query", required_argument, 0, 'q'}, + {"database", required_argument, 0, 'D'}, + {0, 0, 0, 0} + }; + + while (1) { + c = getopt_long (argc, argv, "Vvht:c:w:H:d:o:q:D:", + longopts, &option); + + if (c == EOF) + break; + + switch (c) { + case '?': /* usage */ + usage5 (); + case 'h': /* help */ + print_help (); + exit (STATE_OK); + case 'V': /* version */ + print_revision (progname, NP_VERSION); + exit (STATE_OK); + + case 'c': /* critical range */ + critical_range = optarg; + break; + case 'w': /* warning range */ + warning_range = optarg; + break; + case 't': /* timeout */ + if (!is_intnonneg (optarg)) + usage2 (_("Timeout interval must be a positive integer"), optarg); + else + timeout_interval = atoi (optarg); + + case 'H': /* host */ + if (!is_host (optarg)) + usage2 (_("Invalid hostname/address"), optarg); + else + host = optarg; + break; + case 'v': + verbose++; + break; + + case 'd': + np_dbi_driver = optarg; + break; + case 'o': + { + driver_option_t *new; + + char *k, *v; + + k = optarg; + v = strchr (k, (int)'='); + + if (! v) + usage2 (_("Option must be '='"), optarg); + + *v = '\0'; + ++v; + + new = realloc (np_dbi_options, + (np_dbi_options_num + 1) * sizeof (*new)); + if (! new) { + printf ("UNKOWN - failed to reallocate memory\n"); + exit (STATE_UNKNOWN); + } + + np_dbi_options = new; + new = np_dbi_options + np_dbi_options_num; + ++np_dbi_options_num; + + new->key = k; + new->value = v; + } + break; + case 'q': + np_dbi_query = optarg; + break; + case 'D': + np_dbi_database = optarg; + break; + } + } + + set_thresholds (&query_thresholds, warning_range, critical_range); + + return validate_arguments (); +} + +int +validate_arguments () +{ + if (! np_dbi_driver) + usage ("Must specify a DBI driver"); + + if (! np_dbi_query) + usage ("Must specify an SQL query to execute"); + + return OK; +} + +void +print_help (void) +{ + print_revision (progname, NP_VERSION); + + printf (COPYRIGHT, copyright, email); + + printf (_("This program checks a query result against threshold levels")); + + printf ("\n\n"); + + print_usage (); + + printf (UT_HELP_VRSN); + printf ("\n"); + + printf (" %s\n", "-d, --driver=STRING"); + printf (" %s\n", _("DBI driver to use")); + printf (" %s\n", "-o, --option=STRING"); + printf (" %s\n", _("DBI driver options")); + printf (" %s\n", "-q, --query=STRING"); + printf (" %s\n", _("SQL query to execute")); + printf ("\n"); + + printf (UT_WARN_CRIT_RANGE); + + printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + + printf (UT_VERBOSE); + + printf ("\n"); + printf (" %s\n", _("A DBI driver (-d option) and a query (-q option) are required.")); + printf (" %s\n", _("This plugin connects to an SQL database using libdbi and executes the")); + printf (" %s\n", _("specified SQL query. The first column of the first row of the result")); + printf (" %s\n", _("will be used as the check result and, if specified, compared with the")); + printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric")); + printf (" %s\n\n", _("(strings representing numbers are fine).")); + + printf (" %s\n", _("The number and type of required DBI driver options depends on the actual")); + printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/")); + printf (" %s\n", _("for details.")); + + printf (UT_SUPPORT); +} + +void +print_usage (void) +{ + printf ("%s\n", _("Usage:")); + printf ("%s -d [-o [...]] -q \n", progname); + printf (" [-H ] [-c ] [-w ]\n"); +} + +double +get_field (dbi_conn conn, dbi_result res, unsigned short *field_type) +{ + double val = 0.0; + + if (*field_type == DBI_TYPE_INTEGER) { + val = (double)dbi_result_get_longlong_idx (res, 1); + } + else if (*field_type == DBI_TYPE_DECIMAL) { + val = dbi_result_get_double_idx (res, 1); + } + else if (*field_type == DBI_TYPE_STRING) { + const char *val_str; + char *endptr = NULL; + + val_str = dbi_result_get_string_idx (res, 1); + if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) { + np_dbi_print_error (conn, "CRITICAL - failed to fetch string value"); + *field_type = DBI_TYPE_ERROR; + return 0.0; + } + + if (verbose > 2) + printf ("Query returned string '%s'\n", val_str); + + val = strtod (val_str, &endptr); + if (endptr == val_str) { + printf ("CRITICAL - result value is not a numeric: %s\n", val_str); + *field_type = DBI_TYPE_ERROR; + return 0.0; + } + else if ((endptr != NULL) && (*endptr != '\0')) { + if (verbose) + printf ("Garbage after value: %s\n", endptr); + } + } + else { + printf ("CRITICAL - cannot parse value of type %s (%i)\n", + (*field_type == DBI_TYPE_BINARY) + ? "BINARY" + : (*field_type == DBI_TYPE_DATETIME) + ? "DATETIME" + : "", + *field_type); + *field_type = DBI_TYPE_ERROR; + return 0.0; + } + return val; +} + +int +do_query (dbi_conn conn, double *res_val) +{ + dbi_result res; + + unsigned short field_type; + double val = 0.0; + + if (verbose) + printf ("Executing query '%s'\n", np_dbi_query); + + res = dbi_conn_query (conn, np_dbi_query); + if (! res) { + np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); + return STATE_CRITICAL; + } + + if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) { + np_dbi_print_error (conn, "CRITICAL - failed to fetch rows"); + return STATE_CRITICAL; + } + + if (dbi_result_get_numrows (res) < 1) { + printf ("WARNING - no rows returned\n"); + return STATE_WARNING; + } + + if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) { + np_dbi_print_error (conn, "CRITICAL - failed to fetch fields"); + return STATE_CRITICAL; + } + + if (dbi_result_get_numfields (res) < 1) { + printf ("WARNING - no fields returned\n"); + return STATE_WARNING; + } + + if (dbi_result_first_row (res) != 1) { + np_dbi_print_error (conn, "CRITICAL - failed to fetch first row"); + return STATE_CRITICAL; + } + + field_type = dbi_result_get_field_type_idx (res, 1); + if (field_type != DBI_TYPE_ERROR) + val = get_field (conn, res, &field_type); + + if (field_type == DBI_TYPE_ERROR) { + np_dbi_print_error (conn, "CRITICAL - failed to fetch data"); + return STATE_CRITICAL; + } + + *res_val = val; + + dbi_result_free (res); + return STATE_OK; +} + +void +np_dbi_print_error (dbi_conn conn, char *fmt, ...) +{ + const char *errmsg = NULL; + va_list ap; + + va_start (ap, fmt); + + dbi_conn_error (conn, &errmsg); + vprintf (fmt, ap); + printf (": %s\n", errmsg); + + va_end (ap); +} + -- 2.30.2