Code

check_dbi: Replaced -W/-C with -m.
[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 <assert.h>
43 #include <dbi/dbi.h>
45 #include <stdarg.h>
47 typedef enum {
48         METRIC_CONN_TIME,
49         METRIC_QUERY_RESULT,
50 } np_dbi_metric_t;
52 typedef struct {
53         char *key;
54         char *value;
55 } driver_option_t;
57 char *host = NULL;
58 int verbose = 0;
60 char *warning_range = NULL;
61 char *critical_range = NULL;
62 thresholds *dbi_thresholds = NULL;
64 np_dbi_metric_t metric = METRIC_QUERY_RESULT;
66 char *np_dbi_driver = NULL;
67 driver_option_t *np_dbi_options = NULL;
68 int np_dbi_options_num = 0;
69 char *np_dbi_database = NULL;
70 char *np_dbi_query = NULL;
72 int process_arguments (int, char **);
73 int validate_arguments (void);
74 void print_usage (void);
75 void print_help (void);
77 double timediff (struct timeval, struct timeval);
79 void np_dbi_print_error (dbi_conn, char *, ...);
81 int do_query (dbi_conn, double *, double *);
83 int
84 main (int argc, char **argv)
85 {
86         int status = STATE_UNKNOWN;
88         dbi_driver driver;
89         dbi_conn conn;
91         struct timeval start_timeval, end_timeval;
92         double conn_time = 0.0;
93         double query_time = 0.0;
95         double query_val = 0.0;
97         int i;
99         setlocale (LC_ALL, "");
100         bindtextdomain (PACKAGE, LOCALEDIR);
101         textdomain (PACKAGE);
103         /* Parse extra opts if any */
104         argv = np_extra_opts (&argc, argv, progname);
106         if (process_arguments (argc, argv) == ERROR)
107                 usage4 (_("Could not parse arguments"));
109         /* Set signal handling and alarm */
110         if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
111                 usage4 (_("Cannot catch SIGALRM"));
112         }
113         alarm (timeout_interval);
115         if (verbose > 2)
116                 printf ("Initializing DBI\n");
118         if (dbi_initialize (NULL) < 0) {
119                 printf ("UNKNOWN - failed to initialize DBI.\n");
120                 return STATE_UNKNOWN;
121         }
123         if (verbose)
124                 printf ("Opening DBI driver '%s'\n", np_dbi_driver);
126         driver = dbi_driver_open (np_dbi_driver);
127         if (! driver) {
128                 printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
129                                 np_dbi_driver);
131                 printf ("Known drivers:\n");
132                 for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) {
133                         printf (" - %s\n", dbi_driver_get_name (driver));
134                 }
135                 return STATE_UNKNOWN;
136         }
138         /* make a connection to the database */
139         gettimeofday (&start_timeval, NULL);
141         conn = dbi_conn_open (driver);
142         if (! conn) {
143                 printf ("UNKNOWN - failed top open connection object.\n");
144                 dbi_conn_close (conn);
145                 return STATE_UNKNOWN;
146         }
148         for (i = 0; i < np_dbi_options_num; ++i) {
149                 const char *opt;
151                 if (verbose > 1)
152                         printf ("Setting DBI driver option '%s' to '%s'\n",
153                                         np_dbi_options[i].key, np_dbi_options[i].value);
155                 if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value))
156                         continue;
157                 /* else: status != 0 */
159                 np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'",
160                                 np_dbi_options[i].key, np_dbi_options[i].value);
161                 printf ("Known driver options:\n");
163                 for (opt = dbi_conn_get_option_list (conn, NULL); opt;
164                                 opt = dbi_conn_get_option_list (conn, opt)) {
165                         printf (" - %s\n", opt);
166                 }
167                 dbi_conn_close (conn);
168                 return STATE_UNKNOWN;
169         }
171         if (host) {
172                 if (verbose > 1)
173                         printf ("Setting DBI driver option 'host' to '%s'\n", host);
174                 dbi_conn_set_option (conn, "host", host);
175         }
177         if (verbose) {
178                 const char *dbname, *host;
180                 dbname = dbi_conn_get_option (conn, "dbname");
181                 host = dbi_conn_get_option (conn, "host");
183                 if (! dbname)
184                         dbname = "<unspecified>";
185                 if (! host)
186                         host = "<unspecified>";
188                 printf ("Connecting to database '%s' at host '%s'\n",
189                                 dbname, host);
190         }
192         if (dbi_conn_connect (conn) < 0) {
193                 np_dbi_print_error (conn, "UNKOWN - failed to connect to database");
194                 return STATE_UNKNOWN;
195         }
197         gettimeofday (&end_timeval, NULL);
198         conn_time = timediff (start_timeval, end_timeval);
200         if (verbose)
201                 printf("Time elapsed: %f\n", conn_time);
203         if (metric == METRIC_CONN_TIME)
204                 status = get_status (conn_time, dbi_thresholds);
206         /* select a database */
207         if (np_dbi_database) {
208                 if (verbose > 1)
209                         printf ("Selecting database '%s'\n", np_dbi_database);
211                 if (dbi_conn_select_db (conn, np_dbi_database)) {
212                         np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'",
213                                         np_dbi_database);
214                         return STATE_UNKNOWN;
215                 }
216         }
218         if (np_dbi_query) {
219                 /* execute query */
220                 status = do_query (conn, &query_val, &query_time);
221                 if (status != STATE_OK)
222                         /* do_query prints an error message in this case */
223                         return status;
225                 if (metric == METRIC_QUERY_RESULT)
226                         status = get_status (query_val, dbi_thresholds);
227         }
229         if (verbose)
230                 printf("Closing connection\n");
231         dbi_conn_close (conn);
233         printf ("%s - connection time: %fs", state_text (status), conn_time);
234         if (np_dbi_query)
235                 printf (", '%s' returned %f in %fs", np_dbi_query, query_val, query_time);
237         printf (" | conntime=%fs;%s;%s;0;", conn_time,
238                         ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "",
239                         ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "");
240         if (np_dbi_query)
241                 printf (" query=%f;%s;%s;; querytime=%fs;;;0;", query_val,
242                                 ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "",
243                                 ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : "",
244                                 query_time);
245         printf ("\n");
246         return status;
249 /* process command-line arguments */
250 int
251 process_arguments (int argc, char **argv)
253         int c;
255         int option = 0;
256         static struct option longopts[] = {
257                 STD_LONG_OPTS,
259                 {"metric", required_argument, 0, 'm'},
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:m:H: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 'm':
291                         if (! strcasecmp (optarg, "CONN_TIME"))
292                                 metric = METRIC_CONN_TIME;
293                         else if (! strcasecmp (optarg, "QUERY_RESULT"))
294                                 metric = METRIC_QUERY_RESULT;
295                         else
296                                 usage2 (_("Invalid metric"), optarg);
297                         break;
298                 case 't':     /* timeout */
299                         if (!is_intnonneg (optarg))
300                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
301                         else
302                                 timeout_interval = atoi (optarg);
304                 case 'H':     /* host */
305                         if (!is_host (optarg))
306                                 usage2 (_("Invalid hostname/address"), optarg);
307                         else
308                                 host = optarg;
309                         break;
310                 case 'v':
311                         verbose++;
312                         break;
314                 case 'd':
315                         np_dbi_driver = optarg;
316                         break;
317                 case 'o':
318                         {
319                                 driver_option_t *new;
321                                 char *k, *v;
323                                 k = optarg;
324                                 v = strchr (k, (int)'=');
326                                 if (! v)
327                                         usage2 (_("Option must be '<key>=<value>'"), optarg);
329                                 *v = '\0';
330                                 ++v;
332                                 new = realloc (np_dbi_options,
333                                                 (np_dbi_options_num + 1) * sizeof (*new));
334                                 if (! new) {
335                                         printf ("UNKOWN - failed to reallocate memory\n");
336                                         exit (STATE_UNKNOWN);
337                                 }
339                                 np_dbi_options = new;
340                                 new = np_dbi_options + np_dbi_options_num;
341                                 ++np_dbi_options_num;
343                                 new->key = k;
344                                 new->value = v;
345                         }
346                         break;
347                 case 'q':
348                         np_dbi_query = optarg;
349                         break;
350                 case 'D':
351                         np_dbi_database = optarg;
352                         break;
353                 }
354         }
356         set_thresholds (&dbi_thresholds, warning_range, 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 ((metric == METRIC_QUERY_RESULT) && (! np_dbi_query))
368                 usage ("Must specify a query to execute (metric == QUERY_RESULT)");
370         if ((metric != METRIC_CONN_TIME)
371                         && (metric != METRIC_QUERY_RESULT))
372                 usage ("Invalid metric specified");
374         return OK;
377 void
378 print_help (void)
380         print_revision (progname, NP_VERSION);
382         printf (COPYRIGHT, copyright, email);
384         printf (_("This program connects to an (SQL) database using DBI and checks the\n"
385                         "specified metric against threshold levels. The default metric is\n"
386                         "the result of the specified query.\n"));
388         printf ("\n\n");
390         print_usage ();
392         printf (UT_HELP_VRSN);
393 /* include this conditionally to avoid 'zero-length printf format string'
394  * compiler warnings */
395 #ifdef NP_EXTRA_OPTS
396         printf (UT_EXTRA_OPTS);
397 #endif
398         printf ("\n");
400         printf (" %s\n", "-d, --driver=STRING");
401         printf ("    %s\n", _("DBI driver to use"));
402         printf (" %s\n", "-o, --option=STRING");
403         printf ("    %s\n", _("DBI driver options"));
404         printf (" %s\n", "-q, --query=STRING");
405         printf ("    %s\n", _("query to execute"));
406         printf ("\n");
408         printf (UT_WARN_CRIT_RANGE);
409         printf (" %s\n", "-m, --metric=METRIC");
410         printf ("    %s\n", _("Metric to check thresholds against. Available metrics:"));
411         printf ("    CONN_TIME    - %s\n", _("time used for setting up the database connection"));
412         printf ("    QUERY_RESULT - %s\n", _("result (first column of first row) of the query"));
413         printf ("\n");
415         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
417         printf (UT_VERBOSE);
419         printf ("\n");
420         printf (" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates"));
421         printf (" %s\n\n", _("on a query, one has to be specified (-q option)."));
423         printf (" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,"));
424         printf (" %s\n", _("executes the specified query. The first column of the first row of the"));
425         printf (" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the"));
426         printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
427         printf (" %s\n\n", _("(strings representing numbers are fine)."));
429         printf (" %s\n", _("The number and type of required DBI driver options depends on the actual"));
430         printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
431         printf (" %s\n", _("for details."));
433         printf (UT_SUPPORT);
436 void
437 print_usage (void)
439         printf ("%s\n", _("Usage:"));
440         printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname);
441         printf (" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
444 double
445 get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
447         double val = 0.0;
449         if (*field_type == DBI_TYPE_INTEGER) {
450                 val = (double)dbi_result_get_longlong_idx (res, 1);
451         }
452         else if (*field_type == DBI_TYPE_DECIMAL) {
453                 val = dbi_result_get_double_idx (res, 1);
454         }
455         else if (*field_type == DBI_TYPE_STRING) {
456                 const char *val_str;
457                 char *endptr = NULL;
459                 val_str = dbi_result_get_string_idx (res, 1);
460                 if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) {
461                         np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
462                         *field_type = DBI_TYPE_ERROR;
463                         return 0.0;
464                 }
466                 if (verbose > 2)
467                         printf ("Query returned string '%s'\n", val_str);
469                 val = strtod (val_str, &endptr);
470                 if (endptr == val_str) {
471                         printf ("CRITICAL - result value is not a numeric: %s\n", val_str);
472                         *field_type = DBI_TYPE_ERROR;
473                         return 0.0;
474                 }
475                 else if ((endptr != NULL) && (*endptr != '\0')) {
476                         if (verbose)
477                                 printf ("Garbage after value: %s\n", endptr);
478                 }
479         }
480         else {
481                 printf ("CRITICAL - cannot parse value of type %s (%i)\n",
482                                 (*field_type == DBI_TYPE_BINARY)
483                                         ? "BINARY"
484                                         : (*field_type == DBI_TYPE_DATETIME)
485                                                 ? "DATETIME"
486                                                 : "<unknown>",
487                                 *field_type);
488                 *field_type = DBI_TYPE_ERROR;
489                 return 0.0;
490         }
491         return val;
494 int
495 do_query (dbi_conn conn, double *res_val, double *res_time)
497         dbi_result res;
499         unsigned short field_type;
500         double val = 0.0;
502         struct timeval timeval_start, timeval_end;
504         assert (np_dbi_query);
506         if (verbose)
507                 printf ("Executing query '%s'\n", np_dbi_query);
509         gettimeofday (&timeval_start, NULL);
511         res = dbi_conn_query (conn, np_dbi_query);
512         if (! res) {
513                 np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
514                 return STATE_CRITICAL;
515         }
517         if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
518                 np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
519                 return STATE_CRITICAL;
520         }
522         if (dbi_result_get_numrows (res) < 1) {
523                 printf ("WARNING - no rows returned\n");
524                 return STATE_WARNING;
525         }
527         if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) {
528                 np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
529                 return STATE_CRITICAL;
530         }
532         if (dbi_result_get_numfields (res) < 1) {
533                 printf ("WARNING - no fields returned\n");
534                 return STATE_WARNING;
535         }
537         if (dbi_result_first_row (res) != 1) {
538                 np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
539                 return STATE_CRITICAL;
540         }
542         field_type = dbi_result_get_field_type_idx (res, 1);
543         if (field_type != DBI_TYPE_ERROR)
544                 val = get_field (conn, res, &field_type);
546         gettimeofday (&timeval_end, NULL);
547         *res_time = timediff (timeval_start, timeval_end);
549         if (field_type == DBI_TYPE_ERROR) {
550                 np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
551                 return STATE_CRITICAL;
552         }
554         *res_val = val;
556         dbi_result_free (res);
557         return STATE_OK;
560 double
561 timediff (struct timeval start, struct timeval end)
563         double diff;
565         while (start.tv_usec > end.tv_usec) {
566                 --end.tv_sec;
567                 end.tv_usec += 1000000;
568         }
569         diff = (double)(end.tv_sec - start.tv_sec)
570                 + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
571         return diff;
574 void
575 np_dbi_print_error (dbi_conn conn, char *fmt, ...)
577         const char *errmsg = NULL;
578         va_list ap;
580         va_start (ap, fmt);
582         dbi_conn_error (conn, &errmsg);
583         vprintf (fmt, ap);
584         printf (": %s\n", errmsg);
586         va_end (ap);