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 void np_dbi_print_error (dbi_conn, char *, ...);
74 int do_query (dbi_conn, double *);
76 int
77 main (int argc, char **argv)
78 {
79 int conntime_status = STATE_UNKNOWN;
80 int status = STATE_UNKNOWN;
82 int exit_status = STATE_UNKNOWN;
84 dbi_driver driver;
85 dbi_conn conn;
87 struct timeval start_timeval, end_timeval;
88 double elapsed_time;
90 double query_val = 0.0;
92 int i;
94 setlocale (LC_ALL, "");
95 bindtextdomain (PACKAGE, LOCALEDIR);
96 textdomain (PACKAGE);
98 /* Parse extra opts if any */
99 argv = np_extra_opts (&argc, argv, progname);
101 if (process_arguments (argc, argv) == ERROR)
102 usage4 (_("Could not parse arguments"));
104 /* Set signal handling and alarm */
105 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
106 usage4 (_("Cannot catch SIGALRM"));
107 }
108 alarm (timeout_interval);
110 if (verbose > 2)
111 printf ("Initializing DBI\n");
113 if (dbi_initialize (NULL) < 0) {
114 printf ("UNKNOWN - failed to initialize DBI.\n");
115 return STATE_UNKNOWN;
116 }
118 if (verbose)
119 printf ("Opening DBI driver '%s'\n", np_dbi_driver);
121 driver = dbi_driver_open (np_dbi_driver);
122 if (! driver) {
123 printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
124 np_dbi_driver);
126 printf ("Known drivers:\n");
127 for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) {
128 printf (" - %s\n", dbi_driver_get_name (driver));
129 }
130 return STATE_UNKNOWN;
131 }
133 /* make a connection to the database */
134 gettimeofday (&start_timeval, NULL);
136 conn = dbi_conn_open (driver);
137 if (! conn) {
138 printf ("UNKNOWN - failed top open connection object.\n");
139 dbi_conn_close (conn);
140 return STATE_UNKNOWN;
141 }
143 for (i = 0; i < np_dbi_options_num; ++i) {
144 const char *opt;
146 if (verbose > 1)
147 printf ("Setting DBI driver option '%s' to '%s'\n",
148 np_dbi_options[i].key, np_dbi_options[i].value);
150 if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value))
151 continue;
152 /* else: status != 0 */
154 np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'",
155 np_dbi_options[i].key, np_dbi_options[i].value);
156 printf ("Known driver options:\n");
158 for (opt = dbi_conn_get_option_list (conn, NULL); opt;
159 opt = dbi_conn_get_option_list (conn, opt)) {
160 printf (" - %s\n", opt);
161 }
162 dbi_conn_close (conn);
163 return STATE_UNKNOWN;
164 }
166 if (host) {
167 if (verbose > 1)
168 printf ("Setting DBI driver option 'host' to '%s'\n", host);
169 dbi_conn_set_option (conn, "host", host);
170 }
172 if (verbose) {
173 const char *dbname, *host;
175 dbname = dbi_conn_get_option (conn, "dbname");
176 host = dbi_conn_get_option (conn, "host");
178 if (! dbname)
179 dbname = "<unspecified>";
180 if (! host)
181 host = "<unspecified>";
183 printf ("Connecting to database '%s' at host '%s'\n",
184 dbname, host);
185 }
187 if (dbi_conn_connect (conn) < 0) {
188 np_dbi_print_error (conn, "UNKOWN - failed to connect to database");
189 return STATE_UNKNOWN;
190 }
192 gettimeofday (&end_timeval, NULL);
193 while (start_timeval.tv_usec > end_timeval.tv_usec) {
194 --end_timeval.tv_sec;
195 end_timeval.tv_usec += 1000000;
196 }
197 elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec)
198 + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0;
200 if (verbose)
201 printf("Time elapsed: %f\n", elapsed_time);
203 conntime_status = get_status (elapsed_time, conntime_thresholds);
205 /* select a database */
206 if (np_dbi_database) {
207 if (verbose > 1)
208 printf ("Selecting database '%s'\n", np_dbi_database);
210 if (dbi_conn_select_db (conn, np_dbi_database)) {
211 np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'",
212 np_dbi_database);
213 return STATE_UNKNOWN;
214 }
215 }
217 /* execute query */
218 status = do_query (conn, &query_val);
219 if (status != STATE_OK)
220 /* do_query prints an error message in this case */
221 return status;
223 status = get_status (query_val, query_thresholds);
225 if (verbose)
226 printf("Closing connection\n");
227 dbi_conn_close (conn);
229 /* 'conntime_status' is worse than 'status' (but not UNKOWN) */
230 if (((conntime_status < STATE_UNKNOWN) && (conntime_status > status))
231 /* 'status' is UNKNOWN and 'conntime_status' is not OK */
232 || ((status >= STATE_UNKNOWN) && (conntime_status != STATE_OK)))
233 exit_status = conntime_status;
234 else
235 exit_status = status;
237 printf ("%s - %s: connection time: %fs, %s: '%s' returned %f",
238 state_text (exit_status),
239 state_text (conntime_status), elapsed_time,
240 state_text (status), np_dbi_query, query_val);
241 printf (" | conntime=%fs;%s;%s;0 query=%f;%s;%s;0\n", elapsed_time,
242 conntime_warning_range ? conntime_warning_range : "",
243 conntime_critical_range ? conntime_critical_range : "",
244 query_val, warning_range ? warning_range : "", critical_range ? critical_range : "");
245 return exit_status;
246 }
248 /* process command-line arguments */
249 int
250 process_arguments (int argc, char **argv)
251 {
252 int c;
254 int option = 0;
255 static struct option longopts[] = {
256 STD_LONG_OPTS,
258 {"conntime-warning", required_argument, 0, 'W'},
259 {"conntime-critical", required_argument, 0, 'C'},
261 {"driver", required_argument, 0, 'd'},
262 {"option", required_argument, 0, 'o'},
263 {"query", required_argument, 0, 'q'},
264 {"database", required_argument, 0, 'D'},
265 {0, 0, 0, 0}
266 };
268 while (1) {
269 c = getopt_long (argc, argv, "Vvht:c:w:H:W:C:d:o:q:D:",
270 longopts, &option);
272 if (c == EOF)
273 break;
275 switch (c) {
276 case '?': /* usage */
277 usage5 ();
278 case 'h': /* help */
279 print_help ();
280 exit (STATE_OK);
281 case 'V': /* version */
282 print_revision (progname, NP_VERSION);
283 exit (STATE_OK);
285 case 'c': /* critical range */
286 critical_range = optarg;
287 break;
288 case 'w': /* warning range */
289 warning_range = optarg;
290 break;
291 case 't': /* timeout */
292 if (!is_intnonneg (optarg))
293 usage2 (_("Timeout interval must be a positive integer"), optarg);
294 else
295 timeout_interval = atoi (optarg);
297 case 'C': /* critical conntime range */
298 conntime_critical_range = optarg;
299 break;
300 case 'W': /* warning conntime range */
301 conntime_warning_range = optarg;
302 break;
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 (&query_thresholds, warning_range, critical_range);
357 set_thresholds (&conntime_thresholds, conntime_warning_range, conntime_critical_range);
359 return validate_arguments ();
360 }
362 int
363 validate_arguments ()
364 {
365 if (! np_dbi_driver)
366 usage ("Must specify a DBI driver");
368 if (! np_dbi_query)
369 usage ("Must specify an SQL query to execute");
371 return OK;
372 }
374 void
375 print_help (void)
376 {
377 print_revision (progname, NP_VERSION);
379 printf (COPYRIGHT, copyright, email);
381 printf (_("This program checks a query result against threshold levels"));
383 printf ("\n\n");
385 print_usage ();
387 printf (UT_HELP_VRSN);
388 /* include this conditionally to avoid 'zero-length printf format string'
389 * compiler warnings */
390 #ifdef NP_EXTRA_OPTS
391 printf (UT_EXTRA_OPTS);
392 #endif
393 printf ("\n");
395 printf (" %s\n", "-d, --driver=STRING");
396 printf (" %s\n", _("DBI driver to use"));
397 printf (" %s\n", "-o, --option=STRING");
398 printf (" %s\n", _("DBI driver options"));
399 printf (" %s\n", "-q, --query=STRING");
400 printf (" %s\n", _("SQL query to execute"));
401 printf ("\n");
403 printf (UT_WARN_CRIT_RANGE);
404 printf (" %s\n", "-W, --conntime-warning=RANGE");
405 printf (" %s\n", _("Connection time warning range"));
406 printf (" %s\n", "-C, --conntime-critical=RANGE");
407 printf (" %s\n", _("Connection time critical range"));
408 printf ("\n");
410 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
412 printf (UT_VERBOSE);
414 printf ("\n");
415 printf (" %s\n", _("A DBI driver (-d option) and a query (-q option) are required."));
416 printf (" %s\n", _("This plugin connects to an SQL database using libdbi and executes the"));
417 printf (" %s\n", _("specified SQL query. The first column of the first row of the result"));
418 printf (" %s\n", _("will be used as the check result and, if specified, compared with the"));
419 printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
420 printf (" %s\n\n", _("(strings representing numbers are fine)."));
422 printf (" %s\n", _("The number and type of required DBI driver options depends on the actual"));
423 printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
424 printf (" %s\n", _("for details."));
426 printf (UT_SUPPORT);
427 }
429 void
430 print_usage (void)
431 {
432 printf ("%s\n", _("Usage:"));
433 printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] -q <SQL query>\n", progname);
434 printf (" [-H <host>] [-c <critical range>] [-w <warning range>]\n");
435 printf (" [-C <critical conntime range>] [-W <warning conntime range>]\n");
436 }
438 double
439 get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
440 {
441 double val = 0.0;
443 if (*field_type == DBI_TYPE_INTEGER) {
444 val = (double)dbi_result_get_longlong_idx (res, 1);
445 }
446 else if (*field_type == DBI_TYPE_DECIMAL) {
447 val = dbi_result_get_double_idx (res, 1);
448 }
449 else if (*field_type == DBI_TYPE_STRING) {
450 const char *val_str;
451 char *endptr = NULL;
453 val_str = dbi_result_get_string_idx (res, 1);
454 if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) {
455 np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
456 *field_type = DBI_TYPE_ERROR;
457 return 0.0;
458 }
460 if (verbose > 2)
461 printf ("Query returned string '%s'\n", val_str);
463 val = strtod (val_str, &endptr);
464 if (endptr == val_str) {
465 printf ("CRITICAL - result value is not a numeric: %s\n", val_str);
466 *field_type = DBI_TYPE_ERROR;
467 return 0.0;
468 }
469 else if ((endptr != NULL) && (*endptr != '\0')) {
470 if (verbose)
471 printf ("Garbage after value: %s\n", endptr);
472 }
473 }
474 else {
475 printf ("CRITICAL - cannot parse value of type %s (%i)\n",
476 (*field_type == DBI_TYPE_BINARY)
477 ? "BINARY"
478 : (*field_type == DBI_TYPE_DATETIME)
479 ? "DATETIME"
480 : "<unknown>",
481 *field_type);
482 *field_type = DBI_TYPE_ERROR;
483 return 0.0;
484 }
485 return val;
486 }
488 int
489 do_query (dbi_conn conn, double *res_val)
490 {
491 dbi_result res;
493 unsigned short field_type;
494 double val = 0.0;
496 if (verbose)
497 printf ("Executing query '%s'\n", np_dbi_query);
499 res = dbi_conn_query (conn, np_dbi_query);
500 if (! res) {
501 np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
502 return STATE_CRITICAL;
503 }
505 if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
506 np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
507 return STATE_CRITICAL;
508 }
510 if (dbi_result_get_numrows (res) < 1) {
511 printf ("WARNING - no rows returned\n");
512 return STATE_WARNING;
513 }
515 if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) {
516 np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
517 return STATE_CRITICAL;
518 }
520 if (dbi_result_get_numfields (res) < 1) {
521 printf ("WARNING - no fields returned\n");
522 return STATE_WARNING;
523 }
525 if (dbi_result_first_row (res) != 1) {
526 np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
527 return STATE_CRITICAL;
528 }
530 field_type = dbi_result_get_field_type_idx (res, 1);
531 if (field_type != DBI_TYPE_ERROR)
532 val = get_field (conn, res, &field_type);
534 if (field_type == DBI_TYPE_ERROR) {
535 np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
536 return STATE_CRITICAL;
537 }
539 *res_val = val;
541 dbi_result_free (res);
542 return STATE_OK;
543 }
545 void
546 np_dbi_print_error (dbi_conn conn, char *fmt, ...)
547 {
548 const char *errmsg = NULL;
549 va_list ap;
551 va_start (ap, fmt);
553 dbi_conn_error (conn, &errmsg);
554 vprintf (fmt, ap);
555 printf (": %s\n", errmsg);
557 va_end (ap);
558 }