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.c693
1 files changed, 418 insertions, 275 deletions
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 9efcd1cb..9bc68eb3 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -34,6 +34,10 @@ const 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" 36#include "../lib/monitoringplug.h"
37#include "thresholds.h"
38#include "perfdata.h"
39#include "output.h"
40#include "states.h"
37#include "check_dbi.d/config.h" 41#include "check_dbi.d/config.h"
38#include "common.h" 42#include "common.h"
39#include "utils.h" 43#include "utils.h"
@@ -63,7 +67,6 @@ typedef struct {
63} check_dbi_config_wrapper; 67} check_dbi_config_wrapper;
64 68
65static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); 69static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
66static check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper /*config_wrapper*/);
67void print_usage(void); 70void print_usage(void);
68static void print_help(void); 71static void print_help(void);
69 72
@@ -71,25 +74,18 @@ static double timediff(struct timeval /*start*/, struct timeval /*end*/);
71 74
72static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); 75static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...);
73 76
74static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/, double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/, 77typedef struct {
75 mp_dbi_type /*type*/, char * /*np_dbi_query*/); 78 char *result_string;
79 double result_number;
80 double query_duration;
81 int error_code;
82 const char *error_string;
83 mp_state_enum query_processing_status;
84} do_query_result;
85static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
86 char *query);
76 87
77int main(int argc, char **argv) { 88int main(int argc, char **argv) {
78 int status = STATE_UNKNOWN;
79
80 dbi_driver driver;
81 dbi_conn conn;
82
83 unsigned int server_version;
84
85 struct timeval start_timeval;
86 struct timeval end_timeval;
87 double conn_time = 0.0;
88 double query_time = 0.0;
89
90 const char *query_val_str = NULL;
91 double query_val = 0.0;
92
93 setlocale(LC_ALL, ""); 89 setlocale(LC_ALL, "");
94 bindtextdomain(PACKAGE, LOCALEDIR); 90 bindtextdomain(PACKAGE, LOCALEDIR);
95 textdomain(PACKAGE); 91 textdomain(PACKAGE);
@@ -105,6 +101,10 @@ int main(int argc, char **argv) {
105 101
106 const check_dbi_config config = tmp.config; 102 const check_dbi_config config = tmp.config;
107 103
104 if (config.output_format_is_set) {
105 mp_set_format(config.output_format);
106 }
107
108 /* Set signal handling and alarm */ 108 /* Set signal handling and alarm */
109 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 109 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
110 usage4(_("Cannot catch SIGALRM")); 110 usage4(_("Cannot catch SIGALRM"));
@@ -115,63 +115,71 @@ int main(int argc, char **argv) {
115 printf("Initializing DBI\n"); 115 printf("Initializing DBI\n");
116 } 116 }
117 117
118 dbi_inst *instance_p = {0}; 118 dbi_inst instance_p = NULL;
119 119 if (dbi_initialize_r(NULL, &instance_p) < 0) {
120 if (dbi_initialize_r(NULL, instance_p) < 0) { 120 printf("failed to initialize DBI; possibly you don't have any drivers installed.\n");
121 printf("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); 121 exit(STATE_UNKNOWN);
122 return STATE_UNKNOWN;
123 } 122 }
124 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
125 if (instance_p == NULL) { 128 if (instance_p == NULL) {
126 printf("UNKNOWN - failed to initialize DBI.\n"); 129 printf("failed to initialize DBI.\n");
127 return STATE_UNKNOWN; 130 exit(STATE_UNKNOWN);
128 } 131 }
129 132
130 if (verbose) { 133 if (verbose) {
131 printf("Opening DBI driver '%s'\n", config.dbi_driver); 134 printf("Opening DBI driver '%s'\n", config.dbi_driver);
132 } 135 }
133 136
134 driver = dbi_driver_open_r(config.dbi_driver, instance_p); 137 dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p);
135 if (!driver) { 138 if (!driver) {
136 printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver); 139 printf("failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver);
137 140
138 printf("Known drivers:\n"); 141 printf("Known drivers:\n");
139 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;
143 driver = dbi_driver_list_r(driver, instance_p)) {
140 printf(" - %s\n", dbi_driver_get_name(driver)); 144 printf(" - %s\n", dbi_driver_get_name(driver));
141 } 145 }
142 return STATE_UNKNOWN; 146 exit(STATE_UNKNOWN);
143 } 147 }
144 148
145 /* make a connection to the database */ 149 /* make a connection to the database */
150 struct timeval start_timeval;
146 gettimeofday(&start_timeval, NULL); 151 gettimeofday(&start_timeval, NULL);
147 152
148 conn = dbi_conn_open(driver); 153 dbi_conn conn = dbi_conn_open(driver);
149 if (!conn) { 154 if (!conn) {
150 printf("UNKNOWN - failed top open connection object.\n"); 155 printf("UNKNOWN - failed top open connection object.\n");
151 dbi_conn_close(conn); 156 dbi_conn_close(conn);
152 return STATE_UNKNOWN; 157 exit(STATE_UNKNOWN);
153 } 158 }
154 159
155 for (size_t i = 0; i < config.dbi_options_num; ++i) { 160 for (size_t i = 0; i < config.dbi_options_num; ++i) {
156 const char *opt; 161 const char *opt;
157 162
158 if (verbose > 1) { 163 if (verbose > 1) {
159 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key, config.dbi_options[i].value); 164 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key,
165 config.dbi_options[i].value);
160 } 166 }
161 167
162 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { 168 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) {
163 continue; 169 continue;
164 } 170 }
165 /* else: status != 0 */
166 171
167 np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", config.dbi_options[i].key, config.dbi_options[i].value); 172 // Failing to set option
173 np_dbi_print_error(conn, "failed to set option '%s' to '%s'", config.dbi_options[i].key,
174 config.dbi_options[i].value);
168 printf("Known driver options:\n"); 175 printf("Known driver options:\n");
169 176
170 for (opt = dbi_conn_get_option_list(conn, NULL); opt; opt = dbi_conn_get_option_list(conn, opt)) { 177 for (opt = dbi_conn_get_option_list(conn, NULL); opt;
178 opt = dbi_conn_get_option_list(conn, opt)) {
171 printf(" - %s\n", opt); 179 printf(" - %s\n", opt);
172 } 180 }
173 dbi_conn_close(conn); 181 dbi_conn_close(conn);
174 return STATE_UNKNOWN; 182 exit(STATE_UNKNOWN);
175 } 183 }
176 184
177 if (config.host) { 185 if (config.host) {
@@ -199,78 +207,216 @@ int main(int argc, char **argv) {
199 } 207 }
200 208
201 if (dbi_conn_connect(conn) < 0) { 209 if (dbi_conn_connect(conn) < 0) {
202 np_dbi_print_error(conn, "UNKNOWN - failed to connect to database"); 210 np_dbi_print_error(conn, "failed to connect to database");
203 return STATE_UNKNOWN; 211 exit(STATE_UNKNOWN);
204 } 212 }
205 213
214 struct timeval end_timeval;
206 gettimeofday(&end_timeval, NULL); 215 gettimeofday(&end_timeval, NULL);
207 conn_time = timediff(start_timeval, end_timeval); 216 double conn_time = timediff(start_timeval, end_timeval);
208
209 server_version = dbi_conn_get_engine_version(conn);
210 if (verbose) { 217 if (verbose) {
211 printf("Connected to server version %u\n", server_version); 218 printf("Time elapsed: %f\n", conn_time);
212 } 219 }
213 220
214 if (config.metric == METRIC_SERVER_VERSION) { 221 mp_check overall = mp_check_init();
215 status = get_status(server_version, config.dbi_thresholds); 222
223 mp_subcheck sc_connection_time = mp_subcheck_init();
224 sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_OK);
225 xasprintf(&sc_connection_time.output, "Connection time: %f", conn_time);
226
227 mp_perfdata pd_conn_duration = perfdata_init();
228 pd_conn_duration.label = "conntime";
229 pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time);
230
231 if (config.metric == METRIC_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 }
216 } 239 }
217 240
241 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_conn_duration);
242 mp_add_subcheck_to_check(&overall, sc_connection_time);
243
244 unsigned int server_version = dbi_conn_get_engine_version(conn);
218 if (verbose) { 245 if (verbose) {
219 printf("Time elapsed: %f\n", conn_time); 246 printf("Connected to server version %u\n", server_version);
220 } 247 }
221 248
222 if (config.metric == METRIC_CONN_TIME) { 249 mp_subcheck sc_server_version = mp_subcheck_init();
223 status = get_status(conn_time, config.dbi_thresholds); 250 sc_server_version = mp_set_subcheck_default_state(sc_server_version, STATE_OK);
224 } 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);
225 268
226 /* select a database */ 269 /* select a database */
227 if (config.dbi_database) { 270 if (config.database) {
228 if (verbose > 1) { 271 if (verbose > 1) {
229 printf("Selecting database '%s'\n", config.dbi_database); 272 printf("Selecting database '%s'\n", config.database);
230 } 273 }
231 274
232 if (dbi_conn_select_db(conn, config.dbi_database)) { 275 mp_subcheck sc_select_db = mp_subcheck_init();
233 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.dbi_database); 276 sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK);
234 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);
235 } 283 }
236 } 284 }
237 285
238 if (config.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
239 /* execute query */ 291 /* execute query */
240 status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, config.dbi_query); 292 do_query_result query_res = do_query(conn, config.metric, config.type, config.query);
241 if (status != STATE_OK) { 293
242 /* do_query prints an error message in this case */ 294 if (query_res.error_code != 0) {
243 return status; 295 xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string);
244 } 296 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
297 } else if (query_res.query_processing_status != STATE_OK) {
298 if (query_res.error_string) {
299 xasprintf(&sc_query.output, "Failed to process query: %s", query_res.error_string);
300 } else {
301 xasprintf(&sc_query.output, "Failed to process query");
302 }
303 sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status);
304 } else {
305 // query succeeded in general
306 xasprintf(&sc_query.output, "Query '%s' succeeded", config.query);
307
308 // that's a OK by default now
309 sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK);
310
311 // query duration first
312 mp_perfdata pd_query_duration = perfdata_init();
313 pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration);
314 pd_query_duration.label = "querytime";
315 if (config.metric == METRIC_QUERY_TIME) {
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);
245 320
246 if (config.metric == METRIC_QUERY_RESULT) { 321 if (config.metric == METRIC_QUERY_RESULT) {
247 if (config.expect) { 322 if (config.expect) {
248 if ((!query_val_str) || strcmp(query_val_str, config.expect)) { 323 if ((!query_res.result_string) ||
249 status = STATE_CRITICAL; 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 }
250 } else { 362 } else {
251 status = STATE_OK; 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 }
252 } 399 }
253 } else if (config.expect_re_str) { 400 } else if (config.metric == METRIC_QUERY_TIME) {
254 int err; 401 mp_state_enum query_time_status = mp_get_pd_status(pd_query_duration);
255 402 mp_set_subcheck_state(sc_query, query_time_status);
256 regex_t expect_re = {}; 403
257 err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); 404 if (query_time_status == STATE_OK) {
258 if (!err) { 405 xasprintf(&sc_query.output, "Query duration '%f' is within given thresholds",
259 status = STATE_OK; 406 query_res.query_duration);
260 } else if (err == REG_NOMATCH) {
261 status = STATE_CRITICAL;
262 } else { 407 } else {
263 char errmsg[1024]; 408 xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds",
264 regerror(err, &expect_re, errmsg, sizeof(errmsg)); 409 query_res.query_duration);
265 printf("ERROR - failed to execute regular expression: %s\n", errmsg);
266 status = STATE_CRITICAL;
267 } 410 }
268 } else { 411 } else {
269 status = get_status(query_val, config.dbi_thresholds); 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));
270 } 416 }
271 } else if (config.metric == METRIC_QUERY_TIME) {
272 status = get_status(query_time, config.dbi_thresholds);
273 } 417 }
418
419 mp_add_subcheck_to_check(&overall, sc_query);
274 } 420 }
275 421
276 if (verbose) { 422 if (verbose) {
@@ -278,55 +424,17 @@ int main(int argc, char **argv) {
278 } 424 }
279 dbi_conn_close(conn); 425 dbi_conn_close(conn);
280 426
281 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error 427 mp_exit(overall);
282 * which should have been reported and handled (abort) before
283 * ... unless we expected a string to be returned */
284 assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || (config.type == TYPE_STRING));
285
286 assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str));
287
288 printf("%s - connection time: %fs", state_text(status), conn_time);
289 if (config.dbi_query) {
290 if (config.type == TYPE_STRING) {
291 assert(config.expect || config.expect_re_str);
292 printf(", '%s' returned '%s' in %fs", config.dbi_query, query_val_str ? query_val_str : "<nothing>", query_time);
293 if (status != STATE_OK) {
294 if (config.expect) {
295 printf(" (expected '%s')", config.expect);
296 } else if (config.expect_re_str) {
297 printf(" (expected regex /%s/%s)", config.expect_re_str, ((config.expect_re_cflags & REG_ICASE) ? "i" : ""));
298 }
299 }
300 } else if (isnan(query_val)) {
301 printf(", '%s' query execution time: %fs", config.dbi_query, query_time);
302 } else {
303 printf(", '%s' returned %f in %fs", config.dbi_query, query_val, query_time);
304 }
305 }
306
307 printf(" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
308 ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "",
309 ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "", server_version,
310 ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range : "",
311 ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range : "");
312 if (config.dbi_query) {
313 if (!isnan(query_val)) { /* this is also true when -e is used */
314 printf(" query=%f;%s;%s;;", query_val, ((config.metric == METRIC_QUERY_RESULT) && config.warning_range) ? config.warning_range : "",
315 ((config.metric == METRIC_QUERY_RESULT) && config.critical_range) ? config.critical_range : "");
316 }
317 printf(" querytime=%fs;%s;%s;0;", query_time, ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range : "",
318 ((config.metric == METRIC_QUERY_TIME) && config.critical_range) ? config.critical_range : "");
319 }
320 printf("\n");
321 return status;
322} 428}
323 429
324/* process command-line arguments */ 430/* process command-line arguments */
325check_dbi_config_wrapper process_arguments(int argc, char **argv) { 431check_dbi_config_wrapper process_arguments(int argc, char **argv) {
432 enum {
433 output_format_index = CHAR_MAX + 1,
434 };
326 435
327 int option = 0; 436 int option = 0;
328 static struct option longopts[] = {STD_LONG_OPTS, 437 static struct option longopts[] = {STD_LONG_OPTS,
329
330 {"expect", required_argument, 0, 'e'}, 438 {"expect", required_argument, 0, 'e'},
331 {"regex", required_argument, 0, 'r'}, 439 {"regex", required_argument, 0, 'r'},
332 {"regexi", required_argument, 0, 'R'}, 440 {"regexi", required_argument, 0, 'R'},
@@ -335,6 +443,7 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
335 {"option", required_argument, 0, 'o'}, 443 {"option", required_argument, 0, 'o'},
336 {"query", required_argument, 0, 'q'}, 444 {"query", required_argument, 0, 'q'},
337 {"database", required_argument, 0, 'D'}, 445 {"database", required_argument, 0, 'D'},
446 {"output-format", required_argument, 0, output_format_index},
338 {0, 0, 0, 0}}; 447 {0, 0, 0, 0}};
339 448
340 check_dbi_config_wrapper result = { 449 check_dbi_config_wrapper result = {
@@ -359,14 +468,22 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
359 print_revision(progname, NP_VERSION); 468 print_revision(progname, NP_VERSION);
360 exit(STATE_UNKNOWN); 469 exit(STATE_UNKNOWN);
361 470
362 case 'c': /* critical range */ 471 case 'c': /* critical range */ {
363 result.config.critical_range = optarg; 472 mp_range_parsed tmp = mp_parse_range_string(optarg);
473 if (tmp.error != MP_PARSING_SUCCES) {
474 die(STATE_UNKNOWN, "failed to parse critical threshold");
475 }
476 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
364 result.config.type = TYPE_NUMERIC; 477 result.config.type = TYPE_NUMERIC;
365 break; 478 } break;
366 case 'w': /* warning range */ 479 case 'w': /* warning range */ {
367 result.config.warning_range = optarg; 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);
368 result.config.type = TYPE_NUMERIC; 485 result.config.type = TYPE_NUMERIC;
369 break; 486 } break;
370 case 'e': 487 case 'e':
371 result.config.expect = optarg; 488 result.config.expect = optarg;
372 result.config.type = TYPE_STRING; 489 result.config.type = TYPE_STRING;
@@ -393,7 +510,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
393 } 510 }
394 break; 511 break;
395 } 512 }
396
397 case 'm': 513 case 'm':
398 if (!strcasecmp(optarg, "CONN_TIME")) { 514 if (!strcasecmp(optarg, "CONN_TIME")) {
399 result.config.metric = METRIC_CONN_TIME; 515 result.config.metric = METRIC_CONN_TIME;
@@ -413,7 +529,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
413 } else { 529 } else {
414 timeout_interval = atoi(optarg); 530 timeout_interval = atoi(optarg);
415 } 531 }
416
417 break; 532 break;
418 case 'H': /* host */ 533 case 'H': /* host */
419 if (!is_host(optarg)) { 534 if (!is_host(optarg)) {
@@ -425,7 +540,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
425 case 'v': 540 case 'v':
426 verbose++; 541 verbose++;
427 break; 542 break;
428
429 case 'd': 543 case 'd':
430 result.config.dbi_driver = optarg; 544 result.config.dbi_driver = optarg;
431 break; 545 break;
@@ -442,7 +556,8 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
442 *value = '\0'; 556 *value = '\0';
443 ++value; 557 ++value;
444 558
445 new = realloc(result.config.dbi_options, (result.config.dbi_options_num + 1) * sizeof(*new)); 559 new = realloc(result.config.dbi_options,
560 (result.config.dbi_options_num + 1) * sizeof(*new));
446 if (!new) { 561 if (!new) {
447 printf("UNKNOWN - failed to reallocate memory\n"); 562 printf("UNKNOWN - failed to reallocate memory\n");
448 exit(STATE_UNKNOWN); 563 exit(STATE_UNKNOWN);
@@ -456,52 +571,68 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
456 new->value = value; 571 new->value = value;
457 } break; 572 } break;
458 case 'q': 573 case 'q':
459 result.config.dbi_query = optarg; 574 result.config.query = optarg;
460 break; 575 break;
461 case 'D': 576 case 'D':
462 result.config.dbi_database = optarg; 577 result.config.database = optarg;
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;
463 break; 589 break;
464 } 590 }
591 }
465 } 592 }
466 593
467 set_thresholds(&result.config.dbi_thresholds, result.config.warning_range, result.config.critical_range); 594 if (!result.config.dbi_driver) {
468
469 return validate_arguments(result);
470}
471
472check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrapper) {
473 if (!config_wrapper.config.dbi_driver) {
474 usage("Must specify a DBI driver"); 595 usage("Must specify a DBI driver");
475 } 596 }
476 597
477 if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) || (config_wrapper.config.metric == METRIC_QUERY_TIME)) && 598 if (((result.config.metric == METRIC_QUERY_RESULT) ||
478 (!config_wrapper.config.dbi_query)) { 599 (result.config.metric == METRIC_QUERY_TIME)) &&
600 (!result.config.query)) {
479 usage("Must specify a query to execute (metric == QUERY_RESULT)"); 601 usage("Must specify a query to execute (metric == QUERY_RESULT)");
480 } 602 }
481 603
482 if ((config_wrapper.config.metric != METRIC_CONN_TIME) && (config_wrapper.config.metric != METRIC_SERVER_VERSION) && 604 if ((result.config.metric != METRIC_CONN_TIME) &&
483 (config_wrapper.config.metric != METRIC_QUERY_RESULT) && (config_wrapper.config.metric != METRIC_QUERY_TIME)) { 605 (result.config.metric != METRIC_SERVER_VERSION) &&
606 (result.config.metric != METRIC_QUERY_RESULT) &&
607 (result.config.metric != METRIC_QUERY_TIME)) {
484 usage("Invalid metric specified"); 608 usage("Invalid metric specified");
485 } 609 }
486 610
487 if (config_wrapper.config.expect && (config_wrapper.config.warning_range || config_wrapper.config.critical_range || config_wrapper.config.expect_re_str)) { 611 if (result.config.expect &&
612 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
613 result.config.expect_re_str)) {
488 usage("Do not mix -e and -w/-c/-r/-R"); 614 usage("Do not mix -e and -w/-c/-r/-R");
489 } 615 }
490 616
491 if (config_wrapper.config.expect_re_str && (config_wrapper.config.warning_range || config_wrapper.config.critical_range || config_wrapper.config.expect)) { 617 if (result.config.expect_re_str &&
618 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
619 result.config.expect)) {
492 usage("Do not mix -r/-R and -w/-c/-e"); 620 usage("Do not mix -r/-R and -w/-c/-e");
493 } 621 }
494 622
495 if (config_wrapper.config.expect && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { 623 if (result.config.expect && (result.config.metric != METRIC_QUERY_RESULT)) {
496 usage("Option -e requires metric QUERY_RESULT"); 624 usage("Option -e requires metric QUERY_RESULT");
497 } 625 }
498 626
499 if (config_wrapper.config.expect_re_str && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { 627 if (result.config.expect_re_str && (result.config.metric != METRIC_QUERY_RESULT)) {
500 usage("Options -r/-R require metric QUERY_RESULT"); 628 usage("Options -r/-R require metric QUERY_RESULT");
501 } 629 }
502 630
503 config_wrapper.errorcode = OK; 631 if (result.config.type == TYPE_STRING) {
504 return config_wrapper; 632 assert(result.config.expect || result.config.expect_re_str);
633 }
634
635 return result;
505} 636}
506 637
507void print_help(void) { 638void print_help(void) {
@@ -607,20 +738,12 @@ void print_usage(void) {
607 printf(" [-e <string>] [-r|-R <regex>]\n"); 738 printf(" [-e <string>] [-r|-R <regex>]\n");
608} 739}
609 740
610const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type, mp_dbi_metric metric, mp_dbi_type type) { 741const char *get_field_str(dbi_result res, check_dbi_metric metric, check_dbi_type type) {
611 const char *str; 742 const char *str = dbi_result_get_string_idx(res, 1);
612
613 if (field_type != DBI_TYPE_STRING) {
614 printf("CRITICAL - result value is not a string\n");
615 return NULL;
616 }
617
618 str = dbi_result_get_string_idx(res, 1);
619 if ((!str) || (strcmp(str, "ERROR") == 0)) { 743 if ((!str) || (strcmp(str, "ERROR") == 0)) {
620 if (metric != METRIC_QUERY_RESULT) { 744 if (metric != METRIC_QUERY_RESULT) {
621 return NULL; 745 return NULL;
622 } 746 }
623 np_dbi_print_error(conn, "CRITICAL - failed to fetch string value");
624 return NULL; 747 return NULL;
625 } 748 }
626 749
@@ -630,35 +753,50 @@ const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_ty
630 return str; 753 return str;
631} 754}
632 755
633double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric, mp_dbi_type type) { 756typedef struct {
634 double val = NAN; 757 double value;
758 int error_code;
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 };
635 768
636 if (*field_type == DBI_TYPE_INTEGER) { 769 if (field_type == DBI_TYPE_INTEGER) {
637 val = (double)dbi_result_get_longlong_idx(res, 1); 770 result.value = (double)dbi_result_get_longlong_idx(res, 1);
638 } else if (*field_type == DBI_TYPE_DECIMAL) { 771 } else if (field_type == DBI_TYPE_DECIMAL) {
639 val = dbi_result_get_double_idx(res, 1); 772 result.value = dbi_result_get_double_idx(res, 1);
640 } else if (*field_type == DBI_TYPE_STRING) { 773 } else if (field_type == DBI_TYPE_STRING) {
641 const char *val_str; 774 const char *val_str;
642 char *endptr = NULL; 775 char *endptr = NULL;
643 776
644 val_str = get_field_str(conn, res, *field_type, metric, type); 777 val_str = get_field_str(res, metric, type);
645 if (!val_str) { 778 if (!val_str) {
646 if (metric != METRIC_QUERY_RESULT) { 779 result.error_code = ERROR;
647 return NAN; 780 field_type = DBI_TYPE_ERROR;
648 } 781 return result;
649 *field_type = DBI_TYPE_ERROR;
650 return NAN;
651 } 782 }
652 783
653 val = strtod(val_str, &endptr); 784 result.value = strtod(val_str, &endptr);
654 if (endptr == val_str) { 785 if (endptr == val_str) {
655 if (metric != METRIC_QUERY_RESULT) { 786 if (metric != METRIC_QUERY_RESULT) {
656 return NAN; 787 result.error_code = ERROR;
788 return result;
657 } 789 }
658 printf("CRITICAL - result value is not a numeric: %s\n", val_str); 790
659 *field_type = DBI_TYPE_ERROR; 791 if (verbose) {
660 return NAN; 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;
661 } 798 }
799
662 if ((endptr != NULL) && (*endptr != '\0')) { 800 if ((endptr != NULL) && (*endptr != '\0')) {
663 if (verbose) { 801 if (verbose) {
664 printf("Garbage after value: %s\n", endptr); 802 printf("Garbage after value: %s\n", endptr);
@@ -666,122 +804,127 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_d
666 } 804 }
667 } else { 805 } else {
668 if (metric != METRIC_QUERY_RESULT) { 806 if (metric != METRIC_QUERY_RESULT) {
669 return NAN; 807 result.error_code = ERROR;
808 return result;
670 } 809 }
671 printf("CRITICAL - cannot parse value of type %s (%i)\n", 810 // printf("CRITICAL - cannot parse value of type %s (%i)\n",
672 (*field_type == DBI_TYPE_BINARY) ? "BINARY" 811 // (*field_type == DBI_TYPE_BINARY) ? "BINARY"
673 : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" 812 // : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME"
674 : "<unknown>", 813 // : "<unknown>",
675 *field_type); 814 // *field_type);
676 *field_type = DBI_TYPE_ERROR; 815 field_type = DBI_TYPE_ERROR;
677 return NAN; 816 result.error_code = ERROR;
678 } 817 }
679 return val; 818 return result;
680} 819}
681 820
682mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val, mp_dbi_metric metric, mp_dbi_type type) { 821static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
683 unsigned short field_type; 822 char *query) {
684 double val = NAN; 823 assert(query);
685 824
686 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { 825 if (verbose) {
687 if (metric != METRIC_QUERY_RESULT) { 826 printf("Executing query '%s'\n", query);
688 return STATE_OK;
689 }
690 np_dbi_print_error(conn, "CRITICAL - failed to fetch rows");
691 return STATE_CRITICAL;
692 }
693
694 if (dbi_result_get_numrows(res) < 1) {
695 if (metric != METRIC_QUERY_RESULT) {
696 return STATE_OK;
697 }
698 printf("WARNING - no rows returned\n");
699 return STATE_WARNING;
700 }
701
702 if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) {
703 if (metric != METRIC_QUERY_RESULT) {
704 return STATE_OK;
705 }
706 np_dbi_print_error(conn, "CRITICAL - failed to fetch fields");
707 return STATE_CRITICAL;
708 }
709
710 if (dbi_result_get_numfields(res) < 1) {
711 if (metric != METRIC_QUERY_RESULT) {
712 return STATE_OK;
713 }
714 printf("WARNING - no fields returned\n");
715 return STATE_WARNING;
716 }
717
718 if (dbi_result_first_row(res) != 1) {
719 if (metric != METRIC_QUERY_RESULT) {
720 return STATE_OK;
721 }
722 np_dbi_print_error(conn, "CRITICAL - failed to fetch first row");
723 return STATE_CRITICAL;
724 } 827 }
725 828
726 field_type = dbi_result_get_field_type_idx(res, 1); 829 do_query_result result = {
727 if (field_type != DBI_TYPE_ERROR) { 830 .query_duration = 0,
728 if (type == TYPE_STRING) { 831 .result_string = NULL,
729 /* the value will be freed in dbi_result_free */ 832 .result_number = 0,
730 *res_val_str = strdup(get_field_str(conn, res, field_type, metric, type)); 833 .error_code = 0,
731 } else { 834 .query_processing_status = STATE_UNKNOWN,
732 val = get_field(conn, res, &field_type, metric, type); 835 };
733 }
734 }
735 836
736 *res_val = val; 837 struct timeval timeval_start;
838 gettimeofday(&timeval_start, NULL);
737 839
738 if (field_type == DBI_TYPE_ERROR) { 840 dbi_result res = dbi_conn_query(conn, query);
739 if (metric != METRIC_QUERY_RESULT) { 841 if (!res) {
740 return STATE_OK; 842 dbi_conn_error(conn, &result.error_string);
741 } 843 result.error_code = 1;
742 np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); 844 return result;
743 return STATE_CRITICAL;
744 } 845 }
745 846
746 dbi_result_free(res);
747 return STATE_OK;
748}
749
750mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time, mp_dbi_metric metric, mp_dbi_type type,
751 char *np_dbi_query) {
752 dbi_result res;
753
754 struct timeval timeval_start;
755 struct timeval timeval_end; 847 struct timeval timeval_end;
756 mp_state_enum status = STATE_OK; 848 gettimeofday(&timeval_end, NULL);
757 849 result.query_duration = timediff(timeval_start, timeval_end);
758 assert(np_dbi_query);
759 850
760 if (verbose) { 851 if (verbose) {
761 printf("Executing query '%s'\n", np_dbi_query); 852 printf("Query duration: %f\n", result.query_duration);
762 } 853 }
763 854
764 gettimeofday(&timeval_start, NULL); 855 // Default state is OK, all error will be set explicitly
856 mp_state_enum query_processing_state = STATE_OK;
857 {
765 858
766 res = dbi_conn_query(conn, np_dbi_query); 859 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
767 if (!res) { 860 if (metric != METRIC_QUERY_RESULT) {
768 np_dbi_print_error(conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); 861 query_processing_state = STATE_OK;
769 return STATE_CRITICAL; 862 } else {
863 dbi_conn_error(conn, &result.error_string);
864 query_processing_state = STATE_CRITICAL;
865 }
866 } else if (dbi_result_get_numrows(res) < 1) {
867 if (metric != METRIC_QUERY_RESULT) {
868 query_processing_state = STATE_OK;
869 } else {
870 result.error_string = "no rows returned";
871 // printf("WARNING - no rows returned\n");
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 }
770 } 919 }
920 dbi_result_free(res);
771 921
772 status = get_query_result(conn, res, res_val_str, res_val, metric, type); 922 result.query_processing_status = query_processing_state;
773
774 gettimeofday(&timeval_end, NULL);
775 *res_time = timediff(timeval_start, timeval_end);
776
777 if (verbose) {
778 printf("Time elapsed: %f\n", *res_time);
779 }
780 923
781 return status; 924 return result;
782} 925}
783 926
784double timediff(struct timeval start, struct timeval end) { 927static double timediff(struct timeval start, struct timeval end) {
785 double diff; 928 double diff;
786 929
787 while (start.tv_usec > end.tv_usec) { 930 while (start.tv_usec > end.tv_usec) {
@@ -792,7 +935,7 @@ double timediff(struct timeval start, struct timeval end) {
792 return diff; 935 return diff;
793} 936}
794 937
795void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { 938static void np_dbi_print_error(dbi_conn conn, char *fmt, ...) {
796 const char *errmsg = NULL; 939 const char *errmsg = NULL;
797 va_list ap; 940 va_list ap;
798 941