summaryrefslogtreecommitdiffstats
path: root/plugins/check_dbi.c
diff options
context:
space:
mode:
authorSebastian Harl <sh@teamix.net>2011-04-07 17:24:23 +0200
committerSebastian Harl <sh@teamix.net>2012-06-06 14:10:55 +0200
commitf9a942d2a11e70ee68d5ea2c9aa762bff004bf43 (patch)
tree31ce3a05d1bdc715bf7a0230280d1d16940212ad /plugins/check_dbi.c
parent804e7d878cad5c76474fd2abf2130ff446dbda5d (diff)
downloadmonitoring-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.c516
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
32const char *progname = "check_dbi";
33const char *copyright = "2011";
34const 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
45typedef struct {
46 char *key;
47 char *value;
48} driver_option_t;
49
50char *host = NULL;
51int verbose = 0;
52
53char *warning_range = NULL;
54char *critical_range = NULL;
55thresholds *query_thresholds = NULL;
56
57char *np_dbi_driver = NULL;
58driver_option_t *np_dbi_options = NULL;
59int np_dbi_options_num = 0;
60char *np_dbi_database = NULL;
61char *np_dbi_query = NULL;
62
63int process_arguments (int, char **);
64int validate_arguments (void);
65void print_usage (void);
66void print_help (void);
67
68void np_dbi_print_error (dbi_conn, char *, ...);
69
70int do_query (dbi_conn, double *);
71
72int
73main (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 */
228int
229process_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
330int
331validate_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
342void
343print_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
387void
388print_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
395double
396get_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
445int
446do_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
502void
503np_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