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 *np_dbi_driver = NULL;
58 driver_option_t *np_dbi_options = NULL;
59 int np_dbi_options_num = 0;
60 char *np_dbi_database = NULL;
61 char *np_dbi_query = NULL;
63 int process_arguments (int, char **);
64 int validate_arguments (void);
65 void print_usage (void);
66 void print_help (void);
68 void np_dbi_print_error (dbi_conn, char *, ...);
70 int do_query (dbi_conn, double *);
72 int
73 main (int argc, char **argv)
74 {
75 int status = STATE_UNKNOWN;
77 dbi_driver driver;
78 dbi_conn conn;
80 struct timeval start_timeval, end_timeval;
81 double elapsed_time;
83 double query_val = 0.0;
85 int i;
87 setlocale (LC_ALL, "");
88 bindtextdomain (PACKAGE, LOCALEDIR);
89 textdomain (PACKAGE);
91 /* Parse extra opts if any */
92 argv = np_extra_opts (&argc, argv, progname);
94 if (process_arguments (argc, argv) == ERROR)
95 usage4 (_("Could not parse arguments"));
97 /* Set signal handling and alarm */
98 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
99 usage4 (_("Cannot catch SIGALRM"));
100 }
101 alarm (timeout_interval);
103 if (verbose > 2)
104 printf ("Initializing DBI\n");
106 if (dbi_initialize (NULL) < 0) {
107 printf ("UNKNOWN - failed to initialize DBI.\n");
108 return STATE_UNKNOWN;
109 }
111 if (verbose)
112 printf ("Opening DBI driver '%s'\n", np_dbi_driver);
114 driver = dbi_driver_open (np_dbi_driver);
115 if (! driver) {
116 printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
117 np_dbi_driver);
119 printf ("Known drivers:\n");
120 for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) {
121 printf (" - %s\n", dbi_driver_get_name (driver));
122 }
123 return STATE_UNKNOWN;
124 }
126 /* make a connection to the database */
127 gettimeofday (&start_timeval, NULL);
129 conn = dbi_conn_open (driver);
130 if (! conn) {
131 printf ("UNKNOWN - failed top open connection object.\n");
132 dbi_conn_close (conn);
133 return STATE_UNKNOWN;
134 }
136 for (i = 0; i < np_dbi_options_num; ++i) {
137 const char *opt;
139 if (verbose > 1)
140 printf ("Setting DBI driver option '%s' to '%s'\n",
141 np_dbi_options[i].key, np_dbi_options[i].value);
143 if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value))
144 continue;
145 /* else: status != 0 */
147 np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'",
148 np_dbi_options[i].key, np_dbi_options[i].value);
149 printf ("Known driver options:\n");
151 for (opt = dbi_conn_get_option_list (conn, NULL); opt;
152 opt = dbi_conn_get_option_list (conn, opt)) {
153 printf (" - %s\n", opt);
154 }
155 dbi_conn_close (conn);
156 return STATE_UNKNOWN;
157 }
159 if (host) {
160 if (verbose > 1)
161 printf ("Setting DBI driver option 'host' to '%s'\n", host);
162 dbi_conn_set_option (conn, "host", host);
163 }
165 if (verbose) {
166 const char *dbname, *host;
168 dbname = dbi_conn_get_option (conn, "dbname");
169 host = dbi_conn_get_option (conn, "host");
171 if (! dbname)
172 dbname = "<unspecified>";
173 if (! host)
174 host = "<unspecified>";
176 printf ("Connecting to database '%s' at host '%s'\n",
177 dbname, host);
178 }
180 if (dbi_conn_connect (conn) < 0) {
181 np_dbi_print_error (conn, "UNKOWN - failed to connect to database");
182 return STATE_UNKNOWN;
183 }
185 gettimeofday (&end_timeval, NULL);
186 while (start_timeval.tv_usec > end_timeval.tv_usec) {
187 --end_timeval.tv_sec;
188 end_timeval.tv_usec += 1000000;
189 }
190 elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec)
191 + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0;
193 if (verbose)
194 printf("Time elapsed: %f\n", elapsed_time);
196 /* select a database */
197 if (np_dbi_database) {
198 if (verbose > 1)
199 printf ("Selecting database '%s'\n", np_dbi_database);
201 if (dbi_conn_select_db (conn, np_dbi_database)) {
202 np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'",
203 np_dbi_database);
204 return STATE_UNKNOWN;
205 }
206 }
208 /* execute query */
209 status = do_query (conn, &query_val);
210 if (status != STATE_OK)
211 /* do_query prints an error message in this case */
212 return status;
214 status = get_status (query_val, query_thresholds);
216 if (verbose)
217 printf("Closing connection\n");
218 dbi_conn_close (conn);
220 printf ("%s - connection time: %fs, '%s' returned %f",
221 state_text (status), elapsed_time, np_dbi_query, query_val);
222 printf (" | conntime=%fs;;;0 query=%f;%s;%s;0\n", elapsed_time, query_val,
223 warning_range ? warning_range : "", critical_range ? critical_range : "");
224 return status;
225 }
227 /* process command-line arguments */
228 int
229 process_arguments (int argc, char **argv)
230 {
231 int c;
233 int option = 0;
234 static struct option longopts[] = {
235 STD_LONG_OPTS,
237 {"driver", required_argument, 0, 'd'},
238 {"option", required_argument, 0, 'o'},
239 {"query", required_argument, 0, 'q'},
240 {"database", required_argument, 0, 'D'},
241 {0, 0, 0, 0}
242 };
244 while (1) {
245 c = getopt_long (argc, argv, "Vvht:c:w:H:d:o:q:D:",
246 longopts, &option);
248 if (c == EOF)
249 break;
251 switch (c) {
252 case '?': /* usage */
253 usage5 ();
254 case 'h': /* help */
255 print_help ();
256 exit (STATE_OK);
257 case 'V': /* version */
258 print_revision (progname, NP_VERSION);
259 exit (STATE_OK);
261 case 'c': /* critical range */
262 critical_range = optarg;
263 break;
264 case 'w': /* warning range */
265 warning_range = optarg;
266 break;
267 case 't': /* timeout */
268 if (!is_intnonneg (optarg))
269 usage2 (_("Timeout interval must be a positive integer"), optarg);
270 else
271 timeout_interval = atoi (optarg);
273 case 'H': /* host */
274 if (!is_host (optarg))
275 usage2 (_("Invalid hostname/address"), optarg);
276 else
277 host = optarg;
278 break;
279 case 'v':
280 verbose++;
281 break;
283 case 'd':
284 np_dbi_driver = optarg;
285 break;
286 case 'o':
287 {
288 driver_option_t *new;
290 char *k, *v;
292 k = optarg;
293 v = strchr (k, (int)'=');
295 if (! v)
296 usage2 (_("Option must be '<key>=<value>'"), optarg);
298 *v = '\0';
299 ++v;
301 new = realloc (np_dbi_options,
302 (np_dbi_options_num + 1) * sizeof (*new));
303 if (! new) {
304 printf ("UNKOWN - failed to reallocate memory\n");
305 exit (STATE_UNKNOWN);
306 }
308 np_dbi_options = new;
309 new = np_dbi_options + np_dbi_options_num;
310 ++np_dbi_options_num;
312 new->key = k;
313 new->value = v;
314 }
315 break;
316 case 'q':
317 np_dbi_query = optarg;
318 break;
319 case 'D':
320 np_dbi_database = optarg;
321 break;
322 }
323 }
325 set_thresholds (&query_thresholds, warning_range, critical_range);
327 return validate_arguments ();
328 }
330 int
331 validate_arguments ()
332 {
333 if (! np_dbi_driver)
334 usage ("Must specify a DBI driver");
336 if (! np_dbi_query)
337 usage ("Must specify an SQL query to execute");
339 return OK;
340 }
342 void
343 print_help (void)
344 {
345 print_revision (progname, NP_VERSION);
347 printf (COPYRIGHT, copyright, email);
349 printf (_("This program checks a query result against threshold levels"));
351 printf ("\n\n");
353 print_usage ();
355 printf (UT_HELP_VRSN);
356 /* include this conditionally to avoid 'zero-length printf format string'
357 * compiler warnings */
358 #ifdef NP_EXTRA_OPTS
359 printf (UT_EXTRA_OPTS);
360 #endif
361 printf ("\n");
363 printf (" %s\n", "-d, --driver=STRING");
364 printf (" %s\n", _("DBI driver to use"));
365 printf (" %s\n", "-o, --option=STRING");
366 printf (" %s\n", _("DBI driver options"));
367 printf (" %s\n", "-q, --query=STRING");
368 printf (" %s\n", _("SQL query to execute"));
369 printf ("\n");
371 printf (UT_WARN_CRIT_RANGE);
373 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
375 printf (UT_VERBOSE);
377 printf ("\n");
378 printf (" %s\n", _("A DBI driver (-d option) and a query (-q option) are required."));
379 printf (" %s\n", _("This plugin connects to an SQL database using libdbi and executes the"));
380 printf (" %s\n", _("specified SQL query. The first column of the first row of the result"));
381 printf (" %s\n", _("will be used as the check result and, if specified, compared with the"));
382 printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
383 printf (" %s\n\n", _("(strings representing numbers are fine)."));
385 printf (" %s\n", _("The number and type of required DBI driver options depends on the actual"));
386 printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
387 printf (" %s\n", _("for details."));
389 printf (UT_SUPPORT);
390 }
392 void
393 print_usage (void)
394 {
395 printf ("%s\n", _("Usage:"));
396 printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] -q <SQL query>\n", progname);
397 printf (" [-H <host>] [-c <critical value>] [-w <warning value>]\n");
398 }
400 double
401 get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
402 {
403 double val = 0.0;
405 if (*field_type == DBI_TYPE_INTEGER) {
406 val = (double)dbi_result_get_longlong_idx (res, 1);
407 }
408 else if (*field_type == DBI_TYPE_DECIMAL) {
409 val = dbi_result_get_double_idx (res, 1);
410 }
411 else if (*field_type == DBI_TYPE_STRING) {
412 const char *val_str;
413 char *endptr = NULL;
415 val_str = dbi_result_get_string_idx (res, 1);
416 if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) {
417 np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
418 *field_type = DBI_TYPE_ERROR;
419 return 0.0;
420 }
422 if (verbose > 2)
423 printf ("Query returned string '%s'\n", val_str);
425 val = strtod (val_str, &endptr);
426 if (endptr == val_str) {
427 printf ("CRITICAL - result value is not a numeric: %s\n", val_str);
428 *field_type = DBI_TYPE_ERROR;
429 return 0.0;
430 }
431 else if ((endptr != NULL) && (*endptr != '\0')) {
432 if (verbose)
433 printf ("Garbage after value: %s\n", endptr);
434 }
435 }
436 else {
437 printf ("CRITICAL - cannot parse value of type %s (%i)\n",
438 (*field_type == DBI_TYPE_BINARY)
439 ? "BINARY"
440 : (*field_type == DBI_TYPE_DATETIME)
441 ? "DATETIME"
442 : "<unknown>",
443 *field_type);
444 *field_type = DBI_TYPE_ERROR;
445 return 0.0;
446 }
447 return val;
448 }
450 int
451 do_query (dbi_conn conn, double *res_val)
452 {
453 dbi_result res;
455 unsigned short field_type;
456 double val = 0.0;
458 if (verbose)
459 printf ("Executing query '%s'\n", np_dbi_query);
461 res = dbi_conn_query (conn, np_dbi_query);
462 if (! res) {
463 np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
464 return STATE_CRITICAL;
465 }
467 if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
468 np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
469 return STATE_CRITICAL;
470 }
472 if (dbi_result_get_numrows (res) < 1) {
473 printf ("WARNING - no rows returned\n");
474 return STATE_WARNING;
475 }
477 if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) {
478 np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
479 return STATE_CRITICAL;
480 }
482 if (dbi_result_get_numfields (res) < 1) {
483 printf ("WARNING - no fields returned\n");
484 return STATE_WARNING;
485 }
487 if (dbi_result_first_row (res) != 1) {
488 np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
489 return STATE_CRITICAL;
490 }
492 field_type = dbi_result_get_field_type_idx (res, 1);
493 if (field_type != DBI_TYPE_ERROR)
494 val = get_field (conn, res, &field_type);
496 if (field_type == DBI_TYPE_ERROR) {
497 np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
498 return STATE_CRITICAL;
499 }
501 *res_val = val;
503 dbi_result_free (res);
504 return STATE_OK;
505 }
507 void
508 np_dbi_print_error (dbi_conn conn, char *fmt, ...)
509 {
510 const char *errmsg = NULL;
511 va_list ap;
513 va_start (ap, fmt);
515 dbi_conn_error (conn, &errmsg);
516 vprintf (fmt, ap);
517 printf (": %s\n", errmsg);
519 va_end (ap);
520 }