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