diff options
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | plugins/.cvsignore | 1 | ||||
-rw-r--r-- | plugins/Makefile.am | 4 | ||||
-rw-r--r-- | plugins/check_mysql_query.c | 299 | ||||
-rw-r--r-- | plugins/t/check_mysql_query.t | 66 | ||||
-rw-r--r-- | plugins/utils.h | 6 |
6 files changed, 376 insertions, 2 deletions
diff --git a/configure.in b/configure.in index d56501ac..6a041852 100644 --- a/configure.in +++ b/configure.in @@ -287,7 +287,7 @@ fi if test "$ac_cv_lib_mysqlclient_mysql_init" = "yes" -o "$ac_cv_lib_mysqlclient_mysql_close" = "yes"; then AC_CHECK_HEADERS(mysql/mysql.h mysql/errmsg.h, MYSQLINCLUDE="-I$MYSQL/include" ) if test "$ac_cv_header_mysql_mysql_h" = "yes" -a "$ac_cv_header_mysql_errmsg_h" = "yes"; then - EXTRAS="$EXTRAS check_mysql" + EXTRAS="$EXTRAS check_mysql check_mysql_query" AC_SUBST(MYSQLINCLUDE) AC_SUBST(MYSQLLIBS) AC_SUBST(check_mysql_LDFLAGS) diff --git a/plugins/.cvsignore b/plugins/.cvsignore index 66bad73e..7ac18354 100644 --- a/plugins/.cvsignore +++ b/plugins/.cvsignore @@ -31,6 +31,7 @@ check_pgsql check_radius check_ldap check_mysql +check_mysql_query check_netsaint check_hpjd check_snmp diff --git a/plugins/Makefile.am b/plugins/Makefile.am index a67911ce..70d0cf98 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -25,7 +25,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_procs check_mysql_query EXTRA_DIST = t utils.c netutils.c sslutils.c popen.c utils.h netutils.h \ popen.h common.h getaddrinfo.c getaddrinfo.h \ @@ -64,6 +64,7 @@ check_load_LDADD = $(BASEOBJS) popen.o check_mrtg_LDADD = $(BASEOBJS) check_mrtgtraf_LDADD = $(BASEOBJS) check_mysql_LDADD = $(NETLIBS) $(MYSQLLIBS) +check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) check_nagios_LDADD = $(BASEOBJS) runcmd.o check_nt_LDADD = $(NETLIBS) check_nwstat_LDADD = $(NETLIBS) @@ -101,6 +102,7 @@ check_load_DEPENDENCIES = check_load.c $(BASEOBJS) popen.o $(DEPLIBS) check_mrtg_DEPENDENCIES = check_mrtg.c $(DEPLIBS) check_mrtgtraf_DEPENDENCIES = check_mrtgtraf.c $(DEPLIBS) check_mysql_DEPENDENCIES = check_mysql.c $(NETOBJS) $(DEPLIBS) +check_mysql_query_DEPENDENCIES = check_mysql.c $(NETOBJS) $(DEPLIBS) check_nagios_DEPENDENCIES = check_nagios.c $(BASEOBJS) runcmd.o $(DEPLIBS) check_nt_DEPENDENCIES = check_nt.c $(NETOBJS) $(DEPLIBS) check_nwstat_DEPENDENCIES = check_nwstat.c $(NETOBJS) $(DEPLIBS) diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c new file mode 100644 index 00000000..321af7aa --- /dev/null +++ b/plugins/check_mysql_query.c @@ -0,0 +1,299 @@ +/****************************************************************************** +* +* CHECK_MYSQL_QUERY.C +* +* Program: Mysql plugin for Nagios +* License: GPL +* Copyright (c) 2006 Nagios Plugins Team, after Didi Rieder (check_mysql) +* +* $Id$ +* +* Description: +* This plugin is for running arbitrary SQL and checking the results +* +******************************************************************************/ + +const char *progname = "check_mysql_query"; +const char *revision = "$Revision$"; +const char *copyright = "2006"; +const char *email = "nagiosplug-devel@lists.sourceforge.net"; + +#include "common.h" +#include "utils.h" +#include "netutils.h" + +#include <mysql/mysql.h> +#include <mysql/errmsg.h> + +char *db_user = NULL; +char *db_host = NULL; +char *db_pass = NULL; +char *db = NULL; +unsigned int db_port = MYSQL_PORT; + +int process_arguments (int, char **); +int validate_arguments (void); +void print_help (void); +void print_usage (void); + +char *sql_query = NULL; +int verbose = 0; +thresholds *my_thresholds = NULL; + + +int +main (int argc, char **argv) +{ + + MYSQL mysql; + MYSQL_RES *res; + MYSQL_ROW row; + + double value; + char *error = NULL; + int status; + + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + if (process_arguments (argc, argv) == ERROR) + usage4 (_("Could not parse arguments")); + + /* initialize mysql */ + mysql_init (&mysql); + + mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"client"); + + /* establish a connection to the server and error checking */ + if (!mysql_real_connect(&mysql,db_host,db_user,db_pass,db,db_port,NULL,0)) { + if (mysql_errno (&mysql) == CR_UNKNOWN_HOST) + die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); + else if (mysql_errno (&mysql) == CR_VERSION_ERROR) + die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); + else if (mysql_errno (&mysql) == CR_OUT_OF_MEMORY) + die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); + else if (mysql_errno (&mysql) == CR_IPSOCK_ERROR) + die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); + else if (mysql_errno (&mysql) == CR_SOCKET_CREATE_ERROR) + die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); + else + die (STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error (&mysql)); + } + + if (mysql_query (&mysql, sql_query) != 0) { + error = strdup(mysql_error(&mysql)); + mysql_close (&mysql); + die (STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); + } + + /* store the result */ + if ( (res = mysql_store_result (&mysql)) == NULL) { + error = strdup(mysql_error(&mysql)); + mysql_close (&mysql); + die (STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error); + } + + /* Check there is some data */ + if (mysql_num_rows(res) == 0) { + mysql_close(&mysql); + die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned")); + } + + /* fetch the first row */ + if ( (row = mysql_fetch_row (res)) == NULL) { + error = strdup(mysql_error(&mysql)); + mysql_free_result (res); + mysql_close (&mysql); + die (STATE_CRITICAL, "QUERY %s: Fetch row error - %s\n", _("CRITICAL"), error); + } + + /* free the result */ + mysql_free_result (res); + + /* close the connection */ + mysql_close (&mysql); + + if (! is_numeric(row[0])) { + die (STATE_CRITICAL, "QUERY %s: %s - '%s'\n", _("CRITICAL"), _("Is not a numeric"), row[0]); + } + + value = strtod(row[0], NULL); + + if (verbose >= 3) + printf("mysql result: %f\n", value); + + status = get_status(value, my_thresholds); + + if (status == STATE_OK) { + printf("QUERY %s: ", _("OK")); + } else if (status == STATE_WARNING) { + printf("QUERY %s: ", _("WARNING")); + } else if (status == STATE_CRITICAL) { + printf("QUERY %s: ", _("CRITICAL")); + } + printf(_("'%s' returned %f"), sql_query, value); + printf("\n"); + + return status; +} + + +/* process command-line arguments */ +int +process_arguments (int argc, char **argv) +{ + int c; + char *warning = NULL; + char *critical = NULL; + + int option = 0; + static struct option longopts[] = { + {"hostname", required_argument, 0, 'H'}, + {"database", required_argument, 0, 'd'}, + {"username", required_argument, 0, 'u'}, + {"password", required_argument, 0, 'p'}, + {"port", required_argument, 0, 'P'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"query", required_argument, 0, 'q'}, + {"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, + {0, 0, 0, 0} + }; + + if (argc < 1) + return ERROR; + + while (1) { + c = getopt_long (argc, argv, "hvVSP:p:u:d:H:q:w:c:", longopts, &option); + + if (c == -1 || c == EOF) + break; + + switch (c) { + case 'H': /* hostname */ + if (is_host (optarg)) { + db_host = optarg; + } + else { + usage2 (_("Invalid hostname/address"), optarg); + } + break; + case 'd': /* hostname */ + db = optarg; + break; + case 'u': /* username */ + db_user = optarg; + break; + case 'p': /* authentication information: password */ + asprintf(&db_pass, "%s", optarg); + + /* Delete the password from process list */ + while (*optarg != '\0') { + *optarg = 'X'; + optarg++; + } + break; + case 'P': /* critical time threshold */ + db_port = atoi (optarg); + break; + case 'v': + verbose++; + break; + case 'V': /* version */ + print_revision (progname, revision); + exit (STATE_OK); + case 'h': /* help */ + print_help (); + exit (STATE_OK); + case 'q': + asprintf(&sql_query, "%s", optarg); + break; + case 'w': + warning = optarg; + break; + case 'c': + critical = optarg; + break; + case '?': /* help */ + usage2 (_("Unknown argument"), optarg); + } + } + + c = optind; + + set_thresholds(&my_thresholds, warning, critical); + + return validate_arguments (); +} + + +int +validate_arguments (void) +{ + if (sql_query == NULL) + usage("Must specify a SQL query to run"); + + if (db_user == NULL) + db_user = strdup(""); + + if (db_host == NULL) + db_host = strdup(""); + + if (db_pass == NULL) + db_pass == strdup(""); + + if (db == NULL) + db = strdup(""); + + return OK; +} + + +void +print_help (void) +{ + char *myport; + asprintf (&myport, "%d", MYSQL_PORT); + + print_revision (progname, revision); + + printf (_(COPYRIGHT), copyright, email); + + printf ("%s\n", _("This program checks a query result against threshold levels")); + + print_usage (); + + + printf (_(UT_HELP_VRSN)); + printf (" -q, --query=STRING\n"); + printf (" %s\n", _("SQL query to run. Only first column in first row will be read")); + printf (_(UT_WARN_CRIT_RANGE)); + printf (_(UT_HOST_PORT), 'P', myport); + printf (" -d, --database=STRING\n"); + printf (" %s\n", _("Database to check")); + printf (" -u, --username=STRING\n"); + printf (" %s\n", _("Username to login with")); + printf (" -p, --password=STRING\n"); + printf (" %s\n", _("Password to login with")); + printf (" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); + + printf ("\n"); + + printf ("%s\n", _("A query is required. The result from the query should be numeric.")); + printf ("%s\n", _("For extra security, create a user with minimal access.")); + + printf (_(UT_SUPPORT)); +} + + +void +print_usage (void) +{ + printf ("\ +Usage: %s -q SQL_query [-w warn] [-c crit]\n\ + [-d database] [-H host] [-P port] [-u user] [-p password]\n", + progname); +} diff --git a/plugins/t/check_mysql_query.t b/plugins/t/check_mysql_query.t new file mode 100644 index 00000000..f4743b7b --- /dev/null +++ b/plugins/t/check_mysql_query.t @@ -0,0 +1,66 @@ +#! /usr/bin/perl -w -I .. +# +# MySQL Database Server Tests via check_mysql +# +# $Id$ +# +# +# These are the database permissions required for this test: +# GRANT SELECT ON $db.* TO $user@$host INDENTIFIED BY '$password'; +# Check with: +# mysql -u$user -p$password -h$host $db + +use strict; +use Test::More; +use NPTest; + +use vars qw($tests); + +plan skip_all => "check_mysql_query not compiled" unless (-x "check_mysql_query"); + +my $mysqlserver = getTestParameter( + "NP_MYSQL_SERVER", + "A MySQL Server with no slaves setup" + ); +my $mysql_login_details = getTestParameter( + "MYSQL_LOGIN_DETAILS", + "Command line parameters to specify login access", + "-u user -ppw -d db", + ); +my $result; + +if (! $mysqlserver) { + plan skip_all => "No mysql server defined"; +} else { + plan tests => 13; +} + +$result = NPTest->testCmd("./check_mysql_query -q 'SELECT 1+1' -H $mysqlserver $mysql_login_details"); +cmp_ok( $result->return_code, '==', 0, "Can run query"); + +$result = NPTest->testCmd("./check_mysql_query -H $mysqlserver $mysql_login_details"); +cmp_ok( $result->return_code, '==', 3, "Missing query parmeter"); +like( $result->output, "/Must specify a SQL query to run/", "Missing query error message"); + +$result = NPTest->testCmd("./check_mysql_query -q 'SELECT 1+1' -H $mysqlserver -u dummy"); +cmp_ok( $result->return_code, '==', 2, "Login failure"); +like( $result->output, "/Access denied for user /", "Expected login failure message"); + +$result = NPTest->testCmd("./check_mysql_query -q 'SELECT PI()' -w 3 -c 4 -H $mysqlserver $mysql_login_details"); +cmp_ok( $result->return_code, '==', 1, "Got warning"); + +$result = NPTest->testCmd("./check_mysql_query -q 'SELECT PI()*2' -w 3 -c 4 -H $mysqlserver $mysql_login_details"); +cmp_ok( $result->return_code, '==', 2, "Got critical"); + +$result = NPTest->testCmd("./check_mysql_query -q 'SELECT * FROM adsf' -H $mysqlserver $mysql_login_details"); +cmp_ok( $result->return_code, '==', 2, "Bad query"); +like( $result->output, "/Error with query/", "Bad query error message"); + +$result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES LIKE \"bob\"' -H $mysqlserver $mysql_login_details"); +cmp_ok( $result->return_code, '==', 1, "No rows"); +like( $result->output, "/No rows returned/", "No rows error message"); + +$result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details"); +cmp_ok( $result->return_code, '==', 2, "Data not numeric"); +like( $result->output, "/Is not a numeric/", "Data not numeric error message"); + diff --git a/plugins/utils.h b/plugins/utils.h index ffcb39da..2345ed56 100644 --- a/plugins/utils.h +++ b/plugins/utils.h @@ -181,6 +181,12 @@ char *fperfdata (const char *, -c, --critical=DOUBLE\n\ Response time to result in critical status (seconds)\n" +#define UT_WARN_CRIT_RANGE "\ + -w, --warning=RANGE\n\ + Warning range (format: start:end). Alert if outside this range\n\ + -c, --critical=RANGE\n\ + Critical range\n" + #define UT_TIMEOUT "\ -t, --timeout=INTEGER\n\ Seconds before connection times out (default: %d)\n" |