summaryrefslogtreecommitdiffstats
path: root/plugins/check_dbi.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_dbi.c')
-rw-r--r--plugins/check_dbi.c1375
1 files changed, 749 insertions, 626 deletions
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 29c85206..9bc68eb3 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -1,38 +1,44 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dbi plugin 3 * Monitoring check_dbi plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2011 Monitoring Plugins Development Team 6 * Copyright (c) 2011-2024 Monitoring Plugins Development Team
7* Author: Sebastian 'tokkee' Harl <sh@teamix.net> 7 * Original Author: Sebastian 'tokkee' Harl <sh@teamix.net>
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_dbi plugin 11 * This file contains the check_dbi plugin
12* 12 *
13* Runs an arbitrary (SQL) command and checks the result. 13 * Runs an arbitrary (SQL) command and checks the result.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_dbi"; 32const char *progname = "check_dbi";
33const char *copyright = "2011"; 33const char *copyright = "2011-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "../lib/monitoringplug.h"
37#include "thresholds.h"
38#include "perfdata.h"
39#include "output.h"
40#include "states.h"
41#include "check_dbi.d/config.h"
36#include "common.h" 42#include "common.h"
37#include "utils.h" 43#include "utils.h"
38#include "utils_cmd.h" 44#include "utils_cmd.h"
@@ -43,7 +49,7 @@ const char *email = "devel@monitoring-plugins.org";
43 49
44/* required for NAN */ 50/* required for NAN */
45#ifndef _ISOC99_SOURCE 51#ifndef _ISOC99_SOURCE
46#define _ISOC99_SOURCE 52# define _ISOC99_SOURCE
47#endif 53#endif
48 54
49#include <assert.h> 55#include <assert.h>
@@ -53,774 +59,891 @@ const char *email = "devel@monitoring-plugins.org";
53 59
54#include <stdarg.h> 60#include <stdarg.h>
55 61
56typedef enum { 62static int verbose = 0;
57 METRIC_CONN_TIME,
58 METRIC_SERVER_VERSION,
59 METRIC_QUERY_RESULT,
60 METRIC_QUERY_TIME,
61} np_dbi_metric_t;
62
63typedef enum {
64 TYPE_NUMERIC,
65 TYPE_STRING,
66} np_dbi_type_t;
67 63
68typedef struct { 64typedef struct {
69 char *key; 65 int errorcode;
70 char *value; 66 check_dbi_config config;
71} driver_option_t; 67} check_dbi_config_wrapper;
72
73char *host = NULL;
74int verbose = 0;
75
76char *warning_range = NULL;
77char *critical_range = NULL;
78thresholds *dbi_thresholds = NULL;
79
80char *expect = NULL;
81
82regex_t expect_re;
83char *expect_re_str = NULL;
84int expect_re_cflags = 0;
85
86np_dbi_metric_t metric = METRIC_QUERY_RESULT;
87np_dbi_type_t type = TYPE_NUMERIC;
88
89char *np_dbi_driver = NULL;
90driver_option_t *np_dbi_options = NULL;
91int np_dbi_options_num = 0;
92char *np_dbi_database = NULL;
93char *np_dbi_query = NULL;
94
95int process_arguments (int, char **);
96int validate_arguments (void);
97void print_usage (void);
98void print_help (void);
99
100double timediff (struct timeval, struct timeval);
101 68
102void np_dbi_print_error (dbi_conn, char *, ...); 69static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
70void print_usage(void);
71static void print_help(void);
103 72
104int do_query (dbi_conn, const char **, double *, double *); 73static double timediff(struct timeval /*start*/, struct timeval /*end*/);
105 74
106int 75static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...);
107main (int argc, char **argv)
108{
109 int status = STATE_UNKNOWN;
110 76
111 dbi_driver driver; 77typedef struct {
112 dbi_conn conn; 78 char *result_string;
113 79 double result_number;
114 unsigned int server_version; 80 double query_duration;
115 81 int error_code;
116 struct timeval start_timeval, end_timeval; 82 const char *error_string;
117 double conn_time = 0.0; 83 mp_state_enum query_processing_status;
118 double query_time = 0.0; 84} do_query_result;
85static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
86 char *query);
87
88int main(int argc, char **argv) {
89 setlocale(LC_ALL, "");
90 bindtextdomain(PACKAGE, LOCALEDIR);
91 textdomain(PACKAGE);
119 92
120 const char *query_val_str = NULL; 93 /* Parse extra opts if any */
121 double query_val = 0.0; 94 argv = np_extra_opts(&argc, argv, progname);
122 95
123 int i; 96 check_dbi_config_wrapper tmp = process_arguments(argc, argv);
124 97
125 setlocale (LC_ALL, ""); 98 if (tmp.errorcode == ERROR) {
126 bindtextdomain (PACKAGE, LOCALEDIR); 99 usage4(_("Could not parse arguments"));
127 textdomain (PACKAGE); 100 }
128 101
129 /* Parse extra opts if any */ 102 const check_dbi_config config = tmp.config;
130 argv = np_extra_opts (&argc, argv, progname);
131 103
132 if (process_arguments (argc, argv) == ERROR) 104 if (config.output_format_is_set) {
133 usage4 (_("Could not parse arguments")); 105 mp_set_format(config.output_format);
106 }
134 107
135 /* Set signal handling and alarm */ 108 /* Set signal handling and alarm */
136 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 109 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
137 usage4 (_("Cannot catch SIGALRM")); 110 usage4(_("Cannot catch SIGALRM"));
138 } 111 }
139 alarm (timeout_interval); 112 alarm(timeout_interval);
140
141 if (verbose > 2)
142 printf ("Initializing DBI\n");
143 113
144 dbi_inst *instance_p = { 0 }; 114 if (verbose > 2) {
115 printf("Initializing DBI\n");
116 }
145 117
146 if (dbi_initialize_r(NULL, instance_p) < 0) { 118 dbi_inst instance_p = NULL;
147 printf ("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); 119 if (dbi_initialize_r(NULL, &instance_p) < 0) {
148 return STATE_UNKNOWN; 120 printf("failed to initialize DBI; possibly you don't have any drivers installed.\n");
121 exit(STATE_UNKNOWN);
149 } 122 }
150 123
124 // Try to prevent libdbi from printing stuff on stderr
125 // Who thought that would be a good idea anyway?
126 dbi_set_verbosity_r(0, instance_p);
127
151 if (instance_p == NULL) { 128 if (instance_p == NULL) {
152 printf ("UNKNOWN - failed to initialize DBI.\n"); 129 printf("failed to initialize DBI.\n");
153 return STATE_UNKNOWN; 130 exit(STATE_UNKNOWN);
154 } 131 }
155 132
156 if (verbose) 133 if (verbose) {
157 printf ("Opening DBI driver '%s'\n", np_dbi_driver); 134 printf("Opening DBI driver '%s'\n", config.dbi_driver);
135 }
158 136
159 driver = dbi_driver_open_r(np_dbi_driver, instance_p); 137 dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p);
160 if (! driver) { 138 if (!driver) {
161 printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", 139 printf("failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver);
162 np_dbi_driver);
163 140
164 printf ("Known drivers:\n"); 141 printf("Known drivers:\n");
165 for (driver = dbi_driver_list_r(NULL, instance_p); driver; driver = dbi_driver_list_r(driver, instance_p)) { 142 for (driver = dbi_driver_list_r(NULL, instance_p); driver;
166 printf (" - %s\n", dbi_driver_get_name (driver)); 143 driver = dbi_driver_list_r(driver, instance_p)) {
144 printf(" - %s\n", dbi_driver_get_name(driver));
167 } 145 }
168 return STATE_UNKNOWN; 146 exit(STATE_UNKNOWN);
169 } 147 }
170 148
171 /* make a connection to the database */ 149 /* make a connection to the database */
172 gettimeofday (&start_timeval, NULL); 150 struct timeval start_timeval;
173 151 gettimeofday(&start_timeval, NULL);
174 conn = dbi_conn_open (driver); 152
175 if (! conn) { 153 dbi_conn conn = dbi_conn_open(driver);
176 printf ("UNKNOWN - failed top open connection object.\n"); 154 if (!conn) {
177 dbi_conn_close (conn); 155 printf("UNKNOWN - failed top open connection object.\n");
178 return STATE_UNKNOWN; 156 dbi_conn_close(conn);
157 exit(STATE_UNKNOWN);
179 } 158 }
180 159
181 for (i = 0; i < np_dbi_options_num; ++i) { 160 for (size_t i = 0; i < config.dbi_options_num; ++i) {
182 const char *opt; 161 const char *opt;
183 162
184 if (verbose > 1) 163 if (verbose > 1) {
185 printf ("Setting DBI driver option '%s' to '%s'\n", 164 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key,
186 np_dbi_options[i].key, np_dbi_options[i].value); 165 config.dbi_options[i].value);
166 }
187 167
188 if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value)) 168 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) {
189 continue; 169 continue;
190 /* else: status != 0 */ 170 }
191 171
192 np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'", 172 // Failing to set option
193 np_dbi_options[i].key, np_dbi_options[i].value); 173 np_dbi_print_error(conn, "failed to set option '%s' to '%s'", config.dbi_options[i].key,
194 printf ("Known driver options:\n"); 174 config.dbi_options[i].value);
175 printf("Known driver options:\n");
195 176
196 for (opt = dbi_conn_get_option_list (conn, NULL); opt; 177 for (opt = dbi_conn_get_option_list(conn, NULL); opt;
197 opt = dbi_conn_get_option_list (conn, opt)) { 178 opt = dbi_conn_get_option_list(conn, opt)) {
198 printf (" - %s\n", opt); 179 printf(" - %s\n", opt);
199 } 180 }
200 dbi_conn_close (conn); 181 dbi_conn_close(conn);
201 return STATE_UNKNOWN; 182 exit(STATE_UNKNOWN);
202 } 183 }
203 184
204 if (host) { 185 if (config.host) {
205 if (verbose > 1) 186 if (verbose > 1) {
206 printf ("Setting DBI driver option 'host' to '%s'\n", host); 187 printf("Setting DBI driver option 'host' to '%s'\n", config.host);
207 dbi_conn_set_option (conn, "host", host); 188 }
189 dbi_conn_set_option(conn, "host", config.host);
208 } 190 }
209 191
210 if (verbose) { 192 if (verbose) {
211 const char *dbname, *host; 193 const char *dbname;
194 const char *host;
212 195
213 dbname = dbi_conn_get_option (conn, "dbname"); 196 dbname = dbi_conn_get_option(conn, "dbname");
214 host = dbi_conn_get_option (conn, "host"); 197 host = dbi_conn_get_option(conn, "host");
215 198
216 if (! dbname) 199 if (!dbname) {
217 dbname = "<unspecified>"; 200 dbname = "<unspecified>";
218 if (! host) 201 }
202 if (!host) {
219 host = "<unspecified>"; 203 host = "<unspecified>";
204 }
205
206 printf("Connecting to database '%s' at host '%s'\n", dbname, host);
207 }
220 208
221 printf ("Connecting to database '%s' at host '%s'\n", 209 if (dbi_conn_connect(conn) < 0) {
222 dbname, host); 210 np_dbi_print_error(conn, "failed to connect to database");
211 exit(STATE_UNKNOWN);
223 } 212 }
224 213
225 if (dbi_conn_connect (conn) < 0) { 214 struct timeval end_timeval;
226 np_dbi_print_error (conn, "UNKNOWN - failed to connect to database"); 215 gettimeofday(&end_timeval, NULL);
227 return STATE_UNKNOWN; 216 double conn_time = timediff(start_timeval, end_timeval);
217 if (verbose) {
218 printf("Time elapsed: %f\n", conn_time);
228 } 219 }
229 220
230 gettimeofday (&end_timeval, NULL); 221 mp_check overall = mp_check_init();
231 conn_time = timediff (start_timeval, end_timeval);
232 222
233 server_version = dbi_conn_get_engine_version (conn); 223 mp_subcheck sc_connection_time = mp_subcheck_init();
234 if (verbose) 224 sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_OK);
235 printf ("Connected to server version %u\n", server_version); 225 xasprintf(&sc_connection_time.output, "Connection time: %f", conn_time);
236 226
237 if (metric == METRIC_SERVER_VERSION) 227 mp_perfdata pd_conn_duration = perfdata_init();
238 status = get_status (server_version, dbi_thresholds); 228 pd_conn_duration.label = "conntime";
229 pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time);
239 230
240 if (verbose) 231 if (config.metric == METRIC_CONN_TIME) {
241 printf ("Time elapsed: %f\n", conn_time); 232 pd_conn_duration = mp_pd_set_thresholds(pd_conn_duration, config.thresholds);
233 mp_state_enum status = mp_get_pd_status(pd_conn_duration);
234 sc_connection_time = mp_set_subcheck_state(sc_connection_time, status);
235 if (status != STATE_OK) {
236 xasprintf(&sc_connection_time.output, "%s violates thresholds",
237 sc_connection_time.output);
238 }
239 }
242 240
243 if (metric == METRIC_CONN_TIME) 241 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_conn_duration);
244 status = get_status (conn_time, dbi_thresholds); 242 mp_add_subcheck_to_check(&overall, sc_connection_time);
243
244 unsigned int server_version = dbi_conn_get_engine_version(conn);
245 if (verbose) {
246 printf("Connected to server version %u\n", server_version);
247 }
248
249 mp_subcheck sc_server_version = mp_subcheck_init();
250 sc_server_version = mp_set_subcheck_default_state(sc_server_version, STATE_OK);
251 xasprintf(&sc_server_version.output, "Connected to server version %u", server_version);
252
253 if (config.metric == METRIC_SERVER_VERSION) {
254 mp_perfdata pd_server_version = perfdata_init();
255 pd_server_version = mp_set_pd_value(pd_server_version, server_version);
256 pd_server_version = mp_pd_set_thresholds(pd_server_version, config.thresholds);
257 mp_state_enum status = mp_get_pd_status(pd_server_version);
258 mp_add_perfdata_to_subcheck(&sc_server_version, pd_server_version);
259
260 sc_server_version = mp_set_subcheck_state(sc_server_version, status);
261
262 if (status != STATE_OK) {
263 xasprintf(&sc_server_version.output, "%s violates thresholds",
264 sc_server_version.output);
265 }
266 };
267 mp_add_subcheck_to_check(&overall, sc_server_version);
245 268
246 /* select a database */ 269 /* select a database */
247 if (np_dbi_database) { 270 if (config.database) {
248 if (verbose > 1) 271 if (verbose > 1) {
249 printf ("Selecting database '%s'\n", np_dbi_database); 272 printf("Selecting database '%s'\n", config.database);
250 273 }
251 if (dbi_conn_select_db (conn, np_dbi_database)) { 274
252 np_dbi_print_error (conn, "UNKNOWN - failed to select database '%s'", 275 mp_subcheck sc_select_db = mp_subcheck_init();
253 np_dbi_database); 276 sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK);
254 return STATE_UNKNOWN; 277
278 if (dbi_conn_select_db(conn, config.database)) {
279 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.database);
280 exit(STATE_UNKNOWN);
281 } else {
282 mp_add_subcheck_to_check(&overall, sc_select_db);
255 } 283 }
256 } 284 }
257 285
258 if (np_dbi_query) { 286 // Do a query (if configured)
287 if (config.query) {
288 mp_subcheck sc_query = mp_subcheck_init();
289 sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN);
290
259 /* execute query */ 291 /* execute query */
260 status = do_query (conn, &query_val_str, &query_val, &query_time); 292 do_query_result query_res = do_query(conn, config.metric, config.type, config.query);
261 if (status != STATE_OK) 293
262 /* do_query prints an error message in this case */ 294 if (query_res.error_code != 0) {
263 return status; 295 xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string);
264 296 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
265 if (metric == METRIC_QUERY_RESULT) { 297 } else if (query_res.query_processing_status != STATE_OK) {
266 if (expect) { 298 if (query_res.error_string) {
267 if ((! query_val_str) || strcmp (query_val_str, expect)) 299 xasprintf(&sc_query.output, "Failed to process query: %s", query_res.error_string);
268 status = STATE_CRITICAL; 300 } else {
269 else 301 xasprintf(&sc_query.output, "Failed to process query");
270 status = STATE_OK;
271 } 302 }
272 else if (expect_re_str) { 303 sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status);
273 int err; 304 } else {
274 305 // query succeeded in general
275 err = regexec (&expect_re, query_val_str, 0, NULL, /* flags = */ 0); 306 xasprintf(&sc_query.output, "Query '%s' succeeded", config.query);
276 if (! err) 307
277 status = STATE_OK; 308 // that's a OK by default now
278 else if (err == REG_NOMATCH) 309 sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK);
279 status = STATE_CRITICAL; 310
280 else { 311 // query duration first
281 char errmsg[1024]; 312 mp_perfdata pd_query_duration = perfdata_init();
282 regerror (err, &expect_re, errmsg, sizeof (errmsg)); 313 pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration);
283 printf ("ERROR - failed to execute regular expression: %s\n", 314 pd_query_duration.label = "querytime";
284 errmsg); 315 if (config.metric == METRIC_QUERY_TIME) {
285 status = STATE_CRITICAL; 316 pd_query_duration = mp_pd_set_thresholds(pd_query_duration, config.thresholds);
317 }
318
319 mp_add_perfdata_to_subcheck(&sc_query, pd_query_duration);
320
321 if (config.metric == METRIC_QUERY_RESULT) {
322 if (config.expect) {
323 if ((!query_res.result_string) ||
324 strcmp(query_res.result_string, config.expect)) {
325 xasprintf(&sc_query.output, "Found string '%s' in query result",
326 config.expect);
327 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
328 } else {
329 xasprintf(&sc_query.output, "Did not find string '%s' in query result",
330 config.expect);
331 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
332 }
333 } else if (config.expect_re_str) {
334 int comp_err;
335 regex_t expect_re = {};
336 comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags);
337 if (comp_err != 0) {
338 // TODO error, failed to compile regex
339 // TODO move this to config sanitatisation
340 printf("Failed to compile regex from string '%s'", config.expect_re_str);
341 exit(STATE_UNKNOWN);
342 }
343
344 int err =
345 regexec(&expect_re, query_res.result_string, 0, NULL, /* flags = */ 0);
346 if (!err) {
347 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
348 xasprintf(&sc_query.output, "Found regular expression '%s' in query result",
349 config.expect_re_str);
350 } else if (err == REG_NOMATCH) {
351 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
352 xasprintf(&sc_query.output,
353 "Did not find regular expression '%s' in query result",
354 config.expect_re_str);
355 } else {
356 char errmsg[1024];
357 regerror(err, &expect_re, errmsg, sizeof(errmsg));
358 xasprintf(&sc_query.output,
359 "ERROR - failed to execute regular expression: %s\n", errmsg);
360 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
361 }
362 } else {
363 // no string matching
364 if (isnan(query_res.result_number)) {
365 // The query result is not a number, but no string checking was configured
366 // so we expected a number
367 // this is a CRITICAL
368 xasprintf(&sc_query.output, "Query '%s' result is not numeric",
369 config.query);
370 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
371
372 } else {
373
374 mp_perfdata pd_query_val = perfdata_init();
375 pd_query_val = mp_set_pd_value(pd_query_val, query_res.result_number);
376 pd_query_val.label = "query";
377 pd_query_val = mp_pd_set_thresholds(pd_query_val, config.thresholds);
378
379 mp_add_perfdata_to_subcheck(&sc_query, pd_query_val);
380 mp_state_enum query_numerical_result = mp_get_pd_status(pd_query_val);
381
382 sc_query = mp_set_subcheck_state(sc_query, query_numerical_result);
383 // TODO set pd thresholds
384 // if (config.dbi_thresholds->warning) {
385 // pd_query_val.warn= config.dbi_thresholds->warning
386 // } else {
387 // }
388
389 if (query_numerical_result == STATE_OK) {
390 xasprintf(&sc_query.output,
391 "Query result '%f' is within given thresholds",
392 query_res.result_number);
393 } else {
394 xasprintf(&sc_query.output,
395 "Query result '%f' violates the given thresholds",
396 query_res.result_number);
397 }
398 }
286 } 399 }
400 } else if (config.metric == METRIC_QUERY_TIME) {
401 mp_state_enum query_time_status = mp_get_pd_status(pd_query_duration);
402 mp_set_subcheck_state(sc_query, query_time_status);
403
404 if (query_time_status == STATE_OK) {
405 xasprintf(&sc_query.output, "Query duration '%f' is within given thresholds",
406 query_res.query_duration);
407 } else {
408 xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds",
409 query_res.query_duration);
410 }
411 } else {
412 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error
413 * which should have been reported and handled (abort) before
414 * ... unless we expected a string to be returned */
415 assert((!isnan(query_res.result_number)) || (config.type == TYPE_STRING));
287 } 416 }
288 else
289 status = get_status (query_val, dbi_thresholds);
290 } 417 }
291 else if (metric == METRIC_QUERY_TIME) 418
292 status = get_status (query_time, dbi_thresholds); 419 mp_add_subcheck_to_check(&overall, sc_query);
293 } 420 }
294 421
295 if (verbose) 422 if (verbose) {
296 printf("Closing connection\n"); 423 printf("Closing connection\n");
297 dbi_conn_close (conn); 424 }
298 425 dbi_conn_close(conn);
299 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error 426
300 * which should have been reported and handled (abort) before 427 mp_exit(overall);
301 * ... unless we expected a string to be returned */
302 assert ((metric != METRIC_QUERY_RESULT) || (! isnan (query_val))
303 || (type == TYPE_STRING));
304
305 assert ((type != TYPE_STRING) || (expect || expect_re_str));
306
307 printf ("%s - connection time: %fs", state_text (status), conn_time);
308 if (np_dbi_query) {
309 if (type == TYPE_STRING) {
310 assert (expect || expect_re_str);
311 printf (", '%s' returned '%s' in %fs", np_dbi_query,
312 query_val_str ? query_val_str : "<nothing>", query_time);
313 if (status != STATE_OK) {
314 if (expect)
315 printf (" (expected '%s')", expect);
316 else if (expect_re_str)
317 printf (" (expected regex /%s/%s)", expect_re_str,
318 ((expect_re_cflags & REG_ICASE) ? "i" : ""));
319 }
320 }
321 else if (isnan (query_val))
322 printf (", '%s' query execution time: %fs", np_dbi_query, query_time);
323 else
324 printf (", '%s' returned %f in %fs", np_dbi_query, query_val, query_time);
325 }
326
327 printf (" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
328 ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "",
329 ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "",
330 server_version,
331 ((metric == METRIC_SERVER_VERSION) && warning_range) ? warning_range : "",
332 ((metric == METRIC_SERVER_VERSION) && critical_range) ? critical_range : "");
333 if (np_dbi_query) {
334 if (! isnan (query_val)) /* this is also true when -e is used */
335 printf (" query=%f;%s;%s;;", query_val,
336 ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "",
337 ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : "");
338 printf (" querytime=%fs;%s;%s;0;", query_time,
339 ((metric == METRIC_QUERY_TIME) && warning_range) ? warning_range : "",
340 ((metric == METRIC_QUERY_TIME) && critical_range) ? critical_range : "");
341 }
342 printf ("\n");
343 return status;
344} 428}
345 429
346/* process command-line arguments */ 430/* process command-line arguments */
347int 431check_dbi_config_wrapper process_arguments(int argc, char **argv) {
348process_arguments (int argc, char **argv) 432 enum {
349{ 433 output_format_index = CHAR_MAX + 1,
350 int c; 434 };
351 435
352 int option = 0; 436 int option = 0;
353 static struct option longopts[] = { 437 static struct option longopts[] = {STD_LONG_OPTS,
354 STD_LONG_OPTS, 438 {"expect", required_argument, 0, 'e'},
355 439 {"regex", required_argument, 0, 'r'},
356 {"expect", required_argument, 0, 'e'}, 440 {"regexi", required_argument, 0, 'R'},
357 {"regex", required_argument, 0, 'r'}, 441 {"metric", required_argument, 0, 'm'},
358 {"regexi", required_argument, 0, 'R'}, 442 {"driver", required_argument, 0, 'd'},
359 {"metric", required_argument, 0, 'm'}, 443 {"option", required_argument, 0, 'o'},
360 {"driver", required_argument, 0, 'd'}, 444 {"query", required_argument, 0, 'q'},
361 {"option", required_argument, 0, 'o'}, 445 {"database", required_argument, 0, 'D'},
362 {"query", required_argument, 0, 'q'}, 446 {"output-format", required_argument, 0, output_format_index},
363 {"database", required_argument, 0, 'D'}, 447 {0, 0, 0, 0}};
364 {0, 0, 0, 0} 448
449 check_dbi_config_wrapper result = {
450 .config = check_dbi_config_init(),
451 .errorcode = OK,
365 }; 452 };
453 int option_char;
454 while (true) {
455 option_char = getopt_long(argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:", longopts, &option);
366 456
367 while (1) { 457 if (option_char == EOF) {
368 c = getopt_long (argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:",
369 longopts, &option);
370
371 if (c == EOF)
372 break; 458 break;
459 }
373 460
374 switch (c) { 461 switch (option_char) {
375 case '?': /* usage */ 462 case '?': /* usage */
376 usage5 (); 463 usage5();
377 case 'h': /* help */ 464 case 'h': /* help */
378 print_help (); 465 print_help();
379 exit (STATE_UNKNOWN); 466 exit(STATE_UNKNOWN);
380 case 'V': /* version */ 467 case 'V': /* version */
381 print_revision (progname, NP_VERSION); 468 print_revision(progname, NP_VERSION);
382 exit (STATE_UNKNOWN); 469 exit(STATE_UNKNOWN);
383 470
384 case 'c': /* critical range */ 471 case 'c': /* critical range */ {
385 critical_range = optarg; 472 mp_range_parsed tmp = mp_parse_range_string(optarg);
386 type = TYPE_NUMERIC; 473 if (tmp.error != MP_PARSING_SUCCES) {
387 break; 474 die(STATE_UNKNOWN, "failed to parse critical threshold");
388 case 'w': /* warning range */ 475 }
389 warning_range = optarg; 476 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
390 type = TYPE_NUMERIC; 477 result.config.type = TYPE_NUMERIC;
391 break; 478 } break;
479 case 'w': /* warning range */ {
480 mp_range_parsed tmp = mp_parse_range_string(optarg);
481 if (tmp.error != MP_PARSING_SUCCES) {
482 die(STATE_UNKNOWN, "failed to parse warning threshold");
483 }
484 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range);
485 result.config.type = TYPE_NUMERIC;
486 } break;
392 case 'e': 487 case 'e':
393 expect = optarg; 488 result.config.expect = optarg;
394 type = TYPE_STRING; 489 result.config.type = TYPE_STRING;
395 break; 490 break;
396 case 'R': 491 case 'R':
397 expect_re_cflags = REG_ICASE; 492 result.config.expect_re_cflags = REG_ICASE;
398 /* fall through */ 493 /* fall through */
399 case 'r': 494 case 'r': {
400 { 495 int err;
401 int err; 496
402 497 result.config.expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
403 expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 498 result.config.expect_re_str = optarg;
404 expect_re_str = optarg; 499 result.config.type = TYPE_STRING;
405 type = TYPE_STRING; 500
406 501 regex_t expect_re = {};
407 err = regcomp (&expect_re, expect_re_str, expect_re_cflags); 502 err = regcomp(&expect_re, result.config.expect_re_str, result.config.expect_re_cflags);
408 if (err) { 503 if (err) {
409 char errmsg[1024]; 504 char errmsg[1024];
410 regerror (err, &expect_re, errmsg, sizeof (errmsg)); 505 regerror(err, &expect_re, errmsg, sizeof(errmsg));
411 printf ("ERROR - failed to compile regular expression: %s\n", 506 printf("ERROR - failed to compile regular expression: %s\n", errmsg);
412 errmsg); 507
413 return ERROR; 508 result.errorcode = ERROR;
414 } 509 return result;
415 break;
416 } 510 }
417 511 break;
512 }
418 case 'm': 513 case 'm':
419 if (! strcasecmp (optarg, "CONN_TIME")) 514 if (!strcasecmp(optarg, "CONN_TIME")) {
420 metric = METRIC_CONN_TIME; 515 result.config.metric = METRIC_CONN_TIME;
421 else if (! strcasecmp (optarg, "SERVER_VERSION")) 516 } else if (!strcasecmp(optarg, "SERVER_VERSION")) {
422 metric = METRIC_SERVER_VERSION; 517 result.config.metric = METRIC_SERVER_VERSION;
423 else if (! strcasecmp (optarg, "QUERY_RESULT")) 518 } else if (!strcasecmp(optarg, "QUERY_RESULT")) {
424 metric = METRIC_QUERY_RESULT; 519 result.config.metric = METRIC_QUERY_RESULT;
425 else if (! strcasecmp (optarg, "QUERY_TIME")) 520 } else if (!strcasecmp(optarg, "QUERY_TIME")) {
426 metric = METRIC_QUERY_TIME; 521 result.config.metric = METRIC_QUERY_TIME;
427 else 522 } else {
428 usage2 (_("Invalid metric"), optarg); 523 usage2(_("Invalid metric"), optarg);
524 }
429 break; 525 break;
430 case 't': /* timeout */ 526 case 't': /* timeout */
431 if (!is_intnonneg (optarg)) 527 if (!is_intnonneg(optarg)) {
432 usage2 (_("Timeout interval must be a positive integer"), optarg); 528 usage2(_("Timeout interval must be a positive integer"), optarg);
433 else 529 } else {
434 timeout_interval = atoi (optarg); 530 timeout_interval = atoi(optarg);
435 531 }
436 break; 532 break;
437 case 'H': /* host */ 533 case 'H': /* host */
438 if (!is_host (optarg)) 534 if (!is_host(optarg)) {
439 usage2 (_("Invalid hostname/address"), optarg); 535 usage2(_("Invalid hostname/address"), optarg);
440 else 536 } else {
441 host = optarg; 537 result.config.host = optarg;
538 }
442 break; 539 break;
443 case 'v': 540 case 'v':
444 verbose++; 541 verbose++;
445 break; 542 break;
446
447 case 'd': 543 case 'd':
448 np_dbi_driver = optarg; 544 result.config.dbi_driver = optarg;
449 break; 545 break;
450 case 'o': 546 case 'o': {
451 { 547 driver_option_t *new = NULL;
452 driver_option_t *new;
453 548
454 char *k, *v; 549 char *key = optarg;
550 char *value = strchr(key, '=');
455 551
456 k = optarg; 552 if (!value) {
457 v = strchr (k, (int)'='); 553 usage2(_("Option must be '<key>=<value>'"), optarg);
458 554 }
459 if (! v)
460 usage2 (_("Option must be '<key>=<value>'"), optarg);
461 555
462 *v = '\0'; 556 *value = '\0';
463 ++v; 557 ++value;
464 558
465 new = realloc (np_dbi_options, 559 new = realloc(result.config.dbi_options,
466 (np_dbi_options_num + 1) * sizeof (*new)); 560 (result.config.dbi_options_num + 1) * sizeof(*new));
467 if (! new) { 561 if (!new) {
468 printf ("UNKNOWN - failed to reallocate memory\n"); 562 printf("UNKNOWN - failed to reallocate memory\n");
469 exit (STATE_UNKNOWN); 563 exit(STATE_UNKNOWN);
470 } 564 }
471 565
472 np_dbi_options = new; 566 result.config.dbi_options = new;
473 new = np_dbi_options + np_dbi_options_num; 567 new = result.config.dbi_options + result.config.dbi_options_num;
474 ++np_dbi_options_num; 568 result.config.dbi_options_num++;
475 569
476 new->key = k; 570 new->key = key;
477 new->value = v; 571 new->value = value;
478 } 572 } break;
479 break;
480 case 'q': 573 case 'q':
481 np_dbi_query = optarg; 574 result.config.query = optarg;
482 break; 575 break;
483 case 'D': 576 case 'D':
484 np_dbi_database = optarg; 577 result.config.database = optarg;
485 break; 578 break;
579 case output_format_index: {
580 parsed_output_format parser = mp_parse_output_format(optarg);
581 if (!parser.parsing_success) {
582 // TODO List all available formats here, maybe add anothoer usage function
583 printf("Invalid output format: %s\n", optarg);
584 exit(STATE_UNKNOWN);
585 }
586
587 result.config.output_format_is_set = true;
588 result.config.output_format = parser.output_format;
589 break;
590 }
486 } 591 }
487 } 592 }
488 593
489 set_thresholds (&dbi_thresholds, warning_range, critical_range); 594 if (!result.config.dbi_driver) {
490 595 usage("Must specify a DBI driver");
491 return validate_arguments (); 596 }
492}
493 597
494int 598 if (((result.config.metric == METRIC_QUERY_RESULT) ||
495validate_arguments () 599 (result.config.metric == METRIC_QUERY_TIME)) &&
496{ 600 (!result.config.query)) {
497 if (! np_dbi_driver) 601 usage("Must specify a query to execute (metric == QUERY_RESULT)");
498 usage ("Must specify a DBI driver"); 602 }
499 603
500 if (((metric == METRIC_QUERY_RESULT) || (metric == METRIC_QUERY_TIME)) 604 if ((result.config.metric != METRIC_CONN_TIME) &&
501 && (! np_dbi_query)) 605 (result.config.metric != METRIC_SERVER_VERSION) &&
502 usage ("Must specify a query to execute (metric == QUERY_RESULT)"); 606 (result.config.metric != METRIC_QUERY_RESULT) &&
607 (result.config.metric != METRIC_QUERY_TIME)) {
608 usage("Invalid metric specified");
609 }
503 610
504 if ((metric != METRIC_CONN_TIME) 611 if (result.config.expect &&
505 && (metric != METRIC_SERVER_VERSION) 612 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
506 && (metric != METRIC_QUERY_RESULT) 613 result.config.expect_re_str)) {
507 && (metric != METRIC_QUERY_TIME)) 614 usage("Do not mix -e and -w/-c/-r/-R");
508 usage ("Invalid metric specified"); 615 }
509 616
510 if (expect && (warning_range || critical_range || expect_re_str)) 617 if (result.config.expect_re_str &&
511 usage ("Do not mix -e and -w/-c/-r/-R"); 618 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
619 result.config.expect)) {
620 usage("Do not mix -r/-R and -w/-c/-e");
621 }
512 622
513 if (expect_re_str && (warning_range || critical_range || expect)) 623 if (result.config.expect && (result.config.metric != METRIC_QUERY_RESULT)) {
514 usage ("Do not mix -r/-R and -w/-c/-e"); 624 usage("Option -e requires metric QUERY_RESULT");
625 }
515 626
516 if (expect && (metric != METRIC_QUERY_RESULT)) 627 if (result.config.expect_re_str && (result.config.metric != METRIC_QUERY_RESULT)) {
517 usage ("Option -e requires metric QUERY_RESULT"); 628 usage("Options -r/-R require metric QUERY_RESULT");
629 }
518 630
519 if (expect_re_str && (metric != METRIC_QUERY_RESULT)) 631 if (result.config.type == TYPE_STRING) {
520 usage ("Options -r/-R require metric QUERY_RESULT"); 632 assert(result.config.expect || result.config.expect_re_str);
633 }
521 634
522 return OK; 635 return result;
523} 636}
524 637
525void 638void print_help(void) {
526print_help (void) 639 print_revision(progname, NP_VERSION);
527{
528 print_revision (progname, NP_VERSION);
529 640
530 printf (COPYRIGHT, copyright, email); 641 printf(COPYRIGHT, copyright, email);
531 642
532 printf (_("This program connects to an (SQL) database using DBI and checks the\n" 643 printf(_("This program connects to an (SQL) database using DBI and checks the\n"
533 "specified metric against threshold levels. The default metric is\n" 644 "specified metric against threshold levels. The default metric is\n"
534 "the result of the specified query.\n")); 645 "the result of the specified query.\n"));
535 646
536 printf ("\n\n"); 647 printf("\n\n");
537 648
538 print_usage (); 649 print_usage();
539 650
540 printf (UT_HELP_VRSN); 651 printf(UT_HELP_VRSN);
541/* include this conditionally to avoid 'zero-length printf format string' 652/* include this conditionally to avoid 'zero-length printf format string'
542 * compiler warnings */ 653 * compiler warnings */
543#ifdef NP_EXTRA_OPTS 654#ifdef NP_EXTRA_OPTS
544 printf (UT_EXTRA_OPTS); 655 printf(UT_EXTRA_OPTS);
545#endif 656#endif
546 printf ("\n"); 657 printf("\n");
547 658
548 printf (" %s\n", "-d, --driver=STRING"); 659 printf(" %s\n", "-d, --driver=STRING");
549 printf (" %s\n", _("DBI driver to use")); 660 printf(" %s\n", _("DBI driver to use"));
550 printf (" %s\n", "-o, --option=STRING"); 661 printf(" %s\n", "-o, --option=STRING");
551 printf (" %s\n", _("DBI driver options")); 662 printf(" %s\n", _("DBI driver options"));
552 printf (" %s\n", "-q, --query=STRING"); 663 printf(" %s\n", "-q, --query=STRING");
553 printf (" %s\n", _("query to execute")); 664 printf(" %s\n", _("query to execute"));
554 printf ("\n"); 665 printf(" %s\n", "-H STRING");
555 666 printf(" %s\n", _("target database host"));
556 printf (UT_WARN_CRIT_RANGE); 667 printf("\n");
557 printf (" %s\n", "-e, --expect=STRING"); 668
558 printf (" %s\n", _("String to expect as query result")); 669 printf(UT_WARN_CRIT_RANGE);
559 printf (" %s\n", _("Do not mix with -w, -c, -r, or -R!")); 670 printf(" %s\n", "-e, --expect=STRING");
560 printf (" %s\n", "-r, --regex=REGEX"); 671 printf(" %s\n", _("String to expect as query result"));
561 printf (" %s\n", _("Extended POSIX regular expression to check query result against")); 672 printf(" %s\n", _("Do not mix with -w, -c, -r, or -R!"));
562 printf (" %s\n", _("Do not mix with -w, -c, -e, or -R!")); 673 printf(" %s\n", "-r, --regex=REGEX");
563 printf (" %s\n", "-R, --regexi=REGEX"); 674 printf(" %s\n", _("Extended POSIX regular expression to check query result against"));
564 printf (" %s\n", _("Case-insensitive extended POSIX regex to check query result against")); 675 printf(" %s\n", _("Do not mix with -w, -c, -e, or -R!"));
565 printf (" %s\n", _("Do not mix with -w, -c, -e, or -r!")); 676 printf(" %s\n", "-R, --regexi=REGEX");
566 printf (" %s\n", "-m, --metric=METRIC"); 677 printf(" %s\n", _("Case-insensitive extended POSIX regex to check query result against"));
567 printf (" %s\n", _("Metric to check thresholds against. Available metrics:")); 678 printf(" %s\n", _("Do not mix with -w, -c, -e, or -r!"));
568 printf (" CONN_TIME - %s\n", _("time used for setting up the database connection")); 679 printf(" %s\n", "-m, --metric=METRIC");
569 printf (" QUERY_RESULT - %s\n", _("result (first column of first row) of the query")); 680 printf(" %s\n", _("Metric to check thresholds against. Available metrics:"));
570 printf (" QUERY_TIME - %s\n", _("time used to execute the query")); 681 printf(" CONN_TIME - %s\n", _("time used for setting up the database connection"));
571 printf (" %s\n", _("(ignore the query result)")); 682 printf(" QUERY_RESULT - %s\n", _("result (first column of first row) of the query"));
572 printf ("\n"); 683 printf(" QUERY_TIME - %s\n", _("time used to execute the query"));
573 684 printf(" %s\n", _("(ignore the query result)"));
574 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 685 printf("\n");
575 686
576 printf (UT_VERBOSE); 687 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
577 688
578 printf ("\n"); 689 printf(UT_VERBOSE);
579 printf (" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates")); 690
580 printf (" %s\n\n", _("on a query, one has to be specified (-q option).")); 691 printf("\n");
581 692 printf(" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates"));
582 printf (" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,")); 693 printf(" %s\n\n", _("on a query, one has to be specified (-q option)."));
583 printf (" %s\n", _("executes the specified query. The first column of the first row of the")); 694
584 printf (" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the")); 695 printf(" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,"));
585 printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric")); 696 printf(" %s\n", _("executes the specified query. The first column of the first row of the"));
586 printf (" %s\n\n", _("(strings representing numbers are fine).")); 697 printf(" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the"));
587 698 printf(" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
588 printf (" %s\n", _("The number and type of required DBI driver options depends on the actual")); 699 printf(" %s\n\n", _("(strings representing numbers are fine)."));
589 printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/")); 700
590 printf (" %s\n\n", _("for details.")); 701 printf(" %s\n", _("The number and type of required DBI driver options depends on the actual"));
591 702 printf(" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
592 printf (" %s\n", _("Examples:")); 703 printf(" %s\n\n", _("for details."));
593 printf (" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n"); 704
594 printf (" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n"); 705 printf(" %s\n", _("Examples:"));
595 printf (" Warning if more than five connections; critical if more than ten.\n\n"); 706 printf(" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n");
596 707 printf(" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n");
597 printf (" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n"); 708 printf(" Warning if more than five connections; critical if more than ten.\n\n");
598 printf (" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n"); 709
599 printf (" Warning if less than 5 or more than 20 users are logged in; critical\n"); 710 printf(" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n");
600 printf (" if more than 50 users.\n\n"); 711 printf(" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n");
601 712 printf(" Warning if less than 5 or more than 20 users are logged in; critical\n");
602 printf (" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n"); 713 printf(" if more than 50 users.\n\n");
603 printf (" -m CONN_TIME -w 0.5 -c 2\n"); 714
604 printf (" Warning if connecting to the database takes more than half of a second;\n"); 715 printf(" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n");
605 printf (" critical if it takes more than 2 seconds.\n\n"); 716 printf(" -m CONN_TIME -w 0.5 -c 2\n");
606 717 printf(" Warning if connecting to the database takes more than half of a second;\n");
607 printf (" check_dbi -d mysql -H localhost -o username=user \\\n"); 718 printf(" critical if it takes more than 2 seconds.\n\n");
608 printf (" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n"); 719
609 printf (" -r '^5\\.[01].*MySQL Enterprise Server'\n"); 720 printf(" check_dbi -d mysql -H localhost -o username=user \\\n");
610 printf (" Critical if the database server is not a MySQL enterprise server in either\n"); 721 printf(" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n");
611 printf (" version 5.0.x or 5.1.x.\n\n"); 722 printf(" -r '^5\\.[01].*MySQL Enterprise Server'\n");
612 723 printf(" Critical if the database server is not a MySQL enterprise server in either\n");
613 printf (" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n"); 724 printf(" version 5.0.x or 5.1.x.\n\n");
614 printf (" -w 090000:090099 -c 090000:090199\n"); 725
615 printf (" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n"); 726 printf(" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n");
616 printf (" is less than 9.x or higher than 9.1.x.\n"); 727 printf(" -w 090000:090099 -c 090000:090199\n");
617 728 printf(" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n");
618 printf (UT_SUPPORT); 729 printf(" is less than 9.x or higher than 9.1.x.\n");
730
731 printf(UT_SUPPORT);
619} 732}
620 733
621void 734void print_usage(void) {
622print_usage (void) 735 printf("%s\n", _("Usage:"));
623{ 736 printf("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname);
624 printf ("%s\n", _("Usage:")); 737 printf(" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
625 printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname); 738 printf(" [-e <string>] [-r|-R <regex>]\n");
626 printf (" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
627 printf (" [-e <string>] [-r|-R <regex>]\n");
628} 739}
629 740
630#define CHECK_IGNORE_ERROR(s) \ 741const char *get_field_str(dbi_result res, check_dbi_metric metric, check_dbi_type type) {
631 do { \ 742 const char *str = dbi_result_get_string_idx(res, 1);
632 if (metric != METRIC_QUERY_RESULT) \ 743 if ((!str) || (strcmp(str, "ERROR") == 0)) {
633 return (s); \ 744 if (metric != METRIC_QUERY_RESULT) {
634 } while (0) 745 return NULL;
635 746 }
636const char *
637get_field_str (dbi_conn conn, dbi_result res, unsigned short field_type)
638{
639 const char *str;
640
641 if (field_type != DBI_TYPE_STRING) {
642 printf ("CRITICAL - result value is not a string\n");
643 return NULL; 747 return NULL;
644 } 748 }
645 749
646 str = dbi_result_get_string_idx (res, 1); 750 if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) {
647 if ((! str) || (strcmp (str, "ERROR") == 0)) { 751 printf("Query returned string '%s'\n", str);
648 CHECK_IGNORE_ERROR (NULL);
649 np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
650 return NULL;
651 } 752 }
652
653 if ((verbose && (type == TYPE_STRING)) || (verbose > 2))
654 printf ("Query returned string '%s'\n", str);
655 return str; 753 return str;
656} 754}
657 755
658double 756typedef struct {
659get_field (dbi_conn conn, dbi_result res, unsigned short *field_type) 757 double value;
660{ 758 int error_code;
661 double val = NAN; 759 int dbi_error_code; // not sure if useful
760} get_field_wrapper;
761get_field_wrapper get_field(dbi_result res, check_dbi_metric metric, check_dbi_type type) {
762
763 unsigned short field_type = dbi_result_get_field_type_idx(res, 1);
764 get_field_wrapper result = {
765 .value = NAN,
766 .error_code = OK,
767 };
662 768
663 if (*field_type == DBI_TYPE_INTEGER) { 769 if (field_type == DBI_TYPE_INTEGER) {
664 val = (double)dbi_result_get_longlong_idx (res, 1); 770 result.value = (double)dbi_result_get_longlong_idx(res, 1);
665 } 771 } else if (field_type == DBI_TYPE_DECIMAL) {
666 else if (*field_type == DBI_TYPE_DECIMAL) { 772 result.value = dbi_result_get_double_idx(res, 1);
667 val = dbi_result_get_double_idx (res, 1); 773 } else if (field_type == DBI_TYPE_STRING) {
668 }
669 else if (*field_type == DBI_TYPE_STRING) {
670 const char *val_str; 774 const char *val_str;
671 char *endptr = NULL; 775 char *endptr = NULL;
672 776
673 val_str = get_field_str (conn, res, *field_type); 777 val_str = get_field_str(res, metric, type);
674 if (! val_str) { 778 if (!val_str) {
675 CHECK_IGNORE_ERROR (NAN); 779 result.error_code = ERROR;
676 *field_type = DBI_TYPE_ERROR; 780 field_type = DBI_TYPE_ERROR;
677 return NAN; 781 return result;
678 } 782 }
679 783
680 val = strtod (val_str, &endptr); 784 result.value = strtod(val_str, &endptr);
681 if (endptr == val_str) { 785 if (endptr == val_str) {
682 CHECK_IGNORE_ERROR (NAN); 786 if (metric != METRIC_QUERY_RESULT) {
683 printf ("CRITICAL - result value is not a numeric: %s\n", val_str); 787 result.error_code = ERROR;
684 *field_type = DBI_TYPE_ERROR; 788 return result;
685 return NAN; 789 }
790
791 if (verbose) {
792 printf("CRITICAL - result value is not a numeric: %s\n", val_str);
793 }
794
795 field_type = DBI_TYPE_ERROR;
796 result.error_code = ERROR;
797 return result;
798 }
799
800 if ((endptr != NULL) && (*endptr != '\0')) {
801 if (verbose) {
802 printf("Garbage after value: %s\n", endptr);
803 }
686 } 804 }
687 else if ((endptr != NULL) && (*endptr != '\0')) { 805 } else {
688 if (verbose) 806 if (metric != METRIC_QUERY_RESULT) {
689 printf ("Garbage after value: %s\n", endptr); 807 result.error_code = ERROR;
808 return result;
690 } 809 }
810 // printf("CRITICAL - cannot parse value of type %s (%i)\n",
811 // (*field_type == DBI_TYPE_BINARY) ? "BINARY"
812 // : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME"
813 // : "<unknown>",
814 // *field_type);
815 field_type = DBI_TYPE_ERROR;
816 result.error_code = ERROR;
691 } 817 }
692 else { 818 return result;
693 CHECK_IGNORE_ERROR (NAN);
694 printf ("CRITICAL - cannot parse value of type %s (%i)\n",
695 (*field_type == DBI_TYPE_BINARY)
696 ? "BINARY"
697 : (*field_type == DBI_TYPE_DATETIME)
698 ? "DATETIME"
699 : "<unknown>",
700 *field_type);
701 *field_type = DBI_TYPE_ERROR;
702 return NAN;
703 }
704 return val;
705} 819}
706 820
707double 821static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
708get_query_result (dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val) 822 char *query) {
709{ 823 assert(query);
710 unsigned short field_type;
711 double val = NAN;
712
713 if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
714 CHECK_IGNORE_ERROR (STATE_OK);
715 np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
716 return STATE_CRITICAL;
717 }
718
719 if (dbi_result_get_numrows (res) < 1) {
720 CHECK_IGNORE_ERROR (STATE_OK);
721 printf ("WARNING - no rows returned\n");
722 return STATE_WARNING;
723 }
724 824
725 if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) { 825 if (verbose) {
726 CHECK_IGNORE_ERROR (STATE_OK); 826 printf("Executing query '%s'\n", query);
727 np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
728 return STATE_CRITICAL;
729 } 827 }
730 828
731 if (dbi_result_get_numfields (res) < 1) { 829 do_query_result result = {
732 CHECK_IGNORE_ERROR (STATE_OK); 830 .query_duration = 0,
733 printf ("WARNING - no fields returned\n"); 831 .result_string = NULL,
734 return STATE_WARNING; 832 .result_number = 0,
735 } 833 .error_code = 0,
834 .query_processing_status = STATE_UNKNOWN,
835 };
736 836
737 if (dbi_result_first_row (res) != 1) { 837 struct timeval timeval_start;
738 CHECK_IGNORE_ERROR (STATE_OK); 838 gettimeofday(&timeval_start, NULL);
739 np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
740 return STATE_CRITICAL;
741 }
742 839
743 field_type = dbi_result_get_field_type_idx (res, 1); 840 dbi_result res = dbi_conn_query(conn, query);
744 if (field_type != DBI_TYPE_ERROR) { 841 if (!res) {
745 if (type == TYPE_STRING) 842 dbi_conn_error(conn, &result.error_string);
746 /* the value will be freed in dbi_result_free */ 843 result.error_code = 1;
747 *res_val_str = strdup (get_field_str (conn, res, field_type)); 844 return result;
748 else
749 val = get_field (conn, res, &field_type);
750 } 845 }
751 846
752 *res_val = val; 847 struct timeval timeval_end;
848 gettimeofday(&timeval_end, NULL);
849 result.query_duration = timediff(timeval_start, timeval_end);
753 850
754 if (field_type == DBI_TYPE_ERROR) { 851 if (verbose) {
755 CHECK_IGNORE_ERROR (STATE_OK); 852 printf("Query duration: %f\n", result.query_duration);
756 np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
757 return STATE_CRITICAL;
758 } 853 }
759 854
760 dbi_result_free (res); 855 // Default state is OK, all error will be set explicitly
761 return STATE_OK; 856 mp_state_enum query_processing_state = STATE_OK;
762} 857 {
763
764#undef CHECK_IGNORE_ERROR
765
766int
767do_query (dbi_conn conn, const char **res_val_str, double *res_val, double *res_time)
768{
769 dbi_result res;
770 858
771 struct timeval timeval_start, timeval_end; 859 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
772 int status = STATE_OK; 860 if (metric != METRIC_QUERY_RESULT) {
773 861 query_processing_state = STATE_OK;
774 assert (np_dbi_query); 862 } else {
775 863 dbi_conn_error(conn, &result.error_string);
776 if (verbose) 864 query_processing_state = STATE_CRITICAL;
777 printf ("Executing query '%s'\n", np_dbi_query); 865 }
778 866 } else if (dbi_result_get_numrows(res) < 1) {
779 gettimeofday (&timeval_start, NULL); 867 if (metric != METRIC_QUERY_RESULT) {
780 868 query_processing_state = STATE_OK;
781 res = dbi_conn_query (conn, np_dbi_query); 869 } else {
782 if (! res) { 870 result.error_string = "no rows returned";
783 np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); 871 // printf("WARNING - no rows returned\n");
784 return STATE_CRITICAL; 872 query_processing_state = STATE_WARNING;
873 }
874 } else if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) {
875 if (metric != METRIC_QUERY_RESULT) {
876 query_processing_state = STATE_OK;
877 } else {
878 dbi_conn_error(conn, &result.error_string);
879 // np_dbi_print_error(conn, "CRITICAL - failed to fetch fields");
880 query_processing_state = STATE_CRITICAL;
881 }
882 } else if (dbi_result_get_numfields(res) < 1) {
883 if (metric != METRIC_QUERY_RESULT) {
884 query_processing_state = STATE_OK;
885 } else {
886 result.error_string = "no fields returned";
887 // printf("WARNING - no fields returned\n");
888 query_processing_state = STATE_WARNING;
889 }
890 } else if (dbi_result_first_row(res) != 1) {
891 if (metric != METRIC_QUERY_RESULT) {
892 query_processing_state = STATE_OK;
893 } else {
894 dbi_conn_error(conn, &result.error_string);
895 // np_dbi_print_error(conn, "CRITICAL - failed to fetch first row");
896 query_processing_state = STATE_CRITICAL;
897 }
898 } else {
899 unsigned short field_type = dbi_result_get_field_type_idx(res, 1);
900 if (field_type != DBI_TYPE_ERROR) {
901 if (type == TYPE_STRING) {
902 result.result_string = strdup(get_field_str(res, metric, type));
903 } else {
904 get_field_wrapper gfw = get_field(res, metric, type);
905 result.result_number = gfw.value;
906 }
907 } else {
908 // Error when retrieving the field, that is OK if the Query result is not of
909 // interest
910 if (metric != METRIC_QUERY_RESULT) {
911 query_processing_state = STATE_OK;
912 } else {
913 dbi_conn_error(conn, &result.error_string);
914 // np_dbi_print_error(conn, "CRITICAL - failed to fetch data");
915 query_processing_state = STATE_CRITICAL;
916 }
917 }
918 }
785 } 919 }
920 dbi_result_free(res);
786 921
787 status = get_query_result (conn, res, res_val_str, res_val); 922 result.query_processing_status = query_processing_state;
788
789 gettimeofday (&timeval_end, NULL);
790 *res_time = timediff (timeval_start, timeval_end);
791 923
792 if (verbose) 924 return result;
793 printf ("Time elapsed: %f\n", *res_time);
794
795 return status;
796} 925}
797 926
798double 927static double timediff(struct timeval start, struct timeval end) {
799timediff (struct timeval start, struct timeval end)
800{
801 double diff; 928 double diff;
802 929
803 while (start.tv_usec > end.tv_usec) { 930 while (start.tv_usec > end.tv_usec) {
804 --end.tv_sec; 931 --end.tv_sec;
805 end.tv_usec += 1000000; 932 end.tv_usec += 1000000;
806 } 933 }
807 diff = (double)(end.tv_sec - start.tv_sec) 934 diff = (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
808 + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
809 return diff; 935 return diff;
810} 936}
811 937
812void 938static void np_dbi_print_error(dbi_conn conn, char *fmt, ...) {
813np_dbi_print_error (dbi_conn conn, char *fmt, ...)
814{
815 const char *errmsg = NULL; 939 const char *errmsg = NULL;
816 va_list ap; 940 va_list ap;
817 941
818 va_start (ap, fmt); 942 va_start(ap, fmt);
819 943
820 dbi_conn_error (conn, &errmsg); 944 dbi_conn_error(conn, &errmsg);
821 vprintf (fmt, ap); 945 vprintf(fmt, ap);
822 printf (": %s\n", errmsg); 946 printf(": %s\n", errmsg);
823 947
824 va_end (ap); 948 va_end(ap);
825} 949}
826