From 802e46f8ea36c344f112d7e1dd8d64d17a4cc939 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:59:37 +0200 Subject: Run clang-format again --- plugins/check_mysql.c | 88 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 31 deletions(-) (limited to 'plugins/check_mysql.c') diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index ca3422b5..3d7ec4cd 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -50,15 +50,23 @@ static int verbose = 0; #define LENGTH_METRIC_UNIT 6 static const char *metric_unit[LENGTH_METRIC_UNIT] = { - "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", "Threads_connected", "Threads_running"}; + "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", + "Threads_connected", "Threads_running"}; #define LENGTH_METRIC_COUNTER 9 -static const char *metric_counter[LENGTH_METRIC_COUNTER] = { - "Connections", "Qcache_hits", "Qcache_inserts", "Qcache_lowmem_prunes", "Qcache_not_cached", "Queries", - "Questions", "Table_locks_waited", "Uptime"}; - -#define MYSQLDUMP_THREADS_QUERY \ - "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE 'SELECT /*!40001 SQL_NO_CACHE */%'" +static const char *metric_counter[LENGTH_METRIC_COUNTER] = {"Connections", + "Qcache_hits", + "Qcache_inserts", + "Qcache_lowmem_prunes", + "Qcache_not_cached", + "Queries", + "Questions", + "Table_locks_waited", + "Uptime"}; + +#define MYSQLDUMP_THREADS_QUERY \ + "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE " \ + "'SELECT /*!40001 SQL_NO_CACHE */%'" typedef struct { int errorcode; @@ -99,16 +107,19 @@ int main(int argc, char **argv) { } if (config.ssl) { - mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir, config.ciphers); + 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 */ - if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, config.db_port, config.db_socket, 0)) { + 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 */ /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */ /* 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)); + 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; } @@ -157,13 +168,17 @@ int main(int argc, char **argv) { 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)); + xasprintf(&perf, "%s%s ", perf, + perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false, + 0, false, 0)); 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)); + xasprintf(&perf, "%s%s ", perf, + perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0, + false, 0, false, 0)); continue; } } @@ -189,8 +204,8 @@ int main(int argc, char **argv) { 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", server_version, major_version, - minor_version, patch_version); + printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n", + server_version, major_version, minor_version, patch_version); } if (strstr(server_version, "MariaDB") != NULL) { @@ -292,11 +307,15 @@ int main(int argc, char **argv) { } /* 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"); - - /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no mysqldump threads running */ - if (strcmp(row[replica_io_field], "Yes") != 0 || strcmp(row[replica_sql_field], "Yes") != 0) { + 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"); + + /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no + * mysqldump threads running */ + if (strcmp(row[replica_io_field], "Yes") != 0 || + strcmp(row[replica_sql_field], "Yes") != 0) { MYSQL_RES *res_mysqldump; MYSQL_ROW row_mysqldump; unsigned int mysqldump_threads = 0; @@ -325,20 +344,23 @@ int main(int argc, char **argv) { if (seconds_behind_field == -1) { printf("seconds_behind_field not found\n"); } else { - printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field, row[seconds_behind_field]); + printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field, + row[seconds_behind_field]); } } /* Check Seconds Behind against threshold */ - if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && strcmp(row[seconds_behind_field], "NULL") != 0)) { + 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)); + 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); @@ -410,7 +432,8 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { int option = 0; while (true) { - int option_index = getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); + int option_index = + getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); if (option_index == -1 || option_index == EOF) { break; @@ -580,15 +603,17 @@ void print_help(void) { printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); printf(" %s\n", _("Your clear-text password could be visible as a process table entry")); printf(" %s\n", "-S, --check-slave"); - printf(" %s\n", - _("Check if the slave thread is running properly. This option is deprecated in favour of check-replica, which does the same")); + printf(" %s\n", _("Check if the slave thread is running properly. This option is deprecated " + "in favour of check-replica, which does the same")); printf(" %s\n", "--check-replica"); printf(" %s\n", _("Check if the replica thread is running properly.")); printf(" %s\n", "-w, --warning"); - printf(" %s\n", _("Exit with WARNING status if replica server is more than INTEGER seconds")); + printf(" %s\n", + _("Exit with WARNING status if replica server is more than INTEGER seconds")); printf(" %s\n", _("behind master")); printf(" %s\n", "-c, --critical"); - printf(" %s\n", _("Exit with CRITICAL status if replica server is more then INTEGER seconds")); + printf(" %s\n", + _("Exit with CRITICAL status if replica server is more then INTEGER seconds")); printf(" %s\n", _("behind master")); printf(" %s\n", "-l, --ssl"); printf(" %s\n", _("Use ssl encryption")); @@ -604,7 +629,8 @@ void print_help(void) { printf(" %s\n", _("List of valid SSL ciphers")); printf("\n"); - printf(" %s\n", _("There are no required arguments. By default, the local database is checked")); + printf(" %s\n", + _("There are no required arguments. By default, the local database is checked")); printf(" %s\n", _("using the default unix socket. You can force TCP on localhost by using an")); printf(" %s\n", _("IP address or FQDN ('localhost' will use the socket as well).")); -- cgit v1.2.3-74-g34f1 From dbe417bdf9d4af8b6c07b9f04579a38231b783ed Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:07:18 +0200 Subject: check_mysql: Assume MySQL server by default (in replica check) In the Debian Bug tracker (and then Github) a person pointed out, that a MySQL server does not respond with a hint that is indeed the MySQL software but only with the version string. Which makes sense if one assumes to be the only implementation. This commit changes the behaviour of the Replica check to assume that the counterpart is a MySQL server if there are not hints that it is a MariaDB server. --- plugins/check_mysql.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'plugins/check_mysql.c') diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index 3d7ec4cd..062ec657 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -219,16 +219,13 @@ int main(int argc, char **argv) { use_deprecated_slave_status = true; } } - } else if (strstr(server_version, "MySQL") != NULL) { - // Looks like MySQL + } else { + // Looks like MySQL or at least not like MariaDB if (major_version < 8) { use_deprecated_slave_status = true; } else if (major_version == 10 && minor_version < 4) { use_deprecated_slave_status = true; } - } else { - printf("Not a known sever implementation: %s\n", server_version); - exit(STATE_UNKNOWN); } char *replica_query = NULL; -- cgit v1.2.3-74-g34f1 From 392c945966d96d1dba9c68ac7a73450c2ad72d85 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Tue, 30 Sep 2025 14:51:39 +0200 Subject: More renaming due to MySQL name chances Due to MySQL changing several term in Version 8.0.22 the way to determine the status of replicas has changed. To adapt to these changes in a517dc614e44650a7e9204c4202feec7a40fd37f check_mysql was modified to adapt to different versions. Some parts were missed though which results in failures to detect the replica status properly. This parts should be contained in this commit. --- plugins/check_mysql.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'plugins/check_mysql.c') diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index 062ec657..6134d6c6 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -282,17 +282,32 @@ int main(int argc, char **argv) { num_fields = mysql_num_fields(res); fields = mysql_fetch_fields(res); for (int i = 0; i < num_fields; i++) { - if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { - replica_io_field = i; - continue; - } - if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { - replica_sql_field = i; - continue; - } - if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) { - seconds_behind_field = i; - continue; + if (use_deprecated_slave_status) { + if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { + replica_io_field = i; + continue; + } + if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { + replica_sql_field = i; + continue; + } + if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) { + seconds_behind_field = i; + continue; + } + } else { + if (strcmp(fields[i].name, "Replica_IO_Running") == 0) { + replica_io_field = i; + continue; + } + if (strcmp(fields[i].name, "Replica_SQL_Running") == 0) { + replica_sql_field = i; + continue; + } + if (strcmp(fields[i].name, "Seconds_Behind_Source") == 0) { + seconds_behind_field = i; + continue; + } } } -- 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/check_mysql.c') 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/check_mysql.c') 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 317ee266a88bd8752113df39f12e2d133edd6802 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 26 Nov 2025 13:50:58 +0100 Subject: Add output formatting option where they were forgotten --- plugins/check_dbi.c | 2 ++ plugins/check_mysql.c | 19 +++++++++++++++++++ plugins/check_mysql.d/config.h | 5 +++++ plugins/check_mysql_query.c | 22 ++++++++++++++++++++++ plugins/check_mysql_query.d/config.h | 6 ++++++ 5 files changed, 54 insertions(+) (limited to 'plugins/check_mysql.c') diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 9bc68eb3..81d92952 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -688,6 +688,8 @@ void print_help(void) { printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); + printf("\n"); printf(" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates")); printf(" %s\n\n", _("on a query, one has to be specified (-q option).")); diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index 9d8094c0..009c9908 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -96,6 +96,10 @@ int main(int argc, char **argv) { const check_mysql_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + MYSQL mysql; /* initialize mysql */ mysql_init(&mysql); @@ -471,6 +475,7 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { enum { CHECK_REPLICA_OPT = CHAR_MAX + 1, + output_format_index, }; static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, @@ -495,6 +500,7 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { {"cert", required_argument, 0, 'a'}, {"ca-dir", required_argument, 0, 'D'}, {"ciphers", required_argument, 0, 'L'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_mysql_config_wrapper result = { @@ -605,6 +611,17 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { break; case '?': /* help */ usage5(); + 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; + } } } @@ -711,6 +728,8 @@ void print_help(void) { printf(" %s\n", "-L, --ciphers=STRING"); printf(" %s\n", _("List of valid SSL ciphers")); + printf(UT_OUTPUT_FORMAT); + printf("\n"); printf(" %s\n", _("There are no required arguments. By default, the local database is checked")); diff --git a/plugins/check_mysql.d/config.h b/plugins/check_mysql.d/config.h index ef086cfc..1d8c82bb 100644 --- a/plugins/check_mysql.d/config.h +++ b/plugins/check_mysql.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include "thresholds.h" #include #include @@ -26,6 +27,8 @@ typedef struct { mp_thresholds replica_thresholds; + bool output_format_is_set; + mp_output_format output_format; } check_mysql_config; check_mysql_config check_mysql_config_init() { @@ -49,6 +52,8 @@ check_mysql_config check_mysql_config_init() { .ignore_auth = false, .replica_thresholds = mp_thresholds_init(), + + .output_format_is_set = false, }; return tmp; } diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c index 8af378d5..ae6cc15d 100644 --- a/plugins/check_mysql_query.c +++ b/plugins/check_mysql_query.c @@ -73,6 +73,10 @@ int main(int argc, char **argv) { const check_mysql_query_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + MYSQL mysql; /* initialize mysql */ mysql_init(&mysql); @@ -185,6 +189,10 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { + enum { + output_format_index = CHAR_MAX + 1, + }; + static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, {"socket", required_argument, 0, 's'}, {"database", required_argument, 0, 'd'}, @@ -199,6 +207,7 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { {"query", required_argument, 0, 'q'}, {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_mysql_query_config_wrapper result = { @@ -282,6 +291,17 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { } break; case '?': /* help */ usage5(); + 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; + } } } @@ -344,6 +364,8 @@ void print_help(void) { printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); printf(" %s\n", _("Your clear-text password could be visible as a process table entry")); + printf(UT_OUTPUT_FORMAT); + printf("\n"); printf(" %s\n", _("A query is required. The result from the query should be numeric.")); printf(" %s\n", _("For extra security, create a user with minimal access.")); diff --git a/plugins/check_mysql_query.d/config.h b/plugins/check_mysql_query.d/config.h index 1c9952e5..32ab455a 100644 --- a/plugins/check_mysql_query.d/config.h +++ b/plugins/check_mysql_query.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include "thresholds.h" #include @@ -16,6 +17,9 @@ typedef struct { char *sql_query; mp_thresholds thresholds; + + bool output_format_is_set; + mp_output_format output_format; } check_mysql_query_config; check_mysql_query_config check_mysql_query_config_init() { @@ -31,6 +35,8 @@ check_mysql_query_config check_mysql_query_config_init() { .sql_query = NULL, .thresholds = mp_thresholds_init(), + + .output_format_is_set = false, }; return tmp; } -- cgit v1.2.3-74-g34f1 From fa6a274ad8a4ade54fe6d0b24be8788bd82b6212 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 27 Nov 2025 12:55:26 +0100 Subject: Look for either replica or slave in replica status --- plugins/check_mysql.c | 48 +++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) (limited to 'plugins/check_mysql.c') diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index 009c9908..26730d4c 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -345,37 +345,23 @@ int main(int argc, char **argv) { int replica_io_field = -1; int replica_sql_field = -1; int seconds_behind_field = -1; - int num_fields; - MYSQL_FIELD *fields; - num_fields = mysql_num_fields(res); - fields = mysql_fetch_fields(res); - for (int i = 0; i < num_fields; i++) { - if (use_deprecated_slave_status) { - if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { - replica_io_field = i; - continue; - } - if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { - replica_sql_field = i; - continue; - } - if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) { - seconds_behind_field = i; - continue; - } - } else { - if (strcmp(fields[i].name, "Replica_IO_Running") == 0) { - replica_io_field = i; - continue; - } - if (strcmp(fields[i].name, "Replica_SQL_Running") == 0) { - replica_sql_field = i; - continue; - } - if (strcmp(fields[i].name, "Seconds_Behind_Source") == 0) { - seconds_behind_field = i; - continue; - } + unsigned int num_fields = mysql_num_fields(res); + MYSQL_FIELD *fields = mysql_fetch_fields(res); + for (int i = 0; i < (int)num_fields; i++) { + if ((strcasecmp(fields[i].name, "Slave_IO_Running") == 0) || + (strcasecmp(fields[i].name, "Replica_IO_Running") == 0)) { + replica_io_field = i; + continue; + } + if ((strcasecmp(fields[i].name, "Slave_SQL_Running") == 0) || + (strcasecmp(fields[i].name, "Replica_SQL_Running") == 0)) { + replica_sql_field = i; + continue; + } + if ((strcasecmp(fields[i].name, "Seconds_Behind_Master") == 0) || + (strcasecmp(fields[i].name, "Seconds_Behind_Source") == 0)) { + seconds_behind_field = i; + continue; } } -- cgit v1.2.3-74-g34f1