From e4a32bfb39ddbe90b95ec4c4dee6ab559d0cec42 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:34:17 +0200 Subject: Refactoring check_dbi --- plugins/check_dbi.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'plugins') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 468ded31..2e110ce3 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -76,21 +76,6 @@ static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/, mp_dbi_type /*type*/, char * /*np_dbi_query*/); int main(int argc, char **argv) { - int status = STATE_UNKNOWN; - - dbi_driver driver; - dbi_conn conn; - - unsigned int server_version; - - struct timeval start_timeval; - struct timeval end_timeval; - double conn_time = 0.0; - double query_time = 0.0; - - const char *query_val_str = NULL; - double query_val = 0.0; - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -116,8 +101,7 @@ int main(int argc, char **argv) { printf("Initializing DBI\n"); } - dbi_inst *instance_p = {0}; - + dbi_inst *instance_p = NULL; if (dbi_initialize_r(NULL, instance_p) < 0) { printf( "UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); @@ -133,7 +117,7 @@ int main(int argc, char **argv) { printf("Opening DBI driver '%s'\n", config.dbi_driver); } - driver = dbi_driver_open_r(config.dbi_driver, instance_p); + dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p); if (!driver) { printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver); @@ -147,9 +131,10 @@ int main(int argc, char **argv) { } /* make a connection to the database */ + struct timeval start_timeval; gettimeofday(&start_timeval, NULL); - conn = dbi_conn_open(driver); + dbi_conn conn = dbi_conn_open(driver); if (!conn) { printf("UNKNOWN - failed top open connection object.\n"); dbi_conn_close(conn); @@ -210,14 +195,16 @@ int main(int argc, char **argv) { return STATE_UNKNOWN; } + struct timeval end_timeval; gettimeofday(&end_timeval, NULL); - conn_time = timediff(start_timeval, end_timeval); + double conn_time = timediff(start_timeval, end_timeval); - server_version = dbi_conn_get_engine_version(conn); + unsigned int server_version = dbi_conn_get_engine_version(conn); if (verbose) { printf("Connected to server version %u\n", server_version); } + int status = STATE_UNKNOWN; if (config.metric == METRIC_SERVER_VERSION) { status = get_status(server_version, config.dbi_thresholds); } @@ -243,6 +230,10 @@ int main(int argc, char **argv) { } } + + const char *query_val_str = NULL; + double query_val = 0.0; + double query_time = 0.0; if (config.dbi_query) { /* execute query */ status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, -- cgit v1.2.3-74-g34f1 From fafaee5813830c5ad963f4855566be9a4a6dfc22 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:25:44 +0200 Subject: Fix some bugs after refactoring --- plugins/check_dbi.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'plugins') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 2e110ce3..786fc1b6 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -29,6 +29,7 @@ * *****************************************************************************/ +#include "states.h" const char *progname = "check_dbi"; const char *copyright = "2011-2024"; const char *email = "devel@monitoring-plugins.org"; @@ -101,13 +102,15 @@ int main(int argc, char **argv) { printf("Initializing DBI\n"); } - dbi_inst *instance_p = NULL; - if (dbi_initialize_r(NULL, instance_p) < 0) { + dbi_inst instance_p = NULL; + if (dbi_initialize_r(NULL, &instance_p) < 0) { printf( "UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); return STATE_UNKNOWN; } + dbi_set_verbosity_r(0, instance_p); + if (instance_p == NULL) { printf("UNKNOWN - failed to initialize DBI.\n"); return STATE_UNKNOWN; @@ -230,7 +233,6 @@ int main(int argc, char **argv) { } } - const char *query_val_str = NULL; double query_val = 0.0; double query_time = 0.0; @@ -251,10 +253,15 @@ int main(int argc, char **argv) { status = STATE_OK; } } else if (config.expect_re_str) { - int err; - + int comp_err; regex_t expect_re = {}; - err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); + comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags); + if (comp_err != 0) { + // TODO error, failed to compile regex + return STATE_UNKNOWN; + } + + int err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); if (!err) { status = STATE_OK; } else if (err == REG_NOMATCH) { -- cgit v1.2.3-74-g34f1 From ec5fd11c1d1f6117e1bccf4955fa99ba5706c6b4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:05:55 +0100 Subject: check_dbi: new output functionality --- plugins/check_dbi.c | 430 ++++++++++++++++++++++++++++++++++---------------- plugins/t/check_dbi.t | 10 +- 2 files changed, 295 insertions(+), 145 deletions(-) (limited to 'plugins') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 786fc1b6..84e3cb5b 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -29,12 +29,14 @@ * *****************************************************************************/ -#include "states.h" const char *progname = "check_dbi"; const char *copyright = "2011-2024"; const char *email = "devel@monitoring-plugins.org"; #include "../lib/monitoringplug.h" +#include "perfdata.h" +#include "output.h" +#include "states.h" #include "check_dbi.d/config.h" #include "common.h" #include "utils.h" @@ -72,9 +74,15 @@ static double timediff(struct timeval /*start*/, struct timeval /*end*/); static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); -static mp_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*/, char * /*np_dbi_query*/); +typedef struct { + char *result_string; + double result_number; + double query_duration; + int error_code; + const char *error_string; + mp_state_enum query_processing_status; +} do_query_result; +static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type type, char *query); int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -104,16 +112,17 @@ int main(int argc, char **argv) { dbi_inst instance_p = NULL; if (dbi_initialize_r(NULL, &instance_p) < 0) { - printf( - "UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); - return STATE_UNKNOWN; + printf("failed to initialize DBI; possibly you don't have any drivers installed.\n"); + exit(STATE_UNKNOWN); } + // Try to prevent libdbi from printing stuff on stderr + // Who thought that would be a good idea anyway? dbi_set_verbosity_r(0, instance_p); if (instance_p == NULL) { - printf("UNKNOWN - failed to initialize DBI.\n"); - return STATE_UNKNOWN; + printf("failed to initialize DBI.\n"); + exit(STATE_UNKNOWN); } if (verbose) { @@ -122,15 +131,14 @@ int main(int argc, char **argv) { dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p); if (!driver) { - printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", - config.dbi_driver); + printf("failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver); printf("Known drivers:\n"); for (driver = dbi_driver_list_r(NULL, instance_p); driver; driver = dbi_driver_list_r(driver, instance_p)) { printf(" - %s\n", dbi_driver_get_name(driver)); } - return STATE_UNKNOWN; + exit(STATE_UNKNOWN); } /* make a connection to the database */ @@ -141,7 +149,7 @@ int main(int argc, char **argv) { if (!conn) { printf("UNKNOWN - failed top open connection object.\n"); dbi_conn_close(conn); - return STATE_UNKNOWN; + exit(STATE_UNKNOWN); } for (size_t i = 0; i < config.dbi_options_num; ++i) { @@ -155,10 +163,10 @@ int main(int argc, char **argv) { if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { continue; } - /* else: status != 0 */ - np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", - config.dbi_options[i].key, config.dbi_options[i].value); + // Failing to set option + np_dbi_print_error(conn, "failed to set option '%s' to '%s'", config.dbi_options[i].key, + config.dbi_options[i].value); printf("Known driver options:\n"); for (opt = dbi_conn_get_option_list(conn, NULL); opt; @@ -166,7 +174,7 @@ int main(int argc, char **argv) { printf(" - %s\n", opt); } dbi_conn_close(conn); - return STATE_UNKNOWN; + exit(STATE_UNKNOWN); } if (config.host) { @@ -194,31 +202,60 @@ int main(int argc, char **argv) { } if (dbi_conn_connect(conn) < 0) { - np_dbi_print_error(conn, "UNKNOWN - failed to connect to database"); - return STATE_UNKNOWN; + np_dbi_print_error(conn, "failed to connect to database"); + exit(STATE_UNKNOWN); } struct timeval end_timeval; gettimeofday(&end_timeval, NULL); double conn_time = timediff(start_timeval, end_timeval); + if (verbose) { + printf("Time elapsed: %f\n", conn_time); + } + + mp_check overall = mp_check_init(); + + mp_subcheck sc_connection_time = mp_subcheck_init(); + sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_OK); + xasprintf(&sc_connection_time.output, "Connection time: %f", conn_time); + + mp_perfdata pd_conn_duration = perfdata_init(); + pd_conn_duration.label = "conntime"; + pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time); + + if (config.metric == METRIC_CONN_TIME) { + // TODO set pd thresholds + mp_state_enum status = get_status(conn_time, config.dbi_thresholds); + sc_connection_time = mp_set_subcheck_state(sc_connection_time, status); + if (status != STATE_OK) { + xasprintf(&sc_connection_time.output, "%s violates thresholds", + sc_connection_time.output); + } + } + + mp_add_perfdata_to_subcheck(&sc_connection_time, pd_conn_duration); + mp_add_subcheck_to_check(&overall, sc_connection_time); unsigned int server_version = dbi_conn_get_engine_version(conn); if (verbose) { printf("Connected to server version %u\n", server_version); } - int status = STATE_UNKNOWN; + mp_subcheck sc_server_version = mp_subcheck_init(); + sc_server_version = mp_set_subcheck_default_state(sc_server_version, STATE_OK); + xasprintf(&sc_server_version.output, "Connected to server version %u", server_version); + if (config.metric == METRIC_SERVER_VERSION) { - status = get_status(server_version, config.dbi_thresholds); - } + mp_state_enum status = get_status(server_version, config.dbi_thresholds); - if (verbose) { - printf("Time elapsed: %f\n", conn_time); - } + sc_server_version = mp_set_subcheck_state(sc_server_version, status); - if (config.metric == METRIC_CONN_TIME) { - status = get_status(conn_time, config.dbi_thresholds); - } + if (status != STATE_OK) { + xasprintf(&sc_server_version.output, "%s violates thresholds", + sc_server_version.output); + } + }; + mp_add_subcheck_to_check(&overall, sc_server_version); /* select a database */ if (config.dbi_database) { @@ -226,58 +263,152 @@ int main(int argc, char **argv) { printf("Selecting database '%s'\n", config.dbi_database); } + mp_subcheck sc_select_db = mp_subcheck_init(); + sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK); + if (dbi_conn_select_db(conn, config.dbi_database)) { np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.dbi_database); - return STATE_UNKNOWN; + exit(STATE_UNKNOWN); + } else { + mp_add_subcheck_to_check(&overall, sc_select_db); } } - const char *query_val_str = NULL; - double query_val = 0.0; - double query_time = 0.0; + // Do a query (if configured) if (config.dbi_query) { + mp_subcheck sc_query = mp_subcheck_init(); + sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN); + /* execute query */ - status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, - config.dbi_query); - if (status != STATE_OK) { - /* do_query prints an error message in this case */ - return status; - } + do_query_result query_res = do_query(conn, config.metric, config.type, config.dbi_query); + + if (query_res.error_code != 0) { + xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string); + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + } else if (query_res.query_processing_status != STATE_OK) { + if (query_res.error_string) { + xasprintf(&sc_query.output, "Failed to process query: %s", query_res.error_string); + } else { + xasprintf(&sc_query.output, "Failed to process query"); + } + sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status); + } else { + // query succeeded in general + xasprintf(&sc_query.output, "Query '%s' succeeded", config.dbi_query); + + // that's a OK by default now + sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK); + + // query duration first + mp_perfdata pd_query_duration = perfdata_init(); + pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration); + pd_query_duration.label = "querytime"; + if (config.metric == METRIC_QUERY_TIME) { + // TODO set thresholds + } + + mp_add_perfdata_to_subcheck(&sc_query, pd_query_duration); - if (config.metric == METRIC_QUERY_RESULT) { - if (config.expect) { - if ((!query_val_str) || strcmp(query_val_str, config.expect)) { - status = STATE_CRITICAL; + if (config.metric == METRIC_QUERY_RESULT) { + if (config.expect) { + if ((!query_res.result_string) || + strcmp(query_res.result_string, config.expect)) { + xasprintf(&sc_query.output, "Found string '%s' in query result", + config.expect); + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + } else { + xasprintf(&sc_query.output, "Did not find string '%s' in query result", + config.expect); + sc_query = mp_set_subcheck_state(sc_query, STATE_OK); + } + } else if (config.expect_re_str) { + int comp_err; + regex_t expect_re = {}; + comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags); + if (comp_err != 0) { + // TODO error, failed to compile regex + // TODO move this to config sanitatisation + printf("Failed to compile regex from string '%s'", config.expect_re_str); + exit(STATE_UNKNOWN); + } + + int err = + regexec(&expect_re, query_res.result_string, 0, NULL, /* flags = */ 0); + if (!err) { + sc_query = mp_set_subcheck_state(sc_query, STATE_OK); + xasprintf(&sc_query.output, "Found regular expression '%s' in query result", + config.expect_re_str); + } else if (err == REG_NOMATCH) { + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + xasprintf(&sc_query.output, + "Did not find regular expression '%s' in query result", + config.expect_re_str); + } else { + char errmsg[1024]; + regerror(err, &expect_re, errmsg, sizeof(errmsg)); + xasprintf(&sc_query.output, + "ERROR - failed to execute regular expression: %s\n", errmsg); + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + } } else { - status = STATE_OK; - } - } else if (config.expect_re_str) { - int comp_err; - regex_t expect_re = {}; - comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags); - if (comp_err != 0) { - // TODO error, failed to compile regex - return STATE_UNKNOWN; + // no string matching + if (isnan(query_res.result_number)) { + // The query result is not a number, but no string checking was configured + // so we expected a number + // this is a CRITICAL + xasprintf(&sc_query.output, "Query '%s' result is not numeric", + config.dbi_query, query_res.result_string); + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + + } else { + mp_state_enum query_numerical_result = + get_status(query_res.result_number, config.dbi_thresholds); + sc_query = mp_set_subcheck_state(sc_query, query_numerical_result); + + mp_perfdata pd_query_val = perfdata_init(); + pd_query_val = mp_set_pd_value(pd_query_val, query_res.result_number); + pd_query_val.label = "query"; + mp_add_perfdata_to_subcheck(&sc_query, pd_query_val); + + // TODO set pd thresholds + // if (config.dbi_thresholds->warning) { + // pd_query_val.warn= config.dbi_thresholds->warning + // } else { + // } + + if (query_numerical_result == STATE_OK) { + xasprintf(&sc_query.output, + "Query result '%f' is within given thresholds", + query_res.result_number); + } else { + xasprintf(&sc_query.output, + "Query result '%f' violates the given thresholds", + query_res.result_number); + } + } } - - int err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); - if (!err) { - status = STATE_OK; - } else if (err == REG_NOMATCH) { - status = STATE_CRITICAL; + } else if (config.metric == METRIC_QUERY_TIME) { + mp_state_enum query_time_status = + get_status(query_res.query_duration, config.dbi_thresholds); + mp_set_subcheck_state(sc_query, query_time_status); + + if (query_time_status == STATE_OK) { + xasprintf(&sc_query.output, "Query duration '%f' is withing given thresholds", + query_res.query_duration); } else { - char errmsg[1024]; - regerror(err, &expect_re, errmsg, sizeof(errmsg)); - printf("ERROR - failed to execute regular expression: %s\n", errmsg); - status = STATE_CRITICAL; + xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds", + query_res.query_duration); } } else { - status = get_status(query_val, config.dbi_thresholds); + /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error + * which should have been reported and handled (abort) before + * ... unless we expected a string to be returned */ + assert((!isnan(query_res.result_number)) || (config.type == TYPE_STRING)); } - } else if (config.metric == METRIC_QUERY_TIME) { - status = get_status(query_time, config.dbi_thresholds); } + + mp_add_subcheck_to_check(&overall, sc_query); } if (verbose) { @@ -285,63 +416,7 @@ int main(int argc, char **argv) { } dbi_conn_close(conn); - /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error - * which should have been reported and handled (abort) before - * ... unless we expected a string to be returned */ - assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || - (config.type == TYPE_STRING)); - - assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str)); - - printf("%s - connection time: %fs", state_text(status), conn_time); - if (config.dbi_query) { - if (config.type == TYPE_STRING) { - assert(config.expect || config.expect_re_str); - printf(", '%s' returned '%s' in %fs", config.dbi_query, - query_val_str ? query_val_str : "", query_time); - if (status != STATE_OK) { - if (config.expect) { - printf(" (expected '%s')", config.expect); - } else if (config.expect_re_str) { - printf(" (expected regex /%s/%s)", config.expect_re_str, - ((config.expect_re_cflags & REG_ICASE) ? "i" : "")); - } - } - } else if (isnan(query_val)) { - printf(", '%s' query execution time: %fs", config.dbi_query, query_time); - } else { - printf(", '%s' returned %f in %fs", config.dbi_query, query_val, query_time); - } - } - - printf( - " | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, - ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "", - ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "", - server_version, - ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range - : "", - ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range - : ""); - if (config.dbi_query) { - if (!isnan(query_val)) { /* this is also true when -e is used */ - printf(" query=%f;%s;%s;;", query_val, - ((config.metric == METRIC_QUERY_RESULT) && config.warning_range) - ? config.warning_range - : "", - ((config.metric == METRIC_QUERY_RESULT) && config.critical_range) - ? config.critical_range - : ""); - } - printf(" querytime=%fs;%s;%s;0;", query_time, - ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range - : "", - ((config.metric == METRIC_QUERY_TIME) && config.critical_range) - ? config.critical_range - : ""); - } - printf("\n"); - return status; + mp_exit(overall); } /* process command-line arguments */ @@ -349,7 +424,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { int option = 0; static struct option longopts[] = {STD_LONG_OPTS, - {"expect", required_argument, 0, 'e'}, {"regex", required_argument, 0, 'r'}, {"regexi", required_argument, 0, 'R'}, @@ -533,6 +607,10 @@ check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrap usage("Options -r/-R require metric QUERY_RESULT"); } + if (config_wrapper.config.type == TYPE_STRING) { + assert(config_wrapper.config.expect || config_wrapper.config.expect_re_str); + } + config_wrapper.errorcode = OK; return config_wrapper; } @@ -783,38 +861,110 @@ mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_v return STATE_OK; } -mp_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, char *np_dbi_query) { - dbi_result res; - - struct timeval timeval_start; - struct timeval timeval_end; - mp_state_enum status = STATE_OK; - - assert(np_dbi_query); +static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type type, + char *query) { + assert(query); if (verbose) { - printf("Executing query '%s'\n", np_dbi_query); + printf("Executing query '%s'\n", query); } + do_query_result result = { + .query_duration = 0, + .result_string = NULL, + .result_number = 0, + .error_code = 0, + .query_processing_status = STATE_UNKNOWN, + }; + + struct timeval timeval_start; gettimeofday(&timeval_start, NULL); - res = dbi_conn_query(conn, np_dbi_query); + dbi_result res = dbi_conn_query(conn, query); if (!res) { - np_dbi_print_error(conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); - return STATE_CRITICAL; + dbi_conn_error(conn, &result.error_string); + result.error_code = 1; + return result; } - status = get_query_result(conn, res, res_val_str, res_val, metric, type); - + struct timeval timeval_end; gettimeofday(&timeval_end, NULL); - *res_time = timediff(timeval_start, timeval_end); + result.query_duration = timediff(timeval_start, timeval_end); if (verbose) { - printf("Time elapsed: %f\n", *res_time); + printf("Query duration: %f\n", result.query_duration); + } + + // Default state is OK, all error will be set explicitely + mp_state_enum query_processing_state = STATE_OK; + { + + if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { + if (metric != METRIC_QUERY_RESULT) { + query_processing_state = STATE_OK; + } else { + dbi_conn_error(conn, &result.error_string); + query_processing_state = STATE_CRITICAL; + } + } else if (dbi_result_get_numrows(res) < 1) { + if (metric != METRIC_QUERY_RESULT) { + query_processing_state = STATE_OK; + } else { + result.error_string = "no rows returned"; + // printf("WARNING - no rows returned\n"); + query_processing_state = STATE_WARNING; + } + } else if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) { + if (metric != METRIC_QUERY_RESULT) { + query_processing_state = STATE_OK; + } else { + dbi_conn_error(conn, &result.error_string); + // np_dbi_print_error(conn, "CRITICAL - failed to fetch fields"); + query_processing_state = STATE_CRITICAL; + } + } else if (dbi_result_get_numfields(res) < 1) { + if (metric != METRIC_QUERY_RESULT) { + query_processing_state = STATE_OK; + } else { + result.error_string = "no fields returned"; + // printf("WARNING - no fields returned\n"); + query_processing_state = STATE_WARNING; + } + } else if (dbi_result_first_row(res) != 1) { + if (metric != METRIC_QUERY_RESULT) { + query_processing_state = STATE_OK; + } else { + dbi_conn_error(conn, &result.error_string); + // np_dbi_print_error(conn, "CRITICAL - failed to fetch first row"); + query_processing_state = STATE_CRITICAL; + } + } else { + unsigned short field_type = dbi_result_get_field_type_idx(res, 1); + if (field_type != DBI_TYPE_ERROR) { + if (type == TYPE_STRING) { + result.result_string = + strdup(get_field_str(conn, res, field_type, metric, type)); + } else { + result.result_number = get_field(conn, res, &field_type, metric, type); + } + } else { + // Error when retrieving the field, that is OK if the Query result is not of + // interest + if (metric != METRIC_QUERY_RESULT) { + query_processing_state = STATE_OK; + } else { + dbi_conn_error(conn, &result.error_string); + // np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); + query_processing_state = STATE_CRITICAL; + } + } + } } + dbi_result_free(res); + + result.query_processing_status = query_processing_state; - return status; + return result; } double timediff(struct timeval start, struct timeval end) { 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; my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; -my $conn_time_output = "/OK - connection time: [0-9\.]+s \|/"; +my $conn_time_output = "/connection time: [0-9\.]+s \|/"; my $missing_query_output = "/Must specify a query to execute/"; -my $no_rows_output = "/WARNING - no rows returned/"; -my $not_numeric_output = "/CRITICAL - result value is not a numeric:/"; -my $query_time_output = "/OK - connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/"; -my $syntax_error_output = "/CRITICAL - failed to execute query 'GET ALL FROM test': 1: near \"GET\": syntax error/"; +my $no_rows_output = "/no rows returned/"; +my $not_numeric_output = "/result value is not a numeric:/"; +my $query_time_output = "/connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/"; +my $syntax_error_output = "/1: near \"GET\": syntax error/"; my $result; -- cgit v1.2.3-74-g34f1 From b504a6284ea35efcea4debe93f3dbd35bf9427f3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:08:23 +0100 Subject: Fix spelling errors --- plugins/check_dbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 84e3cb5b..035e30cd 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -394,7 +394,7 @@ int main(int argc, char **argv) { mp_set_subcheck_state(sc_query, query_time_status); if (query_time_status == STATE_OK) { - xasprintf(&sc_query.output, "Query duration '%f' is withing given thresholds", + xasprintf(&sc_query.output, "Query duration '%f' is within given thresholds", query_res.query_duration); } else { xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds", @@ -895,7 +895,7 @@ static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type printf("Query duration: %f\n", result.query_duration); } - // Default state is OK, all error will be set explicitely + // Default state is OK, all error will be set explicitly mp_state_enum query_processing_state = STATE_OK; { -- cgit v1.2.3-74-g34f1 From 071de8a73a283ac7e262194c2c08fabfef4473b1 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 29 Oct 2025 23:27:12 +0100 Subject: check_dbi: more refactoring for sanities sake --- plugins/check_dbi.c | 168 ++++++++++++++++------------------------------------ 1 file changed, 51 insertions(+), 117 deletions(-) (limited to 'plugins') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 035e30cd..ddd0b328 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -358,7 +358,7 @@ int main(int argc, char **argv) { // so we expected a number // this is a CRITICAL xasprintf(&sc_query.output, "Query '%s' result is not numeric", - config.dbi_query, query_res.result_string); + config.dbi_query); sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); } else { @@ -490,7 +490,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { } break; } - case 'm': if (!strcasecmp(optarg, "CONN_TIME")) { result.config.metric = METRIC_CONN_TIME; @@ -510,7 +509,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { } else { timeout_interval = atoi(optarg); } - break; case 'H': /* host */ if (!is_host(optarg)) { @@ -522,7 +520,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { case 'v': verbose++; break; - case 'd': result.config.dbi_driver = optarg; break; @@ -718,21 +715,12 @@ void print_usage(void) { printf(" [-e ] [-r|-R ]\n"); } -const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type, - mp_dbi_metric metric, mp_dbi_type type) { - const char *str; - - if (field_type != DBI_TYPE_STRING) { - printf("CRITICAL - result value is not a string\n"); - return NULL; - } - - str = dbi_result_get_string_idx(res, 1); +const char *get_field_str(dbi_result res, mp_dbi_metric metric, mp_dbi_type type) { + const char *str = dbi_result_get_string_idx(res, 1); if ((!str) || (strcmp(str, "ERROR") == 0)) { if (metric != METRIC_QUERY_RESULT) { return NULL; } - np_dbi_print_error(conn, "CRITICAL - failed to fetch string value"); return NULL; } @@ -742,36 +730,50 @@ const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_ty return str; } -double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric, - mp_dbi_type type) { - double val = NAN; +typedef struct { + double value; + int error_code; + int dbi_error_code; // not sure if useful +} get_field_wrapper; +get_field_wrapper get_field(dbi_result res, mp_dbi_metric metric, mp_dbi_type type) { + + unsigned short field_type = dbi_result_get_field_type_idx(res, 1); + get_field_wrapper result = { + .value = NAN, + .error_code = OK, + }; - if (*field_type == DBI_TYPE_INTEGER) { - val = (double)dbi_result_get_longlong_idx(res, 1); - } else if (*field_type == DBI_TYPE_DECIMAL) { - val = dbi_result_get_double_idx(res, 1); - } else if (*field_type == DBI_TYPE_STRING) { + if (field_type == DBI_TYPE_INTEGER) { + result.value = (double)dbi_result_get_longlong_idx(res, 1); + } else if (field_type == DBI_TYPE_DECIMAL) { + result.value = dbi_result_get_double_idx(res, 1); + } else if (field_type == DBI_TYPE_STRING) { const char *val_str; char *endptr = NULL; - val_str = get_field_str(conn, res, *field_type, metric, type); + val_str = get_field_str(res, metric, type); if (!val_str) { - if (metric != METRIC_QUERY_RESULT) { - return NAN; - } - *field_type = DBI_TYPE_ERROR; - return NAN; + result.error_code = ERROR; + field_type = DBI_TYPE_ERROR; + return result; } - val = strtod(val_str, &endptr); + result.value = strtod(val_str, &endptr); if (endptr == val_str) { if (metric != METRIC_QUERY_RESULT) { - return NAN; + result.error_code = ERROR; + return result; } - printf("CRITICAL - result value is not a numeric: %s\n", val_str); - *field_type = DBI_TYPE_ERROR; - return NAN; + + if (verbose) { + printf("CRITICAL - result value is not a numeric: %s\n", val_str); + } + + field_type = DBI_TYPE_ERROR; + result.error_code = ERROR; + return result; } + if ((endptr != NULL) && (*endptr != '\0')) { if (verbose) { printf("Garbage after value: %s\n", endptr); @@ -779,86 +781,18 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_d } } else { if (metric != METRIC_QUERY_RESULT) { - return NAN; + result.error_code = ERROR; + return result; } - printf("CRITICAL - cannot parse value of type %s (%i)\n", - (*field_type == DBI_TYPE_BINARY) ? "BINARY" - : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" - : "", - *field_type); - *field_type = DBI_TYPE_ERROR; - return NAN; - } - return val; -} - -mp_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) { - unsigned short field_type; - double val = NAN; - - if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { - if (metric != METRIC_QUERY_RESULT) { - return STATE_OK; - } - np_dbi_print_error(conn, "CRITICAL - failed to fetch rows"); - return STATE_CRITICAL; + // printf("CRITICAL - cannot parse value of type %s (%i)\n", + // (*field_type == DBI_TYPE_BINARY) ? "BINARY" + // : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" + // : "", + // *field_type); + field_type = DBI_TYPE_ERROR; + result.error_code = ERROR; } - - if (dbi_result_get_numrows(res) < 1) { - if (metric != METRIC_QUERY_RESULT) { - return STATE_OK; - } - printf("WARNING - no rows returned\n"); - return STATE_WARNING; - } - - if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) { - if (metric != METRIC_QUERY_RESULT) { - return STATE_OK; - } - np_dbi_print_error(conn, "CRITICAL - failed to fetch fields"); - return STATE_CRITICAL; - } - - if (dbi_result_get_numfields(res) < 1) { - if (metric != METRIC_QUERY_RESULT) { - return STATE_OK; - } - printf("WARNING - no fields returned\n"); - return STATE_WARNING; - } - - if (dbi_result_first_row(res) != 1) { - if (metric != METRIC_QUERY_RESULT) { - return STATE_OK; - } - np_dbi_print_error(conn, "CRITICAL - failed to fetch first row"); - return STATE_CRITICAL; - } - - field_type = dbi_result_get_field_type_idx(res, 1); - if (field_type != DBI_TYPE_ERROR) { - if (type == TYPE_STRING) { - /* the value will be freed in dbi_result_free */ - *res_val_str = strdup(get_field_str(conn, res, field_type, metric, type)); - } else { - val = get_field(conn, res, &field_type, metric, type); - } - } - - *res_val = val; - - if (field_type == DBI_TYPE_ERROR) { - if (metric != METRIC_QUERY_RESULT) { - return STATE_OK; - } - np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); - return STATE_CRITICAL; - } - - dbi_result_free(res); - return STATE_OK; + return result; } static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type type, @@ -942,10 +876,10 @@ static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type unsigned short field_type = dbi_result_get_field_type_idx(res, 1); if (field_type != DBI_TYPE_ERROR) { if (type == TYPE_STRING) { - result.result_string = - strdup(get_field_str(conn, res, field_type, metric, type)); + result.result_string = strdup(get_field_str(res, metric, type)); } else { - result.result_number = get_field(conn, res, &field_type, metric, type); + get_field_wrapper gfw = get_field(res, metric, type); + result.result_number = gfw.value; } } else { // Error when retrieving the field, that is OK if the Query result is not of @@ -967,7 +901,7 @@ static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type return result; } -double timediff(struct timeval start, struct timeval end) { +static double timediff(struct timeval start, struct timeval end) { double diff; while (start.tv_usec > end.tv_usec) { @@ -978,7 +912,7 @@ double timediff(struct timeval start, struct timeval end) { return diff; } -void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { +static void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { const char *errmsg = NULL; va_list ap; -- cgit v1.2.3-74-g34f1 From 408783f53d0baaf7341c7c584ca64a9073d66ee2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 29 Oct 2025 23:27:31 +0100 Subject: check_dbi: add output format parameter --- plugins/check_dbi.c | 20 ++++++++++++++++++++ plugins/check_dbi.d/config.h | 4 ++++ 2 files changed, 24 insertions(+) (limited to 'plugins') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index ddd0b328..286635cc 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -100,6 +100,10 @@ int main(int argc, char **argv) { const check_dbi_config config = tmp.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* Set signal handling and alarm */ if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { usage4(_("Cannot catch SIGALRM")); @@ -421,6 +425,9 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_dbi_config_wrapper process_arguments(int argc, char **argv) { + enum { + output_format_index = CHAR_MAX + 1, + }; int option = 0; static struct option longopts[] = {STD_LONG_OPTS, @@ -432,6 +439,7 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { {"option", required_argument, 0, 'o'}, {"query", required_argument, 0, 'q'}, {"database", required_argument, 0, 'D'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_dbi_config_wrapper result = { @@ -556,6 +564,18 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { case 'D': result.config.dbi_database = optarg; break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } } } diff --git a/plugins/check_dbi.d/config.h b/plugins/check_dbi.d/config.h index f6f0d7b3..09aa67da 100644 --- a/plugins/check_dbi.d/config.h +++ b/plugins/check_dbi.d/config.h @@ -38,6 +38,8 @@ typedef struct { char *critical_range; thresholds *dbi_thresholds; + bool output_format_is_set; + mp_output_format output_format; } check_dbi_config; check_dbi_config check_dbi_config_init() { @@ -58,6 +60,8 @@ check_dbi_config check_dbi_config_init() { .warning_range = NULL, .critical_range = NULL, .dbi_thresholds = NULL, + + .output_format_is_set = false, }; return tmp; } -- cgit v1.2.3-74-g34f1 From 669edf2afca4d3674019bceb037b0ccc2d938b2a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 30 Oct 2025 21:34:50 +0100 Subject: check_curl: accept non standard compliant status line If the status line from a server ended with '\n' instead of '\r\n' (defined by RFC 9112), check_curl failed to parse it and exited with an alarm. The RFC recommends to be lenient here and this change follows that suggestion. --- plugins/check_curl.d/check_curl_helpers.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index c3c2ba55..d1d282be 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "../utils.h" #include "check_curl.d/config.h" #include "output.h" @@ -816,7 +817,8 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) buf = start; } - char *first_line_end = strstr(buf, "\r\n"); + size_t length_of_first_line = strcspn(buf, "\r\n"); + const char *first_line_end = &buf[length_of_first_line]; if (first_line_end == NULL) { return -1; } @@ -826,6 +828,7 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) if (status_line->first_line == NULL) { return -1; } + memcpy(status_line->first_line, buf, first_line_len); status_line->first_line[first_line_len] = '\0'; char *first_line_buf = strdup(status_line->first_line); @@ -833,23 +836,34 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ char *temp_string = strtok(first_line_buf, "/"); if (temp_string == NULL) { + if (verbose > 1) { + printf("%s: no / found\n", __func__); + } free(first_line_buf); return -1; } + if (strcmp(temp_string, "HTTP") != 0) { + if (verbose > 1) { + printf("%s: string 'HTTP' not found\n", __func__); + } free(first_line_buf); return -1; } + // try to find a space in the remaining string? + // the space after HTTP/1.1 probably temp_string = strtok(NULL, " "); if (temp_string == NULL) { + if (verbose > 1) { + printf("%s: no space after protocol definition\n", __func__); + } free(first_line_buf); return -1; } char *temp_string_2; if (strchr(temp_string, '.') != NULL) { - /* HTTP 1.x case */ strtok(temp_string, "."); status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); -- cgit v1.2.3-74-g34f1 From 6abf609ed91ab9b8e2c1fac0058d034ceef5f0e8 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:23:51 +0100 Subject: add some comments to explain changed code --- plugins/check_curl.d/check_curl_helpers.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'plugins') diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index d1d282be..d49d8f07 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -817,6 +817,8 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) buf = start; } + // Accept either LF or CRLF as end of line for the status line + // CRLF is the standard (RFC9112), but it is recommended to accept both size_t length_of_first_line = strcspn(buf, "\r\n"); const char *first_line_end = &buf[length_of_first_line]; if (first_line_end == NULL) { -- cgit v1.2.3-74-g34f1 From 0c70af5fef8bd7cbbad107224fd283f69ae0416a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 30 Oct 2025 23:58:38 +0100 Subject: check_dbi: more refactoring for coherence and simplification --- plugins/check_dbi.c | 121 ++++++++++++++++++++++--------------------- plugins/check_dbi.d/config.h | 27 +++++----- 2 files changed, 75 insertions(+), 73 deletions(-) (limited to 'plugins') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 286635cc..9bc68eb3 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -34,6 +34,7 @@ const char *copyright = "2011-2024"; const char *email = "devel@monitoring-plugins.org"; #include "../lib/monitoringplug.h" +#include "thresholds.h" #include "perfdata.h" #include "output.h" #include "states.h" @@ -66,7 +67,6 @@ typedef struct { } check_dbi_config_wrapper; static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper /*config_wrapper*/); void print_usage(void); static void print_help(void); @@ -82,7 +82,8 @@ typedef struct { const char *error_string; mp_state_enum query_processing_status; } do_query_result; -static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type type, char *query); +static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type, + char *query); int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -228,8 +229,8 @@ int main(int argc, char **argv) { pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time); if (config.metric == METRIC_CONN_TIME) { - // TODO set pd thresholds - mp_state_enum status = get_status(conn_time, config.dbi_thresholds); + pd_conn_duration = mp_pd_set_thresholds(pd_conn_duration, config.thresholds); + mp_state_enum status = mp_get_pd_status(pd_conn_duration); sc_connection_time = mp_set_subcheck_state(sc_connection_time, status); if (status != STATE_OK) { xasprintf(&sc_connection_time.output, "%s violates thresholds", @@ -250,7 +251,11 @@ int main(int argc, char **argv) { xasprintf(&sc_server_version.output, "Connected to server version %u", server_version); if (config.metric == METRIC_SERVER_VERSION) { - mp_state_enum status = get_status(server_version, config.dbi_thresholds); + mp_perfdata pd_server_version = perfdata_init(); + pd_server_version = mp_set_pd_value(pd_server_version, server_version); + pd_server_version = mp_pd_set_thresholds(pd_server_version, config.thresholds); + mp_state_enum status = mp_get_pd_status(pd_server_version); + mp_add_perfdata_to_subcheck(&sc_server_version, pd_server_version); sc_server_version = mp_set_subcheck_state(sc_server_version, status); @@ -262,17 +267,16 @@ int main(int argc, char **argv) { mp_add_subcheck_to_check(&overall, sc_server_version); /* select a database */ - if (config.dbi_database) { + if (config.database) { if (verbose > 1) { - printf("Selecting database '%s'\n", config.dbi_database); + printf("Selecting database '%s'\n", config.database); } mp_subcheck sc_select_db = mp_subcheck_init(); sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK); - if (dbi_conn_select_db(conn, config.dbi_database)) { - np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", - config.dbi_database); + if (dbi_conn_select_db(conn, config.database)) { + np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.database); exit(STATE_UNKNOWN); } else { mp_add_subcheck_to_check(&overall, sc_select_db); @@ -280,12 +284,12 @@ int main(int argc, char **argv) { } // Do a query (if configured) - if (config.dbi_query) { + if (config.query) { mp_subcheck sc_query = mp_subcheck_init(); sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN); /* execute query */ - do_query_result query_res = do_query(conn, config.metric, config.type, config.dbi_query); + do_query_result query_res = do_query(conn, config.metric, config.type, config.query); if (query_res.error_code != 0) { xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string); @@ -299,7 +303,7 @@ int main(int argc, char **argv) { sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status); } else { // query succeeded in general - xasprintf(&sc_query.output, "Query '%s' succeeded", config.dbi_query); + xasprintf(&sc_query.output, "Query '%s' succeeded", config.query); // that's a OK by default now sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK); @@ -309,7 +313,7 @@ int main(int argc, char **argv) { pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration); pd_query_duration.label = "querytime"; if (config.metric == METRIC_QUERY_TIME) { - // TODO set thresholds + pd_query_duration = mp_pd_set_thresholds(pd_query_duration, config.thresholds); } mp_add_perfdata_to_subcheck(&sc_query, pd_query_duration); @@ -362,19 +366,20 @@ int main(int argc, char **argv) { // so we expected a number // this is a CRITICAL xasprintf(&sc_query.output, "Query '%s' result is not numeric", - config.dbi_query); + config.query); sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); } else { - mp_state_enum query_numerical_result = - get_status(query_res.result_number, config.dbi_thresholds); - sc_query = mp_set_subcheck_state(sc_query, query_numerical_result); mp_perfdata pd_query_val = perfdata_init(); pd_query_val = mp_set_pd_value(pd_query_val, query_res.result_number); pd_query_val.label = "query"; + pd_query_val = mp_pd_set_thresholds(pd_query_val, config.thresholds); + mp_add_perfdata_to_subcheck(&sc_query, pd_query_val); + mp_state_enum query_numerical_result = mp_get_pd_status(pd_query_val); + sc_query = mp_set_subcheck_state(sc_query, query_numerical_result); // TODO set pd thresholds // if (config.dbi_thresholds->warning) { // pd_query_val.warn= config.dbi_thresholds->warning @@ -393,8 +398,7 @@ int main(int argc, char **argv) { } } } else if (config.metric == METRIC_QUERY_TIME) { - mp_state_enum query_time_status = - get_status(query_res.query_duration, config.dbi_thresholds); + mp_state_enum query_time_status = mp_get_pd_status(pd_query_duration); mp_set_subcheck_state(sc_query, query_time_status); if (query_time_status == STATE_OK) { @@ -464,14 +468,22 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { print_revision(progname, NP_VERSION); exit(STATE_UNKNOWN); - case 'c': /* critical range */ - result.config.critical_range = optarg; + case 'c': /* critical range */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical threshold"); + } + result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range); result.config.type = TYPE_NUMERIC; - break; - case 'w': /* warning range */ - result.config.warning_range = optarg; + } break; + case 'w': /* warning range */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold"); + } + result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); result.config.type = TYPE_NUMERIC; - break; + } break; case 'e': result.config.expect = optarg; result.config.type = TYPE_STRING; @@ -559,10 +571,10 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { new->value = value; } break; case 'q': - result.config.dbi_query = optarg; + result.config.query = optarg; break; case 'D': - result.config.dbi_database = optarg; + result.config.database = optarg; break; case output_format_index: { parsed_output_format parser = mp_parse_output_format(optarg); @@ -579,57 +591,48 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { } } - set_thresholds(&result.config.dbi_thresholds, result.config.warning_range, - result.config.critical_range); - - return validate_arguments(result); -} - -check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrapper) { - if (!config_wrapper.config.dbi_driver) { + if (!result.config.dbi_driver) { usage("Must specify a DBI driver"); } - if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) || - (config_wrapper.config.metric == METRIC_QUERY_TIME)) && - (!config_wrapper.config.dbi_query)) { + if (((result.config.metric == METRIC_QUERY_RESULT) || + (result.config.metric == METRIC_QUERY_TIME)) && + (!result.config.query)) { usage("Must specify a query to execute (metric == QUERY_RESULT)"); } - if ((config_wrapper.config.metric != METRIC_CONN_TIME) && - (config_wrapper.config.metric != METRIC_SERVER_VERSION) && - (config_wrapper.config.metric != METRIC_QUERY_RESULT) && - (config_wrapper.config.metric != METRIC_QUERY_TIME)) { + if ((result.config.metric != METRIC_CONN_TIME) && + (result.config.metric != METRIC_SERVER_VERSION) && + (result.config.metric != METRIC_QUERY_RESULT) && + (result.config.metric != METRIC_QUERY_TIME)) { usage("Invalid metric specified"); } - if (config_wrapper.config.expect && - (config_wrapper.config.warning_range || config_wrapper.config.critical_range || - config_wrapper.config.expect_re_str)) { + if (result.config.expect && + (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set || + result.config.expect_re_str)) { usage("Do not mix -e and -w/-c/-r/-R"); } - if (config_wrapper.config.expect_re_str && - (config_wrapper.config.warning_range || config_wrapper.config.critical_range || - config_wrapper.config.expect)) { + if (result.config.expect_re_str && + (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set || + result.config.expect)) { usage("Do not mix -r/-R and -w/-c/-e"); } - if (config_wrapper.config.expect && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { + if (result.config.expect && (result.config.metric != METRIC_QUERY_RESULT)) { usage("Option -e requires metric QUERY_RESULT"); } - if (config_wrapper.config.expect_re_str && - (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { + if (result.config.expect_re_str && (result.config.metric != METRIC_QUERY_RESULT)) { usage("Options -r/-R require metric QUERY_RESULT"); } - if (config_wrapper.config.type == TYPE_STRING) { - assert(config_wrapper.config.expect || config_wrapper.config.expect_re_str); + if (result.config.type == TYPE_STRING) { + assert(result.config.expect || result.config.expect_re_str); } - config_wrapper.errorcode = OK; - return config_wrapper; + return result; } void print_help(void) { @@ -735,7 +738,7 @@ void print_usage(void) { printf(" [-e ] [-r|-R ]\n"); } -const char *get_field_str(dbi_result res, mp_dbi_metric metric, mp_dbi_type type) { +const char *get_field_str(dbi_result res, check_dbi_metric metric, check_dbi_type type) { const char *str = dbi_result_get_string_idx(res, 1); if ((!str) || (strcmp(str, "ERROR") == 0)) { if (metric != METRIC_QUERY_RESULT) { @@ -755,7 +758,7 @@ typedef struct { int error_code; int dbi_error_code; // not sure if useful } get_field_wrapper; -get_field_wrapper get_field(dbi_result res, mp_dbi_metric metric, mp_dbi_type type) { +get_field_wrapper get_field(dbi_result res, check_dbi_metric metric, check_dbi_type type) { unsigned short field_type = dbi_result_get_field_type_idx(res, 1); get_field_wrapper result = { @@ -815,7 +818,7 @@ get_field_wrapper get_field(dbi_result res, mp_dbi_metric metric, mp_dbi_type ty return result; } -static do_query_result do_query(dbi_conn conn, mp_dbi_metric metric, mp_dbi_type type, +static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type, char *query) { assert(query); diff --git a/plugins/check_dbi.d/config.h b/plugins/check_dbi.d/config.h index 09aa67da..25d74a12 100644 --- a/plugins/check_dbi.d/config.h +++ b/plugins/check_dbi.d/config.h @@ -3,18 +3,19 @@ #include "../../config.h" #include #include "../../lib/monitoringplug.h" +#include "thresholds.h" typedef enum { METRIC_CONN_TIME, METRIC_SERVER_VERSION, METRIC_QUERY_RESULT, METRIC_QUERY_TIME, -} mp_dbi_metric; +} check_dbi_metric; typedef enum { TYPE_NUMERIC, TYPE_STRING, -} mp_dbi_type; +} check_dbi_type; typedef struct { char *key; @@ -24,19 +25,19 @@ typedef struct { typedef struct { char *dbi_driver; char *host; + driver_option_t *dbi_options; size_t dbi_options_num; - char *dbi_database; - char *dbi_query; + + char *database; + char *query; char *expect; char *expect_re_str; int expect_re_cflags; - mp_dbi_metric metric; - mp_dbi_type type; - char *warning_range; - char *critical_range; - thresholds *dbi_thresholds; + check_dbi_metric metric; + check_dbi_type type; + mp_thresholds thresholds; bool output_format_is_set; mp_output_format output_format; @@ -48,8 +49,8 @@ check_dbi_config check_dbi_config_init() { .host = NULL, .dbi_options = NULL, .dbi_options_num = 0, - .dbi_database = NULL, - .dbi_query = NULL, + .database = NULL, + .query = NULL, .expect = NULL, .expect_re_str = NULL, @@ -57,9 +58,7 @@ check_dbi_config check_dbi_config_init() { .metric = METRIC_QUERY_RESULT, .type = TYPE_NUMERIC, - .warning_range = NULL, - .critical_range = NULL, - .dbi_thresholds = NULL, + .thresholds = mp_thresholds_init(), .output_format_is_set = false, }; -- cgit v1.2.3-74-g34f1 From e867c2ebd3eeea5726136dc8e7227b256b5e117d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 31 Oct 2025 18:13:32 +0100 Subject: check_ntp_peer: small refactoring --- plugins/check_ntp_peer.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 24d1c9b5..37e481c7 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -35,11 +35,11 @@ * *****************************************************************************/ -#include "thresholds.h" const char *progname = "check_ntp_peer"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; +#include "thresholds.h" #include "common.h" #include "netutils.h" #include "utils.h" @@ -47,8 +47,6 @@ const char *email = "devel@monitoring-plugins.org"; #include "check_ntp_peer.d/config.h" static int verbose = 0; -static bool syncsource_found = false; -static bool li_alarm = false; typedef struct { int errorcode; @@ -198,9 +196,7 @@ void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_ * positive value means a success retrieving the value. * - status is set to WARNING if there's no sync.peer (otherwise OK) and is * the return value of the function. - * status is pretty much useless as syncsource_found is a global variable - * used later in main to check is the server was synchronized. It works - * so I left it alone */ + */ typedef struct { mp_state_enum state; mp_state_enum offset_result; @@ -208,6 +204,8 @@ typedef struct { double jitter; long stratum; int num_truechimers; + bool syncsource_found; + bool li_alarm; } ntp_request_result; ntp_request_result ntp_request(const check_ntp_peer_config config) { @@ -217,6 +215,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { .jitter = -1, .stratum = -1, .num_truechimers = 0, + .syncsource_found = false, + .li_alarm = false, }; /* Long-winded explanation: @@ -235,19 +235,16 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { * 4) Extract the offset, jitter and stratum value from the data[] * (it's ASCII) */ - int min_peer_sel = PEER_INCLUDED; - int num_candidates = 0; - void *tmp; - ntp_assoc_status_pair *peers = NULL; - int peer_offset = 0; - size_t peers_size = 0; - size_t npeers = 0; int conn = -1; my_udp_connect(config.server_address, config.port, &conn); /* keep sending requests until the server stops setting the * REM_MORE bit, though usually this is only 1 packet. */ ntp_control_message req; + ntp_assoc_status_pair *peers = NULL; + int peer_offset = 0; + size_t peers_size = 0; + size_t npeers = 0; do { setup_control_request(&req, OP_READSTAT, 1); DBG(printf("sending READSTAT request")); @@ -269,12 +266,13 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); if (LI(req.flags) == LI_ALARM) { - li_alarm = true; + result.li_alarm = true; } /* Each peer identifier is 4 bytes in the data section, which * we represent as a ntp_assoc_status_pair datatype. */ peers_size += ntohs(req.count); + void *tmp; if ((tmp = realloc(peers, peers_size)) == NULL) { free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); } @@ -287,13 +285,15 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { /* first, let's find out if we have a sync source, or if there are * at least some candidates. In the latter case we'll issue * a warning but go ahead with the check on them. */ + int min_peer_sel = PEER_INCLUDED; + int num_candidates = 0; for (size_t i = 0; i < npeers; i++) { if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { result.num_truechimers++; if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { num_candidates++; if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { - syncsource_found = true; + result.syncsource_found = true; min_peer_sel = PEER_SYNCSOURCE; } } @@ -302,18 +302,18 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { if (verbose) { printf("%d candidate peers available\n", num_candidates); - } - if (verbose && syncsource_found) { - printf("synchronization source found\n"); + if (result.syncsource_found) { + printf("synchronization source found\n"); + } } - if (!syncsource_found) { + if (!result.syncsource_found) { result.state = STATE_WARNING; if (verbose) { printf("warning: no synchronization source found\n"); } } - if (li_alarm) { + if (result.li_alarm) { result.state = STATE_WARNING; if (verbose) { printf("warning: LI_ALARM bit is set\n"); @@ -634,7 +634,7 @@ int main(int argc, char *argv[]) { alarm(socket_timeout); /* This returns either OK or WARNING (See comment preceding ntp_request) */ - ntp_request_result ntp_res = ntp_request(config); + const ntp_request_result ntp_res = ntp_request(config); mp_state_enum result = STATE_UNKNOWN; if (ntp_res.offset_result == STATE_UNKNOWN) { @@ -686,9 +686,9 @@ int main(int argc, char *argv[]) { break; } - if (!syncsource_found) { + if (!ntp_res.syncsource_found) { xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); - } else if (li_alarm) { + } else if (ntp_res.li_alarm) { xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); } -- cgit v1.2.3-74-g34f1 From 6392a0f77635d82b9b68bcbd1be4c6acc478767f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 4 Nov 2025 10:13:39 +0100 Subject: check_ntp_peer: implement new output functionality --- plugins/check_ntp_peer.c | 248 ++++++++++++++++++++------------------ plugins/check_ntp_peer.d/config.h | 58 +++++---- 2 files changed, 164 insertions(+), 142 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 37e481c7..0498a7d4 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -39,6 +39,9 @@ const char *progname = "check_ntp_peer"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; +#include "output.h" +#include "perfdata.h" +#include #include "thresholds.h" #include "common.h" #include "netutils.h" @@ -329,7 +332,7 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { if (verbose) { printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); } - xasprintf(&data, ""); + data = strdup(""); do { setup_control_request(&req, OP_READVAR, 2); req.assoc = peers[i].assoc; @@ -518,36 +521,76 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { case 'q': result.config.quiet = true; break; - case 'w': - result.config.owarn = optarg; - break; - case 'c': - result.config.ocrit = optarg; - break; - case 'W': + case 'w': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning offset threshold"); + } + + mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range); + } break; + case 'c': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical offset threshold"); + } + + mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range); + } break; + case 'W': { result.config.do_stratum = true; - result.config.swarn = optarg; - break; - case 'C': + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning stratum threshold"); + } + + mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range); + } break; + case 'C': { result.config.do_stratum = true; - result.config.scrit = optarg; - break; - case 'j': + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical stratum threshold"); + } + + mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range); + } break; + case 'j': { result.config.do_jitter = true; - result.config.jwarn = optarg; - break; - case 'k': + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning jitter threshold"); + } + + mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range); + } break; + case 'k': { result.config.do_jitter = true; - result.config.jcrit = optarg; - break; - case 'm': + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical jitter threshold"); + } + + mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range); + } break; + case 'm': { result.config.do_truechimers = true; - result.config.twarn = optarg; - break; - case 'n': + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning truechimer threshold"); + } + + mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range); + } break; + case 'n': { result.config.do_truechimers = true; - result.config.tcrit = optarg; - break; + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical truechimer threshold"); + } + + mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range); + } break; case 'H': if (!is_host(optarg)) { usage2(_("Invalid hostname/address"), optarg); @@ -581,11 +624,6 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Hostname was not supplied")); } - set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit); - set_thresholds(&result.config.jitter_thresholds, result.config.jwarn, result.config.jcrit); - set_thresholds(&result.config.stratum_thresholds, result.config.swarn, result.config.scrit); - set_thresholds(&result.config.truechimer_thresholds, result.config.twarn, result.config.tcrit); - return result; } @@ -635,124 +673,100 @@ int main(int argc, char *argv[]) { /* This returns either OK or WARNING (See comment preceding ntp_request) */ const ntp_request_result ntp_res = ntp_request(config); - mp_state_enum result = STATE_UNKNOWN; + mp_check overall = mp_check_init(); + mp_subcheck sc_offset = mp_subcheck_init(); + xasprintf(&sc_offset.output, "offset"); if (ntp_res.offset_result == STATE_UNKNOWN) { /* if there's no sync peer (this overrides ntp_request output): */ - result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL); + sc_offset = + mp_set_subcheck_state(sc_offset, (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL)); } else { /* Be quiet if there's no candidates either */ - if (config.quiet && result == STATE_WARNING) { - result = STATE_UNKNOWN; + mp_state_enum tmp = STATE_OK; + if (config.quiet && ntp_res.state == STATE_WARNING) { + tmp = STATE_UNKNOWN; } - result = max_state_alt(result, get_status(fabs(ntp_res.offset), config.offset_thresholds)); + + mp_perfdata pd_offset = perfdata_init(); + pd_offset.value = mp_create_pd_value(fabs(ntp_res.offset)); + pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); + + tmp = max_state_alt(tmp, mp_get_pd_status(pd_offset)); + sc_offset = mp_set_subcheck_state(sc_offset, tmp); } - mp_state_enum oresult = result; - mp_state_enum tresult = STATE_UNKNOWN; + mp_add_subcheck_to_check(&overall, sc_offset); + // truechimers if (config.do_truechimers) { - tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds); - result = max_state_alt(result, tresult); - } + mp_subcheck sc_truechimers; + xasprintf(&sc_truechimers.output, "truechimers: %i", ntp_res.num_truechimers); - mp_state_enum sresult = STATE_UNKNOWN; + mp_perfdata pd_truechimers = perfdata_init(); + pd_truechimers.value = mp_create_pd_value(ntp_res.num_truechimers); + mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds); + mp_add_perfdata_to_subcheck(&sc_truechimers, pd_truechimers); - if (config.do_stratum) { - sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds); - result = max_state_alt(result, sresult); + sc_truechimers = mp_set_subcheck_state(sc_truechimers, mp_get_pd_status(pd_truechimers)); + + mp_add_subcheck_to_check(&overall, sc_truechimers); } - mp_state_enum jresult = STATE_UNKNOWN; + if (config.do_stratum) { + mp_subcheck sc_stratum = mp_subcheck_init(); + xasprintf(&sc_stratum.output, "stratum: %li", ntp_res.stratum); - if (config.do_jitter) { - jresult = get_status(ntp_res.jitter, config.jitter_thresholds); - result = max_state_alt(result, jresult); + mp_perfdata pd_stratum = perfdata_init(); + pd_stratum.value = mp_create_pd_value(ntp_res.stratum); + pd_stratum = mp_pd_set_thresholds(pd_stratum, config.stratum_thresholds); + mp_add_perfdata_to_subcheck(&sc_stratum, pd_stratum); + + sc_stratum = mp_set_subcheck_state(sc_stratum, mp_get_pd_status(pd_stratum)); + + mp_add_subcheck_to_check(&overall, sc_stratum); } - char *result_line; - switch (result) { - case STATE_CRITICAL: - xasprintf(&result_line, _("NTP CRITICAL:")); - break; - case STATE_WARNING: - xasprintf(&result_line, _("NTP WARNING:")); - break; - case STATE_OK: - xasprintf(&result_line, _("NTP OK:")); - break; - default: - xasprintf(&result_line, _("NTP UNKNOWN:")); - break; + if (config.do_jitter) { + mp_subcheck sc_jitter = mp_subcheck_init(); + xasprintf(&sc_jitter.output, "jitter: %f", ntp_res.jitter); + + mp_perfdata pd_jitter = perfdata_init(); + pd_jitter.value = mp_create_pd_value(ntp_res.jitter); + pd_jitter = mp_pd_set_thresholds(pd_jitter, config.jitter_thresholds); + mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter); + + sc_jitter = mp_set_subcheck_state(sc_jitter, mp_get_pd_status(pd_jitter)); + mp_add_subcheck_to_check(&overall, sc_jitter); } + mp_subcheck sc_other_info = mp_subcheck_init(); + sc_other_info = mp_set_subcheck_default_state(sc_other_info, STATE_OK); if (!ntp_res.syncsource_found) { - xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); + xasprintf(&sc_other_info.output, "%s", _("Server not synchronized")); + mp_add_subcheck_to_check(&overall, sc_other_info); } else if (ntp_res.li_alarm) { - xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); + xasprintf(&sc_other_info.output, "%s", _("Server has the LI_ALARM bit set")); + mp_add_subcheck_to_check(&overall, sc_other_info); } - char *perfdata_line; - if (ntp_res.offset_result == STATE_UNKNOWN) { - xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); - xasprintf(&perfdata_line, ""); - } else if (oresult == STATE_WARNING) { - xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), - ntp_res.offset); - } else if (oresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), - ntp_res.offset); - } else { - xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset); - } - xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds)); - - if (config.do_jitter) { - if (jresult == STATE_WARNING) { - xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter); - } else if (jresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter); - } else { - xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter); - } - xasprintf(&perfdata_line, "%s %s", perfdata_line, - perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds)); - } + { + mp_subcheck sc_offset = mp_subcheck_init(); + sc_offset = mp_set_subcheck_default_state(sc_offset, STATE_OK); + xasprintf(&sc_offset.output, "offset: %.10gs", ntp_res.offset); - if (config.do_stratum) { - if (sresult == STATE_WARNING) { - xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum); - } else if (sresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum); - } else { - xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum); - } - xasprintf(&perfdata_line, "%s %s", perfdata_line, - perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds)); - } + mp_perfdata pd_offset = perfdata_init(); + pd_offset.value = mp_create_pd_value(ntp_res.offset); + pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); - if (config.do_truechimers) { - if (tresult == STATE_WARNING) { - xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, - ntp_res.num_truechimers); - } else if (tresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, - ntp_res.num_truechimers); - } else { - xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers); - } - xasprintf(&perfdata_line, "%s %s", perfdata_line, - perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers, - config.truechimer_thresholds)); + sc_offset = mp_set_subcheck_state(sc_offset, ntp_res.offset_result); } - printf("%s|%s\n", result_line, perfdata_line); - if (config.server_address != NULL) { free(config.server_address); } - exit(result); + mp_exit(overall); } void print_help(void) { diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h index 00e6b05d..396edea6 100644 --- a/plugins/check_ntp_peer.d/config.h +++ b/plugins/check_ntp_peer.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "perfdata.h" #include "thresholds.h" #include @@ -16,26 +17,18 @@ typedef struct { // truechimer stuff bool do_truechimers; - char *twarn; - char *tcrit; - thresholds *truechimer_thresholds; + mp_thresholds truechimer_thresholds; - char *owarn; - char *ocrit; - thresholds *offset_thresholds; + // offset thresholds + mp_thresholds offset_thresholds; // stratum stuff bool do_stratum; - char *swarn; - char *scrit; - thresholds *stratum_thresholds; + mp_thresholds stratum_thresholds; // jitter stuff bool do_jitter; - char *jwarn; - char *jcrit; - thresholds *jitter_thresholds; - + mp_thresholds jitter_thresholds; } check_ntp_peer_config; check_ntp_peer_config check_ntp_peer_config_init() { @@ -45,23 +38,38 @@ check_ntp_peer_config check_ntp_peer_config_init() { .quiet = false, .do_truechimers = false, - .twarn = "0:", - .tcrit = "0:", - .truechimer_thresholds = NULL, + .truechimer_thresholds = mp_thresholds_init(), - .owarn = "60", - .ocrit = "120", - .offset_thresholds = NULL, + .offset_thresholds = mp_thresholds_init(), .do_stratum = false, - .swarn = "-1:16", - .scrit = "-1:16", - .stratum_thresholds = NULL, + .stratum_thresholds = mp_thresholds_init(), .do_jitter = false, - .jwarn = "-1:5000", - .jcrit = "-1:10000", - .jitter_thresholds = NULL, + .jitter_thresholds = mp_thresholds_init(), }; + + mp_range stratum_default = mp_range_init(); + stratum_default = mp_range_set_start(stratum_default, mp_create_pd_value(-1)); + stratum_default = mp_range_set_end(stratum_default, mp_create_pd_value(16)); + tmp.stratum_thresholds = mp_thresholds_set_warn(tmp.stratum_thresholds, stratum_default); + tmp.stratum_thresholds = mp_thresholds_set_crit(tmp.stratum_thresholds, stratum_default); + + mp_range jitter_w_default = mp_range_init(); + jitter_w_default = mp_range_set_start(jitter_w_default, mp_create_pd_value(-1)); + jitter_w_default = mp_range_set_end(jitter_w_default, mp_create_pd_value(5000)); + tmp.jitter_thresholds = mp_thresholds_set_warn(tmp.jitter_thresholds, jitter_w_default); + + mp_range jitter_c_default = mp_range_init(); + jitter_c_default = mp_range_set_start(jitter_c_default, mp_create_pd_value(-1)); + jitter_c_default = mp_range_set_end(jitter_c_default, mp_create_pd_value(10000)); + tmp.jitter_thresholds = mp_thresholds_set_crit(tmp.jitter_thresholds, jitter_c_default); + + mp_range offset_w_default = mp_range_init(); + offset_w_default = mp_range_set_start(offset_w_default, mp_create_pd_value(60)); + tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, offset_w_default); + mp_range offset_c_default = mp_range_init(); + offset_c_default = mp_range_set_start(offset_c_default, mp_create_pd_value(120)); + tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, offset_c_default); return tmp; } -- cgit v1.2.3-74-g34f1 From b35853ee4e10c4485a9521d77c95aecae6573e64 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:08:59 +0100 Subject: check_ntp_time: implement modern output --- plugins/check_ntp_time.c | 160 +++++++++++++++++++------------------- plugins/check_ntp_time.d/config.h | 13 +++- 2 files changed, 93 insertions(+), 80 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index ad69b804..eee9c298 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c @@ -34,12 +34,10 @@ * *****************************************************************************/ -const char *progname = "check_ntp_time"; -const char *copyright = "2006-2024"; -const char *email = "devel@monitoring-plugins.org"; - +#include "output.h" #include "common.h" #include "netutils.h" +#include "perfdata.h" #include "utils.h" #include "states.h" #include "thresholds.h" @@ -47,6 +45,10 @@ const char *email = "devel@monitoring-plugins.org"; static int verbose = 0; +const char *progname = "check_ntp_time"; +const char *copyright = "2006-2024"; +const char *email = "devel@monitoring-plugins.org"; + typedef struct { int errorcode; check_ntp_time_config config; @@ -61,9 +63,6 @@ void print_usage(void); # define AVG_NUM 4 #endif -/* max size of control message data */ -#define MAX_CM_SIZE 468 - /* this structure holds everything in an ntp request/response as per rfc1305 */ typedef struct { uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ @@ -169,7 +168,9 @@ typedef struct { : 0) /* convert a struct timeval to a double */ -#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) +static double TVasDOUBLE(struct timeval time) { + return ((double)time.tv_sec + (0.000001 * (double)time.tv_usec)); +} /* convert an ntp 64-bit fp number to a struct timeval */ #define NTP64toTV(n, t) \ @@ -262,8 +263,8 @@ void setup_request(ntp_message *message) { /* select the "best" server from a list of servers, and return its index. * this is done by filtering servers based on stratum, dispersion, and * finally round-trip delay. */ -int best_offset_server(const ntp_server_results *slist, int nservers) { - int best_server = -1; +static int best_offset_server(const ntp_server_results *slist, int nservers) { + int best_server_index = -1; /* for each server */ for (int cserver = 0; cserver < nservers; cserver++) { @@ -286,33 +287,33 @@ int best_offset_server(const ntp_server_results *slist, int nservers) { } /* If we don't have a server yet, use the first one */ - if (best_server == -1) { - best_server = cserver; - DBG(printf("using peer %d as our first candidate\n", best_server)); + if (best_server_index == -1) { + best_server_index = cserver; + DBG(printf("using peer %d as our first candidate\n", best_server_index)); continue; } /* compare the server to the best one we've seen so far */ /* does it have an equal or better stratum? */ - DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); - if (slist[cserver].stratum <= slist[best_server].stratum) { - DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); + DBG(printf("comparing peer %d with peer %d\n", cserver, best_server_index)); + if (slist[cserver].stratum <= slist[best_server_index].stratum) { + DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server_index)); /* does it have an equal or better dispersion? */ - if (slist[cserver].rtdisp <= slist[best_server].rtdisp) { - DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); + if (slist[cserver].rtdisp <= slist[best_server_index].rtdisp) { + DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server_index)); /* does it have a better rtdelay? */ - if (slist[cserver].rtdelay < slist[best_server].rtdelay) { - DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); - best_server = cserver; - DBG(printf("peer %d is now our best candidate\n", best_server)); + if (slist[cserver].rtdelay < slist[best_server_index].rtdelay) { + DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server_index)); + best_server_index = cserver; + DBG(printf("peer %d is now our best candidate\n", best_server_index)); } } } } - if (best_server >= 0) { - DBG(printf("best server selected: peer %d\n", best_server)); - return best_server; + if (best_server_index >= 0) { + DBG(printf("best server selected: peer %d\n", best_server_index)); + return best_server_index; } DBG(printf("no peers meeting synchronization criteria :(\n")); return -1; @@ -323,7 +324,11 @@ int best_offset_server(const ntp_server_results *slist, int nservers) { * we don't waste time sitting around waiting for single packets. * - we also "manually" handle resolving host names and connecting, because * we have to do it in a way that our lazy macros don't handle currently :( */ -double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) { +typedef struct { + mp_state_enum offset_result; + double offset; +} offset_request_wrapper; +static offset_request_wrapper offset_request(const char *host, const char *port, int time_offset) { /* setup hints to only return results from getaddrinfo that we'd like */ struct addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); @@ -462,11 +467,16 @@ double offset_request(const char *host, const char *port, mp_state_enum *status, die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); } + offset_request_wrapper result = { + .offset = 0, + .offset_result = STATE_UNKNOWN, + }; + /* now, pick the best server from the list */ double avg_offset = 0.; int best_index = best_offset_server(servers, num_hosts); if (best_index < 0) { - *status = STATE_UNKNOWN; + result.offset_result = STATE_UNKNOWN; } else { /* finally, calculate the average offset */ for (int i = 0; i < servers[best_index].num_responses; i++) { @@ -488,10 +498,12 @@ double offset_request(const char *host, const char *port, mp_state_enum *status, if (verbose) { printf("overall average offset: %.10g\n", avg_offset); } - return avg_offset; + + result.offset = avg_offset; + return result; } -check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { +static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { static struct option longopts[] = {{"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, @@ -515,9 +527,6 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { .config = check_ntp_time_config_init(), }; - char *owarn = "60"; - char *ocrit = "120"; - while (true) { int option = 0; int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); @@ -540,12 +549,24 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { case 'q': result.config.quiet = true; break; - case 'w': - owarn = optarg; - break; - case 'c': - ocrit = optarg; - break; + case 'w': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold"); + } + + result.config.offset_thresholds = + mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range); + } break; + case 'c': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse crit threshold"); + } + + result.config.offset_thresholds = + mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range); + } break; case 'H': if (!is_host(optarg)) { usage2(_("Invalid hostname/address"), optarg); @@ -582,16 +603,9 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Hostname was not supplied")); } - set_thresholds(&result.config.offset_thresholds, owarn, ocrit); - return result; } -char *perfd_offset(double offset, thresholds *offset_thresholds) { - return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, - offset_thresholds->critical->end, false, 0, false, 0); -} - int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -614,46 +628,36 @@ int main(int argc, char *argv[]) { /* set socket timeout */ alarm(socket_timeout); - mp_state_enum offset_result = STATE_OK; - mp_state_enum result = STATE_OK; - double offset = - offset_request(config.server_address, config.port, &offset_result, config.time_offset); - if (offset_result == STATE_UNKNOWN) { - result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL); - } else { - result = get_status(fabs(offset), config.offset_thresholds); - } + mp_check overall = mp_check_init(); - char *result_line; - switch (result) { - case STATE_CRITICAL: - xasprintf(&result_line, _("NTP CRITICAL:")); - break; - case STATE_WARNING: - xasprintf(&result_line, _("NTP WARNING:")); - break; - case STATE_OK: - xasprintf(&result_line, _("NTP OK:")); - break; - default: - xasprintf(&result_line, _("NTP UNKNOWN:")); - break; - } + mp_subcheck sc_offset = mp_subcheck_init(); + offset_request_wrapper offset_result = + offset_request(config.server_address, config.port, config.time_offset); - char *perfdata_line; - if (offset_result == STATE_UNKNOWN) { - xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); - xasprintf(&perfdata_line, ""); - } else { - xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); - xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds)); + if (offset_result.offset_result == STATE_UNKNOWN) { + sc_offset = + mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL); + xasprintf(&sc_offset.output, "Offset unknown"); + mp_add_subcheck_to_check(&overall, sc_offset); + mp_exit(overall); } - printf("%s|%s\n", result_line, perfdata_line); + + xasprintf(&sc_offset.output, "Offset: %.10g s", offset_result.offset); + + mp_perfdata pd_offset = perfdata_init(); + pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset)); + pd_offset.label = "offset"; + pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); + + sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset)); + + mp_add_perfdata_to_subcheck(&sc_offset, pd_offset); + mp_add_subcheck_to_check(&overall, sc_offset); if (config.server_address != NULL) { free(config.server_address); } - exit(result); + mp_exit(overall); } void print_help(void) { diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h index 99dabbbd..a62e4ceb 100644 --- a/plugins/check_ntp_time.d/config.h +++ b/plugins/check_ntp_time.d/config.h @@ -11,7 +11,7 @@ typedef struct { bool quiet; int time_offset; - thresholds *offset_thresholds; + mp_thresholds offset_thresholds; } check_ntp_time_config; check_ntp_time_config check_ntp_time_config_init() { @@ -22,7 +22,16 @@ check_ntp_time_config check_ntp_time_config_init() { .quiet = false, .time_offset = 0, - .offset_thresholds = NULL, + .offset_thresholds = mp_thresholds_init(), }; + + mp_range warning = mp_range_init(); + warning = mp_range_set_end(warning, mp_create_pd_value(60)); + tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, warning); + + mp_range critical = mp_range_init(); + critical = mp_range_set_end(warning, mp_create_pd_value(120)); + tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, critical); + return tmp; } -- cgit v1.2.3-74-g34f1 From 697374af93322fdd584a219e0cf45021b463404e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:11:24 +0100 Subject: Forgot to actually set thresholds --- plugins/check_ntp_peer.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 0498a7d4..68b2fa9c 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -527,7 +527,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse warning offset threshold"); } - mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range); + result.config.offset_thresholds = + mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range); } break; case 'c': { mp_range_parsed tmp = mp_parse_range_string(optarg); @@ -535,7 +536,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse critical offset threshold"); } - mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range); + result.config.offset_thresholds = + mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range); } break; case 'W': { result.config.do_stratum = true; @@ -544,7 +546,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse warning stratum threshold"); } - mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range); + result.config.stratum_thresholds = + mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range); } break; case 'C': { result.config.do_stratum = true; @@ -553,7 +556,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse critical stratum threshold"); } - mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range); + result.config.stratum_thresholds = + mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range); } break; case 'j': { result.config.do_jitter = true; @@ -562,7 +566,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse warning jitter threshold"); } - mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range); + result.config.jitter_thresholds = + mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range); } break; case 'k': { result.config.do_jitter = true; @@ -571,7 +576,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse critical jitter threshold"); } - mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range); + result.config.jitter_thresholds = + mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range); } break; case 'm': { result.config.do_truechimers = true; @@ -580,7 +586,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse warning truechimer threshold"); } - mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range); + result.config.truechimer_thresholds = + mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range); } break; case 'n': { result.config.do_truechimers = true; @@ -589,7 +596,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "failed to parse critical truechimer threshold"); } - mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range); + result.config.truechimer_thresholds = + mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range); } break; case 'H': if (!is_host(optarg)) { -- cgit v1.2.3-74-g34f1 From b207ac3b0ab4dd4511fb9a25edb84e150b8b3482 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:14:36 +0100 Subject: remove cpp constant and localize that value instead --- plugins/check_pgsql.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index 793a686f..66b9231e 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c @@ -71,8 +71,6 @@ void print_usage(void); static int verbose = 0; -#define OPTID_QUERYNAME -1000 - /****************************************************************************** The (pseudo?)literate programming XML is contained within \@\@\- \-\@\@ @@ -244,6 +242,11 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_pgsql_config_wrapper process_arguments(int argc, char **argv) { + + enum { + OPTID_QUERYNAME = CHAR_MAX + 1, + }; + static struct option longopts[] = {{"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"timeout", required_argument, 0, 't'}, -- cgit v1.2.3-74-g34f1 From 4191aa46a27cd37de40b0d8a5b8cd19a1c00e364 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:15:20 +0100 Subject: put includes before any declarations --- plugins/check_pgsql.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'plugins') diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index 66b9231e..e74c567d 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c @@ -29,20 +29,19 @@ *****************************************************************************/ #include "states.h" -const char *progname = "check_pgsql"; -const char *copyright = "1999-2024"; -const char *email = "devel@monitoring-plugins.org"; - #include "common.h" #include "utils.h" #include "utils_cmd.h" #include "check_pgsql.d/config.h" #include "thresholds.h" - #include "netutils.h" #include #include +const char *progname = "check_pgsql"; +const char *copyright = "1999-2024"; +const char *email = "devel@monitoring-plugins.org"; + #define DEFAULT_HOST "127.0.0.1" /* return the PSQL server version as a 3-tuple */ -- cgit v1.2.3-74-g34f1 From 16daa06e439da1541cbcb4e0622131838b21ba0f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:41:34 +0100 Subject: check_ntp_time: actually accept offset if known --- plugins/check_ntp_time.c | 1 + 1 file changed, 1 insertion(+) (limited to 'plugins') diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index eee9c298..d554089d 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c @@ -478,6 +478,7 @@ static offset_request_wrapper offset_request(const char *host, const char *port, if (best_index < 0) { result.offset_result = STATE_UNKNOWN; } else { + result.offset_result = STATE_OK; /* finally, calculate the average offset */ for (int i = 0; i < servers[best_index].num_responses; i++) { avg_offset += servers[best_index].offset[i]; -- cgit v1.2.3-74-g34f1 From cadcfaecbec56d2ecf44bb33bb394ca815240127 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:42:12 +0100 Subject: check_ntp_time: fix time-offset argument and description --- plugins/check_ntp_time.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index d554089d..8d97f2b9 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c @@ -511,7 +511,7 @@ static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, - {"time-offset", optional_argument, 0, 'o'}, + {"time-offset", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"timeout", required_argument, 0, 't'}, @@ -682,7 +682,7 @@ void print_help(void) { printf(" %s\n", _("Offset to result in warning status (seconds)")); printf(" %s\n", "-c, --critical=THRESHOLD"); printf(" %s\n", _("Offset to result in critical status (seconds)")); - printf(" %s\n", "-o, --time_offset=INTEGER"); + printf(" %s\n", "-o, --time-offset=INTEGER"); printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(UT_VERBOSE); -- cgit v1.2.3-74-g34f1 From 7b226257b7df01dd80af07c39e0f49fdcf84cee4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:42:43 +0100 Subject: check_ntp_time: reduce number of significant bits a bit --- plugins/check_ntp_time.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index 8d97f2b9..7c3fc24d 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c @@ -643,11 +643,12 @@ int main(int argc, char *argv[]) { mp_exit(overall); } - xasprintf(&sc_offset.output, "Offset: %.10g s", offset_result.offset); + xasprintf(&sc_offset.output, "Offset: %.6fs", offset_result.offset); mp_perfdata pd_offset = perfdata_init(); pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset)); pd_offset.label = "offset"; + pd_offset.uom = "s"; pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset)); -- cgit v1.2.3-74-g34f1 From 99351f0560a5f457e499417366bdfdec0172a428 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:54:34 +0100 Subject: check_ntp_peer: fix several missing things and errors --- plugins/check_ntp_peer.c | 16 ++++++++++++++-- plugins/check_ntp_peer.d/config.h | 5 +++-- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 68b2fa9c..032f8ea4 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -689,6 +689,7 @@ int main(int argc, char *argv[]) { /* if there's no sync peer (this overrides ntp_request output): */ sc_offset = mp_set_subcheck_state(sc_offset, (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL)); + xasprintf(&sc_offset.output, "%s unknown", sc_offset.output); } else { /* Be quiet if there's no candidates either */ mp_state_enum tmp = STATE_OK; @@ -696,9 +697,14 @@ int main(int argc, char *argv[]) { tmp = STATE_UNKNOWN; } + xasprintf(&sc_offset.output, "%s: %.6fs", sc_offset.output, ntp_res.offset); + mp_perfdata pd_offset = perfdata_init(); pd_offset.value = mp_create_pd_value(fabs(ntp_res.offset)); pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); + pd_offset.label = "offset"; + pd_offset.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_offset, pd_offset); tmp = max_state_alt(tmp, mp_get_pd_status(pd_offset)); sc_offset = mp_set_subcheck_state(sc_offset, tmp); @@ -708,12 +714,14 @@ int main(int argc, char *argv[]) { // truechimers if (config.do_truechimers) { - mp_subcheck sc_truechimers; + mp_subcheck sc_truechimers = mp_subcheck_init(); xasprintf(&sc_truechimers.output, "truechimers: %i", ntp_res.num_truechimers); mp_perfdata pd_truechimers = perfdata_init(); pd_truechimers.value = mp_create_pd_value(ntp_res.num_truechimers); - mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds); + pd_truechimers.label = "truechimers"; + pd_truechimers = mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds); + mp_add_perfdata_to_subcheck(&sc_truechimers, pd_truechimers); sc_truechimers = mp_set_subcheck_state(sc_truechimers, mp_get_pd_status(pd_truechimers)); @@ -728,6 +736,8 @@ int main(int argc, char *argv[]) { mp_perfdata pd_stratum = perfdata_init(); pd_stratum.value = mp_create_pd_value(ntp_res.stratum); pd_stratum = mp_pd_set_thresholds(pd_stratum, config.stratum_thresholds); + pd_stratum.label = "stratum"; + mp_add_perfdata_to_subcheck(&sc_stratum, pd_stratum); sc_stratum = mp_set_subcheck_state(sc_stratum, mp_get_pd_status(pd_stratum)); @@ -742,6 +752,8 @@ int main(int argc, char *argv[]) { mp_perfdata pd_jitter = perfdata_init(); pd_jitter.value = mp_create_pd_value(ntp_res.jitter); pd_jitter = mp_pd_set_thresholds(pd_jitter, config.jitter_thresholds); + pd_jitter.label = "jitter"; + mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter); sc_jitter = mp_set_subcheck_state(sc_jitter, mp_get_pd_status(pd_jitter)); diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h index 396edea6..a6dd5c4c 100644 --- a/plugins/check_ntp_peer.d/config.h +++ b/plugins/check_ntp_peer.d/config.h @@ -66,10 +66,11 @@ check_ntp_peer_config check_ntp_peer_config_init() { tmp.jitter_thresholds = mp_thresholds_set_crit(tmp.jitter_thresholds, jitter_c_default); mp_range offset_w_default = mp_range_init(); - offset_w_default = mp_range_set_start(offset_w_default, mp_create_pd_value(60)); + offset_w_default = mp_range_set_end(offset_w_default, mp_create_pd_value(60)); tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, offset_w_default); + mp_range offset_c_default = mp_range_init(); - offset_c_default = mp_range_set_start(offset_c_default, mp_create_pd_value(120)); + offset_c_default = mp_range_set_end(offset_c_default, mp_create_pd_value(120)); tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, offset_c_default); return tmp; } -- cgit v1.2.3-74-g34f1 From a9b63deedb1775271fa1335a5d3eb034d0628e91 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:04:30 +0100 Subject: check_ntp_peer: add cli param to set output format --- plugins/check_ntp_peer.c | 50 +++++++++++++++++++++++++++++++-------- plugins/check_ntp_peer.d/config.h | 6 +++++ 2 files changed, 46 insertions(+), 10 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 032f8ea4..f7cad630 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -478,16 +478,30 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { } check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = { - {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, - {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, - {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, - {"swarn", required_argument, 0, 'W'}, {"scrit", required_argument, 0, 'C'}, - {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, - {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, - {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'}, - {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; + + enum { + output_format_index = CHAR_MAX + 1, + }; + + static struct option longopts[] = {{"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"quiet", no_argument, 0, 'q'}, + {"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, + {"swarn", required_argument, 0, 'W'}, + {"scrit", required_argument, 0, 'C'}, + {"jwarn", required_argument, 0, 'j'}, + {"jcrit", required_argument, 0, 'k'}, + {"twarn", required_argument, 0, 'm'}, + {"tcrit", required_argument, 0, 'n'}, + {"timeout", required_argument, 0, 't'}, + {"hostname", required_argument, 0, 'H'}, + {"port", required_argument, 0, 'p'}, + {"output-format", required_argument, 0, output_format_index}, + {0, 0, 0, 0}}; if (argc < 2) { usage("\n"); @@ -507,6 +521,17 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { } switch (option_char) { + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } case 'h': print_help(); exit(STATE_UNKNOWN); @@ -673,6 +698,10 @@ int main(int argc, char *argv[]) { const check_ntp_peer_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* initialize alarm signal handling */ signal(SIGALRM, socket_timeout_alarm_handler); @@ -825,6 +854,7 @@ void print_help(void) { printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h index a6dd5c4c..488d936c 100644 --- a/plugins/check_ntp_peer.d/config.h +++ b/plugins/check_ntp_peer.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include "perfdata.h" #include "thresholds.h" #include @@ -29,6 +30,9 @@ typedef struct { // jitter stuff bool do_jitter; mp_thresholds jitter_thresholds; + + bool output_format_is_set; + mp_output_format output_format; } check_ntp_peer_config; check_ntp_peer_config check_ntp_peer_config_init() { @@ -47,6 +51,8 @@ check_ntp_peer_config check_ntp_peer_config_init() { .do_jitter = false, .jitter_thresholds = mp_thresholds_init(), + + .output_format_is_set = false, }; mp_range stratum_default = mp_range_init(); -- cgit v1.2.3-74-g34f1 From 71e0d5e0732225e95affbacd1a08f2a8513d2802 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:19:20 +0100 Subject: check_ntp_time: add cli option for output format --- plugins/check_ntp_time.c | 22 ++++++++++++++++++++++ plugins/check_ntp_time.d/config.h | 6 ++++++ 2 files changed, 28 insertions(+) (limited to 'plugins') diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index 7c3fc24d..602b6010 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c @@ -505,6 +505,11 @@ static offset_request_wrapper offset_request(const char *host, const char *port, } static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { + + enum { + output_format_index = CHAR_MAX + 1, + }; + static struct option longopts[] = {{"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, @@ -517,6 +522,7 @@ static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; if (argc < 2) { @@ -536,6 +542,17 @@ static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { } switch (option_char) { + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } case 'h': print_help(); exit(STATE_UNKNOWN); @@ -623,6 +640,10 @@ int main(int argc, char *argv[]) { const check_ntp_time_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* initialize alarm signal handling */ signal(SIGALRM, socket_timeout_alarm_handler); @@ -687,6 +708,7 @@ void print_help(void) { printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf("%s\n", _("This plugin checks the clock offset between the local host and a")); diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h index a62e4ceb..9bbd82aa 100644 --- a/plugins/check_ntp_time.d/config.h +++ b/plugins/check_ntp_time.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include "thresholds.h" #include @@ -12,6 +13,9 @@ typedef struct { int time_offset; mp_thresholds offset_thresholds; + + bool output_format_is_set; + mp_output_format output_format; } check_ntp_time_config; check_ntp_time_config check_ntp_time_config_init() { @@ -23,6 +27,8 @@ check_ntp_time_config check_ntp_time_config_init() { .time_offset = 0, .offset_thresholds = mp_thresholds_init(), + + .output_format_is_set = false, }; mp_range warning = mp_range_init(); -- cgit v1.2.3-74-g34f1 From ba6f90373333246bce196dbba494fa4af0bd9253 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:17:16 +0100 Subject: check_pgsql: implement modern output --- plugins/check_pgsql.c | 273 +++++++++++++++++++++++++++++------------ plugins/check_pgsql.d/config.h | 23 ++-- 2 files changed, 206 insertions(+), 90 deletions(-) (limited to 'plugins') diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index e74c567d..bbabd062 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c @@ -28,6 +28,8 @@ * *****************************************************************************/ +#include "output.h" +#include "perfdata.h" #include "states.h" #include "common.h" #include "utils.h" @@ -46,8 +48,9 @@ const char *email = "devel@monitoring-plugins.org"; /* return the PSQL server version as a 3-tuple */ #define PSQL_SERVER_VERSION3(server_version) \ - (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ - (server_version) - (int)((server_version) / 100) * 100 + ((server_version) / 10000), \ + (((server_version) / 100) - (int)(((server_version) / 10000) * 100)), \ + (server_version) - (int)(((server_version) / 100) * 100) /* return true if the given host is a UNIX domain socket */ #define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) /* return a 3-tuple identifying a host/port independent of the socket type */ @@ -63,9 +66,21 @@ static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv static void print_help(void); static bool is_pg_logname(char * /*username*/); -static mp_state_enum do_query(PGconn * /*conn*/, char * /*query*/, const char /*pgqueryname*/[], - thresholds * /*qthresholds*/, char * /*query_warning*/, - char * /*query_critical*/); + +typedef enum { + QUERY_OK, + ERROR_WITH_QUERY, // critical + NO_ROWS_RETURNED, // warning + NO_COLUMNS_RETURNED, // warning + NO_DATA_RETURNED, // critica/ + RESULT_IS_NOT_NUMERIC // critical +} do_query_errorcode; + +typedef struct { + do_query_errorcode error_code; + double numerical_result; +} do_query_wrapper; +static do_query_wrapper do_query(PGconn * /*conn*/, char * /*query*/); void print_usage(void); static int verbose = 0; @@ -196,21 +211,41 @@ int main(int argc, char **argv) { if (verbose) { printf("Verifying connection\n"); } + + mp_check overall = mp_check_init(); + + mp_subcheck sc_connection = mp_subcheck_init(); + if (PQstatus(conn) == CONNECTION_BAD) { - printf(_("CRITICAL - no connection to '%s' (%s).\n"), config.dbName, PQerrorMessage(conn)); + sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL); + xasprintf(&sc_connection.output, "no connection to '%s' (%s)", config.dbName, + PQerrorMessage(conn)); PQfinish(conn); - return STATE_CRITICAL; - } - - mp_state_enum status = STATE_UNKNOWN; - if (elapsed_time > config.tcrit) { - status = STATE_CRITICAL; - } else if (elapsed_time > config.twarn) { - status = STATE_WARNING; + mp_add_subcheck_to_check(&overall, sc_connection); + mp_exit(overall); } else { - status = STATE_OK; + sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK); + xasprintf(&sc_connection.output, "connected to '%s'", config.dbName); + mp_add_subcheck_to_check(&overall, sc_connection); } + mp_subcheck sc_connection_time = mp_subcheck_init(); + sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_UNKNOWN); + + xasprintf(&sc_connection_time.output, "connection time: %.10g", elapsed_time); + + mp_perfdata pd_connection_time = perfdata_init(); + pd_connection_time.label = "time"; + pd_connection_time.uom = "s"; + pd_connection_time = mp_set_pd_value(pd_connection_time, elapsed_time); + pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.time_thresholds); + + mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time); + sc_connection_time = + mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_connection_time)); + + mp_add_subcheck_to_check(&overall, sc_connection_time); + if (verbose) { char *server_host = PQhost(conn); int server_version = PQserverVersion(conn); @@ -222,25 +257,81 @@ int main(int argc, char **argv) { PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn)); } - printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), config.dbName, elapsed_time, - fperfdata("time", elapsed_time, "s", (config.twarn > 0.0), config.twarn, - (config.tcrit > 0.0), config.tcrit, true, 0, false, 0)); - - mp_state_enum query_status = STATE_UNKNOWN; if (config.pgquery) { - query_status = do_query(conn, config.pgquery, config.pgqueryname, config.qthresholds, - config.query_warning, config.query_critical); + mp_subcheck sc_query = mp_subcheck_init(); + sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN); + if (config.pgqueryname) { + xasprintf(&sc_query.output, "query '%s'", config.pgqueryname); + } else { + xasprintf(&sc_query.output, "query '%s'", config.pgquery); + } + + do_query_wrapper query_result = do_query(conn, config.pgquery); + + switch (query_result.error_code) { + case QUERY_OK: { + // Query was succesful and there is a numerical result + sc_query = mp_set_subcheck_state(sc_query, STATE_OK); + xasprintf(&sc_query.output, "%s succeeded", sc_query.output); + + mp_perfdata pd_query = perfdata_init(); + pd_query = mp_set_pd_value(pd_query, query_result.numerical_result); + pd_query = mp_pd_set_thresholds(pd_query, config.qthresholds); + pd_query.label = "query"; + + mp_subcheck sc_query_compare = mp_subcheck_init(); + mp_state_enum query_compare_state = mp_get_pd_status(pd_query); + + sc_query_compare = mp_set_subcheck_state(sc_query_compare, query_compare_state); + mp_add_perfdata_to_subcheck(&sc_query_compare, pd_query); + + if (query_compare_state == STATE_OK) { + xasprintf(&sc_query_compare.output, "query result '%f' is withing thresholds", + query_result.numerical_result); + } else { + xasprintf(&sc_query_compare.output, "query result '%f' is violating thresholds", + query_result.numerical_result); + } + mp_add_subcheck_to_check(&overall, sc_query_compare); + + } break; + case ERROR_WITH_QUERY: + xasprintf(&sc_query.output, "%s - Error with query: %s", sc_query.output, + PQerrorMessage(conn)); + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + break; + case NO_ROWS_RETURNED: + xasprintf(&sc_query.output, "%s - no rows were returned by the query", sc_query.output); + sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING); + break; + case NO_COLUMNS_RETURNED: + xasprintf(&sc_query.output, "%s - no columns were returned by the query", + sc_query.output); + sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING); + break; + case NO_DATA_RETURNED: + xasprintf(&sc_query.output, "%s - no data was returned by the query", sc_query.output); + sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING); + break; + case RESULT_IS_NOT_NUMERIC: + xasprintf(&sc_query.output, "%s - result of the query is not numeric", sc_query.output); + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + break; + }; + + mp_add_subcheck_to_check(&overall, sc_query); } if (verbose) { printf("Closing connection\n"); } PQfinish(conn); - return (config.pgquery && query_status > status) ? query_status : status; + + mp_exit(overall); } /* process command-line arguments */ -check_pgsql_config_wrapper process_arguments(int argc, char **argv) { +static check_pgsql_config_wrapper process_arguments(int argc, char **argv) { enum { OPTID_QUERYNAME = CHAR_MAX + 1, @@ -295,26 +386,40 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) { timeout_interval = atoi(optarg); } break; - case 'c': /* critical time threshold */ - if (!is_nonnegative(optarg)) { - usage2(_("Critical threshold must be a positive integer"), optarg); - } else { - result.config.tcrit = strtod(optarg, NULL); + case 'c': /* critical time threshold */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical time threshold"); } - break; - case 'w': /* warning time threshold */ - if (!is_nonnegative(optarg)) { - usage2(_("Warning threshold must be a positive integer"), optarg); - } else { - result.config.twarn = strtod(optarg, NULL); + result.config.time_thresholds = + mp_thresholds_set_crit(result.config.time_thresholds, tmp.range); + } break; + case 'w': /* warning time threshold */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning time threshold"); } - break; - case 'C': /* critical query threshold */ - result.config.query_critical = optarg; - break; - case 'W': /* warning query threshold */ - result.config.query_warning = optarg; - break; + result.config.time_thresholds = + mp_thresholds_set_warn(result.config.time_thresholds, tmp.range); + } break; + case 'C': /* critical query threshold */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical query threshold"); + } + + result.config.qthresholds = + mp_thresholds_set_crit(result.config.qthresholds, tmp.range); + + } break; + case 'W': /* warning query threshold */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning query threshold"); + } + result.config.qthresholds = + mp_thresholds_set_warn(result.config.qthresholds, tmp.range); + } break; case 'H': /* host */ if ((*optarg != '/') && (!is_host(optarg))) { usage2(_("Invalid hostname/address"), optarg); @@ -365,9 +470,6 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) { } } - set_thresholds(&result.config.qthresholds, result.config.query_warning, - result.config.query_critical); - return result; } @@ -395,7 +497,7 @@ should be added. -@@ ******************************************************************************/ -bool is_pg_logname(char *username) { +static bool is_pg_logname(char *username) { if (strlen(username) > NAMEDATALEN - 1) { return (false); } @@ -449,9 +551,9 @@ void print_help(void) { printf(" %s\n", "--queryname=STRING"); printf(" %s\n", _("A name for the query, this string is used instead of the query")); printf(" %s\n", _("in the long output of the plugin")); - printf(" %s\n", "-W, --query-warning=RANGE"); + printf(" %s\n", "-W, --query_warning=RANGE"); printf(" %s\n", _("SQL query value to result in warning status (double)")); - printf(" %s\n", "-C, --query-critical=RANGE"); + printf(" %s\n", "-C, --query_critical=RANGE"); printf(" %s\n", _("SQL query value to result in critical status (double)")); printf(UT_VERBOSE); @@ -511,33 +613,44 @@ void print_usage(void) { "[-q ] [-C ] [-W ]\n"); } -mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thresholds *qthresholds, - char *query_warning, char *query_critical) { +static do_query_wrapper do_query(PGconn *conn, char *query) { if (verbose) { printf("Executing SQL query \"%s\".\n", query); } PGresult *res = PQexec(conn, query); + do_query_wrapper result = { + .error_code = QUERY_OK, + }; + if (PGRES_TUPLES_OK != PQresultStatus(res)) { - printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), - PQerrorMessage(conn)); - return STATE_CRITICAL; + // TODO + // printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), + // PQerrorMessage(conn)); + result.error_code = ERROR_WITH_QUERY; + return result; } if (PQntuples(res) < 1) { - printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); - return STATE_WARNING; + // TODO + // printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); + result.error_code = NO_ROWS_RETURNED; + return result; } if (PQnfields(res) < 1) { - printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); - return STATE_WARNING; + // TODO + // printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); + result.error_code = NO_COLUMNS_RETURNED; + return result; } char *val_str = PQgetvalue(res, 0, 0); if (!val_str) { - printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); - return STATE_CRITICAL; + // TODO + // printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); + result.error_code = NO_DATA_RETURNED; + return result; } char *endptr = NULL; @@ -547,8 +660,10 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre } if (endptr == val_str) { - printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); - return STATE_CRITICAL; + // TODO + // printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); + result.error_code = RESULT_IS_NOT_NUMERIC; + return result; } if ((endptr != NULL) && (*endptr != '\0')) { @@ -557,24 +672,22 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre } } - mp_state_enum my_status = get_status(value, qthresholds); - printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK") - : (my_status == STATE_WARNING) ? _("WARNING") - : (my_status == STATE_CRITICAL) ? _("CRITICAL") - : _("UNKNOWN")); - if (pgqueryname) { - printf(_("%s returned %f"), pgqueryname, value); - } else { - printf(_("'%s' returned %f"), query, value); - } + result.numerical_result = value; - printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", - query_critical ? query_critical : ""); - if (PQnfields(res) > 1) { - char *extra_info = PQgetvalue(res, 0, 1); - if (extra_info != NULL) { - printf("Extra Info: %s\n", extra_info); - } - } - return my_status; + // if (pgqueryname) { + // printf(_("%s returned %f"), pgqueryname, value); + // } else { + // printf(_("'%s' returned %f"), query, value); + // } + + // printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", + // query_critical ? query_critical : ""); + // if (PQnfields(res) > 1) { + // char *extra_info = PQgetvalue(res, 0, 1); + // if (extra_info != NULL) { + // printf("Extra Info: %s\n", extra_info); + // } + // } + + return result; } diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h index 2d4b8b89..7c1ff55a 100644 --- a/plugins/check_pgsql.d/config.h +++ b/plugins/check_pgsql.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "perfdata.h" #include "thresholds.h" #include #include @@ -24,11 +25,8 @@ typedef struct { char *pgquery; char *pgqueryname; - double twarn; - double tcrit; - thresholds *qthresholds; - char *query_warning; - char *query_critical; + mp_thresholds time_thresholds; + mp_thresholds qthresholds; } check_pgsql_config; /* begin, by setting the parameters for a backend connection if the @@ -51,11 +49,16 @@ check_pgsql_config check_pgsql_config_init() { .pgquery = NULL, .pgqueryname = NULL, - .twarn = (double)DEFAULT_WARN, - .tcrit = (double)DEFAULT_CRIT, - .qthresholds = NULL, - .query_warning = NULL, - .query_critical = NULL, + .time_thresholds = mp_thresholds_init(), + .qthresholds = mp_thresholds_init(), }; + + mp_range tmp_range = mp_range_init(); + tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_WARN)); + tmp.time_thresholds = mp_thresholds_set_warn(tmp.time_thresholds, tmp_range); + + tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_CRIT)); + tmp.time_thresholds = mp_thresholds_set_crit(tmp.time_thresholds, tmp_range); + return tmp; } -- cgit v1.2.3-74-g34f1 From 2f0fc05981996ad11f37319c64a386187d13bdf9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:58:48 +0100 Subject: check_pgsql: cleanup leftover code --- plugins/check_pgsql.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'plugins') diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index bbabd062..cd314f29 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c @@ -674,20 +674,5 @@ static do_query_wrapper do_query(PGconn *conn, char *query) { result.numerical_result = value; - // if (pgqueryname) { - // printf(_("%s returned %f"), pgqueryname, value); - // } else { - // printf(_("'%s' returned %f"), query, value); - // } - - // printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", - // query_critical ? query_critical : ""); - // if (PQnfields(res) > 1) { - // char *extra_info = PQgetvalue(res, 0, 1); - // if (extra_info != NULL) { - // printf("Extra Info: %s\n", extra_info); - // } - // } - return result; } -- cgit v1.2.3-74-g34f1 From 8a71cf947f14d731e8ffaa4214d5c82eead912d1 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:59:08 +0100 Subject: check_pgsql: implement cli params for output format --- plugins/check_pgsql.c | 18 ++++++++++++++++-- plugins/check_pgsql.d/config.h | 6 ++++++ 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index cd314f29..5fbffa70 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c @@ -150,8 +150,8 @@ int main(int argc, char **argv) { const check_pgsql_config config = tmp_config.config; - if (verbose > 2) { - printf("Arguments initialized\n"); + if (config.output_format_is_set) { + mp_set_format(config.output_format); } /* Set signal handling and alarm */ @@ -335,6 +335,7 @@ static check_pgsql_config_wrapper process_arguments(int argc, char **argv) { enum { OPTID_QUERYNAME = CHAR_MAX + 1, + output_format_index, }; static struct option longopts[] = {{"help", no_argument, 0, 'h'}, @@ -354,6 +355,7 @@ static check_pgsql_config_wrapper process_arguments(int argc, char **argv) { {"query_critical", required_argument, 0, 'C'}, {"query_warning", required_argument, 0, 'W'}, {"verbose", no_argument, 0, 'v'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_pgsql_config_wrapper result = { @@ -371,6 +373,17 @@ static check_pgsql_config_wrapper process_arguments(int argc, char **argv) { } switch (option_char) { + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } case '?': /* usage */ usage5(); case 'h': /* help */ @@ -557,6 +570,7 @@ void print_help(void) { printf(" %s\n", _("SQL query value to result in critical status (double)")); printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf(" %s\n", _("All parameters are optional.")); diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h index 7c1ff55a..7cf0637b 100644 --- a/plugins/check_pgsql.d/config.h +++ b/plugins/check_pgsql.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include "perfdata.h" #include "thresholds.h" #include @@ -27,6 +28,9 @@ typedef struct { mp_thresholds time_thresholds; mp_thresholds qthresholds; + + bool output_format_is_set; + mp_output_format output_format; } check_pgsql_config; /* begin, by setting the parameters for a backend connection if the @@ -51,6 +55,8 @@ check_pgsql_config check_pgsql_config_init() { .time_thresholds = mp_thresholds_init(), .qthresholds = mp_thresholds_init(), + + .output_format_is_set = false, }; mp_range tmp_range = mp_range_init(); -- cgit v1.2.3-74-g34f1 From 36ac312e07f6b0ff214b21783f42131b90aa553f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:01:31 +0100 Subject: fix typos --- plugins/check_pgsql.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index 5fbffa70..0ce75e0a 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c @@ -270,7 +270,7 @@ int main(int argc, char **argv) { switch (query_result.error_code) { case QUERY_OK: { - // Query was succesful and there is a numerical result + // Query was successful and there is a numerical result sc_query = mp_set_subcheck_state(sc_query, STATE_OK); xasprintf(&sc_query.output, "%s succeeded", sc_query.output); @@ -286,7 +286,7 @@ int main(int argc, char **argv) { mp_add_perfdata_to_subcheck(&sc_query_compare, pd_query); if (query_compare_state == STATE_OK) { - xasprintf(&sc_query_compare.output, "query result '%f' is withing thresholds", + xasprintf(&sc_query_compare.output, "query result '%f' is within thresholds", query_result.numerical_result); } else { xasprintf(&sc_query_compare.output, "query result '%f' is violating thresholds", -- cgit v1.2.3-74-g34f1 From b6150cded11526ffde40d9fa14f37a76e712c447 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:08:02 +0100 Subject: check_mysql: replace cpp constant with a proper enum --- plugins/check_mysql.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index 6134d6c6..7f2da5ac 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -401,10 +401,13 @@ int main(int argc, char **argv) { return STATE_OK; } -#define CHECK_REPLICA_OPT CHAR_MAX + 1 - /* process command-line arguments */ check_mysql_config_wrapper process_arguments(int argc, char **argv) { + + enum { + CHECK_REPLICA_OPT = CHAR_MAX + 1, + }; + static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, {"socket", required_argument, 0, 's'}, {"database", required_argument, 0, 'd'}, -- cgit v1.2.3-74-g34f1 From 0fb65a3a90e778f942a1f61f210a2dd969dd9597 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:31:27 +0100 Subject: check_mysql: implement modern output --- plugins/check_mysql.c | 270 ++++++++++++++++++++++++++--------------- plugins/check_mysql.d/config.h | 8 +- plugins/t/check_mysql.t | 7 +- 3 files changed, 175 insertions(+), 110 deletions(-) (limited to 'plugins') diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index 7f2da5ac..9d8094c0 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -30,13 +30,11 @@ * *****************************************************************************/ -const char *progname = "check_mysql"; -const char *copyright = "1999-2024"; -const char *email = "devel@monitoring-plugins.org"; - -#define REPLICA_RESULTSIZE 96 - #include "common.h" +#include "output.h" +#include "perfdata.h" +#include "states.h" +#include "thresholds.h" #include "utils.h" #include "utils_base.h" #include "netutils.h" @@ -46,8 +44,14 @@ const char *email = "devel@monitoring-plugins.org"; #include #include +const char *progname = "check_mysql"; +const char *copyright = "1999-2024"; +const char *email = "devel@monitoring-plugins.org"; + static int verbose = 0; +#define REPLICA_RESULTSIZE 96 + #define LENGTH_METRIC_UNIT 6 static const char *metric_unit[LENGTH_METRIC_UNIT] = { "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", @@ -110,7 +114,11 @@ int main(int argc, char **argv) { mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir, config.ciphers); } - /* establish a connection to the server and error checking */ + + mp_check overall = mp_check_init(); + + mp_subcheck sc_connection = mp_subcheck_init(); + /* establish a connection to the server and check for errors */ if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, config.db_port, config.db_socket, 0)) { /* Depending on internally-selected auth plugin MySQL might return */ @@ -118,78 +126,115 @@ int main(int argc, char **argv) { /* Semantically these errors are the same. */ if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR || mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) { - printf("MySQL OK - Version: %s (protocol %d)\n", mysql_get_server_info(&mysql), - mysql_get_proto_info(&mysql)); - mysql_close(&mysql); - return STATE_OK; - } + xasprintf(&sc_connection.output, "Version: %s (protocol %d)", + mysql_get_server_info(&mysql), mysql_get_proto_info(&mysql)); + sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK); - if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { - die(STATE_WARNING, "%s\n", mysql_error(&mysql)); - } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { - die(STATE_WARNING, "%s\n", mysql_error(&mysql)); - } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) { - die(STATE_WARNING, "%s\n", mysql_error(&mysql)); - } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) { - die(STATE_WARNING, "%s\n", mysql_error(&mysql)); - } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) { - die(STATE_WARNING, "%s\n", mysql_error(&mysql)); + mysql_close(&mysql); } else { - die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); + if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { + sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); + xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); + } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { + sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); + xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); + } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) { + sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); + xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); + } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) { + sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); + xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); + } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) { + sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); + xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); + } else { + sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL); + xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); + } } + + mp_add_subcheck_to_check(&overall, sc_connection); + mp_exit(overall); + } else { + // successful connection + sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK); + xasprintf(&sc_connection.output, "Version: %s (protocol %d)", mysql_get_server_info(&mysql), + mysql_get_proto_info(&mysql)); + mp_add_subcheck_to_check(&overall, sc_connection); } /* get the server stats */ - char *result = strdup(mysql_stat(&mysql)); + char *mysql_stats = strdup(mysql_stat(&mysql)); + + mp_subcheck sc_stats = mp_subcheck_init(); + sc_stats = mp_set_subcheck_default_state(sc_stats, STATE_OK); /* error checking once more */ - if (mysql_error(&mysql)) { - if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { - die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); - } else if (mysql_errno(&mysql) == CR_SERVER_LOST) { - die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); - } else if (mysql_errno(&mysql) == CR_UNKNOWN_ERROR) { - die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); + if (mysql_errno(&mysql) != 0) { + if ((mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) || + (mysql_errno(&mysql) == CR_SERVER_LOST) || (mysql_errno(&mysql) == CR_UNKNOWN_ERROR)) { + sc_stats = mp_set_subcheck_state(sc_stats, STATE_CRITICAL); + xasprintf(&sc_stats.output, "Retrieving stats failed: %s", mysql_error(&mysql)); + } else { + // not sure which error modes occur here, but mysql_error indicates an error + sc_stats = mp_set_subcheck_state(sc_stats, STATE_WARNING); + xasprintf(&sc_stats.output, "retrieving stats caused an error: %s", + mysql_error(&mysql)); } + + mp_add_subcheck_to_check(&overall, sc_stats); + mp_exit(overall); + } else { + xasprintf(&sc_stats.output, "retrieved stats: %s", mysql_stats); + sc_stats = mp_set_subcheck_state(sc_stats, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_stats); } - char *perf = strdup(""); - char *error = NULL; MYSQL_RES *res; MYSQL_ROW row; + mp_subcheck sc_query = mp_subcheck_init(); /* try to fetch some perf data */ if (mysql_query(&mysql, "show global status") == 0) { if ((res = mysql_store_result(&mysql)) == NULL) { - error = strdup(mysql_error(&mysql)); + xasprintf(&sc_connection.output, "query failed - status store_result error: %s", + mysql_error(&mysql)); mysql_close(&mysql); - die(STATE_CRITICAL, _("status store_result error: %s\n"), error); + + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_query); + mp_exit(overall); } while ((row = mysql_fetch_row(res)) != NULL) { for (int i = 0; i < LENGTH_METRIC_UNIT; i++) { if (strcmp(row[0], metric_unit[i]) == 0) { - xasprintf(&perf, "%s%s ", perf, - perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false, - 0, false, 0)); + mp_perfdata pd_mysql_stat = perfdata_init(); + pd_mysql_stat.label = (char *)metric_unit[i]; + pd_mysql_stat.value = mp_create_pd_value(atol(row[1])); + mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat); continue; } } + for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) { if (strcmp(row[0], metric_counter[i]) == 0) { - xasprintf(&perf, "%s%s ", perf, - perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0, - false, 0, false, 0)); + mp_perfdata pd_mysql_stat = perfdata_init(); + pd_mysql_stat.label = (char *)metric_counter[i]; + pd_mysql_stat.value = mp_create_pd_value(atol(row[1])); + pd_mysql_stat.uom = "c"; + mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat); continue; } } } - /* remove trailing space */ - if (strlen(perf) > 0) { - perf[strlen(perf) - 1] = '\0'; - } + } else { + // Query failed! + xasprintf(&sc_connection.output, "query failed"); + sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_query); + mp_exit(overall); } - char replica_result[REPLICA_RESULTSIZE] = {0}; if (config.check_replica) { // Detect which version we are, on older version // "show slave status" should work, on newer ones @@ -203,8 +248,10 @@ int main(int argc, char **argv) { unsigned long major_version = server_verion_int / 10000; unsigned long minor_version = (server_verion_int % 10000) / 100; unsigned long patch_version = (server_verion_int % 100); + if (verbose) { - printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n", + printf("Found MariaDB/MySQL: %s, main version: %lu, minor version: %lu, patch version: " + "%lu\n", server_version, major_version, minor_version, patch_version); } @@ -235,43 +282,60 @@ int main(int argc, char **argv) { replica_query = "show replica status"; } + mp_subcheck sc_replica = mp_subcheck_init(); + /* check the replica status */ if (mysql_query(&mysql, replica_query) != 0) { - error = strdup(mysql_error(&mysql)); + xasprintf(&sc_replica.output, "replica query error: %s", mysql_error(&mysql)); mysql_close(&mysql); - die(STATE_CRITICAL, _("replica query error: %s\n"), error); + + sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); } /* store the result */ if ((res = mysql_store_result(&mysql)) == NULL) { - error = strdup(mysql_error(&mysql)); + xasprintf(&sc_replica.output, "replica store_result error: %s", mysql_error(&mysql)); mysql_close(&mysql); - die(STATE_CRITICAL, _("replica store_result error: %s\n"), error); + + sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); } /* Check there is some data */ if (mysql_num_rows(res) == 0) { mysql_close(&mysql); - die(STATE_WARNING, "%s\n", _("No replicas defined")); + + xasprintf(&sc_replica.output, "no replicas defined"); + sc_replica = mp_set_subcheck_state(sc_replica, STATE_WARNING); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); } /* fetch the first row */ if ((row = mysql_fetch_row(res)) == NULL) { - error = strdup(mysql_error(&mysql)); + xasprintf(&sc_replica.output, "replica fetch row error: %s", mysql_error(&mysql)); mysql_free_result(res); mysql_close(&mysql); - die(STATE_CRITICAL, _("replica fetch row error: %s\n"), error); + + sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); } if (mysql_field_count(&mysql) == 12) { /* mysql 3.23.x */ - snprintf(replica_result, REPLICA_RESULTSIZE, _("Replica running: %s"), row[6]); + xasprintf(&sc_replica.output, "Replica running: %s", row[6]); if (strcmp(row[6], "Yes") != 0) { mysql_free_result(res); mysql_close(&mysql); - die(STATE_CRITICAL, "%s\n", replica_result); - } + sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); + } } else { /* mysql 4.x.x and mysql 5.x.x */ int replica_io_field = -1; @@ -315,14 +379,18 @@ int main(int argc, char **argv) { if ((replica_io_field < 0) || (replica_sql_field < 0) || (num_fields == 0)) { mysql_free_result(res); mysql_close(&mysql); - die(STATE_CRITICAL, "Replica status unavailable\n"); + + xasprintf(&sc_replica.output, "Replica status unavailable"); + sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); } /* Save replica status in replica_result */ - snprintf(replica_result, REPLICA_RESULTSIZE, - "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s", - row[replica_io_field], row[replica_sql_field], - seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown"); + xasprintf(&sc_replica.output, + "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s", + row[replica_io_field], row[replica_sql_field], + seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown"); /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no * mysqldump threads running */ @@ -345,10 +413,14 @@ int main(int argc, char **argv) { } mysql_close(&mysql); } + if (mysqldump_threads == 0) { - die(STATE_CRITICAL, "%s\n", replica_result); + sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); } else { - strncat(replica_result, " Mysqldump: in progress", REPLICA_RESULTSIZE - 1); + xasprintf(&sc_replica.output, "%s %s", sc_replica.output, + " Mysqldump: in progress"); } } @@ -364,22 +436,22 @@ int main(int argc, char **argv) { /* Check Seconds Behind against threshold */ if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && strcmp(row[seconds_behind_field], "NULL") != 0)) { - double value = atof(row[seconds_behind_field]); - int status; - - status = get_status(value, config.my_threshold); - - xasprintf(&perf, "%s %s", perf, - fperfdata("seconds behind master", value, "s", true, - (double)config.warning_time, true, (double)config.critical_time, - false, 0, false, 0)); - - if (status == STATE_WARNING) { - printf("SLOW_REPLICA %s: %s|%s\n", _("WARNING"), replica_result, perf); - exit(STATE_WARNING); - } else if (status == STATE_CRITICAL) { - printf("SLOW_REPLICA %s: %s|%s\n", _("CRITICAL"), replica_result, perf); - exit(STATE_CRITICAL); + mp_perfdata pd_seconds_behind = perfdata_init(); + pd_seconds_behind.label = "seconds behind master"; + pd_seconds_behind.value = mp_create_pd_value(atof(row[seconds_behind_field])); + pd_seconds_behind = + mp_pd_set_thresholds(pd_seconds_behind, config.replica_thresholds); + pd_seconds_behind.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_replica, pd_seconds_behind); + + mp_state_enum status = mp_get_pd_status(pd_seconds_behind); + + sc_replica = mp_set_subcheck_state(sc_replica, status); + + if (status != STATE_OK) { + xasprintf(&sc_replica.output, "slow replica - %s", sc_replica.output); + mp_add_subcheck_to_check(&overall, sc_replica); + mp_exit(overall); } } } @@ -391,14 +463,7 @@ int main(int argc, char **argv) { /* close the connection */ mysql_close(&mysql); - /* print out the result of stats */ - if (config.check_replica) { - printf("%s %s|%s\n", result, replica_result, perf); - } else { - printf("%s|%s\n", result, perf); - } - - return STATE_OK; + mp_exit(overall); } /* process command-line arguments */ @@ -442,9 +507,6 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { return result; } - char *warning = NULL; - char *critical = NULL; - int option = 0; while (true) { int option_index = @@ -516,14 +578,22 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { case 'n': result.config.ignore_auth = true; /* ignore-auth */ break; - case 'w': - warning = optarg; - result.config.warning_time = strtod(warning, NULL); - break; - case 'c': - critical = optarg; - result.config.critical_time = strtod(critical, NULL); - break; + case 'w': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning time threshold"); + } + result.config.replica_thresholds = + mp_thresholds_set_warn(result.config.replica_thresholds, tmp.range); + } break; + case 'c': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical time threshold"); + } + result.config.replica_thresholds = + mp_thresholds_set_crit(result.config.replica_thresholds, tmp.range); + } break; case 'V': /* version */ print_revision(progname, NP_VERSION); exit(STATE_UNKNOWN); @@ -540,8 +610,6 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { int index = optind; - set_thresholds(&result.config.my_threshold, warning, critical); - while (argc > index) { if (result.config.db_host == NULL) { if (is_host(argv[index])) { diff --git a/plugins/check_mysql.d/config.h b/plugins/check_mysql.d/config.h index 71ddbe8d..ef086cfc 100644 --- a/plugins/check_mysql.d/config.h +++ b/plugins/check_mysql.d/config.h @@ -24,9 +24,7 @@ typedef struct { bool check_replica; bool ignore_auth; - double warning_time; - double critical_time; - thresholds *my_threshold; + mp_thresholds replica_thresholds; } check_mysql_config; @@ -50,9 +48,7 @@ check_mysql_config check_mysql_config_init() { .check_replica = false, .ignore_auth = false, - .warning_time = 0, - .critical_time = 0, - .my_threshold = NULL, + .replica_thresholds = mp_thresholds_init(), }; return tmp; } diff --git a/plugins/t/check_mysql.t b/plugins/t/check_mysql.t index a383bc99..9114cccc 100644 --- a/plugins/t/check_mysql.t +++ b/plugins/t/check_mysql.t @@ -11,6 +11,7 @@ # mysql -u$user -p$password -h$host $db use strict; +use warnings; use Test::More; use NPTest; @@ -40,7 +41,7 @@ SKIP: { $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details"); cmp_ok( $result->return_code, "==", 1, "No replicas defined" ); - like( $result->output, "/No replicas defined/", "Correct error message"); + like( $result->output, "/no replicas defined/", "Correct error message"); } SKIP: { @@ -54,7 +55,7 @@ SKIP: { $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details"); cmp_ok( $result->return_code, "==", 1, "No replicas defined" ); - like( $result->output, "/No replicas defined/", "Correct error message"); + like( $result->output, "/no replicas defined/", "Correct error message"); } SKIP: { @@ -70,5 +71,5 @@ SKIP: { $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60:"); cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind'); - like( $result->output, "/^SLOW_REPLICA WARNING:/", "Output okay"); + like( $result->output, "/^slow_replica/", "Output okay"); } -- cgit v1.2.3-74-g34f1 From 9d827acbe1aac0edaa91a8765a87412a189cadf1 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:01:36 +0100 Subject: check_mysql_query: implement modern output --- plugins/check_mysql_query.c | 105 +++++++++++++++++++++-------------- plugins/check_mysql_query.d/config.h | 4 +- plugins/t/check_mysql_query.t | 2 +- 3 files changed, 65 insertions(+), 46 deletions(-) (limited to 'plugins') diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c index c7e84deb..cb79b4b4 100644 --- a/plugins/check_mysql_query.c +++ b/plugins/check_mysql_query.c @@ -29,11 +29,11 @@ * *****************************************************************************/ -const char *progname = "check_mysql_query"; -const char *copyright = "1999-2024"; -const char *email = "devel@monitoring-plugins.org"; - #include "common.h" +#include "output.h" +#include "perfdata.h" +#include "states.h" +#include "thresholds.h" #include "utils.h" #include "utils_base.h" #include "netutils.h" @@ -42,6 +42,10 @@ const char *email = "devel@monitoring-plugins.org"; #include #include +const char *progname = "check_mysql_query"; +const char *copyright = "1999-2024"; +const char *email = "devel@monitoring-plugins.org"; + typedef struct { int errorcode; check_mysql_query_config config; @@ -83,27 +87,38 @@ int main(int argc, char **argv) { mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); } + mp_check overall = mp_check_init(); + mp_subcheck sc_connect = mp_subcheck_init(); + /* establish a connection to the server and error checking */ if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, config.db_port, config.db_socket, 0)) { + xasprintf(&sc_connect.output, "query failed: %s", mysql_error(&mysql)); + if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { - die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); + sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { - die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); + sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) { - die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); + sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) { - die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); + sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) { - die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); + sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); } else { - die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql)); + sc_connect = mp_set_subcheck_state(sc_connect, STATE_CRITICAL); } + + mp_add_subcheck_to_check(&overall, sc_connect); + mp_exit(overall); } - char *error = NULL; + sc_connect = mp_set_subcheck_state(sc_connect, STATE_OK); + xasprintf(&sc_connect.output, "query succeeded"); + mp_add_subcheck_to_check(&overall, sc_connect); + if (mysql_query(&mysql, config.sql_query) != 0) { - error = strdup(mysql_error(&mysql)); + char *error = strdup(mysql_error(&mysql)); mysql_close(&mysql); die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); } @@ -111,7 +126,7 @@ int main(int argc, char **argv) { MYSQL_RES *res; /* store the result */ if ((res = mysql_store_result(&mysql)) == NULL) { - error = strdup(mysql_error(&mysql)); + char *error = strdup(mysql_error(&mysql)); mysql_close(&mysql); die(STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error); } @@ -122,17 +137,24 @@ int main(int argc, char **argv) { die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned")); } + mp_subcheck sc_value = mp_subcheck_init(); MYSQL_ROW row; /* fetch the first row */ if ((row = mysql_fetch_row(res)) == NULL) { - error = strdup(mysql_error(&mysql)); + xasprintf(&sc_value.output, "fetch row error - %s", mysql_error(&mysql)); mysql_free_result(res); mysql_close(&mysql); - die(STATE_CRITICAL, "QUERY %s: Fetch row error - %s\n", _("CRITICAL"), error); + + sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_value); + mp_exit(overall); } if (!is_numeric(row[0])) { - die(STATE_CRITICAL, "QUERY %s: %s - '%s'\n", _("CRITICAL"), _("Is not a numeric"), row[0]); + xasprintf(&sc_value.output, "query result is not numeric"); + sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_value); + mp_exit(overall); } double value = strtod(row[0], NULL); @@ -147,24 +169,18 @@ int main(int argc, char **argv) { printf("mysql result: %f\n", value); } - int status = get_status(value, config.my_thresholds); + mp_perfdata pd_query_result = perfdata_init(); + pd_query_result = mp_set_pd_value(pd_query_result, value); + pd_query_result = mp_pd_set_thresholds(pd_query_result, config.thresholds); + pd_query_result.label = "result"; + mp_add_perfdata_to_subcheck(&sc_value, pd_query_result); - if (status == STATE_OK) { - printf("QUERY %s: ", _("OK")); - } else if (status == STATE_WARNING) { - printf("QUERY %s: ", _("WARNING")); - } else if (status == STATE_CRITICAL) { - printf("QUERY %s: ", _("CRITICAL")); - } - printf(_("'%s' returned %f | %s"), config.sql_query, value, - fperfdata("result", value, "", config.my_thresholds->warning, - config.my_thresholds->warning ? config.my_thresholds->warning->end : 0, - config.my_thresholds->critical, - config.my_thresholds->critical ? config.my_thresholds->critical->end : 0, - false, 0, false, 0)); - printf("\n"); + sc_value = mp_set_subcheck_state(sc_value, mp_get_pd_status(pd_query_result)); + xasprintf(&sc_value.output, "'%s' returned '%f'", config.sql_query, value); + + mp_add_subcheck_to_check(&overall, sc_value); - return status; + mp_exit(overall); } /* process command-line arguments */ @@ -195,9 +211,6 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { return result; } - char *warning = NULL; - char *critical = NULL; - while (true) { int option = 0; int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); @@ -253,19 +266,25 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { case 'q': xasprintf(&result.config.sql_query, "%s", optarg); break; - case 'w': - warning = optarg; - break; - case 'c': - critical = optarg; - break; + case 'w': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warnign threshold"); + } + result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); + } break; + case 'c': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical threshold"); + } + result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range); + } break; case '?': /* help */ usage5(); } } - set_thresholds(&result.config.my_thresholds, warning, critical); - return validate_arguments(result); } diff --git a/plugins/check_mysql_query.d/config.h b/plugins/check_mysql_query.d/config.h index be019160..1c9952e5 100644 --- a/plugins/check_mysql_query.d/config.h +++ b/plugins/check_mysql_query.d/config.h @@ -15,7 +15,7 @@ typedef struct { unsigned int db_port; char *sql_query; - thresholds *my_thresholds; + mp_thresholds thresholds; } check_mysql_query_config; check_mysql_query_config check_mysql_query_config_init() { @@ -30,7 +30,7 @@ check_mysql_query_config check_mysql_query_config_init() { .db_port = MYSQL_PORT, .sql_query = NULL, - .my_thresholds = NULL, + .thresholds = mp_thresholds_init(), }; return tmp; } diff --git a/plugins/t/check_mysql_query.t b/plugins/t/check_mysql_query.t index c30245b2..6de48bf6 100644 --- a/plugins/t/check_mysql_query.t +++ b/plugins/t/check_mysql_query.t @@ -54,5 +54,5 @@ like( $result->output, "/No rows returned/", "No rows error message"); $result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details"); cmp_ok( $result->return_code, '==', 2, "Data not numeric"); -like( $result->output, "/Is not a numeric/", "Data not numeric error message"); +like( $result->output, "/is not numeric/", "Data not numeric error message"); -- cgit v1.2.3-74-g34f1 From 5bbfd58105766d453a6d0de5b9afcaf6daf4b460 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:10:39 +0100 Subject: Fix typo --- plugins/check_mysql_query.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c index cb79b4b4..8af378d5 100644 --- a/plugins/check_mysql_query.c +++ b/plugins/check_mysql_query.c @@ -269,7 +269,7 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { case 'w': { mp_range_parsed tmp = mp_parse_range_string(optarg); if (tmp.error != MP_PARSING_SUCCES) { - die(STATE_UNKNOWN, "failed to parse warnign threshold"); + die(STATE_UNKNOWN, "failed to parse warning threshold"); } result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); } break; -- cgit v1.2.3-74-g34f1 From 6bc9e518b247e85a39479a0ac6685e68c3a61b40 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 8 Nov 2025 00:19:25 +0100 Subject: check_smtp: modern output + some tls cert helper functions --- plugins/check_smtp.c | 676 ++++++++++++++++++++++++------------------ plugins/check_smtp.d/config.h | 16 +- plugins/netutils.h | 20 ++ plugins/sslutils.c | 132 +++++++++ 4 files changed, 550 insertions(+), 294 deletions(-) (limited to 'plugins') diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index 83ad575c..f2c7f05c 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -28,20 +28,24 @@ * *****************************************************************************/ -const char *progname = "check_smtp"; -const char *copyright = "2000-2024"; -const char *email = "devel@monitoring-plugins.org"; - #include "common.h" #include "netutils.h" +#include "output.h" +#include "perfdata.h" +#include "thresholds.h" #include "utils.h" #include "base64.h" #include "regex.h" #include +#include #include "check_smtp.d/config.h" #include "../lib/states.h" +const char *progname = "check_smtp"; +const char *copyright = "2000-2024"; +const char *email = "devel@monitoring-plugins.org"; + #define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" #define SMTP_HELO "HELO " #define SMTP_EHLO "EHLO " @@ -161,323 +165,414 @@ int main(int argc, char **argv) { gettimeofday(&start_time, NULL); int socket_descriptor = 0; + /* try to connect to the host at the given port number */ - mp_state_enum result = + mp_state_enum tcp_result = my_tcp_connect(config.server_address, config.server_port, &socket_descriptor); - char *error_msg = ""; + mp_check overall = mp_check_init(); + mp_subcheck sc_tcp_connect = mp_subcheck_init(); char buffer[MAX_INPUT_BUFFER]; bool ssl_established = false; - if (result == STATE_OK) { /* we connected */ - /* If requested, send PROXY header */ - if (config.use_proxy_prefix) { - if (verbose) { - printf("Sending header %s\n", PROXY_PREFIX); - } - my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established); + + if (tcp_result != STATE_OK) { + // Connect failed + sc_tcp_connect = mp_set_subcheck_state(sc_tcp_connect, STATE_CRITICAL); + xasprintf(&sc_tcp_connect.output, "TCP connect to '%s' failed", config.server_address); + mp_add_subcheck_to_check(&overall, sc_tcp_connect); + mp_exit(overall); + } + + /* we connected */ + /* If requested, send PROXY header */ + if (config.use_proxy_prefix) { + if (verbose) { + printf("Sending header %s\n", PROXY_PREFIX); } + my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established); + } #ifdef HAVE_SSL - if (config.use_ssl) { - result = np_net_ssl_init_with_hostname(socket_descriptor, - (config.use_sni ? config.server_address : NULL)); - if (result != STATE_OK) { - printf(_("CRITICAL - Cannot create SSL context.\n")); - close(socket_descriptor); - np_net_ssl_cleanup(); - exit(STATE_CRITICAL); - } - ssl_established = true; + if (config.use_ssl) { + int tls_result = np_net_ssl_init_with_hostname( + socket_descriptor, (config.use_sni ? config.server_address : NULL)); + + mp_subcheck sc_tls_connection = mp_subcheck_init(); + + if (tls_result != STATE_OK) { + close(socket_descriptor); + np_net_ssl_cleanup(); + + sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_CRITICAL); + xasprintf(&sc_tls_connection.output, "cannot create TLS context"); + mp_add_subcheck_to_check(&overall, sc_tls_connection); + mp_exit(overall); } + + sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_OK); + xasprintf(&sc_tls_connection.output, "TLS context established"); + mp_add_subcheck_to_check(&overall, sc_tls_connection); + ssl_established = true; + } #endif - /* watch for the SMTP connection string and */ - /* return a WARNING status if we couldn't read any data */ - if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { - printf(_("recv() failed\n")); - exit(STATE_WARNING); + /* watch for the SMTP connection string and */ + /* return a WARNING status if we couldn't read any data */ + if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { + mp_subcheck sc_read_data = mp_subcheck_init(); + sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING); + xasprintf(&sc_read_data.output, "recv() failed"); + mp_add_subcheck_to_check(&overall, sc_read_data); + mp_exit(overall); + } + + char *server_response = NULL; + /* save connect return (220 hostname ..) for later use */ + xasprintf(&server_response, "%s", buffer); + + /* send the HELO/EHLO command */ + my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established); + + /* allow for response to helo command to reach us */ + if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { + mp_subcheck sc_read_data = mp_subcheck_init(); + sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING); + xasprintf(&sc_read_data.output, "recv() failed"); + mp_add_subcheck_to_check(&overall, sc_read_data); + mp_exit(overall); + } + + bool supports_tls = false; + if (config.use_ehlo || config.use_lhlo) { + if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) { + supports_tls = true; } + } - char *server_response = NULL; - /* save connect return (220 hostname ..) for later use */ - xasprintf(&server_response, "%s", buffer); + if (config.use_starttls && !supports_tls) { + smtp_quit(config, buffer, socket_descriptor, ssl_established); - /* send the HELO/EHLO command */ - my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established); + mp_subcheck sc_read_data = mp_subcheck_init(); + sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING); + xasprintf(&sc_read_data.output, "StartTLS not supported by server"); + mp_add_subcheck_to_check(&overall, sc_read_data); + mp_exit(overall); + } + +#ifdef HAVE_SSL + if (config.use_starttls) { + /* send the STARTTLS command */ + send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); + + mp_subcheck sc_starttls_init = mp_subcheck_init(); + recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, + ssl_established); /* wait for it */ + if (!strstr(buffer, SMTP_EXPECT)) { + smtp_quit(config, buffer, socket_descriptor, ssl_established); + + xasprintf(&sc_starttls_init.output, "StartTLS not supported by server"); + sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_UNKNOWN); + mp_add_subcheck_to_check(&overall, sc_starttls_init); + mp_exit(overall); + } + + mp_state_enum starttls_result = np_net_ssl_init_with_hostname( + socket_descriptor, (config.use_sni ? config.server_address : NULL)); + if (starttls_result != STATE_OK) { + close(socket_descriptor); + np_net_ssl_cleanup(); + + sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_CRITICAL); + xasprintf(&sc_starttls_init.output, "failed to create StartTLS context"); + mp_add_subcheck_to_check(&overall, sc_starttls_init); + mp_exit(overall); + } + sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_OK); + xasprintf(&sc_starttls_init.output, "created StartTLS context"); + mp_add_subcheck_to_check(&overall, sc_starttls_init); + + ssl_established = true; + + /* + * Resend the EHLO command. + * + * RFC 3207 (4.2) says: ``The client MUST discard any knowledge + * obtained from the server, such as the list of SMTP service + * extensions, which was not obtained from the TLS negotiation + * itself. The client SHOULD send an EHLO command as the first + * command after a successful TLS negotiation.'' For this + * reason, some MTAs will not allow an AUTH LOGIN command before + * we resent EHLO via TLS. + */ + if (my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established) <= + 0) { + my_close(socket_descriptor); + + mp_subcheck sc_ehlo = mp_subcheck_init(); + sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN); + xasprintf(&sc_ehlo.output, "cannot send EHLO command via StartTLS"); + mp_add_subcheck_to_check(&overall, sc_ehlo); + mp_exit(overall); + } + + if (verbose) { + printf(_("sent %s"), helocmd); + } - /* allow for response to helo command to reach us */ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { - printf(_("recv() failed\n")); - exit(STATE_WARNING); + my_close(socket_descriptor); + + mp_subcheck sc_ehlo = mp_subcheck_init(); + sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN); + xasprintf(&sc_ehlo.output, "cannot read EHLO response via StartTLS"); + mp_add_subcheck_to_check(&overall, sc_ehlo); + mp_exit(overall); } - bool supports_tls = false; - if (config.use_ehlo || config.use_lhlo) { - if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) { - supports_tls = true; - } + if (verbose) { + printf("%s", buffer); } + } - if (config.use_starttls && !supports_tls) { - printf(_("WARNING - TLS not supported by server\n")); +# ifdef USE_OPENSSL + if (ssl_established) { + net_ssl_check_cert_result cert_check_result = + np_net_ssl_check_cert2(config.days_till_exp_warn, config.days_till_exp_crit); + + mp_subcheck sc_cert_check = mp_subcheck_init(); + + switch (cert_check_result.errors) { + case ALL_OK: { + xasprintf(&sc_cert_check.output, "Certificate expiration. Remaining time %g days", + cert_check_result.remaining_seconds / 86400); + sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); + } break; + case NO_SERVER_CERTIFICATE_PRESENT: { + xasprintf(&sc_cert_check.output, "no server certificate present"); + sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); + } break; + case UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT: { + xasprintf(&sc_cert_check.output, "can not retrieve certificate subject"); + sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); + } break; + case WRONG_TIME_FORMAT_IN_CERTIFICATE: { + xasprintf(&sc_cert_check.output, "wrong time format in certificate"); + sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); + } break; + }; + + mp_add_subcheck_to_check(&overall, sc_cert_check); + + if (config.check_cert) { smtp_quit(config, buffer, socket_descriptor, ssl_established); - exit(STATE_WARNING); + my_close(socket_descriptor); + mp_exit(overall); } + } +# endif /* USE_OPENSSL */ -#ifdef HAVE_SSL - if (config.use_starttls) { - /* send the STARTTLS command */ - send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); - - recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, - ssl_established); /* wait for it */ - if (!strstr(buffer, SMTP_EXPECT)) { - printf(_("Server does not support STARTTLS\n")); - smtp_quit(config, buffer, socket_descriptor, ssl_established); - exit(STATE_UNKNOWN); - } +#endif - result = np_net_ssl_init_with_hostname(socket_descriptor, - (config.use_sni ? config.server_address : NULL)); - if (result != STATE_OK) { - printf(_("CRITICAL - Cannot create SSL context.\n")); - close(socket_descriptor); - np_net_ssl_cleanup(); - exit(STATE_CRITICAL); - } + if (verbose) { + printf("%s", buffer); + } - ssl_established = true; - - /* - * Resend the EHLO command. - * - * RFC 3207 (4.2) says: ``The client MUST discard any knowledge - * obtained from the server, such as the list of SMTP service - * extensions, which was not obtained from the TLS negotiation - * itself. The client SHOULD send an EHLO command as the first - * command after a successful TLS negotiation.'' For this - * reason, some MTAs will not allow an AUTH LOGIN command before - * we resent EHLO via TLS. - */ - if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <= - 0) { - printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); - my_close(socket_descriptor); - exit(STATE_UNKNOWN); - } + /* save buffer for later use */ + xasprintf(&server_response, "%s%s", server_response, buffer); + /* strip the buffer of carriage returns */ + strip(server_response); - if (verbose) { - printf(_("sent %s"), helocmd); - } + /* make sure we find the droids we are looking for */ + mp_subcheck sc_expect_response = mp_subcheck_init(); - if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= - 0) { - printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); - my_close(socket_descriptor); - exit(STATE_UNKNOWN); - } + if (!strstr(server_response, config.server_expect)) { + sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_WARNING); + if (config.server_port == SMTP_PORT) { + xasprintf(&sc_expect_response.output, _("invalid SMTP response received from host: %s"), + server_response); + } else { + xasprintf(&sc_expect_response.output, + _("invalid SMTP response received from host on port %d: %s"), + config.server_port, server_response); + } + exit(STATE_WARNING); + } else { + xasprintf(&sc_expect_response.output, "received valid SMTP response '%s' from host: '%s'", + config.server_expect, server_response); + sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_OK); + } - if (verbose) { - printf("%s", buffer); - } + mp_add_subcheck_to_check(&overall, sc_expect_response); -# ifdef USE_OPENSSL - if (config.check_cert) { - result = - np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit); - smtp_quit(config, buffer, socket_descriptor, ssl_established); - my_close(socket_descriptor); - exit(result); - } -# endif /* USE_OPENSSL */ + if (config.send_mail_from) { + my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); + if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 && + verbose) { + printf("%s", buffer); } -#endif + } - if (verbose) { + size_t counter = 0; + while (counter < config.ncommands) { + xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n"); + my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); + if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 && + verbose) { printf("%s", buffer); } - /* save buffer for later use */ - xasprintf(&server_response, "%s%s", server_response, buffer); - /* strip the buffer of carriage returns */ - strip(server_response); + strip(buffer); - /* make sure we find the droids we are looking for */ - if (!strstr(server_response, config.server_expect)) { - if (config.server_port == SMTP_PORT) { - printf(_("Invalid SMTP response received from host: %s\n"), server_response); - } else { - printf(_("Invalid SMTP response received from host on port %d: %s\n"), - config.server_port, server_response); + if (counter < config.nresponses) { + int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; + regex_t preg; + int errcode = regcomp(&preg, config.responses[counter], cflags); + char errbuf[MAX_INPUT_BUFFER]; + if (errcode != 0) { + regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); + printf(_("Could Not Compile Regular Expression")); + exit(STATE_UNKNOWN); } - exit(STATE_WARNING); - } - if (config.send_mail_from) { - my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); - if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= - 1 && - verbose) { - printf("%s", buffer); + regmatch_t pmatch[10]; + int eflags = 0; + int excode = regexec(&preg, buffer, 10, pmatch, eflags); + mp_subcheck sc_expected_responses = mp_subcheck_init(); + if (excode == 0) { + xasprintf(&sc_expected_responses.output, "valid response '%s' to command '%s'", + buffer, config.commands[counter]); + sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_OK); + } else if (excode == REG_NOMATCH) { + sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_WARNING); + xasprintf(&sc_expected_responses.output, "invalid response '%s' to command '%s'", + buffer, config.commands[counter]); + } else { + regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER); + xasprintf(&sc_expected_responses.output, "regexec execute error: %s", errbuf); + sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_UNKNOWN); } } + counter++; + } - int counter = 0; - while (counter < config.ncommands) { - xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n"); - my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); - if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= - 1 && - verbose) { - printf("%s", buffer); - } - strip(buffer); - if (counter < config.nresponses) { - int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; - regex_t preg; - int errcode = regcomp(&preg, config.responses[counter], cflags); - char errbuf[MAX_INPUT_BUFFER]; - if (errcode != 0) { - regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); - printf(_("Could Not Compile Regular Expression")); - exit(STATE_UNKNOWN); + if (config.authtype != NULL) { + mp_subcheck sc_auth = mp_subcheck_init(); + + if (strcmp(config.authtype, "LOGIN") == 0) { + char *abuf; + int ret; + do { + /* send AUTH LOGIN */ + my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor, + ssl_established); + + if (verbose) { + printf(_("sent %s\n"), "AUTH LOGIN"); } - regmatch_t pmatch[10]; - int eflags = 0; - int excode = regexec(&preg, buffer, 10, pmatch, eflags); - if (excode == 0) { - result = STATE_OK; - } else if (excode == REG_NOMATCH) { - result = STATE_WARNING; - printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"), - state_text(result), buffer, config.commands[counter]); - } else { - regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER); - printf(_("Execute Error: %s\n"), errbuf); - result = STATE_UNKNOWN; + if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, + ssl_established)) <= 0) { + xasprintf(&sc_auth.output, _("recv() failed after AUTH LOGIN")); + sc_auth = mp_set_subcheck_state(sc_auth, STATE_WARNING); + break; } - } - counter++; - } - if (config.authtype != NULL) { - if (strcmp(config.authtype, "LOGIN") == 0) { - char *abuf; - int ret; - do { - if (config.authuser == NULL) { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("no authuser specified, ")); - break; - } - if (config.authpass == NULL) { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("no authpass specified, ")); - break; - } - - /* send AUTH LOGIN */ - my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor, - ssl_established); - if (verbose) { - printf(_("sent %s\n"), "AUTH LOGIN"); - } - - if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, - ssl_established)) <= 0) { - xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, ")); - result = STATE_WARNING; - break; - } - if (verbose) { - printf(_("received %s\n"), buffer); - } - - if (strncmp(buffer, "334", 3) != 0) { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, ")); - break; - } - - /* encode authuser with base64 */ - base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf); - xasprintf(&abuf, "%s\r\n", abuf); - my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); - if (verbose) { - printf(_("sent %s\n"), abuf); - } - - if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, - ssl_established)) <= 0) { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("recv() failed after sending authuser, ")); - break; - } - if (verbose) { - printf(_("received %s\n"), buffer); - } - if (strncmp(buffer, "334", 3) != 0) { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("invalid response received after authuser, ")); - break; - } - /* encode authpass with base64 */ - base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf); - xasprintf(&abuf, "%s\r\n", abuf); - my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); - if (verbose) { - printf(_("sent %s\n"), abuf); - } - if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, - ssl_established)) <= 0) { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("recv() failed after sending authpass, ")); - break; - } - if (verbose) { - printf(_("received %s\n"), buffer); - } - if (strncmp(buffer, "235", 3) != 0) { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("invalid response received after authpass, ")); - break; - } + if (verbose) { + printf(_("received %s\n"), buffer); + } + + if (strncmp(buffer, "334", 3) != 0) { + xasprintf(&sc_auth.output, "invalid response received after AUTH LOGIN"); + sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); break; - } while (false); - } else { - result = STATE_CRITICAL; - xasprintf(&error_msg, _("only authtype LOGIN is supported, ")); - } - } + } - /* tell the server we're done */ - smtp_quit(config, buffer, socket_descriptor, ssl_established); + /* encode authuser with base64 */ + base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf); + xasprintf(&abuf, "%s\r\n", abuf); + my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); + if (verbose) { + printf(_("sent %s\n"), abuf); + } - /* finally close the connection */ - close(socket_descriptor); + if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, + ssl_established)) <= 0) { + xasprintf(&sc_auth.output, "recv() failed after sending authuser"); + sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); + break; + } + + if (verbose) { + printf(_("received %s\n"), buffer); + } + + if (strncmp(buffer, "334", 3) != 0) { + xasprintf(&sc_auth.output, "invalid response received after authuser"); + sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); + break; + } + + /* encode authpass with base64 */ + base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf); + xasprintf(&abuf, "%s\r\n", abuf); + my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); + + if (verbose) { + printf(_("sent %s\n"), abuf); + } + + if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, + ssl_established)) <= 0) { + xasprintf(&sc_auth.output, "recv() failed after sending authpass"); + sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); + break; + } + + if (verbose) { + printf(_("received %s\n"), buffer); + } + + if (strncmp(buffer, "235", 3) != 0) { + xasprintf(&sc_auth.output, "invalid response received after authpass"); + sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); + break; + } + break; + } while (false); + } else { + sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); + xasprintf(&sc_auth.output, "only authtype LOGIN is supported"); + } + + mp_add_subcheck_to_check(&overall, sc_auth); } + /* tell the server we're done */ + smtp_quit(config, buffer, socket_descriptor, ssl_established); + + /* finally close the connection */ + close(socket_descriptor); + /* reset the alarm */ alarm(0); long microsec = deltime(start_time); double elapsed_time = (double)microsec / 1.0e6; - if (result == STATE_OK) { - if (config.check_critical_time && elapsed_time > config.critical_time) { - result = STATE_CRITICAL; - } else if (config.check_warning_time && elapsed_time > config.warning_time) { - result = STATE_WARNING; - } - } + mp_perfdata pd_elapsed_time = perfdata_init(); + pd_elapsed_time = mp_set_pd_value(pd_elapsed_time, elapsed_time); + pd_elapsed_time.label = "time"; + pd_elapsed_time.uom = "s"; - printf(_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), state_text(result), error_msg, - elapsed_time, verbose ? ", " : "", verbose ? buffer : "", - fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time, - config.check_critical_time, config.critical_time, true, 0, false, 0)); + pd_elapsed_time = mp_pd_set_thresholds(pd_elapsed_time, config.connection_time); - exit(result); + mp_subcheck sc_connection_time = mp_subcheck_init(); + xasprintf(&sc_connection_time.output, "connection time: %.3gs", elapsed_time); + sc_connection_time = + mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_elapsed_time)); + mp_add_subcheck_to_check(&overall, sc_connection_time); + + mp_exit(overall); } /* process command-line arguments */ @@ -535,8 +630,8 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { } } - int command_size = 0; - int response_size = 0; + unsigned long command_size = 0; + unsigned long response_size = 0; bool implicit_tls = false; int server_port_option = 0; while (true) { @@ -591,7 +686,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { result.config.commands = realloc(result.config.commands, sizeof(char *) * command_size); if (result.config.commands == NULL) { - die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), + die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"), result.config.ncommands); } } @@ -605,7 +700,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { result.config.responses = realloc(result.config.responses, sizeof(char *) * response_size); if (result.config.responses == NULL) { - die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), + die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"), result.config.nresponses); } } @@ -613,22 +708,22 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { strncpy(result.config.responses[result.config.nresponses], optarg, 255); result.config.nresponses++; break; - case 'c': /* critical time threshold */ - if (!is_nonnegative(optarg)) { - usage4(_("Critical time must be a positive")); - } else { - result.config.critical_time = strtod(optarg, NULL); - result.config.check_critical_time = true; + case 'c': /* critical time threshold */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical time threshold"); } - break; - case 'w': /* warning time threshold */ - if (!is_nonnegative(optarg)) { - usage4(_("Warning time must be a positive")); - } else { - result.config.warning_time = strtod(optarg, NULL); - result.config.check_warning_time = true; + result.config.connection_time = + mp_thresholds_set_warn(result.config.connection_time, tmp.range); + } break; + case 'w': /* warning time threshold */ { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning time threshold"); } - break; + result.config.connection_time = + mp_thresholds_set_crit(result.config.connection_time, tmp.range); + } break; case 'v': /* verbose */ verbose++; break; @@ -742,6 +837,19 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { result.config.server_port = server_port_option; } + if (result.config.authtype) { + if (strcmp(result.config.authtype, "LOGIN") == 0) { + if (result.config.authuser == NULL) { + usage4("no authuser specified"); + } + if (result.config.authpass == NULL) { + usage4("no authpass specified"); + } + } else { + usage4("only authtype LOGIN is supported"); + } + } + return result; } @@ -791,7 +899,7 @@ char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int soc int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor, bool ssl_established) { int result; - int counter; + size_t counter; for (counter = result = 0; counter < bufsize - 1; counter++) { if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) { @@ -799,7 +907,7 @@ int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_des } if (buf[counter] == '\n') { buf[++counter] = '\0'; - return counter; + return (int)counter; } } return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */ diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h index 0a6511ef..bc433093 100644 --- a/plugins/check_smtp.d/config.h +++ b/plugins/check_smtp.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "thresholds.h" #include #include @@ -18,20 +19,18 @@ typedef struct { char *server_expect; bool ignore_send_quit_failure; - double warning_time; - bool check_warning_time; - double critical_time; - bool check_critical_time; + mp_thresholds connection_time; + bool use_ehlo; bool use_lhlo; char *from_arg; bool send_mail_from; - int ncommands; + unsigned long ncommands; char **commands; - int nresponses; + unsigned long nresponses; char **responses; char *authtype; @@ -58,10 +57,7 @@ check_smtp_config check_smtp_config_init() { .server_expect = SMTP_EXPECT, .ignore_send_quit_failure = false, - .warning_time = 0, - .check_warning_time = false, - .critical_time = 0, - .check_critical_time = false, + .connection_time = mp_thresholds_init(), .use_ehlo = false, .use_lhlo = false, diff --git a/plugins/netutils.h b/plugins/netutils.h index c4461113..dbd22398 100644 --- a/plugins/netutils.h +++ b/plugins/netutils.h @@ -114,6 +114,26 @@ int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name, void np_net_ssl_cleanup(void); int np_net_ssl_write(const void *buf, int num); int np_net_ssl_read(void *buf, int num); + +typedef enum { + ALL_OK, + NO_SERVER_CERTIFICATE_PRESENT, + UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT, + WRONG_TIME_FORMAT_IN_CERTIFICATE, +} retrieve_expiration_date_errors; + +typedef struct { + double remaining_seconds; + retrieve_expiration_date_errors errors; +} retrieve_expiration_time_result; + +typedef struct { + mp_state_enum result_state; + double remaining_seconds; + retrieve_expiration_date_errors errors; +} net_ssl_check_cert_result; +net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit); + mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); #endif /* HAVE_SSL */ diff --git a/plugins/sslutils.c b/plugins/sslutils.c index 0e6d7525..c1d15534 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -312,6 +312,138 @@ mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_ # endif /* USE_OPENSSL */ } +retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate) { +# ifdef USE_OPENSSL + retrieve_expiration_time_result result = { + .errors = ALL_OK, + .remaining_seconds = {}, + }; + + if (!certificate) { + // printf("%s\n", _("CRITICAL - No server certificate present to inspect.")); + result.errors = NO_SERVER_CERTIFICATE_PRESENT; + return result; + } + + /* Extract CN from certificate subject */ + X509_NAME *subj = X509_get_subject_name(certificate); + + if (!subj) { + // printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); + result.errors = UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT; + return result; + } + + char cn[MAX_CN_LENGTH] = ""; + int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); + if (cnlen == -1) { + strcpy(cn, _("Unknown CN")); + } + + /* Retrieve timestamp of certificate */ + ASN1_STRING *expiration_timestamp = X509_get_notAfter(certificate); + + int offset = 0; + struct tm stamp = {}; + /* Generate tm structure to process timestamp */ + if (expiration_timestamp->type == V_ASN1_UTCTIME) { + if (expiration_timestamp->length < 10) { + result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE; + return result; + } + + stamp.tm_year = + (expiration_timestamp->data[0] - '0') * 10 + (expiration_timestamp->data[1] - '0'); + if (stamp.tm_year < 50) { + stamp.tm_year += 100; + } + offset = 0; + } else { + if (expiration_timestamp->length < 12) { + result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE; + return result; + } + + stamp.tm_year = (expiration_timestamp->data[0] - '0') * 1000 + + (expiration_timestamp->data[1] - '0') * 100 + + (expiration_timestamp->data[2] - '0') * 10 + + (expiration_timestamp->data[3] - '0'); + stamp.tm_year -= 1900; + offset = 2; + } + stamp.tm_mon = (expiration_timestamp->data[2 + offset] - '0') * 10 + + (expiration_timestamp->data[3 + offset] - '0') - 1; + stamp.tm_mday = (expiration_timestamp->data[4 + offset] - '0') * 10 + + (expiration_timestamp->data[5 + offset] - '0'); + stamp.tm_hour = (expiration_timestamp->data[6 + offset] - '0') * 10 + + (expiration_timestamp->data[7 + offset] - '0'); + stamp.tm_min = (expiration_timestamp->data[8 + offset] - '0') * 10 + + (expiration_timestamp->data[9 + offset] - '0'); + stamp.tm_sec = (expiration_timestamp->data[10 + offset] - '0') * 10 + + (expiration_timestamp->data[11 + offset] - '0'); + stamp.tm_isdst = -1; + + time_t tm_t = timegm(&stamp); + double time_left = difftime(tm_t, time(NULL)); + result.remaining_seconds = time_left; + + char *timezone = getenv("TZ"); + setenv("TZ", "GMT", 1); + tzset(); + + char timestamp[50] = ""; + strftime(timestamp, 50, "%c %z", localtime(&tm_t)); + if (timezone) { + setenv("TZ", timezone, 1); + } else { + unsetenv("TZ"); + } + + tzset(); + + X509_free(certificate); + + return result; +# else /* ifndef USE_OPENSSL */ + printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); + return STATE_WARNING; +# endif /* USE_OPENSSL */ +} + +net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit) { +# ifdef USE_OPENSSL + X509 *certificate = NULL; + certificate = SSL_get_peer_certificate(s); + + retrieve_expiration_time_result expiration_date = np_net_ssl_get_cert_expiration(certificate); + + net_ssl_check_cert_result result = { + .result_state = STATE_UNKNOWN, + .remaining_seconds = expiration_date.remaining_seconds, + .errors = expiration_date.errors, + }; + + if (expiration_date.errors == ALL_OK) { + // got a valid expiration date + unsigned int remaining_days = result.remaining_seconds / 86400; + + if (remaining_days < days_till_exp_crit) { + result.result_state = STATE_CRITICAL; + } else if (remaining_days < days_till_exp_warn) { + result.result_state = STATE_WARNING; + } else { + result.result_state = STATE_OK; + } + } + + return result; + +# else /* ifndef USE_OPENSSL */ + printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); + return STATE_WARNING; +# endif /* USE_OPENSSL */ +} + mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { # ifdef USE_OPENSSL X509 *certificate = NULL; -- cgit v1.2.3-74-g34f1 From e0b127312797cd4619886dc6df57411a77fe2d33 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 8 Nov 2025 00:46:37 +0100 Subject: check_smtp: adapt tests --- plugins/t/check_smtp.t | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'plugins') diff --git a/plugins/t/check_smtp.t b/plugins/t/check_smtp.t index 73b4a1fd..6c8601d3 100644 --- a/plugins/t/check_smtp.t +++ b/plugins/t/check_smtp.t @@ -5,6 +5,7 @@ # use strict; +use warnings; use Test::More; use NPTest; @@ -42,12 +43,11 @@ SKIP: { TODO: { local $TODO = "Output is over two lines"; - like ( $res->output, qr/^SMTP WARNING/, "Correct error message" ); } $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" ); is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" ); - like ($res->output, qr/^CRITICAL - Cannot make SSL connection\./, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); + like ($res->output, qr/^cannot create TLS context\./, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); } SKIP: { @@ -68,7 +68,6 @@ SKIP: { skip "No SMTP server with TLS defined", 1 unless $host_tcp_smtp_tls; $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls --ssl" ); is ($res->return_code, 0, "Check rc of connecting to $host_tcp_smtp_tls with TLS" ); - like ($res->output, qr/^SMTP OK - /, "Check output of connecting to $host_tcp_smtp_tls with TLS" ); my $unused_port = 4465; $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" ); -- cgit v1.2.3-74-g34f1 From 034d4ee15840550dac4a57b41f5b60732ca1df6b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 8 Nov 2025 00:59:46 +0100 Subject: Fix initialisation on old compilers --- plugins/sslutils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/sslutils.c b/plugins/sslutils.c index c1d15534..c58a35ab 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -316,7 +316,7 @@ retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate # ifdef USE_OPENSSL retrieve_expiration_time_result result = { .errors = ALL_OK, - .remaining_seconds = {}, + .remaining_seconds = 0, }; if (!certificate) { -- cgit v1.2.3-74-g34f1 From 4442ea917b9b3a7bc4fe4b980bbfc70b533a6010 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 8 Nov 2025 01:15:54 +0100 Subject: small test correction --- plugins/t/check_smtp.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/t/check_smtp.t b/plugins/t/check_smtp.t index 6c8601d3..c2f53c3d 100644 --- a/plugins/t/check_smtp.t +++ b/plugins/t/check_smtp.t @@ -25,7 +25,7 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost" ); my $res; -plan tests => 15; +plan tests => 13; SKIP: { skip "No SMTP server defined", 4 unless $host_tcp_smtp; @@ -47,7 +47,7 @@ SKIP: { $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" ); is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" ); - like ($res->output, qr/^cannot create TLS context\./, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); + like ($res->output, qr/cannot create TLS context/, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); } SKIP: { -- cgit v1.2.3-74-g34f1 From 62035adf6c8199eba54755f23e8affe97e645300 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:32:43 +0100 Subject: check_smtp: implement output format cli parameter --- plugins/check_smtp.c | 22 +++++++++++++++++++++- plugins/check_smtp.d/config.h | 6 ++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index f2c7f05c..cb92421c 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -115,6 +115,10 @@ int main(int argc, char **argv) { const check_smtp_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* If localhostname not set on command line, use gethostname to set */ char *localhostname = config.localhostname; if (!localhostname) { @@ -578,7 +582,8 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_smtp_config_wrapper process_arguments(int argc, char **argv) { enum { - SNI_OPTION = CHAR_MAX + 1 + SNI_OPTION = CHAR_MAX + 1, + output_format_index, }; int option = 0; @@ -608,6 +613,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { {"certificate", required_argument, 0, 'D'}, {"ignore-quit-failure", no_argument, 0, 'q'}, {"proxy", no_argument, 0, 'r'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_smtp_config_wrapper result = { @@ -809,6 +815,18 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { exit(STATE_UNKNOWN); case '?': /* help */ usage5(); + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } } } @@ -1015,6 +1033,8 @@ void print_help(void) { printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + printf(UT_OUTPUT_FORMAT); + printf(UT_VERBOSE); printf("\n"); diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h index bc433093..11d7fe56 100644 --- a/plugins/check_smtp.d/config.h +++ b/plugins/check_smtp.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include "thresholds.h" #include #include @@ -46,6 +47,9 @@ typedef struct { bool use_starttls; bool use_sni; #endif + + bool output_format_is_set; + mp_output_format output_format; } check_smtp_config; check_smtp_config check_smtp_config_init() { @@ -83,6 +87,8 @@ check_smtp_config check_smtp_config_init() { .use_starttls = false, .use_sni = false, #endif + + .output_format_is_set = false, }; return tmp; } -- cgit v1.2.3-74-g34f1 From bc2720abddf8e379c4e1f23ed25f7702ef29ad08 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:46:36 +0100 Subject: check_smtp: certificate check is no longer opt-in This is a breaking change. Testing whether a TLS certificate is still valid (expiration wise) is now the default in check_smtp. The reasoning is, that in most scenarios an expired certificate will effectively mean that the service is not working anymore due to the refusal of other software to talk to it. There is a new cli parameter though to explicitly ignore that. --- plugins/check_smtp.c | 88 ++++++++++++++++++++++++------------------- plugins/check_smtp.d/config.h | 6 ++- 2 files changed, 54 insertions(+), 40 deletions(-) (limited to 'plugins') diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index cb92421c..e806ad29 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -37,6 +37,7 @@ #include "base64.h" #include "regex.h" +#include #include #include #include "check_smtp.d/config.h" @@ -347,9 +348,19 @@ int main(int argc, char **argv) { switch (cert_check_result.errors) { case ALL_OK: { - xasprintf(&sc_cert_check.output, "Certificate expiration. Remaining time %g days", - cert_check_result.remaining_seconds / 86400); - sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); + + if (cert_check_result.result_state != STATE_OK && + config.ignore_certificate_expiration) { + xasprintf(&sc_cert_check.output, + "Remaining certificate lifetime: %d days. Expiration will be ignored", + (int)(cert_check_result.remaining_seconds / 86400)); + sc_cert_check = mp_set_subcheck_state(sc_cert_check, STATE_OK); + } else { + xasprintf(&sc_cert_check.output, "Remaining certificate lifetime: %d days", + (int)(cert_check_result.remaining_seconds / 86400)); + sc_cert_check = + mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); + } } break; case NO_SERVER_CERTIFICATE_PRESENT: { xasprintf(&sc_cert_check.output, "no server certificate present"); @@ -366,12 +377,6 @@ int main(int argc, char **argv) { }; mp_add_subcheck_to_check(&overall, sc_cert_check); - - if (config.check_cert) { - smtp_quit(config, buffer, socket_descriptor, ssl_established); - my_close(socket_descriptor); - mp_exit(overall); - } } # endif /* USE_OPENSSL */ @@ -584,37 +589,40 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { enum { SNI_OPTION = CHAR_MAX + 1, output_format_index, + ignore_certificate_expiration_index, }; int option = 0; - static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, - {"expect", required_argument, 0, 'e'}, - {"critical", required_argument, 0, 'c'}, - {"warning", required_argument, 0, 'w'}, - {"timeout", required_argument, 0, 't'}, - {"port", required_argument, 0, 'p'}, - {"from", required_argument, 0, 'f'}, - {"fqdn", required_argument, 0, 'F'}, - {"authtype", required_argument, 0, 'A'}, - {"authuser", required_argument, 0, 'U'}, - {"authpass", required_argument, 0, 'P'}, - {"command", required_argument, 0, 'C'}, - {"response", required_argument, 0, 'R'}, - {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"help", no_argument, 0, 'h'}, - {"lmtp", no_argument, 0, 'L'}, - {"ssl", no_argument, 0, 's'}, - {"tls", no_argument, 0, 's'}, - {"starttls", no_argument, 0, 'S'}, - {"sni", no_argument, 0, SNI_OPTION}, - {"certificate", required_argument, 0, 'D'}, - {"ignore-quit-failure", no_argument, 0, 'q'}, - {"proxy", no_argument, 0, 'r'}, - {"output-format", required_argument, 0, output_format_index}, - {0, 0, 0, 0}}; + static struct option longopts[] = { + {"hostname", required_argument, 0, 'H'}, + {"expect", required_argument, 0, 'e'}, + {"critical", required_argument, 0, 'c'}, + {"warning", required_argument, 0, 'w'}, + {"timeout", required_argument, 0, 't'}, + {"port", required_argument, 0, 'p'}, + {"from", required_argument, 0, 'f'}, + {"fqdn", required_argument, 0, 'F'}, + {"authtype", required_argument, 0, 'A'}, + {"authuser", required_argument, 0, 'U'}, + {"authpass", required_argument, 0, 'P'}, + {"command", required_argument, 0, 'C'}, + {"response", required_argument, 0, 'R'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"help", no_argument, 0, 'h'}, + {"lmtp", no_argument, 0, 'L'}, + {"ssl", no_argument, 0, 's'}, + {"tls", no_argument, 0, 's'}, + {"starttls", no_argument, 0, 'S'}, + {"sni", no_argument, 0, SNI_OPTION}, + {"certificate", required_argument, 0, 'D'}, + {"ignore-quit-failure", no_argument, 0, 'q'}, + {"proxy", no_argument, 0, 'r'}, + {"ignore-certificate-expiration", no_argument, 0, ignore_certificate_expiration_index}, + {"output-format", required_argument, 0, output_format_index}, + {0, 0, 0, 0}}; check_smtp_config_wrapper result = { .config = check_smtp_config_init(), @@ -766,7 +774,6 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { } result.config.days_till_exp_warn = atoi(optarg); } - result.config.check_cert = true; result.config.ignore_send_quit_failure = true; #else usage(_("SSL support not available - install OpenSSL and recompile")); @@ -827,6 +834,9 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { result.config.output_format = parser.output_format; break; } + case ignore_certificate_expiration_index: { + result.config.ignore_certificate_expiration = true; + } } } @@ -1028,6 +1038,8 @@ void print_help(void) { printf(" %s\n", _("Send LHLO instead of HELO/EHLO")); printf(" %s\n", "-q, --ignore-quit-failure"); printf(" %s\n", _("Ignore failure when sending QUIT command to server")); + printf(" %s\n", "--ignore-certificate-expiration"); + printf(" %s\n", _("Ignore certificate expiration")); printf(UT_WARN_CRIT); diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h index 11d7fe56..b0d42ed1 100644 --- a/plugins/check_smtp.d/config.h +++ b/plugins/check_smtp.d/config.h @@ -40,12 +40,13 @@ typedef struct { bool use_proxy_prefix; #ifdef HAVE_SSL - bool check_cert; int days_till_exp_warn; int days_till_exp_crit; bool use_ssl; bool use_starttls; bool use_sni; + + bool ignore_certificate_expiration; #endif bool output_format_is_set; @@ -80,12 +81,13 @@ check_smtp_config check_smtp_config_init() { .use_proxy_prefix = false, #ifdef HAVE_SSL - .check_cert = false, .days_till_exp_warn = 0, .days_till_exp_crit = 0, .use_ssl = false, .use_starttls = false, .use_sni = false, + + .ignore_certificate_expiration = false, #endif .output_format_is_set = false, -- cgit v1.2.3-74-g34f1 From 7bfb16e0da721dcf50558f9104d3ed84efe03516 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:26:41 +0100 Subject: Implement replacement functions for executing commands This commit implements replacement functions for the previous exec functions. The replacements are implemented in a more "pure" style, the do no longer receive pointer arguments which they will write to, but create the pointers themselves and should therefore be easier to use, since it is more obvious what goes in and what comes out. Also a essentialy unused variable was removed with this. --- lib/utils_cmd.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/utils_cmd.h | 10 +- plugins/runcmd.c | 5 - 3 files changed, 299 insertions(+), 15 deletions(-) (limited to 'plugins') diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index 35b83297..a0213f6b 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c @@ -36,6 +36,7 @@ * *****************************************************************************/ +#include #define NAGIOSPLUG_API_C 1 /** includes **/ @@ -79,7 +80,8 @@ static pid_t *_cmd_pids = NULL; static int _cmd_open(char *const *argv, int *pfd, int *pfderr) __attribute__((__nonnull__(1, 2, 3))); -static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) __attribute__((__nonnull__(2))); +static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) + __attribute__((__nonnull__(2))); static int _cmd_close(int fileDescriptor); @@ -102,14 +104,85 @@ void cmd_init(void) { } } +typedef struct { + int stdout_pipe_fd[2]; + int stderr_pipe_fd[2]; + int file_descriptor; + int error_code; +} int_cmd_open_result; +static int_cmd_open_result _cmd_open2(char *const *argv) { +#ifdef RLIMIT_CORE + struct rlimit limit; +#endif + + if (!_cmd_pids) { + CMD_INIT; + } + + setenv("LC_ALL", "C", 1); + + int_cmd_open_result result = { + .error_code = 0, + .stdout_pipe_fd = {0, 0}, + .stderr_pipe_fd = {0, 0}, + }; + pid_t pid; + if (pipe(result.stdout_pipe_fd) < 0 || pipe(result.stderr_pipe_fd) < 0 || (pid = fork()) < 0) { + result.error_code = -1; + return result; /* errno set by the failing function */ + } + + /* child runs exceve() and _exit. */ + if (pid == 0) { +#ifdef RLIMIT_CORE + /* the program we execve shouldn't leave core files */ + getrlimit(RLIMIT_CORE, &limit); + limit.rlim_cur = 0; + setrlimit(RLIMIT_CORE, &limit); +#endif + close(result.stdout_pipe_fd[0]); + if (result.stdout_pipe_fd[1] != STDOUT_FILENO) { + dup2(result.stdout_pipe_fd[1], STDOUT_FILENO); + close(result.stdout_pipe_fd[1]); + } + close(result.stderr_pipe_fd[0]); + if (result.stderr_pipe_fd[1] != STDERR_FILENO) { + dup2(result.stderr_pipe_fd[1], STDERR_FILENO); + close(result.stderr_pipe_fd[1]); + } + + /* close all descriptors in _cmd_pids[] + * This is executed in a separate address space (pure child), + * so we don't have to worry about async safety */ + long maxfd = mp_open_max(); + for (int i = 0; i < maxfd; i++) { + if (_cmd_pids[i] > 0) { + close(i); + } + } + + execve(argv[0], argv, environ); + _exit(STATE_UNKNOWN); + } + + /* parent picks up execution here */ + /* close children descriptors in our address space */ + close(result.stdout_pipe_fd[1]); + close(result.stderr_pipe_fd[1]); + + /* tag our file's entry in the pid-list and return it */ + _cmd_pids[result.stdout_pipe_fd[0]] = pid; + + result.file_descriptor = result.stdout_pipe_fd[0]; + return result; +} + /* Start running a command, array style */ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { #ifdef RLIMIT_CORE struct rlimit limit; #endif - int i = 0; - if (!_cmd_pids) { CMD_INIT; } @@ -144,7 +217,7 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { * This is executed in a separate address space (pure child), * so we don't have to worry about async safety */ long maxfd = mp_open_max(); - for (i = 0; i < maxfd; i++) { + for (int i = 0; i < maxfd; i++) { if (_cmd_pids[i] > 0) { close(i); } @@ -192,6 +265,87 @@ static int _cmd_close(int fileDescriptor) { return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; } +typedef struct { + int error_code; + output output_container; +} int_cmd_fetch_output2; +static int_cmd_fetch_output2 _cmd_fetch_output2(int fileDescriptor, int flags) { + char tmpbuf[4096]; + + int_cmd_fetch_output2 result = { + .error_code = 0, + .output_container = + { + .buf = NULL, + .buflen = 0, + .line = NULL, + .lines = 0, + }, + }; + ssize_t ret; + while ((ret = read(fileDescriptor, tmpbuf, sizeof(tmpbuf))) > 0) { + size_t len = (size_t)ret; + result.output_container.buf = + realloc(result.output_container.buf, result.output_container.buflen + len + 1); + memcpy(result.output_container.buf + result.output_container.buflen, tmpbuf, len); + result.output_container.buflen += len; + } + + if (ret < 0) { + printf("read() returned %zd: %s\n", ret, strerror(errno)); + result.error_code = -1; + return result; + } + + /* some plugins may want to keep output unbroken, and some commands + * will yield no output, so return here for those */ + if (flags & CMD_NO_ARRAYS || !result.output_container.buf || !result.output_container.buflen) { + return result; + } + + /* and some may want both */ + char *buf = NULL; + if (flags & CMD_NO_ASSOC) { + buf = malloc(result.output_container.buflen); + memcpy(buf, result.output_container.buf, result.output_container.buflen); + } else { + buf = result.output_container.buf; + } + + result.output_container.line = NULL; + size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ + size_t rsf = 6; + size_t lineno = 0; + for (size_t i = 0; i < result.output_container.buflen;) { + /* make sure we have enough memory */ + if (lineno >= ary_size) { + /* ary_size must never be zero */ + do { + ary_size = result.output_container.buflen >> --rsf; + } while (!ary_size); + + result.output_container.line = + realloc(result.output_container.line, ary_size * sizeof(char *)); + } + + /* set the pointer to the string */ + result.output_container.line[lineno] = &buf[i]; + + /* hop to next newline or end of buffer */ + while (buf[i] != '\n' && i < result.output_container.buflen) { + i++; + } + buf[i] = '\0'; + + lineno++; + i++; + } + + result.output_container.lines = lineno; + + return result; +} + static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) { char tmpbuf[4096]; cmd_output->buf = NULL; @@ -225,7 +379,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) } cmd_output->line = NULL; - cmd_output->lens = NULL; size_t i = 0; size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ size_t rsf = 6; @@ -239,7 +392,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) } while (!ary_size); cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *)); - cmd_output->lens = realloc(cmd_output->lens, ary_size * sizeof(size_t)); } /* set the pointer to the string */ @@ -251,9 +403,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) } buf[i] = '\0'; - /* calculate the string length using pointer difference */ - cmd_output->lens[lineno] = (size_t)&buf[i] - (size_t)cmd_output->line[lineno]; - lineno++; i++; } @@ -336,6 +485,138 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { return cmd_run_array(argv, out, err, flags); } +cmd_run_result cmd_run2(const char *cmd_string, int flags) { + cmd_run_result result = { + .cmd_error_code = 0, + .error_code = 0, + .stderr = + { + .buf = NULL, + .buflen = 0, + .line = NULL, + .lines = 0, + }, + .stdout = + { + .buf = NULL, + .buflen = 0, + .line = NULL, + .lines = 0, + }, + }; + + if (cmd_string == NULL) { + result.error_code = -1; + return result; + } + + /* make copy of command string so strtok() doesn't silently modify it */ + /* (the calling program may want to access it later) */ + char *cmd = strdup(cmd_string); + if (cmd == NULL) { + result.error_code = -1; + return result; + } + + /* This is not a shell, so we don't handle "???" */ + if (strstr(cmd, "\"")) { + result.error_code = -1; + return result; + } + + /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ + if (strstr(cmd, " ' ") || strstr(cmd, "'''")) { + result.error_code = -1; + return result; + } + + /* each arg must be whitespace-separated, so args can be a maximum + * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ + size_t cmdlen = strlen(cmd_string); + size_t argc = (cmdlen >> 1) + 2; + char **argv = calloc(argc, sizeof(char *)); + + if (argv == NULL) { + printf("%s\n", _("Could not malloc argv array in popen()")); + result.error_code = -1; + return result; + } + + /* get command arguments (stupidly, but fairly quickly) */ + for (int i = 0; cmd; i++) { + char *str = cmd; + str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ + + if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ + str++; + if (!strstr(str, "'")) { + result.error_code = -1; + return result; /* balanced? */ + } + + cmd = 1 + strstr(str, "'"); + str[strcspn(str, "'")] = 0; + } else { + if (strpbrk(str, " \t\r\n")) { + cmd = 1 + strpbrk(str, " \t\r\n"); + str[strcspn(str, " \t\r\n")] = 0; + } else { + cmd = NULL; + } + } + + if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) { + cmd = NULL; + } + + argv[i++] = str; + } + + result = cmd_run_array2(argv, flags); + + return result; +} + +cmd_run_result cmd_run_array2(char *const *cmd, int flags) { + cmd_run_result result = { + .cmd_error_code = 0, + .error_code = 0, + .stderr = + { + .buf = NULL, + .buflen = 0, + .line = NULL, + .lines = 0, + }, + .stdout = + { + .buf = NULL, + .buflen = 0, + .line = NULL, + .lines = 0, + }, + }; + + int_cmd_open_result cmd_open_result = _cmd_open2(cmd); + if (cmd_open_result.error_code != 0) { + // result.error_code = -1; + // return result; + die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd[0]); + } + + int file_descriptor = cmd_open_result.file_descriptor; + int pfd_out[2] = {cmd_open_result.stdout_pipe_fd[0], cmd_open_result.stdout_pipe_fd[1]}; + int pfd_err[2] = {cmd_open_result.stderr_pipe_fd[0], cmd_open_result.stderr_pipe_fd[1]}; + + int_cmd_fetch_output2 tmp_stdout = _cmd_fetch_output2(pfd_out[0], flags); + result.stdout = tmp_stdout.output_container; + int_cmd_fetch_output2 tmp_stderr = _cmd_fetch_output2(pfd_err[0], flags); + result.stderr = tmp_stderr.output_container; + + result.cmd_error_code = _cmd_close(file_descriptor); + return result; +} + int cmd_run_array(char *const *argv, output *out, output *err, int flags) { /* initialize the structs */ if (out) { diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h index 3672cdc9..d3a8f14f 100644 --- a/lib/utils_cmd.h +++ b/lib/utils_cmd.h @@ -13,7 +13,6 @@ typedef struct { char *buf; /* output buffer */ size_t buflen; /* output buffer content length */ char **line; /* array of lines (points to buf) */ - size_t *lens; /* string lengths */ size_t lines; /* lines of output */ } output; @@ -22,6 +21,15 @@ int cmd_run(const char *, output *, output *, int); int cmd_run_array(char *const *, output *, output *, int); int cmd_file_read(const char *, output *, int); +typedef struct { + int error_code; + int cmd_error_code; + output stdout; + output stderr; +} cmd_run_result; +cmd_run_result cmd_run2(const char *cmd, int flags); +cmd_run_result cmd_run_array2(char * const *cmd, int flags); + /* only multi-threaded plugins need to bother with this */ void cmd_init(void); #define CMD_INIT cmd_init() diff --git a/plugins/runcmd.c b/plugins/runcmd.c index 7c583b85..be6691d2 100644 --- a/plugins/runcmd.c +++ b/plugins/runcmd.c @@ -300,7 +300,6 @@ static int np_fetch_output(int fd, output *op, int flags) { } op->line = NULL; - op->lens = NULL; i = 0; while (i < op->buflen) { /* make sure we have enough memory */ @@ -311,7 +310,6 @@ static int np_fetch_output(int fd, output *op, int flags) { } while (!ary_size); op->line = realloc(op->line, ary_size * sizeof(char *)); - op->lens = realloc(op->lens, ary_size * sizeof(size_t)); } /* set the pointer to the string */ @@ -323,9 +321,6 @@ static int np_fetch_output(int fd, output *op, int flags) { } buf[i] = '\0'; - /* calculate the string length using pointer difference */ - op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno]; - lineno++; i++; } -- cgit v1.2.3-74-g34f1 From e77ce530c44178521b7e0c4012feed1e8006e41e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:32:03 +0100 Subject: check_by_ssh: Implement modern output functionality --- plugins/check_by_ssh.c | 184 +++++++++++++++++++++++++++++----------- plugins/check_by_ssh.d/config.h | 21 ++++- 2 files changed, 152 insertions(+), 53 deletions(-) (limited to 'plugins') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index a43c0d34..ad385fbd 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -26,16 +26,17 @@ * *****************************************************************************/ -const char *progname = "check_by_ssh"; -const char *copyright = "2000-2024"; -const char *email = "devel@monitoring-plugins.org"; - #include "common.h" +#include "output.h" #include "utils.h" #include "utils_cmd.h" #include "check_by_ssh.d/config.h" #include "states.h" +const char *progname = "check_by_ssh"; +const char *copyright = "2000-2024"; +const char *email = "devel@monitoring-plugins.org"; + #ifndef NP_MAXARGS # define NP_MAXARGS 1024 #endif @@ -71,6 +72,10 @@ int main(int argc, char **argv) { const check_by_ssh_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* Set signal handling and alarm timeout */ if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { usage_va(_("Cannot catch SIGALRM")); @@ -85,62 +90,101 @@ int main(int argc, char **argv) { } } - output chld_out; - output chld_err; - mp_state_enum result = cmd_run_array(config.cmd.commargv, &chld_out, &chld_err, 0); + cmd_run_result child_result = cmd_run_array2(config.cmd.commargv, 0); + mp_check overall = mp_check_init(); /* SSH returns 255 if connection attempt fails; include the first line of error output */ - if (result == 255 && config.unknown_timeout) { - printf(_("SSH connection failed: %s\n"), - chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); - return STATE_UNKNOWN; + mp_subcheck sc_ssh_execution = mp_subcheck_init(); + if (child_result.cmd_error_code == 255 && config.unknown_timeout) { + xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", + child_result.stderr.lines > 0 ? child_result.stderr.line[0] + : "(no error output)"); + + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); + mp_add_subcheck_to_check(&overall, sc_ssh_execution); + mp_exit(overall); } + xasprintf(&sc_ssh_execution.output, "SSH connection succeeded"); + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ssh_execution); if (verbose) { - for (size_t i = 0; i < chld_out.lines; i++) { - printf("stdout: %s\n", chld_out.line[i]); + for (size_t i = 0; i < child_result.stdout.lines; i++) { + printf("stdout: %s\n", child_result.stdout.line[i]); } - for (size_t i = 0; i < chld_err.lines; i++) { - printf("stderr: %s\n", chld_err.line[i]); + for (size_t i = 0; i < child_result.stderr.lines; i++) { + printf("stderr: %s\n", child_result.stderr.line[i]); } } size_t skip_stdout = 0; - if (config.skip_stdout == -1) { /* --skip-stdout specified without argument */ - skip_stdout = chld_out.lines; + if (config.skip_stdout) { /* --skip-stdout specified without argument */ + skip_stdout = child_result.stdout.lines; } else { - skip_stdout = config.skip_stdout; + skip_stdout = config.stdout_lines_to_ignore; } size_t skip_stderr = 0; - if (config.skip_stderr == -1) { /* --skip-stderr specified without argument */ - skip_stderr = chld_err.lines; + if (config.skip_stderr) { /* --skip-stderr specified without argument */ + skip_stderr = child_result.stderr.lines; } else { - skip_stderr = config.skip_stderr; + skip_stderr = config.sterr_lines_to_ignore; } /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ - if (chld_err.lines > (size_t)skip_stderr && (config.unknown_on_stderr || config.warn_on_stderr)) { - printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); + if (child_result.stderr.lines > skip_stderr && + (config.unknown_on_stderr || config.warn_on_stderr)) { + mp_subcheck sc_stderr = mp_subcheck_init(); + xasprintf(&sc_stderr.output, "remote command execution failed: %s", + child_result.stderr.line[skip_stderr]); + if (config.unknown_on_stderr) { - return max_state_alt(result, STATE_UNKNOWN); - } else if (config.warn_on_stderr) { - return max_state_alt(result, STATE_WARNING); + sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN); + } + + if (config.warn_on_stderr) { + sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_WARNING); } + + mp_add_subcheck_to_check(&overall, sc_stderr); + // TODO still exit here? } /* this is simple if we're not supposed to be passive. * Wrap up quickly and keep the tricks below */ if (!config.passive) { - if (chld_out.lines > (size_t)skip_stdout) { - for (size_t i = skip_stdout; i < chld_out.lines; i++) { - puts(chld_out.line[i]); + mp_subcheck sc_active_check = mp_subcheck_init(); + xasprintf(&sc_active_check.output, "command stdout:"); + + if (child_result.stdout.lines > skip_stdout) { + for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { + xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output, + child_result.stdout.line[i]); } } else { - printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), - state_text(result), config.remotecmd, result); + xasprintf(&sc_active_check.output, "remote command '%s' returned status %d", + config.remotecmd, child_result.cmd_error_code); + } + + /* return error status from remote command */ + + switch (child_result.cmd_error_code) { + case 0: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_OK); + break; + case 1: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_WARNING); + break; + case 2: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_CRITICAL); + break; + default: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_UNKNOWN); + break; } - return result; /* return error status from remote command */ + + mp_add_subcheck_to_check(&overall, sc_active_check); + mp_exit(overall); } /* @@ -148,36 +192,57 @@ int main(int argc, char **argv) { */ /* process output */ - FILE *file_pointer = NULL; - if (!(file_pointer = fopen(config.outputfile, "a"))) { - printf(_("SSH WARNING: could not open %s\n"), config.outputfile); - exit(STATE_UNKNOWN); + mp_subcheck sc_passive_file = mp_subcheck_init(); + FILE *output_file = NULL; + if (!(output_file = fopen(config.outputfile, "a"))) { + xasprintf(&sc_passive_file.output, "could not open %s", config.outputfile); + sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_UNKNOWN); + + mp_add_subcheck_to_check(&overall, sc_passive_file); + mp_exit(overall); } + xasprintf(&sc_passive_file.output, "opened output file %s", config.outputfile); + sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_passive_file); + time_t local_time = time(NULL); unsigned int commands = 0; char *status_text; int cresult; - for (size_t i = skip_stdout; i < chld_out.lines; i++) { - status_text = chld_out.line[i++]; - if (i == chld_out.lines || strstr(chld_out.line[i], "STATUS CODE: ") == NULL) { - die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); + mp_subcheck sc_parse_passive = mp_subcheck_init(); + for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { + status_text = child_result.stdout.line[i++]; + if (i == child_result.stdout.lines || + strstr(child_result.stdout.line[i], "STATUS CODE: ") == NULL) { + + sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN); + xasprintf(&sc_parse_passive.output, "failed to parse output"); + mp_add_subcheck_to_check(&overall, sc_parse_passive); + mp_exit(overall); } if (config.service[commands] && status_text && - sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { - fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", - (int)local_time, config.host_shortname, config.service[commands++], cresult, - status_text); + sscanf(child_result.stdout.line[i], "STATUS CODE: %d", &cresult) == 1) { + fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, + config.host_shortname, config.service[commands++], cresult, status_text); } } + sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_OK); + xasprintf(&sc_parse_passive.output, "parsed and wrote output"); + mp_add_subcheck_to_check(&overall, sc_parse_passive); + /* Multiple commands and passive checking should always return OK */ - exit(result); + mp_exit(overall); } /* process command-line arguments */ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { + enum { + output_format_index = CHAR_MAX + 1, + }; + static struct option longopts[] = { {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, @@ -207,6 +272,7 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { {"ssh-option", required_argument, 0, 'o'}, {"quiet", no_argument, 0, 'q'}, {"configfile", optional_argument, 0, 'F'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_by_ssh_config_wrapper result = { @@ -327,20 +393,27 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { break; case 'S': /* skip n (or all) lines on stdout */ if (optarg == NULL) { - result.config.skip_stdout = -1; /* skip all output on stdout */ + result.config.skip_stdout = true; /* skip all output on stdout */ + + if (verbose) { + printf("Setting the skip_stdout flag\n"); + } } else if (!is_integer(optarg)) { usage_va(_("skip-stdout argument must be an integer")); } else { - result.config.skip_stdout = atoi(optarg); + result.config.stdout_lines_to_ignore = atoi(optarg); } break; case 'E': /* skip n (or all) lines on stderr */ if (optarg == NULL) { - result.config.skip_stderr = -1; /* skip all output on stderr */ + result.config.skip_stderr = true; /* skip all output on stderr */ + if (verbose) { + printf("Setting the skip_stderr flag\n"); + } } else if (!is_integer(optarg)) { usage_va(_("skip-stderr argument must be an integer")); } else { - result.config.skip_stderr = atoi(optarg); + result.config.sterr_lines_to_ignore = atoi(optarg); } break; case 'e': /* exit with unknown if there is an output on stderr */ @@ -360,6 +433,18 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { result.config.cmd = comm_append(result.config.cmd, "-F"); result.config.cmd = comm_append(result.config.cmd, optarg); break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } default: /* help */ usage5(); } @@ -502,6 +587,7 @@ void print_help(void) { printf(" %s\n", "-U, --unknown-timeout"); printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf(" %s\n", _("The most common mode of use is to refer to a local identity file with")); printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h index 0e4b56d4..b6a57964 100644 --- a/plugins/check_by_ssh.d/config.h +++ b/plugins/check_by_ssh.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include typedef struct { @@ -23,10 +24,16 @@ typedef struct { bool unknown_timeout; bool unknown_on_stderr; bool warn_on_stderr; - int skip_stdout; - int skip_stderr; + bool skip_stdout; + size_t stdout_lines_to_ignore; + bool skip_stderr; + size_t sterr_lines_to_ignore; + bool passive; char *outputfile; + + bool output_format_is_set; + mp_output_format output_format; } check_by_ssh_config; check_by_ssh_config check_by_ssh_config_init() { @@ -49,10 +56,16 @@ check_by_ssh_config check_by_ssh_config_init() { .unknown_timeout = false, .unknown_on_stderr = false, .warn_on_stderr = false, - .skip_stderr = 0, - .skip_stdout = 0, + + .skip_stderr = false, + .stdout_lines_to_ignore = 0, + .skip_stdout = false, + .sterr_lines_to_ignore = 0, + .passive = false, .outputfile = NULL, + + .output_format_is_set = false, }; return tmp; } -- cgit v1.2.3-74-g34f1 From 463223790cb67421d420ba982c1f5ee6cc6f2b4a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:52:07 +0100 Subject: check_by_ssh: handle errrors of ssh (1) directly --- plugins/check_by_ssh.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'plugins') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index ad385fbd..8036ffa4 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -100,10 +100,27 @@ int main(int argc, char **argv) { child_result.stderr.lines > 0 ? child_result.stderr.line[0] : "(no error output)"); + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); + mp_add_subcheck_to_check(&overall, sc_ssh_execution); + mp_exit(overall); + } else if (child_result.cmd_error_code != 0) { + xasprintf(&sc_ssh_execution.output, "SSH connection failed: "); + + if (child_result.stderr.lines > 0) { + for (size_t i = 0; i < child_result.stderr.lines; i++) { + xasprintf(&sc_ssh_execution.output, "%s\n%s", sc_ssh_execution.output, + child_result.stderr.line[i]); + } + } else { + xasprintf(&sc_ssh_execution.output, "%s %s", sc_ssh_execution.output, + "no output on stderr"); + } + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); mp_add_subcheck_to_check(&overall, sc_ssh_execution); mp_exit(overall); } + xasprintf(&sc_ssh_execution.output, "SSH connection succeeded"); sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_OK); mp_add_subcheck_to_check(&overall, sc_ssh_execution); -- cgit v1.2.3-74-g34f1 From 62242ddcf5caa09eee79c15a94a5d861b9be95c8 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:27:19 +0100 Subject: check_by_ssh: do not incorrectly assume that ssh (1) succeeded --- plugins/check_by_ssh.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) (limited to 'plugins') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index 8036ffa4..ebe54b80 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -94,37 +94,18 @@ int main(int argc, char **argv) { mp_check overall = mp_check_init(); /* SSH returns 255 if connection attempt fails; include the first line of error output */ - mp_subcheck sc_ssh_execution = mp_subcheck_init(); + // we can sadly not detect other SSH errors if (child_result.cmd_error_code == 255 && config.unknown_timeout) { + mp_subcheck sc_ssh_execution = mp_subcheck_init(); xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", child_result.stderr.lines > 0 ? child_result.stderr.line[0] : "(no error output)"); - sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); - mp_add_subcheck_to_check(&overall, sc_ssh_execution); - mp_exit(overall); - } else if (child_result.cmd_error_code != 0) { - xasprintf(&sc_ssh_execution.output, "SSH connection failed: "); - - if (child_result.stderr.lines > 0) { - for (size_t i = 0; i < child_result.stderr.lines; i++) { - xasprintf(&sc_ssh_execution.output, "%s\n%s", sc_ssh_execution.output, - child_result.stderr.line[i]); - } - } else { - xasprintf(&sc_ssh_execution.output, "%s %s", sc_ssh_execution.output, - "no output on stderr"); - } - sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); mp_add_subcheck_to_check(&overall, sc_ssh_execution); mp_exit(overall); } - xasprintf(&sc_ssh_execution.output, "SSH connection succeeded"); - sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_OK); - mp_add_subcheck_to_check(&overall, sc_ssh_execution); - if (verbose) { for (size_t i = 0; i < child_result.stdout.lines; i++) { printf("stdout: %s\n", child_result.stdout.line[i]); -- cgit v1.2.3-74-g34f1 From c3d931fa1acc9ad61328f873cf0206c765d93ac6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:27:58 +0100 Subject: check_by_ssh: some formatting --- plugins/check_by_ssh.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'plugins') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index ebe54b80..df8907d9 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -599,9 +599,8 @@ void print_help(void) { printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); printf("\n"); printf("%s\n", _("Examples:")); - printf( - " %s\n", - "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); + printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C " + "uptime -O /tmp/foo"); printf(" %s\n", "$ cat /tmp/foo"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); -- cgit v1.2.3-74-g34f1 From 584272e97d5c72ad6a7fb9b91844592252040ed9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:28:19 +0100 Subject: check_by_ssh: fix some tests --- plugins/t/check_by_ssh.t | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'plugins') diff --git a/plugins/t/check_by_ssh.t b/plugins/t/check_by_ssh.t index b6479f1f..0ee310cd 100644 --- a/plugins/t/check_by_ssh.t +++ b/plugins/t/check_by_ssh.t @@ -16,7 +16,7 @@ my $ssh_conf = getTestParameter( "NP_SSH_CONFIGFILE", "A config file with ssh plan skip_all => "SSH_HOST and SSH_IDENTITY must be defined" unless ($ssh_service && $ssh_key); -plan tests => 42; +plan tests => 33; # Some random check strings/response my @response = ('OK: Everything is fine', @@ -47,70 +47,70 @@ for (my $i=0; $i<4; $i++) { "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[$i]; exit $i'" ); cmp_ok($result->return_code, '==', $i, "Exit with return code $i"); - is($result->output, $response[$i], "Status text is correct for check $i"); + like($result->output, "/$response[$i]/", "Status text is correct for check $i"); } $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 0'" ); cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); -is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); +like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)"); $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 1'" ); cmp_ok($result->return_code, '==', 1, "Exit with return code 1 (WARNING)"); -is($result->output, 'WARNING - check_by_ssh: Remote command \'exit 1\' returned status 1', "Status text if command returned none (WARNING)"); +like($result->output, '/command \'exit 1\' returned status 1/', "Status text if command returned none (WARNING)"); $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 2'" ); cmp_ok($result->return_code, '==', 2, "Exit with return code 2 (CRITICAL)"); -is($result->output, 'CRITICAL - check_by_ssh: Remote command \'exit 2\' returned status 2', "Status text if command returned none (CRITICAL)"); +like($result->output, '/command \'exit 2\' returned status 2/', "Status text if command returned none (CRITICAL)"); $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 3'" ); cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)"); -is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 3\' returned status 3', "Status text if command returned none (UNKNOWN)"); +like($result->output, '/command \'exit 3\' returned status 3/', "Status text if command returned none (UNKNOWN)"); $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 7'" ); -cmp_ok($result->return_code, '==', 7, "Exit with return code 7 (out of bounds)"); -is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 7\' returned status 7', "Status text if command returned none (out of bounds)"); +cmp_ok($result->return_code, '==', 3, "Exit with return code 3"); +like($result->output, '/command \'exit 7\' returned status 7/', "Status text if command returned none (out of bounds)"); $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[4]; exit 8'" ); -cmp_ok($result->return_code, '==', 8, "Exit with return code 8 (out of bounds)"); -is($result->output, $response[4], "Return proper status text even with unknown status codes"); +cmp_ok($result->return_code, '==', 3, "Exit with return code 3"); +like($result->output, "/$response[4]/", "Return proper status text even with unknown status codes"); $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -F $ssh_conf -C 'exit 0'" ); cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); -is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); +like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)"); # Multiple active checks $result = NPTest->testCmd( "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[1]; sh -c exit\\ 1' -C '$check[0]; sh -c exit\\ 0' -C '$check[3]; sh -c exit\\ 3' -C '$check[2]; sh -c exit\\ 2'" ); cmp_ok($result->return_code, '==', 0, "Multiple checks always return OK"); -my @lines = split(/\n/, $result->output); -cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks"); -my %linemap = ( - '0' => '1', - '2' => '0', - '4' => '3', - '6' => '2', -); -foreach my $line (0, 2, 4, 6) { - my $code = $linemap{$line}; - my $statline = $line+1; - is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line"); - is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line"); -} +# my @lines = split(/\n/, $result->output); +# cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks"); +# my %linemap = ( +# '0' => '1', +# '2' => '0', +# '4' => '3', +# '6' => '2', +# ); +# foreach my $line (0, 2, 4, 6) { + # my $code = $linemap{$line}; + # my $statline = $line+1; + # is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line"); + # is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line"); +# } # Passive checks unlink("/tmp/check_by_ssh.$$"); -- cgit v1.2.3-74-g34f1 From 07d3eb9e2c729b6ab5effc0f664b6d5d9958fa72 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:31:00 +0100 Subject: check_ldap: modern output implementation --- plugins/check_ldap.c | 310 ++++++++++++++++++++++++++---------------- plugins/check_ldap.d/config.h | 18 +-- plugins/t/check_ldap.t | 14 +- 3 files changed, 207 insertions(+), 135 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c index 77a33304..f1380be4 100644 --- a/plugins/check_ldap.c +++ b/plugins/check_ldap.c @@ -27,12 +27,11 @@ *****************************************************************************/ /* progname may be check_ldaps */ -char *progname = "check_ldap"; -const char *copyright = "2000-2024"; -const char *email = "devel@monitoring-plugins.org"; - +#include "output.h" #include "common.h" #include "netutils.h" +#include "perfdata.h" +#include "thresholds.h" #include "utils.h" #include "check_ldap.d/config.h" @@ -41,6 +40,10 @@ const char *email = "devel@monitoring-plugins.org"; #define LDAP_DEPRECATED 1 #include +char *progname = "check_ldap"; +const char *copyright = "2000-2024"; +const char *email = "devel@monitoring-plugins.org"; + enum { DEFAULT_PORT = 389 }; @@ -89,101 +92,172 @@ int main(int argc, char *argv[]) { struct timeval start_time; gettimeofday(&start_time, NULL); + mp_check overall = mp_check_init(); + LDAP *ldap_connection; /* initialize ldap */ + { #ifdef HAVE_LDAP_INIT - if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) { - printf("Could not connect to the server at port %i\n", config.ld_port); - return STATE_CRITICAL; - } + mp_subcheck sc_ldap_init = mp_subcheck_init(); + if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) { + xasprintf(&sc_ldap_init.output, "could not connect to the server at port %i", + config.ld_port); + sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_ldap_init); + mp_exit(overall); + } else { + xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port); + sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ldap_init); + } #else - if (!(ld = ldap_open(config.ld_host, config.ld_port))) { - if (verbose) { - ldap_perror(ldap_connection, "ldap_open"); + mp_subcheck sc_ldap_init = mp_subcheck_init(); + if (!(ld = ldap_open(config.ld_host, config.ld_port))) { + if (verbose) { + ldap_perror(ldap_connection, "ldap_open"); + } + xasprintf(&sc_ldap_init.output, "Could not connect to the server at port %i"), config.ld_port); + sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_ldap_init); + mp_exit(overall); + } else { + xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port); + sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ldap_init); } - printf(_("Could not connect to the server at port %i\n"), config.ld_port); - return STATE_CRITICAL; - } #endif /* HAVE_LDAP_INIT */ + } #ifdef HAVE_LDAP_SET_OPTION /* set ldap options */ + mp_subcheck sc_ldap_set_opts = mp_subcheck_init(); if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) != LDAP_OPT_SUCCESS) { - printf(_("Could not set protocol version %d\n"), config.ld_protocol); - return STATE_CRITICAL; + xasprintf(&sc_ldap_set_opts.output, "Could not set protocol version %d", + config.ld_protocol); + sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_ldap_set_opts); + mp_exit(overall); + } else { + xasprintf(&sc_ldap_set_opts.output, "set protocol version %d", config.ld_protocol); + sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ldap_set_opts); } #endif int version = 3; int tls; - if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) { + { + if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) { #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) - /* ldaps: set option tls */ - tls = LDAP_OPT_X_TLS_HARD; + /* ldaps: set option tls */ + tls = LDAP_OPT_X_TLS_HARD; - if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) { - if (verbose) { - ldap_perror(ldap_connection, "ldaps_option"); + mp_subcheck sc_ldap_tls_init = mp_subcheck_init(); + if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) { + if (verbose) { + ldap_perror(ldap_connection, "ldaps_option"); + } + xasprintf(&sc_ldap_tls_init.output, "could not init TLS at port %i!", + config.ld_port); + sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_ldap_tls_init); + mp_exit(overall); + } else { + xasprintf(&sc_ldap_tls_init.output, "initiated TLS at port %i!", config.ld_port); + sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ldap_tls_init); } - printf(_("Could not init TLS at port %i!\n"), config.ld_port); - return STATE_CRITICAL; - } #else - printf(_("TLS not supported by the libraries!\n")); - return STATE_CRITICAL; + printf(_("TLS not supported by the libraries!\n")); + exit(STATE_CRITICAL); #endif /* LDAP_OPT_X_TLS */ - } else if (config.starttls) { + } else if (config.starttls) { #if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) - /* ldap with startTLS: set option version */ - if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) == - LDAP_OPT_SUCCESS) { - if (version < LDAP_VERSION3) { - version = LDAP_VERSION3; - ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version); + /* ldap with startTLS: set option version */ + if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) == + LDAP_OPT_SUCCESS) { + if (version < LDAP_VERSION3) { + version = LDAP_VERSION3; + ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version); + } } - } - /* call start_tls */ - if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) { - if (verbose) { - ldap_perror(ldap_connection, "ldap_start_tls"); + /* call start_tls */ + mp_subcheck sc_ldap_starttls = mp_subcheck_init(); + if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) { + if (verbose) { + ldap_perror(ldap_connection, "ldap_start_tls"); + } + xasprintf(&sc_ldap_starttls.output, "could not init STARTTLS at port %i!", + config.ld_port); + sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_ldap_starttls); + mp_exit(overall); + } else { + xasprintf(&sc_ldap_starttls.output, "initiated STARTTLS at port %i!", + config.ld_port); + sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ldap_starttls); } - printf(_("Could not init startTLS at port %i!\n"), config.ld_port); - return STATE_CRITICAL; - } #else - printf(_("startTLS not supported by the library, needs LDAPv3!\n")); - return STATE_CRITICAL; + printf(_("startTLS not supported by the library, needs LDAPv3!\n")); + exit(STATE_CRITICAL); #endif /* HAVE_LDAP_START_TLS_S */ + } } /* bind to the ldap server */ - if (ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE) != - LDAP_SUCCESS) { - if (verbose) { - ldap_perror(ldap_connection, "ldap_bind"); + { + mp_subcheck sc_ldap_bind = mp_subcheck_init(); + int ldap_error = + ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE); + if (ldap_error != LDAP_SUCCESS) { + if (verbose) { + ldap_perror(ldap_connection, "ldap_bind"); + } + + xasprintf(&sc_ldap_bind.output, "could not bind to the LDAP server: %s", + ldap_err2string(ldap_error)); + sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_ldap_bind); + mp_exit(overall); + } else { + xasprintf(&sc_ldap_bind.output, "execute bind to the LDAP server"); + sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ldap_bind); } - printf(_("Could not bind to the LDAP server\n")); - return STATE_CRITICAL; } LDAPMessage *result; - int num_entries = 0; /* do a search of all objectclasses in the base dn */ - if (ldap_search_s(ldap_connection, config.ld_base, - (config.crit_entries != NULL || config.warn_entries != NULL) - ? LDAP_SCOPE_SUBTREE - : LDAP_SCOPE_BASE, - config.ld_attr, NULL, 0, &result) != LDAP_SUCCESS) { - if (verbose) { - ldap_perror(ldap_connection, "ldap_search"); + { + mp_subcheck sc_ldap_search = mp_subcheck_init(); + int ldap_error = ldap_search_s( + ldap_connection, config.ld_base, + (config.entries_thresholds.warning_is_set || config.entries_thresholds.critical_is_set) + ? LDAP_SCOPE_SUBTREE + : LDAP_SCOPE_BASE, + config.ld_attr, NULL, 0, &result); + + if (ldap_error != LDAP_SUCCESS) { + if (verbose) { + ldap_perror(ldap_connection, "ldap_search"); + } + xasprintf(&sc_ldap_search.output, "could not search/find objectclasses in %s: %s", + config.ld_base, ldap_err2string(ldap_error)); + sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_CRITICAL); + mp_add_subcheck_to_check(&overall, sc_ldap_search); + mp_exit(overall); + } else { + xasprintf(&sc_ldap_search.output, "search/find objectclasses in %s", config.ld_base); + sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ldap_search); } - printf(_("Could not search/find objectclasses in %s\n"), config.ld_base); - return STATE_CRITICAL; } - if (config.crit_entries != NULL || config.warn_entries != NULL) { - num_entries = ldap_count_entries(ldap_connection, result); + int num_entries = ldap_count_entries(ldap_connection, result); + if (verbose) { + printf("entries found: %d\n", num_entries); } /* unbind from the ldap server */ @@ -193,46 +267,41 @@ int main(int argc, char *argv[]) { alarm(0); /* calculate the elapsed time and compare to thresholds */ - long microsec = deltime(start_time); double elapsed_time = (double)microsec / 1.0e6; - mp_state_enum status = STATE_UNKNOWN; - if (config.crit_time_set && elapsed_time > config.crit_time) { - status = STATE_CRITICAL; - } else if (config.warn_time_set && elapsed_time > config.warn_time) { - status = STATE_WARNING; - } else { - status = STATE_OK; - } + mp_perfdata pd_connection_time = perfdata_init(); + pd_connection_time.label = "time"; + pd_connection_time.value = mp_create_pd_value(elapsed_time); + pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.connection_time_threshold); - if (config.entries_thresholds != NULL) { - if (verbose) { - printf("entries found: %d\n", num_entries); - print_thresholds("entry thresholds", config.entries_thresholds); - } - mp_state_enum status_entries = get_status(num_entries, config.entries_thresholds); - if (status_entries == STATE_CRITICAL) { - status = STATE_CRITICAL; - } else if (status != STATE_CRITICAL) { - status = status_entries; - } - } + mp_subcheck sc_connection_time = mp_subcheck_init(); + mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time); + + mp_state_enum connection_time_state = mp_get_pd_status(pd_connection_time); + sc_connection_time = mp_set_subcheck_state(sc_connection_time, connection_time_state); - /* print out the result */ - if (config.crit_entries != NULL || config.warn_entries != NULL) { - printf(_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), state_text(status), - num_entries, elapsed_time, - fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time, - config.crit_time_set, config.crit_time, true, 0, false, 0), - sperfdata("entries", (double)num_entries, "", config.warn_entries, - config.crit_entries, true, 0.0, false, 0.0)); + if (connection_time_state == STATE_OK) { + xasprintf(&sc_connection_time.output, "connection time %.3fs is withing thresholds", + elapsed_time); } else { - printf(_("LDAP %s - %.3f seconds response time|%s\n"), state_text(status), elapsed_time, - fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time, - config.crit_time_set, config.crit_time, true, 0, false, 0)); + xasprintf(&sc_connection_time.output, "connection time %.3fs is violating thresholds", + elapsed_time); } - exit(status); + mp_add_subcheck_to_check(&overall, sc_connection_time); + + mp_perfdata pd_num_entries = perfdata_init(); + pd_num_entries.label = "entries"; + pd_num_entries.value = mp_create_pd_value(num_entries); + pd_num_entries = mp_pd_set_thresholds(pd_num_entries, config.entries_thresholds); + + mp_subcheck sc_num_entries = mp_subcheck_init(); + xasprintf(&sc_num_entries.output, "found %d entries", num_entries); + sc_num_entries = mp_set_subcheck_state(sc_num_entries, mp_get_pd_status(pd_num_entries)); + + mp_add_subcheck_to_check(&overall, sc_num_entries); + + mp_exit(overall); } /* process command-line arguments */ @@ -319,20 +388,38 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) { case 'P': result.config.ld_passwd = optarg; break; - case 'w': - result.config.warn_time_set = true; - result.config.warn_time = strtod(optarg, NULL); - break; - case 'c': - result.config.crit_time_set = true; - result.config.crit_time = strtod(optarg, NULL); - break; - case 'W': - result.config.warn_entries = optarg; - break; - case 'C': - result.config.crit_entries = optarg; - break; + case 'w': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning connection time threshold"); + } + result.config.connection_time_threshold = + mp_thresholds_set_warn(result.config.connection_time_threshold, tmp.range); + } break; + case 'c': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical connection time threshold"); + } + result.config.connection_time_threshold = + mp_thresholds_set_crit(result.config.connection_time_threshold, tmp.range); + } break; + case 'W': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse number of entries warning threshold"); + } + result.config.connection_time_threshold = + mp_thresholds_set_warn(result.config.entries_thresholds, tmp.range); + } break; + case 'C': { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse number of entries critical threshold"); + } + result.config.connection_time_threshold = + mp_thresholds_set_crit(result.config.entries_thresholds, tmp.range); + } break; #ifdef HAVE_LDAP_SET_OPTION case '2': result.config.ld_protocol = 2; @@ -406,11 +493,6 @@ check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper config_wr usage4(_("Please specify the LDAP base\n")); } - if (config_wrapper.config.crit_entries != NULL || config_wrapper.config.warn_entries != NULL) { - set_thresholds(&config_wrapper.config.entries_thresholds, - config_wrapper.config.warn_entries, config_wrapper.config.crit_entries); - } - if (config_wrapper.config.ld_passwd == NULL) { config_wrapper.config.ld_passwd = getenv("LDAP_PASSWORD"); } diff --git a/plugins/check_ldap.d/config.h b/plugins/check_ldap.d/config.h index c8a40610..9e6bb845 100644 --- a/plugins/check_ldap.d/config.h +++ b/plugins/check_ldap.d/config.h @@ -25,13 +25,8 @@ typedef struct { int ld_protocol; #endif - char *warn_entries; - char *crit_entries; - thresholds *entries_thresholds; - bool warn_time_set; - double warn_time; - bool crit_time_set; - double crit_time; + mp_thresholds entries_thresholds; + mp_thresholds connection_time_threshold; } check_ldap_config; check_ldap_config check_ldap_config_init() { @@ -48,13 +43,8 @@ check_ldap_config check_ldap_config_init() { .ld_protocol = DEFAULT_PROTOCOL, #endif - .warn_entries = NULL, - .crit_entries = NULL, - .entries_thresholds = NULL, - .warn_time_set = false, - .warn_time = 0, - .crit_time_set = false, - .crit_time = 0, + .entries_thresholds = mp_thresholds_init(), + .connection_time_threshold = mp_thresholds_init(), }; return tmp; } diff --git a/plugins/t/check_ldap.t b/plugins/t/check_ldap.t index fcba0393..f3162ebb 100644 --- a/plugins/t/check_ldap.t +++ b/plugins/t/check_ldap.t @@ -32,7 +32,7 @@ SKIP: { $result = NPTest->testCmd("$command -H $hostname_invalid -b ou=blah -t 5"); is( $result->return_code, 2, "$command -H $hostname_invalid -b ou=blah -t 5" ); - is( $result->output, 'Could not bind to the LDAP server', "output ok" ); + like( $result->output, '/could not bind to the LDAP server/', "output ok" ); }; SKIP: { @@ -42,30 +42,30 @@ SKIP: { $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3"; $result = NPTest->testCmd($cmd); is( $result->return_code, 0, $cmd ); - like( $result->output, '/^LDAP OK - \d+.\d+ seconds response time\|time=\d+\.\d+s;2\.0+;3\.0+;0\.0+$/', "output ok" ); + like( $result->output, '/connection time \d+.\d+s/', "output ok" ); $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000 -C 10000001"; $result = NPTest->testCmd($cmd); is( $result->return_code, 0, $cmd ); - like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000;10000001;0\.0+$/', "output ok" ); + like( $result->output, '/found \d+ entries/', "output ok" ); $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001:"; $result = NPTest->testCmd($cmd); is( $result->return_code, 2, $cmd ); - like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001:;0\.0+$/', "output ok" ); + like( $result->output, '/found \d+ entries/', "output ok" ); $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 0 -C 0"; $result = NPTest->testCmd($cmd); is( $result->return_code, 2, $cmd ); - like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;0;0;0\.0+$/', "output ok" ); + like( $result->output, '/found \d+ entries/', "output ok" ); $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001"; $result = NPTest->testCmd($cmd); is( $result->return_code, 1, $cmd ); - like( $result->output, '/^LDAP WARNING - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001;0\.0+$/', "output ok" ); + like( $result->output, '/found \d+ entries/', "output ok" ); $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -C 10000001"; $result = NPTest->testCmd($cmd); is( $result->return_code, 0, $cmd ); - like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;;10000001;0\.0+$/', "output ok" ); + like( $result->output, '/found \d+ entries/', "output ok" ); }; -- cgit v1.2.3-74-g34f1 From 2e3dff775dd92ea9880221c800f6dc6e53162fb4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:37:42 +0100 Subject: check_ldap: fix typo --- plugins/check_ldap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c index f1380be4..c3f83901 100644 --- a/plugins/check_ldap.c +++ b/plugins/check_ldap.c @@ -281,7 +281,7 @@ int main(int argc, char *argv[]) { sc_connection_time = mp_set_subcheck_state(sc_connection_time, connection_time_state); if (connection_time_state == STATE_OK) { - xasprintf(&sc_connection_time.output, "connection time %.3fs is withing thresholds", + xasprintf(&sc_connection_time.output, "connection time %.3fs is within thresholds", elapsed_time); } else { xasprintf(&sc_connection_time.output, "connection time %.3fs is violating thresholds", -- cgit v1.2.3-74-g34f1 From d6c4b799e3416d70448dea9ea886d29dbf69c820 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:41:07 +0100 Subject: check_ldap: implement output format selection --- plugins/check_ldap.c | 22 ++++++++++++++++++++++ plugins/check_ldap.d/config.h | 6 ++++++ 2 files changed, 28 insertions(+) (limited to 'plugins') diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c index c3f83901..8ee0e617 100644 --- a/plugins/check_ldap.c +++ b/plugins/check_ldap.c @@ -82,6 +82,10 @@ int main(int argc, char *argv[]) { const check_ldap_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* initialize alarm signal handling */ signal(SIGALRM, socket_timeout_alarm_handler); @@ -306,6 +310,10 @@ int main(int argc, char *argv[]) { /* process command-line arguments */ check_ldap_config_wrapper process_arguments(int argc, char **argv) { + enum { + output_format_index = CHAR_MAX + 1, + }; + /* initialize the long option struct */ static struct option longopts[] = {{"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, @@ -329,6 +337,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) { {"warn-entries", required_argument, 0, 'W'}, {"crit-entries", required_argument, 0, 'C'}, {"verbose", no_argument, 0, 'v'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_ldap_config_wrapper result = { @@ -458,6 +467,18 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) { usage(_("IPv6 support not available\n")); #endif break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } default: usage5(); } @@ -553,6 +574,7 @@ void print_help(void) { printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf("%s\n", _("Notes:")); diff --git a/plugins/check_ldap.d/config.h b/plugins/check_ldap.d/config.h index 9e6bb845..50191725 100644 --- a/plugins/check_ldap.d/config.h +++ b/plugins/check_ldap.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include "thresholds.h" #include @@ -27,6 +28,9 @@ typedef struct { mp_thresholds entries_thresholds; mp_thresholds connection_time_threshold; + + bool output_format_is_set; + mp_output_format output_format; } check_ldap_config; check_ldap_config check_ldap_config_init() { @@ -45,6 +49,8 @@ check_ldap_config check_ldap_config_init() { .entries_thresholds = mp_thresholds_init(), .connection_time_threshold = mp_thresholds_init(), + + .output_format_is_set = false, }; return tmp; } -- cgit v1.2.3-74-g34f1 From 94642b3171d3dd0045004fdce834d54989e67523 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:47:47 +0100 Subject: check_ldap: add number of entries perfdata --- plugins/check_ldap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'plugins') diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c index 8ee0e617..1070650e 100644 --- a/plugins/check_ldap.c +++ b/plugins/check_ldap.c @@ -300,6 +300,7 @@ int main(int argc, char *argv[]) { pd_num_entries = mp_pd_set_thresholds(pd_num_entries, config.entries_thresholds); mp_subcheck sc_num_entries = mp_subcheck_init(); + mp_add_perfdata_to_subcheck(&sc_num_entries, pd_num_entries); xasprintf(&sc_num_entries.output, "found %d entries", num_entries); sc_num_entries = mp_set_subcheck_state(sc_num_entries, mp_get_pd_status(pd_num_entries)); -- cgit v1.2.3-74-g34f1 From dccc974e45b2ee533f83b3485caf5130f5736196 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:48:01 +0100 Subject: check_ldap: fix thresholds for number of entries --- plugins/check_ldap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'plugins') diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c index 1070650e..1b2e2826 100644 --- a/plugins/check_ldap.c +++ b/plugins/check_ldap.c @@ -419,7 +419,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) { if (tmp.error != MP_PARSING_SUCCES) { die(STATE_UNKNOWN, "failed to parse number of entries warning threshold"); } - result.config.connection_time_threshold = + result.config.entries_thresholds = mp_thresholds_set_warn(result.config.entries_thresholds, tmp.range); } break; case 'C': { @@ -427,8 +427,8 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) { if (tmp.error != MP_PARSING_SUCCES) { die(STATE_UNKNOWN, "failed to parse number of entries critical threshold"); } - result.config.connection_time_threshold = - mp_thresholds_set_crit(result.config.entries_thresholds, tmp.range); + result.config.entries_thresholds = + mp_thresholds_set_crit(result.config.entries_thresholds, tmp.range); } break; #ifdef HAVE_LDAP_SET_OPTION case '2': -- cgit v1.2.3-74-g34f1