diff options
author | Sebastian Harl <sh@teamix.net> | 2011-04-07 17:24:23 +0200 |
---|---|---|
committer | Sebastian Harl <sh@teamix.net> | 2012-06-06 14:10:55 +0200 |
commit | f9a942d2a11e70ee68d5ea2c9aa762bff004bf43 (patch) | |
tree | 31ce3a05d1bdc715bf7a0230280d1d16940212ad /plugins/check_dbi.c | |
parent | 804e7d878cad5c76474fd2abf2130ff446dbda5d (diff) | |
download | monitoring-plugins-f9a942d2a11e70ee68d5ea2c9aa762bff004bf43.tar.gz |
Initial version of the 'check_dbi' plugin.
This plugin connects to an SQL database using libdbi, thus supporting all
database backends supported by libdbi. It will then issue the specified SQL
query and check the result (the numeric value of the first column of the first
row to be precise) against the specified warning/critical ranges.
The performance data includes the connection time (µs-resolution as provided
by gettimeofday()) and the query result.
Diffstat (limited to 'plugins/check_dbi.c')
-rw-r--r-- | plugins/check_dbi.c | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c new file mode 100644 index 00000000..b52602c4 --- /dev/null +++ b/plugins/check_dbi.c | |||
@@ -0,0 +1,516 @@ | |||
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 | *****************************************************************************/ | ||
31 | |||
32 | const char *progname = "check_dbi"; | ||
33 | const char *copyright = "2011"; | ||
34 | const char *email = "nagiosplug-devel@lists.sourceforge.net"; | ||
35 | |||
36 | #include "common.h" | ||
37 | #include "utils.h" | ||
38 | |||
39 | #include "netutils.h" | ||
40 | |||
41 | #include <dbi/dbi.h> | ||
42 | |||
43 | #include <stdarg.h> | ||
44 | |||
45 | typedef struct { | ||
46 | char *key; | ||
47 | char *value; | ||
48 | } driver_option_t; | ||
49 | |||
50 | char *host = NULL; | ||
51 | int verbose = 0; | ||
52 | |||
53 | char *warning_range = NULL; | ||
54 | char *critical_range = NULL; | ||
55 | thresholds *query_thresholds = NULL; | ||
56 | |||
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; | ||
62 | |||
63 | int process_arguments (int, char **); | ||
64 | int validate_arguments (void); | ||
65 | void print_usage (void); | ||
66 | void print_help (void); | ||
67 | |||
68 | void np_dbi_print_error (dbi_conn, char *, ...); | ||
69 | |||
70 | int do_query (dbi_conn, double *); | ||
71 | |||
72 | int | ||
73 | main (int argc, char **argv) | ||
74 | { | ||
75 | int status = STATE_UNKNOWN; | ||
76 | |||
77 | dbi_driver driver; | ||
78 | dbi_conn conn; | ||
79 | |||
80 | struct timeval start_timeval, end_timeval; | ||
81 | double elapsed_time; | ||
82 | |||
83 | double query_val = 0.0; | ||
84 | |||
85 | int i; | ||
86 | |||
87 | setlocale (LC_ALL, ""); | ||
88 | bindtextdomain (PACKAGE, LOCALEDIR); | ||
89 | textdomain (PACKAGE); | ||
90 | |||
91 | /* Parse extra opts if any */ | ||
92 | argv = np_extra_opts (&argc, argv, progname); | ||
93 | |||
94 | if (process_arguments (argc, argv) == ERROR) | ||
95 | usage4 (_("Could not parse arguments")); | ||
96 | |||
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); | ||
102 | |||
103 | if (verbose > 2) | ||
104 | printf ("Initializing DBI\n"); | ||
105 | |||
106 | if (dbi_initialize (NULL) < 0) { | ||
107 | printf ("UNKNOWN - failed to initialize DBI.\n"); | ||
108 | return STATE_UNKNOWN; | ||
109 | } | ||
110 | |||
111 | if (verbose) | ||
112 | printf ("Opening DBI driver '%s'\n", np_dbi_driver); | ||
113 | |||
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); | ||
118 | |||
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 | } | ||
125 | |||
126 | /* make a connection to the database */ | ||
127 | gettimeofday (&start_timeval, NULL); | ||
128 | |||
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 | } | ||
135 | |||
136 | for (i = 0; i < np_dbi_options_num; ++i) { | ||
137 | const char *opt; | ||
138 | |||
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); | ||
142 | |||
143 | if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value)) | ||
144 | continue; | ||
145 | /* else: status != 0 */ | ||
146 | |||
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"); | ||
150 | |||
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 | } | ||
158 | |||
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 | } | ||
164 | |||
165 | if (verbose) { | ||
166 | const char *dbname, *host; | ||
167 | |||
168 | dbname = dbi_conn_get_option (conn, "dbname"); | ||
169 | host = dbi_conn_get_option (conn, "host"); | ||
170 | |||
171 | if (! dbname) | ||
172 | dbname = "<unspecified>"; | ||
173 | if (! host) | ||
174 | host = "<unspecified>"; | ||
175 | |||
176 | printf ("Connecting to database '%s' at host '%s'\n", | ||
177 | dbname, host); | ||
178 | } | ||
179 | |||
180 | if (dbi_conn_connect (conn) < 0) { | ||
181 | np_dbi_print_error (conn, "UNKOWN - failed to connect to database"); | ||
182 | return STATE_UNKNOWN; | ||
183 | } | ||
184 | |||
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; | ||
192 | |||
193 | if (verbose) | ||
194 | printf("Time elapsed: %f\n", elapsed_time); | ||
195 | |||
196 | /* select a database */ | ||
197 | if (np_dbi_database) { | ||
198 | if (verbose > 1) | ||
199 | printf ("Selecting database '%s'\n", np_dbi_database); | ||
200 | |||
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 | } | ||
207 | |||
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; | ||
213 | |||
214 | status = get_status (query_val, query_thresholds); | ||
215 | |||
216 | if (verbose) | ||
217 | printf("Closing connection\n"); | ||
218 | dbi_conn_close (conn); | ||
219 | |||
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 | } | ||
226 | |||
227 | /* process command-line arguments */ | ||
228 | int | ||
229 | process_arguments (int argc, char **argv) | ||
230 | { | ||
231 | int c; | ||
232 | |||
233 | int option = 0; | ||
234 | static struct option longopts[] = { | ||
235 | STD_LONG_OPTS, | ||
236 | |||
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 | }; | ||
243 | |||
244 | while (1) { | ||
245 | c = getopt_long (argc, argv, "Vvht:c:w:H:d:o:q:D:", | ||
246 | longopts, &option); | ||
247 | |||
248 | if (c == EOF) | ||
249 | break; | ||
250 | |||
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); | ||
260 | |||
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); | ||
272 | |||
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; | ||
282 | |||
283 | case 'd': | ||
284 | np_dbi_driver = optarg; | ||
285 | break; | ||
286 | case 'o': | ||
287 | { | ||
288 | driver_option_t *new; | ||
289 | |||
290 | char *k, *v; | ||
291 | |||
292 | k = optarg; | ||
293 | v = strchr (k, (int)'='); | ||
294 | |||
295 | if (! v) | ||
296 | usage2 (_("Option must be '<key>=<value>'"), optarg); | ||
297 | |||
298 | *v = '\0'; | ||
299 | ++v; | ||
300 | |||
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 | } | ||
307 | |||
308 | np_dbi_options = new; | ||
309 | new = np_dbi_options + np_dbi_options_num; | ||
310 | ++np_dbi_options_num; | ||
311 | |||
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 | } | ||
324 | |||
325 | set_thresholds (&query_thresholds, warning_range, critical_range); | ||
326 | |||
327 | return validate_arguments (); | ||
328 | } | ||
329 | |||
330 | int | ||
331 | validate_arguments () | ||
332 | { | ||
333 | if (! np_dbi_driver) | ||
334 | usage ("Must specify a DBI driver"); | ||
335 | |||
336 | if (! np_dbi_query) | ||
337 | usage ("Must specify an SQL query to execute"); | ||
338 | |||
339 | return OK; | ||
340 | } | ||
341 | |||
342 | void | ||
343 | print_help (void) | ||
344 | { | ||
345 | print_revision (progname, NP_VERSION); | ||
346 | |||
347 | printf (COPYRIGHT, copyright, email); | ||
348 | |||
349 | printf (_("This program checks a query result against threshold levels")); | ||
350 | |||
351 | printf ("\n\n"); | ||
352 | |||
353 | print_usage (); | ||
354 | |||
355 | printf (UT_HELP_VRSN); | ||
356 | printf ("\n"); | ||
357 | |||
358 | printf (" %s\n", "-d, --driver=STRING"); | ||
359 | printf (" %s\n", _("DBI driver to use")); | ||
360 | printf (" %s\n", "-o, --option=STRING"); | ||
361 | printf (" %s\n", _("DBI driver options")); | ||
362 | printf (" %s\n", "-q, --query=STRING"); | ||
363 | printf (" %s\n", _("SQL query to execute")); | ||
364 | printf ("\n"); | ||
365 | |||
366 | printf (UT_WARN_CRIT_RANGE); | ||
367 | |||
368 | printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
369 | |||
370 | printf (UT_VERBOSE); | ||
371 | |||
372 | printf ("\n"); | ||
373 | printf (" %s\n", _("A DBI driver (-d option) and a query (-q option) are required.")); | ||
374 | printf (" %s\n", _("This plugin connects to an SQL database using libdbi and executes the")); | ||
375 | printf (" %s\n", _("specified SQL query. The first column of the first row of the result")); | ||
376 | printf (" %s\n", _("will be used as the check result and, if specified, compared with the")); | ||
377 | printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric")); | ||
378 | printf (" %s\n\n", _("(strings representing numbers are fine).")); | ||
379 | |||
380 | printf (" %s\n", _("The number and type of required DBI driver options depends on the actual")); | ||
381 | printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/")); | ||
382 | printf (" %s\n", _("for details.")); | ||
383 | |||
384 | printf (UT_SUPPORT); | ||
385 | } | ||
386 | |||
387 | void | ||
388 | print_usage (void) | ||
389 | { | ||
390 | printf ("%s\n", _("Usage:")); | ||
391 | printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] -q <SQL query>\n", progname); | ||
392 | printf (" [-H <host>] [-c <critical value>] [-w <warning value>]\n"); | ||
393 | } | ||
394 | |||
395 | double | ||
396 | get_field (dbi_conn conn, dbi_result res, unsigned short *field_type) | ||
397 | { | ||
398 | double val = 0.0; | ||
399 | |||
400 | if (*field_type == DBI_TYPE_INTEGER) { | ||
401 | val = (double)dbi_result_get_longlong_idx (res, 1); | ||
402 | } | ||
403 | else if (*field_type == DBI_TYPE_DECIMAL) { | ||
404 | val = dbi_result_get_double_idx (res, 1); | ||
405 | } | ||
406 | else if (*field_type == DBI_TYPE_STRING) { | ||
407 | const char *val_str; | ||
408 | char *endptr = NULL; | ||
409 | |||
410 | val_str = dbi_result_get_string_idx (res, 1); | ||
411 | if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) { | ||
412 | np_dbi_print_error (conn, "CRITICAL - failed to fetch string value"); | ||
413 | *field_type = DBI_TYPE_ERROR; | ||
414 | return 0.0; | ||
415 | } | ||
416 | |||
417 | if (verbose > 2) | ||
418 | printf ("Query returned string '%s'\n", val_str); | ||
419 | |||
420 | val = strtod (val_str, &endptr); | ||
421 | if (endptr == val_str) { | ||
422 | printf ("CRITICAL - result value is not a numeric: %s\n", val_str); | ||
423 | *field_type = DBI_TYPE_ERROR; | ||
424 | return 0.0; | ||
425 | } | ||
426 | else if ((endptr != NULL) && (*endptr != '\0')) { | ||
427 | if (verbose) | ||
428 | printf ("Garbage after value: %s\n", endptr); | ||
429 | } | ||
430 | } | ||
431 | else { | ||
432 | printf ("CRITICAL - cannot parse value of type %s (%i)\n", | ||
433 | (*field_type == DBI_TYPE_BINARY) | ||
434 | ? "BINARY" | ||
435 | : (*field_type == DBI_TYPE_DATETIME) | ||
436 | ? "DATETIME" | ||
437 | : "<unknown>", | ||
438 | *field_type); | ||
439 | *field_type = DBI_TYPE_ERROR; | ||
440 | return 0.0; | ||
441 | } | ||
442 | return val; | ||
443 | } | ||
444 | |||
445 | int | ||
446 | do_query (dbi_conn conn, double *res_val) | ||
447 | { | ||
448 | dbi_result res; | ||
449 | |||
450 | unsigned short field_type; | ||
451 | double val = 0.0; | ||
452 | |||
453 | if (verbose) | ||
454 | printf ("Executing query '%s'\n", np_dbi_query); | ||
455 | |||
456 | res = dbi_conn_query (conn, np_dbi_query); | ||
457 | if (! res) { | ||
458 | np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); | ||
459 | return STATE_CRITICAL; | ||
460 | } | ||
461 | |||
462 | if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) { | ||
463 | np_dbi_print_error (conn, "CRITICAL - failed to fetch rows"); | ||
464 | return STATE_CRITICAL; | ||
465 | } | ||
466 | |||
467 | if (dbi_result_get_numrows (res) < 1) { | ||
468 | printf ("WARNING - no rows returned\n"); | ||
469 | return STATE_WARNING; | ||
470 | } | ||
471 | |||
472 | if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) { | ||
473 | np_dbi_print_error (conn, "CRITICAL - failed to fetch fields"); | ||
474 | return STATE_CRITICAL; | ||
475 | } | ||
476 | |||
477 | if (dbi_result_get_numfields (res) < 1) { | ||
478 | printf ("WARNING - no fields returned\n"); | ||
479 | return STATE_WARNING; | ||
480 | } | ||
481 | |||
482 | if (dbi_result_first_row (res) != 1) { | ||
483 | np_dbi_print_error (conn, "CRITICAL - failed to fetch first row"); | ||
484 | return STATE_CRITICAL; | ||
485 | } | ||
486 | |||
487 | field_type = dbi_result_get_field_type_idx (res, 1); | ||
488 | if (field_type != DBI_TYPE_ERROR) | ||
489 | val = get_field (conn, res, &field_type); | ||
490 | |||
491 | if (field_type == DBI_TYPE_ERROR) { | ||
492 | np_dbi_print_error (conn, "CRITICAL - failed to fetch data"); | ||
493 | return STATE_CRITICAL; | ||
494 | } | ||
495 | |||
496 | *res_val = val; | ||
497 | |||
498 | dbi_result_free (res); | ||
499 | return STATE_OK; | ||
500 | } | ||
501 | |||
502 | void | ||
503 | np_dbi_print_error (dbi_conn conn, char *fmt, ...) | ||
504 | { | ||
505 | const char *errmsg = NULL; | ||
506 | va_list ap; | ||
507 | |||
508 | va_start (ap, fmt); | ||
509 | |||
510 | dbi_conn_error (conn, &errmsg); | ||
511 | vprintf (fmt, ap); | ||
512 | printf (": %s\n", errmsg); | ||
513 | |||
514 | va_end (ap); | ||
515 | } | ||
516 | |||