Code

611e0e78472159f27c6f21546b97d72495549618
[nagiosplug.git] / plugins / check_dbi.c
1 /*****************************************************************************
2
3 * Nagios check_dbi plugin
4
5 * License: GPL
6 * Copyright (c) 2011 Nagios Plugins Development Team
7 * Author: Sebastian 'tokkee' Harl <sh@teamix.net>
8
9 * Description:
10
11 * This file contains the check_dbi plugin
12
13 * Runs an arbitrary SQL command and checks the result.
14
15
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 * GNU General Public License for more details.
25
26 * You should have received a copy of the GNU General Public License
27 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
29
30 *****************************************************************************/
32 const char *progname = "check_dbi";
33 const char *copyright = "2011";
34 const char *email = "nagiosplug-devel@lists.sourceforge.net";
36 #include "common.h"
37 #include "utils.h"
39 #include "netutils.h"
41 #include <dbi/dbi.h>
43 #include <stdarg.h>
45 typedef struct {
46         char *key;
47         char *value;
48 } driver_option_t;
50 char *host = NULL;
51 int verbose = 0;
53 char *warning_range = NULL;
54 char *critical_range = NULL;
55 thresholds *query_thresholds = NULL;
57 char *conntime_warning_range = NULL;
58 char *conntime_critical_range = NULL;
59 thresholds *conntime_thresholds = NULL;
61 char *np_dbi_driver = NULL;
62 driver_option_t *np_dbi_options = NULL;
63 int np_dbi_options_num = 0;
64 char *np_dbi_database = NULL;
65 char *np_dbi_query = NULL;
67 int process_arguments (int, char **);
68 int validate_arguments (void);
69 void print_usage (void);
70 void print_help (void);
72 double timediff (struct timeval, struct timeval);
74 void np_dbi_print_error (dbi_conn, char *, ...);
76 int do_query (dbi_conn, double *, double *);
78 int
79 main (int argc, char **argv)
80 {
81         int conntime_status = STATE_UNKNOWN;
82         int status = STATE_UNKNOWN;
84         int exit_status = STATE_UNKNOWN;
86         dbi_driver driver;
87         dbi_conn conn;
89         struct timeval start_timeval, end_timeval;
90         double conn_time = 0.0;
91         double query_time = 0.0;
93         double query_val = 0.0;
95         int i;
97         setlocale (LC_ALL, "");
98         bindtextdomain (PACKAGE, LOCALEDIR);
99         textdomain (PACKAGE);
101         /* Parse extra opts if any */
102         argv = np_extra_opts (&argc, argv, progname);
104         if (process_arguments (argc, argv) == ERROR)
105                 usage4 (_("Could not parse arguments"));
107         /* Set signal handling and alarm */
108         if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
109                 usage4 (_("Cannot catch SIGALRM"));
110         }
111         alarm (timeout_interval);
113         if (verbose > 2)
114                 printf ("Initializing DBI\n");
116         if (dbi_initialize (NULL) < 0) {
117                 printf ("UNKNOWN - failed to initialize DBI.\n");
118                 return STATE_UNKNOWN;
119         }
121         if (verbose)
122                 printf ("Opening DBI driver '%s'\n", np_dbi_driver);
124         driver = dbi_driver_open (np_dbi_driver);
125         if (! driver) {
126                 printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
127                                 np_dbi_driver);
129                 printf ("Known drivers:\n");
130                 for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) {
131                         printf (" - %s\n", dbi_driver_get_name (driver));
132                 }
133                 return STATE_UNKNOWN;
134         }
136         /* make a connection to the database */
137         gettimeofday (&start_timeval, NULL);
139         conn = dbi_conn_open (driver);
140         if (! conn) {
141                 printf ("UNKNOWN - failed top open connection object.\n");
142                 dbi_conn_close (conn);
143                 return STATE_UNKNOWN;
144         }
146         for (i = 0; i < np_dbi_options_num; ++i) {
147                 const char *opt;
149                 if (verbose > 1)
150                         printf ("Setting DBI driver option '%s' to '%s'\n",
151                                         np_dbi_options[i].key, np_dbi_options[i].value);
153                 if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value))
154                         continue;
155                 /* else: status != 0 */
157                 np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'",
158                                 np_dbi_options[i].key, np_dbi_options[i].value);
159                 printf ("Known driver options:\n");
161                 for (opt = dbi_conn_get_option_list (conn, NULL); opt;
162                                 opt = dbi_conn_get_option_list (conn, opt)) {
163                         printf (" - %s\n", opt);
164                 }
165                 dbi_conn_close (conn);
166                 return STATE_UNKNOWN;
167         }
169         if (host) {
170                 if (verbose > 1)
171                         printf ("Setting DBI driver option 'host' to '%s'\n", host);
172                 dbi_conn_set_option (conn, "host", host);
173         }
175         if (verbose) {
176                 const char *dbname, *host;
178                 dbname = dbi_conn_get_option (conn, "dbname");
179                 host = dbi_conn_get_option (conn, "host");
181                 if (! dbname)
182                         dbname = "<unspecified>";
183                 if (! host)
184                         host = "<unspecified>";
186                 printf ("Connecting to database '%s' at host '%s'\n",
187                                 dbname, host);
188         }
190         if (dbi_conn_connect (conn) < 0) {
191                 np_dbi_print_error (conn, "UNKOWN - failed to connect to database");
192                 return STATE_UNKNOWN;
193         }
195         gettimeofday (&end_timeval, NULL);
196         conn_time = timediff (start_timeval, end_timeval);
198         if (verbose)
199                 printf("Time elapsed: %f\n", conn_time);
201         conntime_status = get_status (conn_time, conntime_thresholds);
203         /* select a database */
204         if (np_dbi_database) {
205                 if (verbose > 1)
206                         printf ("Selecting database '%s'\n", np_dbi_database);
208                 if (dbi_conn_select_db (conn, np_dbi_database)) {
209                         np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'",
210                                         np_dbi_database);
211                         return STATE_UNKNOWN;
212                 }
213         }
215         /* execute query */
216         status = do_query (conn, &query_val, &query_time);
217         if (status != STATE_OK)
218                 /* do_query prints an error message in this case */
219                 return status;
221         status = get_status (query_val, query_thresholds);
223         if (verbose)
224                 printf("Closing connection\n");
225         dbi_conn_close (conn);
227         /* 'conntime_status' is worse than 'status' (but not UNKOWN) */
228         if (((conntime_status < STATE_UNKNOWN) && (conntime_status > status))
229                         /* 'status' is UNKNOWN and 'conntime_status' is not OK */
230                         || ((status >= STATE_UNKNOWN) && (conntime_status != STATE_OK)))
231                 exit_status = conntime_status;
232         else
233                 exit_status = status;
235         printf ("%s - %s: connection time: %fs, %s: '%s' returned %f in %fs",
236                         state_text (exit_status),
237                         state_text (conntime_status), conn_time,
238                         state_text (status), np_dbi_query, query_val, query_time);
239         printf (" | conntime=%fs;%s;%s;0; query=%f;%s;%s;; querytime=%fs;;;0;\n", conn_time,
240                         conntime_warning_range ? conntime_warning_range : "",
241                         conntime_critical_range ? conntime_critical_range : "",
242                         query_val, warning_range ? warning_range : "", critical_range ? critical_range : "",
243                         query_time);
244         return exit_status;
247 /* process command-line arguments */
248 int
249 process_arguments (int argc, char **argv)
251         int c;
253         int option = 0;
254         static struct option longopts[] = {
255                 STD_LONG_OPTS,
257                 {"conntime-warning", required_argument, 0, 'W'},
258                 {"conntime-critical", required_argument, 0, 'C'},
260                 {"driver", required_argument, 0, 'd'},
261                 {"option", required_argument, 0, 'o'},
262                 {"query", required_argument, 0, 'q'},
263                 {"database", required_argument, 0, 'D'},
264                 {0, 0, 0, 0}
265         };
267         while (1) {
268                 c = getopt_long (argc, argv, "Vvht:c:w:H:W:C:d:o:q:D:",
269                                 longopts, &option);
271                 if (c == EOF)
272                         break;
274                 switch (c) {
275                 case '?':     /* usage */
276                         usage5 ();
277                 case 'h':     /* help */
278                         print_help ();
279                         exit (STATE_OK);
280                 case 'V':     /* version */
281                         print_revision (progname, NP_VERSION);
282                         exit (STATE_OK);
284                 case 'c':     /* critical range */
285                         critical_range = optarg;
286                         break;
287                 case 'w':     /* warning range */
288                         warning_range = optarg;
289                         break;
290                 case 't':     /* timeout */
291                         if (!is_intnonneg (optarg))
292                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
293                         else
294                                 timeout_interval = atoi (optarg);
296                 case 'C':     /* critical conntime range */
297                         conntime_critical_range = optarg;
298                         break;
299                 case 'W':     /* warning conntime range */
300                         conntime_warning_range = optarg;
301                         break;
303                 case 'H':     /* host */
304                         if (!is_host (optarg))
305                                 usage2 (_("Invalid hostname/address"), optarg);
306                         else
307                                 host = optarg;
308                         break;
309                 case 'v':
310                         verbose++;
311                         break;
313                 case 'd':
314                         np_dbi_driver = optarg;
315                         break;
316                 case 'o':
317                         {
318                                 driver_option_t *new;
320                                 char *k, *v;
322                                 k = optarg;
323                                 v = strchr (k, (int)'=');
325                                 if (! v)
326                                         usage2 (_("Option must be '<key>=<value>'"), optarg);
328                                 *v = '\0';
329                                 ++v;
331                                 new = realloc (np_dbi_options,
332                                                 (np_dbi_options_num + 1) * sizeof (*new));
333                                 if (! new) {
334                                         printf ("UNKOWN - failed to reallocate memory\n");
335                                         exit (STATE_UNKNOWN);
336                                 }
338                                 np_dbi_options = new;
339                                 new = np_dbi_options + np_dbi_options_num;
340                                 ++np_dbi_options_num;
342                                 new->key = k;
343                                 new->value = v;
344                         }
345                         break;
346                 case 'q':
347                         np_dbi_query = optarg;
348                         break;
349                 case 'D':
350                         np_dbi_database = optarg;
351                         break;
352                 }
353         }
355         set_thresholds (&query_thresholds, warning_range, critical_range);
356         set_thresholds (&conntime_thresholds, conntime_warning_range, conntime_critical_range);
358         return validate_arguments ();
361 int
362 validate_arguments ()
364         if (! np_dbi_driver)
365                 usage ("Must specify a DBI driver");
367         if (! np_dbi_query)
368                 usage ("Must specify an SQL query to execute");
370         return OK;
373 void
374 print_help (void)
376         print_revision (progname, NP_VERSION);
378         printf (COPYRIGHT, copyright, email);
380         printf (_("This program checks a query result against threshold levels"));
382         printf ("\n\n");
384         print_usage ();
386         printf (UT_HELP_VRSN);
387 /* include this conditionally to avoid 'zero-length printf format string'
388  * compiler warnings */
389 #ifdef NP_EXTRA_OPTS
390         printf (UT_EXTRA_OPTS);
391 #endif
392         printf ("\n");
394         printf (" %s\n", "-d, --driver=STRING");
395         printf ("    %s\n", _("DBI driver to use"));
396         printf (" %s\n", "-o, --option=STRING");
397         printf ("    %s\n", _("DBI driver options"));
398         printf (" %s\n", "-q, --query=STRING");
399         printf ("    %s\n", _("SQL query to execute"));
400         printf ("\n");
402         printf (UT_WARN_CRIT_RANGE);
403         printf (" %s\n", "-W, --conntime-warning=RANGE");
404         printf ("    %s\n", _("Connection time warning range"));
405         printf (" %s\n", "-C, --conntime-critical=RANGE");
406         printf ("    %s\n", _("Connection time critical range"));
407         printf ("\n");
409         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
411         printf (UT_VERBOSE);
413         printf ("\n");
414         printf (" %s\n", _("A DBI driver (-d option) and a query (-q option) are required."));
415         printf (" %s\n", _("This plugin connects to an SQL database using libdbi and executes the"));
416         printf (" %s\n", _("specified SQL query. The first column of the first row of the result"));
417         printf (" %s\n", _("will be used as the check result and, if specified, compared with the"));
418         printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
419         printf (" %s\n\n", _("(strings representing numbers are fine)."));
421         printf (" %s\n", _("The number and type of required DBI driver options depends on the actual"));
422         printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
423         printf (" %s\n", _("for details."));
425         printf (UT_SUPPORT);
428 void
429 print_usage (void)
431         printf ("%s\n", _("Usage:"));
432         printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] -q <SQL query>\n", progname);
433         printf (" [-H <host>] [-c <critical range>] [-w <warning range>]\n");
434         printf (" [-C <critical conntime range>] [-W <warning conntime range>]\n");
437 double
438 get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
440         double val = 0.0;
442         if (*field_type == DBI_TYPE_INTEGER) {
443                 val = (double)dbi_result_get_longlong_idx (res, 1);
444         }
445         else if (*field_type == DBI_TYPE_DECIMAL) {
446                 val = dbi_result_get_double_idx (res, 1);
447         }
448         else if (*field_type == DBI_TYPE_STRING) {
449                 const char *val_str;
450                 char *endptr = NULL;
452                 val_str = dbi_result_get_string_idx (res, 1);
453                 if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) {
454                         np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
455                         *field_type = DBI_TYPE_ERROR;
456                         return 0.0;
457                 }
459                 if (verbose > 2)
460                         printf ("Query returned string '%s'\n", val_str);
462                 val = strtod (val_str, &endptr);
463                 if (endptr == val_str) {
464                         printf ("CRITICAL - result value is not a numeric: %s\n", val_str);
465                         *field_type = DBI_TYPE_ERROR;
466                         return 0.0;
467                 }
468                 else if ((endptr != NULL) && (*endptr != '\0')) {
469                         if (verbose)
470                                 printf ("Garbage after value: %s\n", endptr);
471                 }
472         }
473         else {
474                 printf ("CRITICAL - cannot parse value of type %s (%i)\n",
475                                 (*field_type == DBI_TYPE_BINARY)
476                                         ? "BINARY"
477                                         : (*field_type == DBI_TYPE_DATETIME)
478                                                 ? "DATETIME"
479                                                 : "<unknown>",
480                                 *field_type);
481                 *field_type = DBI_TYPE_ERROR;
482                 return 0.0;
483         }
484         return val;
487 int
488 do_query (dbi_conn conn, double *res_val, double *res_time)
490         dbi_result res;
492         unsigned short field_type;
493         double val = 0.0;
495         struct timeval timeval_start, timeval_end;
497         if (verbose)
498                 printf ("Executing query '%s'\n", np_dbi_query);
500         gettimeofday (&timeval_start, NULL);
502         res = dbi_conn_query (conn, np_dbi_query);
503         if (! res) {
504                 np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
505                 return STATE_CRITICAL;
506         }
508         if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
509                 np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
510                 return STATE_CRITICAL;
511         }
513         if (dbi_result_get_numrows (res) < 1) {
514                 printf ("WARNING - no rows returned\n");
515                 return STATE_WARNING;
516         }
518         if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) {
519                 np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
520                 return STATE_CRITICAL;
521         }
523         if (dbi_result_get_numfields (res) < 1) {
524                 printf ("WARNING - no fields returned\n");
525                 return STATE_WARNING;
526         }
528         if (dbi_result_first_row (res) != 1) {
529                 np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
530                 return STATE_CRITICAL;
531         }
533         field_type = dbi_result_get_field_type_idx (res, 1);
534         if (field_type != DBI_TYPE_ERROR)
535                 val = get_field (conn, res, &field_type);
537         gettimeofday (&timeval_end, NULL);
538         *res_time = timediff (timeval_start, timeval_end);
540         if (field_type == DBI_TYPE_ERROR) {
541                 np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
542                 return STATE_CRITICAL;
543         }
545         *res_val = val;
547         dbi_result_free (res);
548         return STATE_OK;
551 double
552 timediff (struct timeval start, struct timeval end)
554         double diff;
556         while (start.tv_usec > end.tv_usec) {
557                 --end.tv_sec;
558                 end.tv_usec += 1000000;
559         }
560         diff = (double)(end.tv_sec - start.tv_sec)
561                 + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
562         return diff;
565 void
566 np_dbi_print_error (dbi_conn conn, char *fmt, ...)
568         const char *errmsg = NULL;
569         va_list ap;
571         va_start (ap, fmt);
573         dbi_conn_error (conn, &errmsg);
574         vprintf (fmt, ap);
575         printf (": %s\n", errmsg);
577         va_end (ap);