diff options
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 0000000..b52602c --- /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 | |||