Code

Initial version of the 'check_dbi' plugin.
[nagiosplug.git] / plugins / check_dbi.c
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;
227 /* process command-line arguments */
228 int
229 process_arguments (int argc, char **argv)
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 ();
330 int
331 validate_arguments ()
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;
342 void
343 print_help (void)
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         printf ("\n");
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");
366         printf (UT_WARN_CRIT_RANGE);
368         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
370         printf (UT_VERBOSE);
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)."));
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."));
384         printf (UT_SUPPORT);
387 void
388 print_usage (void)
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");
395 double
396 get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
398         double val = 0.0;
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;
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                 }
417                 if (verbose > 2)
418                         printf ("Query returned string '%s'\n", val_str);
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;
445 int
446 do_query (dbi_conn conn, double *res_val)
448         dbi_result res;
450         unsigned short field_type;
451         double val = 0.0;
453         if (verbose)
454                 printf ("Executing query '%s'\n", np_dbi_query);
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         }
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         }
467         if (dbi_result_get_numrows (res) < 1) {
468                 printf ("WARNING - no rows returned\n");
469                 return STATE_WARNING;
470         }
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         }
477         if (dbi_result_get_numfields (res) < 1) {
478                 printf ("WARNING - no fields returned\n");
479                 return STATE_WARNING;
480         }
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         }
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);
491         if (field_type == DBI_TYPE_ERROR) {
492                 np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
493                 return STATE_CRITICAL;
494         }
496         *res_val = val;
498         dbi_result_free (res);
499         return STATE_OK;
502 void
503 np_dbi_print_error (dbi_conn conn, char *fmt, ...)
505         const char *errmsg = NULL;
506         va_list ap;
508         va_start (ap, fmt);
510         dbi_conn_error (conn, &errmsg);
511         vprintf (fmt, ap);
512         printf (": %s\n", errmsg);
514         va_end (ap);