From 39b9b62adda3887e7706754d4e34a7eff2f793b5 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 21:34:58 +0100 Subject: check_users: clang-format --- plugins/check_users.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/plugins/check_users.c b/plugins/check_users.c index f1e1c39d..e91ed4a0 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -83,15 +83,16 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) + if (process_arguments(argc, argv) == ERROR) { usage4(_("Could not parse arguments")); + } users = 0; #ifdef HAVE_LIBSYSTEMD - if (sd_booted() > 0) + if (sd_booted() > 0) { users = sd_get_sessions(NULL); - else { + } else { #endif #if HAVE_WTSAPI32_H if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { @@ -104,18 +105,21 @@ int main(int argc, char **argv) { DWORD size; int len; - if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) + if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) { continue; + } len = lstrlen(username); WTSFreeMemory(username); - if (len == 0) + if (len == 0) { continue; + } - if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) + if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { users++; + } } WTSFreeMemory(wtsinfo); @@ -123,9 +127,11 @@ int main(int argc, char **argv) { /* get currently logged users from utmpx */ setutxent(); - while ((putmpx = getutxent()) != NULL) - if (putmpx->ut_type == USER_PROCESS) + while ((putmpx = getutxent()) != NULL) { + if (putmpx->ut_type == USER_PROCESS) { users++; + } + } endutxent(); #else @@ -137,8 +143,9 @@ int main(int argc, char **argv) { } child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); - if (child_stderr == NULL) + if (child_stderr == NULL) { printf(_("Could not open stderr for %s\n"), WHO_COMMAND); + } while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { /* increment 'users' on all lines except total user count */ @@ -148,18 +155,21 @@ int main(int argc, char **argv) { } /* get total logged in users */ - if (sscanf(input_buffer, _("# users=%d"), &users) == 1) + if (sscanf(input_buffer, _("# users=%d"), &users) == 1) { break; + } } /* check STDERR */ - if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) + if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { result = possibly_set(result, STATE_UNKNOWN); + } (void)fclose(child_stderr); /* close the pipe */ - if (spclose(child_process)) + if (spclose(child_process)) { result = possibly_set(result, STATE_UNKNOWN); + } #endif #ifdef HAVE_LIBSYSTEMD } @@ -168,9 +178,9 @@ int main(int argc, char **argv) { /* check the user count against warning and critical thresholds */ result = get_status((double)users, thlds); - if (result == STATE_UNKNOWN) + if (result == STATE_UNKNOWN) { printf("%s\n", _("Unable to read output")); - else { + } else { printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); } @@ -186,16 +196,18 @@ int process_arguments(int argc, char **argv) { {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; - if (argc < 2) + if (argc < 2) { usage("\n"); + } int option_char; while (true) { int option = 0; option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option); - if (option_char == -1 || option_char == EOF || option_char == 1) + if (option_char == -1 || option_char == EOF || option_char == 1) { break; + } switch (option_char) { case '?': /* print short usage statement if args not parsable */ @@ -217,11 +229,13 @@ int process_arguments(int argc, char **argv) { option_char = optind; - if (warning_range == NULL && argc > option_char) + if (warning_range == NULL && argc > option_char) { warning_range = argv[option_char++]; + } - if (critical_range == NULL && argc > option_char) + if (critical_range == NULL && argc > option_char) { critical_range = argv[option_char++]; + } /* this will abort in case of invalid ranges */ set_thresholds(&thlds, warning_range, critical_range); -- cgit v1.2.3-74-g34f1 From 6ac236c1ef06ef17541d3919aed2a008aabaa7f4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:01:46 +0100 Subject: Refactor check_users --- plugins/Makefile.am | 2 + plugins/check_users.c | 250 +++++++++++++++++++---------------------- plugins/check_users.d/config.h | 20 ++++ plugins/check_users.d/users.c | 166 +++++++++++++++++++++++++++ plugins/check_users.d/users.h | 18 +++ 5 files changed, 319 insertions(+), 137 deletions(-) create mode 100644 plugins/check_users.d/config.h create mode 100644 plugins/check_users.d/users.c create mode 100644 plugins/check_users.d/users.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 5cd20319..0a2a91aa 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -55,6 +55,7 @@ EXTRA_DIST = t \ check_game.d \ check_radius.d \ check_time.d \ + check_users.d \ check_nagios.d \ check_dbi.d \ check_real.d \ @@ -152,6 +153,7 @@ check_tcp_LDADD = $(SSLOBJS) check_time_LDADD = $(NETLIBS) check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) check_ups_LDADD = $(NETLIBS) +check_users_SOURCES = check_users.c check_users.d/users.c check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) check_by_ssh_LDADD = $(NETLIBS) check_ide_smart_LDADD = $(BASEOBJS) diff --git a/plugins/check_users.c b/plugins/check_users.c index e91ed4a0..61427f97 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -34,8 +34,15 @@ const char *progname = "check_users"; const char *copyright = "2000-2024"; const char *email = "devel@monitoring-plugins.org"; -#include "common.h" -#include "utils.h" +#include "check_users.d/users.h" +#include "output.h" +#include "perfdata.h" +#include "states.h" +#include "utils_base.h" +#include "./common.h" +#include "./utils.h" +#include "check_users.d/config.h" +#include "thresholds.h" #if HAVE_WTSAPI32_H # include @@ -53,29 +60,16 @@ const char *email = "devel@monitoring-plugins.org"; # include #endif -#define possibly_set(a, b) ((a) == 0 ? (b) : 0) +typedef struct process_argument_wrapper { + int errorcode; + check_users_config config; +} check_users_config_wrapper; +check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static int process_arguments(int, char **); -static void print_help(void); +void print_help(void); void print_usage(void); -static char *warning_range = NULL; -static char *critical_range = NULL; -static thresholds *thlds = NULL; - int main(int argc, char **argv) { - int users = -1; - int result = STATE_UNKNOWN; -#if HAVE_WTSAPI32_H - WTS_SESSION_INFO *wtsinfo; - DWORD wtscount; - DWORD index; -#elif HAVE_UTMPX_H - struct utmpx *putmpx; -#else - char input_buffer[MAX_INPUT_BUFFER]; -#endif - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -83,133 +77,100 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) { + check_users_config_wrapper tmp_config = process_arguments(argc, argv); + + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } - users = 0; - -#ifdef HAVE_LIBSYSTEMD - if (sd_booted() > 0) { - users = sd_get_sessions(NULL); - } else { -#endif -#if HAVE_WTSAPI32_H - if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { - printf(_("Could not enumerate RD sessions: %d\n"), GetLastError()); - return STATE_UNKNOWN; - } - - for (index = 0; index < wtscount; index++) { - LPTSTR username; - DWORD size; - int len; - - if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) { - continue; - } - - len = lstrlen(username); - - WTSFreeMemory(username); - - if (len == 0) { - continue; - } - - if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { - users++; - } - } + check_users_config config = tmp_config.config; - WTSFreeMemory(wtsinfo); -#elif HAVE_UTMPX_H - /* get currently logged users from utmpx */ - setutxent(); - - while ((putmpx = getutxent()) != NULL) { - if (putmpx->ut_type == USER_PROCESS) { - users++; - } - } - - endutxent(); +#ifdef _WIN32 +# if HAVE_WTSAPI32_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_windows(); +# else +# error Did not find WTSAPI32 +# endif // HAVE_WTSAPI32_H #else - /* run the command */ - child_process = spopen(WHO_COMMAND); - if (child_process == NULL) { - printf(_("Could not open pipe: %s\n"), WHO_COMMAND); - return STATE_UNKNOWN; - } - - child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); - if (child_stderr == NULL) { - printf(_("Could not open stderr for %s\n"), WHO_COMMAND); - } - - while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { - /* increment 'users' on all lines except total user count */ - if (input_buffer[0] != '#') { - users++; - continue; - } - - /* get total logged in users */ - if (sscanf(input_buffer, _("# users=%d"), &users) == 1) { - break; - } - } - - /* check STDERR */ - if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { - result = possibly_set(result, STATE_UNKNOWN); +# ifdef HAVE_LIBSYSTEMD + get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd(); +# elif HAVE_UTMPX_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp(); +# else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command(); +# endif // HAVE_LIBSYSTEMD +#endif // _WIN32 + + mp_check overall = mp_check_init(); + if (config.output_format_is_set) { + mp_set_format(config.output_format); } - (void)fclose(child_stderr); + mp_subcheck sc_users = mp_subcheck_init(); - /* close the pipe */ - if (spclose(child_process)) { - result = possibly_set(result, STATE_UNKNOWN); + if (user_wrapper.errorcode != 0) { + sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN); + sc_users.output = "Failed to retrieve number of users"; + mp_add_subcheck_to_check(&overall, sc_users); + mp_exit(overall); } -#endif -#ifdef HAVE_LIBSYSTEMD - } -#endif - /* check the user count against warning and critical thresholds */ - result = get_status((double)users, thlds); - if (result == STATE_UNKNOWN) { - printf("%s\n", _("Unable to read output")); - } else { - printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, - sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); + mp_perfdata users_pd = { + .label = "users", + .value = mp_create_pd_value(user_wrapper.users), + }; + + users_pd = mp_pd_set_thresholds(users_pd, config.thresholds); + mp_add_perfdata_to_subcheck(&sc_users, users_pd); + + int tmp_status = mp_get_pd_status(users_pd); + sc_users = mp_set_subcheck_state(sc_users, tmp_status); + + switch (tmp_status) { + case STATE_WARNING: + xasprintf(&sc_users.output, "%d users currently logged in. This violates the warning threshold", user_wrapper.users); + break; + case STATE_CRITICAL: + xasprintf(&sc_users.output, "%d users currently logged in. This violates the critical threshold", user_wrapper.users); + break; + default: + xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users); } - return result; + mp_add_subcheck_to_check(&overall, sc_users); + mp_exit(overall); } +#define output_format_index CHAR_MAX + 1 + /* process command-line arguments */ -int process_arguments(int argc, char **argv) { +check_users_config_wrapper process_arguments(int argc, char **argv) { static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; if (argc < 2) { - usage("\n"); + usage(progname); } - int option_char; + char *warning_range = NULL; + char *critical_range = NULL; + check_users_config_wrapper result = { + .config = check_users_config_init(), + .errorcode = OK, + }; + while (true) { - int option = 0; - option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option); + int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL); - if (option_char == -1 || option_char == EOF || option_char == 1) { + if (counter == -1 || counter == EOF || counter == 1) { break; } - switch (option_char) { + switch (counter) { case '?': /* print short usage statement if args not parsable */ usage5(); case 'h': /* help */ @@ -224,31 +185,45 @@ int process_arguments(int argc, char **argv) { case 'w': /* warning */ warning_range = optarg; break; - } - } - - option_char = optind; - - if (warning_range == NULL && argc > option_char) { - warning_range = argv[option_char++]; - } + 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); + } - if (critical_range == NULL && argc > option_char) { - critical_range = argv[option_char++]; + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } + } } - /* this will abort in case of invalid ranges */ - set_thresholds(&thlds, warning_range, critical_range); - - if (!thlds->warning) { - usage4(_("Warning threshold must be a valid range expression")); + // TODO add proper verification for ranges here! + if (warning_range) { + mp_range_parsed tmp = mp_parse_range_string(warning_range); + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.warning = tmp.range; + result.config.thresholds.warning_is_set = true; + } else { + printf("Failed to parse warning range: %s", warning_range); + exit(STATE_UNKNOWN); + } } - if (!thlds->critical) { - usage4(_("Critical threshold must be a valid range expression")); + if (critical_range) { + mp_range_parsed tmp = mp_parse_range_string(critical_range); + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.critical = tmp.range; + result.config.thresholds.critical_is_set = true; + } else { + printf("Failed to parse critical range: %s", critical_range); + exit(STATE_UNKNOWN); + } } - return OK; + return result; } void print_help(void) { @@ -271,6 +246,7 @@ void print_help(void) { printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); + printf(UT_OUTPUT_FORMAT); printf(UT_SUPPORT); } diff --git a/plugins/check_users.d/config.h b/plugins/check_users.d/config.h new file mode 100644 index 00000000..26d3ee70 --- /dev/null +++ b/plugins/check_users.d/config.h @@ -0,0 +1,20 @@ +#pragma once + +#include "output.h" +#include "thresholds.h" + +typedef struct check_users_config { + mp_thresholds thresholds; + + bool output_format_is_set; + mp_output_format output_format; +} check_users_config; + +check_users_config check_users_config_init() { + check_users_config tmp = { + .thresholds = mp_thresholds_init(), + + .output_format_is_set = false, + }; + return tmp; +} diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c new file mode 100644 index 00000000..7969ae79 --- /dev/null +++ b/plugins/check_users.d/users.c @@ -0,0 +1,166 @@ +#include "./users.h" + +#ifdef _WIN32 +# ifdef HAVE_WTSAPI32_H +# include +# include +# undef ERROR +# define ERROR -1 + +get_num_of_users_wrapper get_num_of_users_windows() { + WTS_SESSION_INFO *wtsinfo; + DWORD wtscount; + + get_num_of_users_wrapper result = {}; + + if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { + // printf(_("Could not enumerate RD sessions: %d\n"), GetLastError()); + result.error = WINDOWS_COULD_NOT_ENUMERATE_SESSIONS; + return result; + } + + for (DWORD index = 0; index < wtscount; index++) { + LPTSTR username; + DWORD size; + + if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) { + continue; + } + + int len = lstrlen(username); + + WTSFreeMemory(username); + + if (len == 0) { + continue; + } + + if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { + result.users++; + } + } + + WTSFreeMemory(wtsinfo); + return result; +} +# else // HAVE_WTSAPI32_H +# error On windows but without the WTSAPI32 lib +# endif // HAVE_WTSAPI32_H + +#else // _WIN32 + +# include "../../config.h" + +# ifdef HAVE_LIBSYSTEMD +# include +# include + +get_num_of_users_wrapper get_num_of_users_systemd() { + get_num_of_users_wrapper result = {}; + + // Test whether we booted with systemd + if (sd_booted() > 0) { + int users = sd_get_sessions(NULL); + if (users >= 0) { + // Success + result.users = users; + return result; + } + + // Failure! return the error code + result.errorcode = users; + return result; + } + + // Looks like we are not running systemd, + // return with error here + result.errorcode = NO_SYSTEMD_ERROR; + return result; +} +# endif + +# ifdef HAVE_UTMPX_H +# include + +get_num_of_users_wrapper get_num_of_users_utmp() { + int users = 0; + + /* get currently logged users from utmpx */ + setutxent(); + + struct utmpx *putmpx; + while ((putmpx = getutxent()) != NULL) { + if (putmpx->ut_type == USER_PROCESS) { + users++; + } + } + + endutxent(); + + get_num_of_users_wrapper result = { + .errorcode = 0, + .users = users, + }; + + return result; +} +# endif + +# ifndef HAVE_WTSAPI32_H +# ifndef HAVE_LIBSYSTEMD +# ifndef HAVE_UTMPX_H +// Fall back option here for the others (probably still not on windows) + +# include "../popen.h" +# include "../common.h" +# include "../utils.h" + +get_num_of_users_wrapper get_num_of_users_who_command() { + /* run the command */ + child_process = spopen(WHO_COMMAND); + if (child_process == NULL) { + // printf(_("Could not open pipe: %s\n"), WHO_COMMAND); + get_num_of_users_wrapper result = { + .errorcode = COULD_NOT_OPEN_PIPE, + }; + return result; + } + + child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); + if (child_stderr == NULL) { + // printf(_("Could not open stderr for %s\n"), WHO_COMMAND); + // TODO this error should probably be reported + } + + get_num_of_users_wrapper result = {}; + char input_buffer[MAX_INPUT_BUFFER]; + while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { + /* increment 'users' on all lines except total user count */ + if (input_buffer[0] != '#') { + result.users++; + continue; + } + + /* get total logged in users */ + if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) { + break; + } + } + + /* check STDERR */ + if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { + // if this fails, something broke and the result can not be relied upon or so is the theorie here + result.errorcode = STDERR_COULD_NOT_BE_READ; + } + (void)fclose(child_stderr); + + /* close the pipe */ + spclose(child_process); + + return result; +} + +# endif +# endif +# endif +#endif diff --git a/plugins/check_users.d/users.h b/plugins/check_users.d/users.h new file mode 100644 index 00000000..aacba775 --- /dev/null +++ b/plugins/check_users.d/users.h @@ -0,0 +1,18 @@ +#pragma once + +typedef struct get_num_of_users_wrapper { + int errorcode; + int users; +} get_num_of_users_wrapper; + +enum { + NO_SYSTEMD_ERROR = 64, + WINDOWS_COULD_NOT_ENUMERATE_SESSIONS, + COULD_NOT_OPEN_PIPE, + STDERR_COULD_NOT_BE_READ, +}; + +get_num_of_users_wrapper get_num_of_users_systemd(); +get_num_of_users_wrapper get_num_of_users_utmp(); +get_num_of_users_wrapper get_num_of_users_windows(); +get_num_of_users_wrapper get_num_of_users_who_command(); -- cgit v1.2.3-74-g34f1 From ee3c903fbe37cc5c7537921cdb31e39b4ea25bc6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:06:54 +0100 Subject: users.c: Include default definitions --- plugins/check_users.d/users.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c index 7969ae79..95c05d6f 100644 --- a/plugins/check_users.d/users.c +++ b/plugins/check_users.d/users.c @@ -50,6 +50,7 @@ get_num_of_users_wrapper get_num_of_users_windows() { #else // _WIN32 # include "../../config.h" +# include # ifdef HAVE_LIBSYSTEMD # include -- cgit v1.2.3-74-g34f1 From 5a79cd31c48307d18a637271f4a0a13327fd3073 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:15:57 +0100 Subject: Reintroduce positional arguments --- plugins/check_users.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/plugins/check_users.c b/plugins/check_users.c index 61427f97..b7b7bd24 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -200,27 +200,42 @@ check_users_config_wrapper process_arguments(int argc, char **argv) { } } + int option_char = optind; + + if (warning_range == NULL && argc > option_char) { + warning_range = argv[option_char++]; + } + + if (critical_range == NULL && argc > option_char) { + critical_range = argv[option_char++]; + } + // TODO add proper verification for ranges here! + mp_range_parsed tmp; if (warning_range) { - mp_range_parsed tmp = mp_parse_range_string(warning_range); - if (tmp.error == MP_PARSING_SUCCES) { - result.config.thresholds.warning = tmp.range; - result.config.thresholds.warning_is_set = true; - } else { - printf("Failed to parse warning range: %s", warning_range); - exit(STATE_UNKNOWN); - } + tmp = mp_parse_range_string(warning_range); + } else { + tmp = mp_parse_range_string(argv[option_char++]); + } + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.warning = tmp.range; + result.config.thresholds.warning_is_set = true; + } else { + printf("Failed to parse warning range: %s", warning_range); + exit(STATE_UNKNOWN); } if (critical_range) { - mp_range_parsed tmp = mp_parse_range_string(critical_range); - if (tmp.error == MP_PARSING_SUCCES) { - result.config.thresholds.critical = tmp.range; - result.config.thresholds.critical_is_set = true; - } else { - printf("Failed to parse critical range: %s", critical_range); - exit(STATE_UNKNOWN); - } + tmp = mp_parse_range_string(critical_range); + } else { + tmp = mp_parse_range_string(argv[option_char++]); + } + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.critical = tmp.range; + result.config.thresholds.critical_is_set = true; + } else { + printf("Failed to parse critical range: %s", critical_range); + exit(STATE_UNKNOWN); } return result; -- cgit v1.2.3-74-g34f1 From 270d643d1155afd83a5c699c2e6721a4dea2c5ae Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:16:57 +0100 Subject: Adapt tests --- plugins/t/check_users.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/t/check_users.t b/plugins/t/check_users.t index 21c3e53d..446e0476 100644 --- a/plugins/t/check_users.t +++ b/plugins/t/check_users.t @@ -15,8 +15,8 @@ use NPTest; use vars qw($tests); BEGIN {$tests = 12; plan tests => $tests} -my $successOutput = '/^USERS OK - [0-9]+ users currently logged in/'; -my $failureOutput = '/^USERS CRITICAL - [0-9]+ users currently logged in/'; +my $successOutput = '/[0-9]+ users currently logged in/'; +my $failureOutput = '/[0-9]+ users currently logged in/'; my $wrongOptionOutput = '/Usage:/'; my $t; -- cgit v1.2.3-74-g34f1 From 1f7e8b57e2105e68ba97c9a5c9fbafa6b692a804 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:18:09 +0100 Subject: check_users: ignore temporary files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f9cb37e4..5632b016 100644 --- a/.gitignore +++ b/.gitignore @@ -197,6 +197,8 @@ NP-VERSION-FILE /plugins/check_udp /plugins/check_ups /plugins/check_users +/plugins/check_users.d/.deps +/plugins/check_users.d/.dirstamp /plugins/check_vsz /plugins/config.h /plugins/config.h.in -- cgit v1.2.3-74-g34f1 From 4a0e309f9f5a85af6087e3eb7034a98dd410c6f7 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 23:37:36 +0100 Subject: Fail correctly with missing thresholds --- plugins/check_users.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/check_users.c b/plugins/check_users.c index b7b7bd24..cd3bd181 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -215,8 +215,11 @@ check_users_config_wrapper process_arguments(int argc, char **argv) { if (warning_range) { tmp = mp_parse_range_string(warning_range); } else { - tmp = mp_parse_range_string(argv[option_char++]); - } + printf("Warning threshold missing\n"); + print_usage(); + exit(STATE_UNKNOWN); + } + if (tmp.error == MP_PARSING_SUCCES) { result.config.thresholds.warning = tmp.range; result.config.thresholds.warning_is_set = true; @@ -228,8 +231,11 @@ check_users_config_wrapper process_arguments(int argc, char **argv) { if (critical_range) { tmp = mp_parse_range_string(critical_range); } else { - tmp = mp_parse_range_string(argv[option_char++]); + printf("Critical threshold missing\n"); + print_usage(); + exit(STATE_UNKNOWN); } + if (tmp.error == MP_PARSING_SUCCES) { result.config.thresholds.critical = tmp.range; result.config.thresholds.critical_is_set = true; -- cgit v1.2.3-74-g34f1 From 661ecff45c5f4c41c22ef9fd4fe308100b97d6bf Mon Sep 17 00:00:00 2001 From: Richard Laager Date: Fri, 11 Jul 2025 18:19:31 -0500 Subject: check_ssh: Fix buffer overflow A buffer overflow was occurring when the server responded with: Exceeded MaxStartups\r\n glibc would then abort() with the following output: *** buffer overflow detected ***: terminated It was the memset() that was overflowing the buffer. But the memmove() needed fixing too. First off, there was an off-by-one error in both the memmove() and memset(). byte_offset was already set to the start of the data _past_ the newline (i.e. len + 1). For the memmove(), incrementing that by 1 again lost the first character of the additional output. For the memset(), this causes a buffer overflow. Second, the memset() has multiple issues. The comment claims that it was NULing (sic "null") the "rest". However, it has no idea how long the "rest" is, at this point. It was NULing BUFF_SZ - byte_offset + 1. After fixing the off-by-one / buffer overflow, it would be NULing BUFF_SZ - byte_offset. But that doesn't make any sense. The length of the first line has no relation to the length of the second line. For a quick-and-dirty test, add something like this just inside the while loop: memcpy(output, "Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0", sizeof("Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0")); And, after the memmove(), add: printf("output='%s'\n", output); If you fix the memset() buffer overflow, it will output: output='ext blah1 blah2 blah3 ' As you can see, the first character is lost. If you then fix the memmove(), it will output: output='next blah1 blah2 blah3' Note that this is still losing the "blah4". After moving the memset() after byte_offset is set to the new strlen() of output, then it works correctly: output='next blah1 blah2 blah3 blah4' Signed-off-by: Richard Laager --- plugins/check_ssh.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index 9d0d7cde..fd082e11 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -273,12 +273,14 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ } if (version_control_string == NULL) { - /* move unconsumed data to beginning of buffer, null rest */ - memmove((void *)output, (void *)(output + byte_offset + 1), BUFF_SZ - len + 1); - memset(output + byte_offset + 1, 0, BUFF_SZ - byte_offset + 1); + /* move unconsumed data to beginning of buffer */ + memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset); /*start reading from end of current line chunk on next recv*/ byte_offset = strlen(output); + + /* NUL the rest of the buffer */ + memset(output + byte_offset, 0, BUFF_SZ - byte_offset); } } else { byte_offset += recv_ret; -- cgit v1.2.3-74-g34f1 From 1f2acfd1c6577db6e3d385614922e32ac9fad03f Mon Sep 17 00:00:00 2001 From: Richard Laager Date: Fri, 11 Jul 2025 18:38:42 -0500 Subject: check_ssh: Correct type on len variable strlen() returns a size_t. Signed-off-by: Richard Laager --- plugins/check_ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index fd082e11..2c76fa84 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -255,7 +255,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ byte_offset = 0; char *index = NULL; - unsigned long len = 0; + size_t len = 0; while ((index = strchr(output + byte_offset, '\n')) != NULL) { /*Partition the buffer so that this line is a separate string, * by replacing the newline with NUL*/ -- cgit v1.2.3-74-g34f1 From c43135a5e5d7b03eaaf557581f7a8cbc86fa7b90 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Mon, 14 Jul 2025 09:37:51 +0200 Subject: check_ntp_peer: fix invalid conversion in printf --- plugins/check_ntp_peer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 6e76bf23..5c4ff386 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -707,11 +707,11 @@ int main(int argc, char *argv[]) { if (config.do_stratum) { if (sresult == STATE_WARNING) { - xasprintf(&result_line, "%s, stratum=%l (WARNING)", result_line, ntp_res.stratum); + xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum); } else if (sresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s, stratum=%l (CRITICAL)", result_line, ntp_res.stratum); + xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum); } else { - xasprintf(&result_line, "%s, stratum=%l", result_line, ntp_res.stratum); + 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)); } -- cgit v1.2.3-74-g34f1 From d2bea1d288328d2d387d587b38a0efeba1becc97 Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Wed, 23 Jul 2025 15:22:45 +0200 Subject: CI: Adding workflow_dispatch --- .github/workflows/codeql-analysis.yml | 1 + .github/workflows/spellcheck.yml | 1 + .github/workflows/test.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c402e0cf..e748d2eb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,6 +13,7 @@ name: "CodeQL" on: + workflow_dispatch: {} push: branches: [master] pull_request: diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 72f7c7eb..c8714e64 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -2,6 +2,7 @@ name: Spellcheck on: + workflow_dispatch: {} # Run for pushes on any branch push: branches: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ce0ec547..146d91b6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,7 @@ name: Tests on: + workflow_dispatch: {} push: branches: - '*' -- cgit v1.2.3-74-g34f1 From 61a68da144b726668196ba5cb01fcce9e99f0fdb Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Thu, 24 Jul 2025 10:28:03 +0200 Subject: Adding tmate optional to manual dispatch --- .github/workflows/test-next.yml | 14 +++++++++++++- .github/workflows/test.yml | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml index fd59e85d..7ca255c9 100644 --- a/.github/workflows/test-next.yml +++ b/.github/workflows/test-next.yml @@ -2,7 +2,13 @@ name: Tests Debian:Testing and Fedora:Rawhide on: - workflow_dispatch: {} + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false push: branches-ignore: - '*' @@ -25,6 +31,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol @@ -60,6 +69,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 146d91b6..f9919b2d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,13 @@ name: Tests on: - workflow_dispatch: {} + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false push: branches: - '*' @@ -23,6 +29,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol @@ -61,6 +70,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol -- cgit v1.2.3-74-g34f1 From 69925c782bf70d267ea14a57d46b5390f5555b8f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:34:01 +0200 Subject: check_ssh: fix data type to allow for error checking --- plugins/check_ssh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index 2c76fa84..f93127ce 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -245,7 +245,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); char *buffer = NULL; - size_t recv_ret = 0; + ssize_t recv_ret = 0; char *version_control_string = NULL; size_t byte_offset = 0; while ((version_control_string == NULL) && @@ -283,7 +283,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ memset(output + byte_offset, 0, BUFF_SZ - byte_offset); } } else { - byte_offset += recv_ret; + byte_offset += (size_t)recv_ret; } } -- cgit v1.2.3-74-g34f1 From 3c53bf623d89650ac450be2518d17276a29247cc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:34:29 +0200 Subject: check_ssh: Fix format expression --- plugins/check_ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index f93127ce..af7089a7 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -289,7 +289,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ if (recv_ret < 0) { connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); - xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - %s", strerror(errno)); + xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno)); mp_add_subcheck_to_check(overall, connection_sc); return OK; } -- cgit v1.2.3-74-g34f1 From a69dff15222ad43c56f0142e20d97ee51c2e6697 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:35:13 +0200 Subject: check_ssh: Put variable in the correct scope --- plugins/check_ssh.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index af7089a7..b4a98cdf 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -255,12 +255,11 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ byte_offset = 0; char *index = NULL; - size_t len = 0; while ((index = strchr(output + byte_offset, '\n')) != NULL) { /*Partition the buffer so that this line is a separate string, * by replacing the newline with NUL*/ output[(index - output)] = '\0'; - len = strlen(output + byte_offset); + size_t len = strlen(output + byte_offset); if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { /*if the string starts with SSH-, this _should_ be a valid version control string*/ -- cgit v1.2.3-74-g34f1 From 2757550558d509aa5c95d8834ee76d803e110161 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:35:23 +0200 Subject: clang-format --- plugins/check_ssh.c | 72 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index b4a98cdf..f6c8d551 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -57,7 +57,8 @@ static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv* static void print_help(void); void print_usage(void); -static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, char *remote_protocol); +static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, + char *remote_protocol); int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -85,7 +86,8 @@ int main(int argc, char **argv) { alarm(socket_timeout); /* ssh_connect exits if error is found */ - ssh_connect(&overall, config.server_name, config.port, config.remote_version, config.remote_protocol); + ssh_connect(&overall, config.server_name, config.port, config.remote_version, + config.remote_protocol); alarm(0); @@ -96,19 +98,20 @@ int main(int argc, char **argv) { /* process command-line arguments */ process_arguments_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = {{"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"host", required_argument, 0, 'H'}, /* backward compatibility */ - {"hostname", required_argument, 0, 'H'}, - {"port", required_argument, 0, 'p'}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"timeout", required_argument, 0, 't'}, - {"verbose", no_argument, 0, 'v'}, - {"remote-version", required_argument, 0, 'r'}, - {"remote-protocol", required_argument, 0, 'P'}, - {"output-format", required_argument, 0, output_format_index}, - {0, 0, 0, 0}}; + static struct option longopts[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"host", required_argument, 0, 'H'}, /* backward compatibility */ + {"hostname", required_argument, 0, 'H'}, + {"port", required_argument, 0, 'p'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"timeout", required_argument, 0, 't'}, + {"verbose", no_argument, 0, 'v'}, + {"remote-version", required_argument, 0, 'r'}, + {"remote-protocol", required_argument, 0, 'P'}, + {"output-format", required_argument, 0, output_format_index}, + {0, 0, 0, 0}}; process_arguments_wrapper result = { .config = check_ssh_config_init(), @@ -228,7 +231,8 @@ process_arguments_wrapper process_arguments(int argc, char **argv) { * *-----------------------------------------------------------------------*/ -int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, char *desired_remote_protocol) { +int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, + char *desired_remote_protocol) { struct timeval tv; gettimeofday(&tv, NULL); @@ -238,7 +242,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ mp_subcheck connection_sc = mp_subcheck_init(); if (result != STATE_OK) { connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); - xasprintf(&connection_sc.output, "Failed to establish TCP connection to Host %s and Port %d", haddr, hport); + xasprintf(&connection_sc.output, + "Failed to establish TCP connection to Host %s and Port %d", haddr, hport); mp_add_subcheck_to_check(overall, connection_sc); return result; } @@ -249,7 +254,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ char *version_control_string = NULL; size_t byte_offset = 0; while ((version_control_string == NULL) && - (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), 0) > 0)) { + (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), + 0) > 0)) { if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ byte_offset = 0; @@ -262,7 +268,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ size_t len = strlen(output + byte_offset); if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { - /*if the string starts with SSH-, this _should_ be a valid version control string*/ + /*if the string starts with SSH-, this _should_ be a valid version control + * string*/ version_control_string = output + byte_offset; break; } @@ -334,7 +341,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ * "1.x" (e.g., "1.5" or "1.3")." * - RFC 4253:5 */ - char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ + char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ /* If there's a space in the version string, whatever's after the space is a comment * (which is NOT part of the server name/version)*/ @@ -346,13 +354,15 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ mp_subcheck protocol_validity_sc = mp_subcheck_init(); if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL); - xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", version_control_string); + xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", + version_control_string); mp_add_subcheck_to_check(overall, protocol_validity_sc); return OK; } protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK); - xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", version_control_string); + xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", + version_control_string); mp_add_subcheck_to_check(overall, protocol_validity_sc); ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; @@ -367,8 +377,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) { mp_subcheck remote_version_sc = mp_subcheck_init(); remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL); - xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), ssh_server, ssh_proto, - desired_remote_version); + xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), + ssh_server, ssh_proto, desired_remote_version); close(socket); mp_add_subcheck_to_check(overall, remote_version_sc); return OK; @@ -386,11 +396,13 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) { protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL); - xasprintf(&protocol_version_sc.output, _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, ssh_proto, - desired_remote_protocol); + xasprintf(&protocol_version_sc.output, + _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, + ssh_proto, desired_remote_protocol); } else { protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK); - xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", ssh_server, ssh_proto); + xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", + ssh_server, ssh_proto); } mp_add_subcheck_to_check(overall, protocol_version_sc); @@ -423,7 +435,8 @@ void print_help(void) { printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(" %s\n", "-r, --remote-version=STRING"); - printf(" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); + printf(" %s\n", + _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); printf(" %s\n", "-P, --remote-protocol=STRING"); printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); @@ -436,5 +449,6 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf("%s [-4|-6] [-t ] [-r ] [-p ] --hostname \n", progname); + printf("%s [-4|-6] [-t ] [-r ] [-p ] --hostname \n", + progname); } -- cgit v1.2.3-74-g34f1 From 278954117cabd8e76941d4191a2692bfdeb39372 Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Fri, 1 Aug 2025 21:33:39 +0200 Subject: (Re)construct PLATFORM_ID as it's droped since Fedora 43 See https://fedoraproject.org/wiki/Changes/Drop_PLATFORM_ID?#Drop_PLATFORM_ID --- .github/os_detect.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/os_detect.sh b/.github/os_detect.sh index 47c762d3..3c5956de 100644 --- a/.github/os_detect.sh +++ b/.github/os_detect.sh @@ -22,4 +22,7 @@ else return 1 fi export distro_id=$(grep '^ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') +export version_id=$(grep '^VERSION_ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') export platform_id=$(grep '^PLATFORM_ID=' /etc/os-release|awk -F = '{print $2}'|sed 's/\"//g'| cut -d":" -f2) +# Fedora dropped PLATFORM_ID: https://fedoraproject.org/wiki/Changes/Drop_PLATFORM_ID?#Drop_PLATFORM_ID +if [ -z $platform_id ]; then export platform_id=$(echo ${distro_id:0:1}${version_id}); fi -- cgit v1.2.3-74-g34f1 From fb39f96ac6f72bb56d17f3e8694134dfea9186e9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 11 Aug 2025 21:49:20 +0200 Subject: check_users: Use sd_get_uids instead of sd_get_session Previously check_users in combination with systemd used sd_get_sessions (3) to aquire the number of users, probably with the idea that every users opens a session. Turns out, that a user can have multiple sessions and we only really want to know how many users there are. This commit changes to sd_get_uids (3) to achieve that target. --- plugins/check_users.d/users.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c index 95c05d6f..a8b168a0 100644 --- a/plugins/check_users.d/users.c +++ b/plugins/check_users.d/users.c @@ -61,7 +61,7 @@ get_num_of_users_wrapper get_num_of_users_systemd() { // Test whether we booted with systemd if (sd_booted() > 0) { - int users = sd_get_sessions(NULL); + int users = sd_get_uids(NULL); if (users >= 0) { // Success result.users = users; -- cgit v1.2.3-74-g34f1 From fc0f176f0deb4f473553ae71eac8ee33654d62e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:29:24 +0000 Subject: build(deps): bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/spellcheck.yml | 2 +- .github/workflows/test-next.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e748d2eb..57487eed 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index c8714e64..14b82781 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Codespell uses: codespell-project/actions-codespell@v2 with: diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml index 7ca255c9..0e69c251 100644 --- a/.github/workflows/test-next.yml +++ b/.github/workflows/test-next.yml @@ -30,7 +30,7 @@ jobs: prepare: .github/prepare_debian.sh steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} @@ -68,7 +68,7 @@ jobs: - {"distro": "fedora:rawhide", "build": ".github/mock.sh"} steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9919b2d..1ac8aaf3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: prepare: .github/prepare_debian.sh steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} @@ -69,7 +69,7 @@ jobs: # - {"distro": "oraclelinux:9", "build": ".github/mock.sh"} steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} -- cgit v1.2.3-74-g34f1 From 7fe6ac8d08a2baf63db57fd33185224df7e18e27 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:28:04 +0200 Subject: rebuild check_snmp --- plugins/Makefile.am | 3 + plugins/check_snmp.c | 1654 ++++++++++++++++++----------------------- plugins/check_snmp.d/config.h | 107 +++ 3 files changed, 838 insertions(+), 926 deletions(-) create mode 100644 plugins/check_snmp.d/config.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 5994b405..765e2687 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -78,6 +78,7 @@ EXTRA_DIST = t \ check_ping.d \ check_by_ssh.d \ check_smtp.d \ + check_snmp.d \ check_mysql.d \ check_ntp_time.d \ check_dig.d \ @@ -152,6 +153,8 @@ check_procs_LDADD = $(BASEOBJS) check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) check_real_LDADD = $(NETLIBS) check_snmp_LDADD = $(BASEOBJS) +check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs` +check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags` check_smtp_LDADD = $(SSLOBJS) check_ssh_LDADD = $(NETLIBS) check_swap_SOURCES = check_swap.c check_swap.d/swap.c diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index c1d8e2dd..6c9ed959 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -32,222 +32,71 @@ const char *progname = "check_snmp"; const char *copyright = "1999-2024"; const char *email = "devel@monitoring-plugins.org"; -#include "common.h" -#include "runcmd.h" -#include "utils.h" -#include "utils_cmd.h" - -#define DEFAULT_COMMUNITY "public" -#define DEFAULT_PORT "161" -#define DEFAULT_MIBLIST "ALL" -#define DEFAULT_PROTOCOL "1" -#define DEFAULT_RETRIES 5 -#define DEFAULT_AUTH_PROTOCOL "MD5" -#define DEFAULT_PRIV_PROTOCOL "DES" -#define DEFAULT_DELIMITER "=" -#define DEFAULT_OUTPUT_DELIMITER " " -#define DEFAULT_BUFFER_SIZE 100 - -#define mark(a) ((a) != 0 ? "*" : "") - -#define CHECK_UNDEF 0 -#define CRIT_PRESENT 1 -#define CRIT_STRING 2 -#define CRIT_REGEX 4 -#define WARN_PRESENT 8 - -#define OID_COUNT_STEP 8 +#include "./common.h" +#include "./runcmd.h" +#include "./utils.h" +#include "../lib/states.h" +#include "../lib/utils_cmd.h" +#include "../lib/thresholds.h" +#include "../lib/utils_base.h" +#include "../lib/output.h" +#include "../lib/perfdata.h" + +#include +#include +#include +#include + +#include "check_snmp.d/config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../gl/regex.h" +#include + +const char DEFAULT_COMMUNITY[] = "public"; +const char DEFAULT_MIBLIST[] = "ALL"; +#define DEFAULT_AUTH_PROTOCOL "MD5" +#define DEFAULT_PRIV_PROTOCOL "DES" +#define DEFAULT_DELIMITER "=" +#define DEFAULT_BUFFER_SIZE 100 /* Longopts only arguments */ -#define L_CALCULATE_RATE CHAR_MAX + 1 -#define L_RATE_MULTIPLIER CHAR_MAX + 2 #define L_INVERT_SEARCH CHAR_MAX + 3 #define L_OFFSET CHAR_MAX + 4 #define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 +#define L_CONNECTION_PREFIX CHAR_MAX + 6 -/* Gobble to string - stop incrementing c when c[0] match one of the - * characters in s */ -#define GOBBLE_TOS(c, s) \ - while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \ - c++; \ - } -/* Given c, keep track of backslashes (bk) and double-quotes (dq) - * from c[0] */ -#define COUNT_SEQ(c, bk, dq) \ - switch (c[0]) { \ - case '\\': \ - if (bk) \ - bk--; \ - else \ - bk++; \ - break; \ - case '"': \ - if (!dq) { \ - dq++; \ - } else if (!bk) { \ - dq--; \ - } else { \ - bk--; \ - } \ - break; \ - } +typedef struct proces_arguments_wrapper { + int errorcode; + check_snmp_config config; +} process_arguments_wrapper; -static int process_arguments(int, char **); -static int validate_arguments(void); -static char *thisarg(char *str); -static char *nextarg(char *str); +static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); +char *trim_whitespaces_and_check_quoting(char *str); +char *get_next_argument(char *str); void print_usage(void); -static void print_help(void); -static char *multiply(char *str); - -#include "regex.h" -static char regex_expect[MAX_INPUT_BUFFER] = ""; -static regex_t preg; -static regmatch_t pmatch[10]; -static char errbuf[MAX_INPUT_BUFFER] = ""; -static char perfstr[MAX_INPUT_BUFFER] = "| "; -static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; -static int eflags = 0; -static int errcode, excode; - -static char *server_address = NULL; -static char *community = NULL; -static char **contextargs = NULL; -static char *context = NULL; -static char **authpriv = NULL; -static char *proto = NULL; -static char *seclevel = NULL; -static char *secname = NULL; -static char *authproto = NULL; -static char *privproto = NULL; -static char *authpasswd = NULL; -static char *privpasswd = NULL; -static int nulloid = STATE_UNKNOWN; -static char **oids = NULL; -static size_t oids_size = 0; -static char *label; -static char *units; -static char *port; -static char *snmpcmd; -static char string_value[MAX_INPUT_BUFFER] = ""; -static int invert_search = 0; -static char **labels = NULL; -static char **unitv = NULL; -static size_t nlabels = 0; -static size_t labels_size = OID_COUNT_STEP; -static size_t nunits = 0; -static size_t unitv_size = OID_COUNT_STEP; -static size_t numoids = 0; -static int numauthpriv = 0; -static int numcontext = 0; +void print_help(void); +char *multiply(char *str, double multiplier, char *fmt_str); + static int verbose = 0; -static bool usesnmpgetnext = false; -static char *warning_thresholds = NULL; -static char *critical_thresholds = NULL; -static thresholds **thlds; -static size_t thlds_size = OID_COUNT_STEP; -static double *response_value; -static size_t response_size = OID_COUNT_STEP; -static int retries = 0; -static int *eval_method; -static size_t eval_size = OID_COUNT_STEP; -static char *delimiter; -static char *output_delim; -static char *miblist = NULL; -static bool needmibs = false; -static int calculate_rate = 0; -static double offset = 0.0; -static int rate_multiplier = 1; -static state_data *previous_state; -static double *previous_value; -static size_t previous_size = OID_COUNT_STEP; -static int perf_labels = 1; -static char *ip_version = ""; -static double multiplier = 1.0; -static char *fmtstr = ""; -static bool fmtstr_set = false; -static char buffer[DEFAULT_BUFFER_SIZE]; -static bool ignore_mib_parsing_errors = false; - -static char *fix_snmp_range(char *th) { - double left; - double right; - char *colon; - char *ret; - - if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0') - return th; - - left = strtod(th, NULL); - right = strtod(colon + 1, NULL); - if (right >= left) - return th; - - if ((ret = malloc(strlen(th) + 2)) == NULL) - die(STATE_UNKNOWN, _("Cannot malloc")); - *colon = '\0'; - sprintf(ret, "@%s:%s", colon + 1, th); - free(th); - return ret; -} int main(int argc, char **argv) { - int len; - int total_oids; - size_t line; - unsigned int bk_count = 0; - unsigned int dq_count = 0; - int iresult = STATE_UNKNOWN; - int result = STATE_UNKNOWN; - int return_code = 0; - int external_error = 0; - char **command_line = NULL; - char *cl_hidden_auth = NULL; - char *oidname = NULL; - char *response = NULL; - char *mult_resp = NULL; - char *outbuff; - char *ptr = NULL; - char *show = NULL; - char *th_warn = NULL; - char *th_crit = NULL; - char type[8] = ""; - output chld_out; - output chld_err; - char *previous_string = NULL; - char *ap = NULL; - char *state_string = NULL; - size_t response_length; - size_t current_length; - size_t string_length; - char *temp_string = NULL; - char *quote_string = NULL; - time_t current_time; - double temp_double; - time_t duration; - char *conv = "12345678"; - int is_counter = 0; - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - labels = malloc(labels_size * sizeof(*labels)); - unitv = malloc(unitv_size * sizeof(*unitv)); - thlds = malloc(thlds_size * sizeof(*thlds)); - response_value = malloc(response_size * sizeof(*response_value)); - previous_value = malloc(previous_size * sizeof(*previous_value)); - eval_method = calloc(eval_size, sizeof(*eval_method)); - oids = calloc(oids_size, sizeof(char *)); - - label = strdup("SNMP"); - units = strdup(""); - port = strdup(DEFAULT_PORT); - outbuff = strdup(""); - delimiter = strdup(" = "); - output_delim = strdup(DEFAULT_OUTPUT_DELIMITER); timeout_interval = DEFAULT_SOCKET_TIMEOUT; - retries = DEFAULT_RETRIES; np_init((char *)progname, argc, argv); @@ -256,492 +105,416 @@ int main(int argc, char **argv) { np_set_args(argc, argv); + // Initialize net-snmp before touching the sessio we are going to use + init_snmp("check_snmp"); + + time_t current_time; time(¤t_time); - if (process_arguments(argc, argv) == ERROR) + process_arguments_wrapper paw_tmp = process_arguments(argc, argv); + if (paw_tmp.errorcode == ERROR) { usage4(_("Could not parse arguments")); - - if (calculate_rate) { - if (!strcmp(label, "SNMP")) - label = strdup("SNMP RATE"); - - size_t i = 0; - - previous_state = np_state_read(); - if (previous_state != NULL) { - /* Split colon separated values */ - previous_string = strdup((char *)previous_state->data); - while ((ap = strsep(&previous_string, ":")) != NULL) { - if (verbose > 2) - printf("State for %zd=%s\n", i, ap); - while (i >= previous_size) { - previous_size += OID_COUNT_STEP; - previous_value = realloc(previous_value, previous_size * sizeof(*previous_value)); - } - previous_value[i++] = strtod(ap, NULL); - } - } } - /* Populate the thresholds */ - th_warn = warning_thresholds; - th_crit = critical_thresholds; - for (size_t i = 0; i < numoids; i++) { - char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL; - char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL; - /* translate "2:1" to "@1:2" for backwards compatibility */ - w = w ? fix_snmp_range(w) : NULL; - c = c ? fix_snmp_range(c) : NULL; - - while (i >= thlds_size) { - thlds_size += OID_COUNT_STEP; - thlds = realloc(thlds, thlds_size * sizeof(*thlds)); - } + check_snmp_config config = paw_tmp.config; - /* Skip empty thresholds, while avoiding segfault */ - set_thresholds(&thlds[i], w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL); - if (w) { - th_warn = strchr(th_warn, ','); - if (th_warn) - th_warn++; - free(w); - } - if (c) { - th_crit = strchr(th_crit, ','); - if (th_crit) - th_crit++; - free(c); + if (config.ignore_mib_parsing_errors) { + char *opt_toggle_res = snmp_mib_toggle_options("e"); + if (opt_toggle_res != NULL) { + die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); } } - /* Create the command array to execute */ - if (usesnmpgetnext) { - snmpcmd = strdup(PATH_TO_SNMPGETNEXT); + struct snmp_pdu *pdu = NULL; + if (config.use_getnext) { + pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); } else { - snmpcmd = strdup(PATH_TO_SNMPGET); + pdu = snmp_pdu_create(SNMP_MSG_GET); } - /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ - - unsigned index = 0; - command_line = calloc(11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof(char *)); - - command_line[index++] = snmpcmd; - command_line[index++] = strdup("-Le"); - command_line[index++] = strdup("-t"); - xasprintf(&command_line[index++], "%d", timeout_interval); - command_line[index++] = strdup("-r"); - xasprintf(&command_line[index++], "%d", retries); - command_line[index++] = strdup("-m"); - command_line[index++] = strdup(miblist); - command_line[index++] = "-v"; - command_line[index++] = strdup(proto); - - xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", - proto); - - if (ignore_mib_parsing_errors) { - command_line[index++] = "-Pe"; - xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth); - } + for (size_t i = 0; i < config.num_of_test_units; i++) { + assert(config.test_units[i].oid != NULL); + if (verbose > 0) { + printf("OID %zu to parse: %s\n", i, config.test_units[i].oid); + } - for (int i = 0; i < numcontext; i++) { - command_line[index++] = contextargs[i]; + oid tmp_OID[MAX_OID_LEN]; + size_t tmp_OID_len = MAX_OID_LEN; + if (snmp_parse_oid(config.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { + // success + snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); + } else { + // failed + snmp_perror("Parsing failure"); + die(STATE_UNKNOWN, "Failed to parse OID\n"); + } } - for (int i = 0; i < numauthpriv; i++) { - command_line[index++] = authpriv[i]; + /* Set signal handling and alarm */ + if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { + usage4(_("Cannot catch SIGALRM")); } - xasprintf(&command_line[index++], "%s:%s", server_address, port); - - xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", cl_hidden_auth, server_address, port); + const int timeout_safety_tolerance = 5; + alarm(timeout_interval * config.snmp_session.retries + timeout_safety_tolerance); - for (size_t i = 0; i < numoids; i++) { - command_line[index++] = oids[i]; - xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); + struct snmp_session *active_session = snmp_open(&config.snmp_session); + if (active_session == NULL) { + snmp_sess_perror("Failed to open session", &config.snmp_session); + die(STATE_UNKNOWN, "Failed to open SNMP session\n"); } - command_line[index++] = NULL; + struct snmp_pdu *response = NULL; + int snmp_query_status = snmp_synch_response(active_session, pdu, &response); - if (verbose) { - printf("%s\n", cl_hidden_auth); + if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { + snmp_sess_perror("Failed to query", active_session); + // FAILED somehow + // TODO do some error analysis here + die(STATE_UNKNOWN, "SNMP query failed\n"); } - /* Set signal handling and alarm */ - if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { - usage4(_("Cannot catch SIGALRM")); - } - alarm(timeout_interval * retries + 5); - - /* Run the command */ - return_code = cmd_run_array(command_line, &chld_out, &chld_err, 0); + snmp_close(active_session); /* disable alarm again */ alarm(0); - /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs, - only return state unknown if return code is non zero or there is no stdout. - Do this way so that if there is stderr, will get added to output, which helps problem diagnosis - */ - if (return_code != 0) - external_error = 1; - if (chld_out.lines == 0) - external_error = 1; - if (external_error) { - if (chld_err.lines > 0) { - printf(_("External command error: %s\n"), chld_err.line[0]); - for (size_t i = 1; i < chld_err.lines; i++) { - printf("%s\n", chld_err.line[i]); - } - } else { - printf(_("External command error with no output (return code: %d)\n"), return_code); + mp_check overall = mp_check_init(); + + mp_subcheck sc_succesfull_query = mp_subcheck_init(); + xasprintf(&sc_succesfull_query.output, "SNMP query was succesful"); + sc_succesfull_query = mp_set_subcheck_state(sc_succesfull_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_succesfull_query); + + // We got the the query results, now process them + size_t loop_index = 0; + for (netsnmp_variable_list *vars = response->variables; vars; + vars = vars->next_variable, loop_index++) { + mp_subcheck sc_oid_test = mp_subcheck_init(); + + if (verbose > 0) { + printf("loop_index: %zu\n", loop_index); } - exit(STATE_UNKNOWN); - } - if (verbose) { - for (size_t i = 0; i < chld_out.lines; i++) { - printf("%s\n", chld_out.line[i]); + if ((config.test_units[loop_index].label != NULL) && + (strcmp(config.test_units[loop_index].label, "") != 0)) { + xasprintf(&sc_oid_test.output, "%s - ", config.test_units[loop_index].label); + } else { + sc_oid_test.output = strdup(""); } - } - line = 0; - total_oids = 0; - for (size_t i = 0; line < chld_out.lines && i < numoids; line++, i++, total_oids++) { - if (calculate_rate) - conv = "%.10g"; - else - conv = "%.0f"; - - ptr = chld_out.line[line]; - oidname = strpcpy(oidname, ptr, delimiter); - response = strstr(ptr, delimiter); - if (response == NULL) - break; + char oid_string[(MAX_OID_LEN * 2) + 1]; + memset(oid_string, 0, (MAX_OID_LEN * 2) + 1); + + int oid_string_result = + snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length); + if (oid_string_result <= 0) { + // TODO error here + } if (verbose > 2) { - printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i + 1, line + 1, oidname, response); + printf("Processing oid %s\n", oid_string); } - /* Clean up type array - Sol10 does not necessarily zero it out */ - bzero(type, sizeof(type)); - - is_counter = 0; - /* We strip out the datatype indicator for PHBs */ - if (strstr(response, "Gauge: ")) { - show = multiply(strstr(response, "Gauge: ") + 7); - } else if (strstr(response, "Gauge32: ")) { - show = multiply(strstr(response, "Gauge32: ") + 9); - } else if (strstr(response, "Counter32: ")) { - show = strstr(response, "Counter32: ") + 11; - is_counter = 1; - if (!calculate_rate) - strcpy(type, "c"); - } else if (strstr(response, "Counter64: ")) { - show = strstr(response, "Counter64: ") + 11; - is_counter = 1; - if (!calculate_rate) - strcpy(type, "c"); - } else if (strstr(response, "INTEGER: ")) { - show = multiply(strstr(response, "INTEGER: ") + 9); - - if (fmtstr_set) { - conv = fmtstr; + mp_perfdata_value pd_result_val = {0}; + xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); + sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); + + switch (vars->type) { + case ASN_OCTET_STR: { + if (verbose) { + printf("Debug: Got a string\n"); } - } else if (strstr(response, "OID: ")) { - show = strstr(response, "OID: ") + 5; - } else if (strstr(response, "STRING: ")) { - show = strstr(response, "STRING: ") + 8; - conv = "%.10g"; - - /* Get the rest of the string on multi-line strings */ - ptr = show; - COUNT_SEQ(ptr, bk_count, dq_count) - while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') { - ptr++; - GOBBLE_TOS(ptr, "\n\"\\") - COUNT_SEQ(ptr, bk_count, dq_count) + char *tmp = (char *)vars->val.string; + xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + + if (strlen(tmp) == 0) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result); } - if (dq_count) { /* unfinished line */ - /* copy show verbatim first */ - if (!mult_resp) - mult_resp = strdup(""); - xasprintf(&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show); - /* then strip out unmatched double-quote from single-line output */ - if (show[0] == '"') - show++; - - /* Keep reading until we match end of double-quoted string */ - for (line++; line < chld_out.lines; line++) { - ptr = chld_out.line[line]; - xasprintf(&mult_resp, "%s%s\n", mult_resp, ptr); - - COUNT_SEQ(ptr, bk_count, dq_count) - while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') { - ptr++; - GOBBLE_TOS(ptr, "\n\"\\") - COUNT_SEQ(ptr, bk_count, dq_count) - } - /* Break for loop before next line increment when done */ - if (!dq_count) - break; + // String matching test + if ((config.test_units[loop_index].eval_mthd.crit_string)) { + if (strcmp(tmp, config.string_cmp_value)) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (config.invert_search) ? STATE_CRITICAL : STATE_OK); + } else { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); + } + } else if (config.test_units[loop_index].eval_mthd.crit_regex) { + const int nmatch = config.regex_cmp_value.re_nsub + 1; + regmatch_t pmatch[nmatch]; + memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); + + int excode = regexec(&config.regex_cmp_value, tmp, nmatch, pmatch, 0); + if (excode == 0) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); + } else if (excode != REG_NOMATCH) { + char errbuf[MAX_INPUT_BUFFER] = ""; + regerror(excode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + printf(_("Execute Error: %s\n"), errbuf); + exit(STATE_CRITICAL); + } else { // REG_NOMATCH + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, config.invert_search ? STATE_CRITICAL : STATE_OK); } } - } else if (strstr(response, "Timeticks: ")) { - show = strstr(response, "Timeticks: "); - } else - show = response + 3; - - iresult = STATE_DEPENDENT; + mp_add_subcheck_to_check(&overall, sc_oid_test); + } + continue; + case ASN_OPAQUE: + if (verbose) { + printf("Debug: Got OPAQUE\n"); + } + break; + case ASN_COUNTER64: { + if (verbose) { + printf("Debug: Got counter64\n"); + } + struct counter64 tmp = *(vars->val.counter64); + uint64_t counter = (tmp.high << 32) + tmp.low; + counter *= config.multiplier; + counter += config.offset; + pd_result_val = mp_create_pd_value(counter); + } break; + /* Numerical values */ + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + if (verbose) { + printf("Debug: Got a Integer like\n"); + } + unsigned long tmp = *(vars->val.integer); + tmp *= config.multiplier; - /* Process this block for numeric comparisons */ - /* Make some special values,like Timeticks numeric only if a threshold is defined */ - if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { - if (verbose > 2) { - print_thresholds(" thresholds", thlds[i]); + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + break; + } + case ASN_INTEGER: { + if (verbose) { + printf("Debug: Got a Integer\n"); } - ptr = strpbrk(show, "-0123456789"); - if (ptr == NULL) { - if (nulloid == 3) - die(STATE_UNKNOWN, _("No valid data returned (%s)\n"), show); - else if (nulloid == 0) - die(STATE_OK, _("No valid data returned (%s)\n"), show); - else if (nulloid == 1) - die(STATE_WARNING, _("No valid data returned (%s)\n"), show); - else if (nulloid == 2) - die(STATE_CRITICAL, _("No valid data returned (%s)\n"), show); + unsigned long tmp = *(vars->val.integer); + tmp *= config.multiplier; + + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + } break; + case ASN_FLOAT: { + if (verbose) { + printf("Debug: Got a float\n"); } - while (i >= response_size) { - response_size += OID_COUNT_STEP; - response_value = realloc(response_value, response_size * sizeof(*response_value)); + float tmp = *(vars->val.floatVal); + tmp *= config.multiplier; + + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + break; + } + case ASN_DOUBLE: { + if (verbose) { + printf("Debug: Got a double\n"); } - response_value[i] = strtod(ptr, NULL) + offset; - - if (calculate_rate) { - if (previous_state != NULL) { - duration = current_time - previous_state->time; - if (duration <= 0) - die(STATE_UNKNOWN, _("Time duration between plugin calls is invalid")); - temp_double = response_value[i] - previous_value[i]; - /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */ - if (is_counter) { - if (temp_double < (double)0.0) - temp_double += (double)4294967296.0; /* 2^32 */ - if (temp_double < (double)0.0) - temp_double += (double)18446744069414584320.0; /* 2^64-2^32 */ - ; - } - /* Convert to per second, then use multiplier */ - temp_double = temp_double / duration * rate_multiplier; - iresult = get_status(temp_double, thlds[i]); - xasprintf(&show, conv, temp_double); - } - } else { - iresult = get_status(response_value[i], thlds[i]); - xasprintf(&show, conv, response_value[i]); + double tmp = *(vars->val.doubleVal); + tmp *= config.multiplier; + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + break; + } + case ASN_IPADDRESS: + if (verbose) { + printf("Debug: Got an IP address\n"); } + continue; + default: + if (verbose) { + printf("Debug: Got a unmatched result type: %hhu\n", vars->type); + } + // TODO: Error here? + continue; } - /* Process this block for string matching */ - else if (eval_size > i && eval_method[i] & CRIT_STRING) { - if (strcmp(show, string_value)) - iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK; - else - iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL; + // some kind of numerical value + mp_perfdata pd_num_val = { + .value = pd_result_val, + }; + + if (!config.use_perf_data_labels_from_input) { + // Use oid for perdata label + pd_num_val.label = strdup(oid_string); + // TODO strdup error checking + } else if (config.test_units[loop_index].label != NULL || + strcmp(config.test_units[loop_index].label, "") != 0) { + pd_num_val.label = config.test_units[loop_index].label; } - /* Process this block for regex matching */ - else if (eval_size > i && eval_method[i] & CRIT_REGEX) { - excode = regexec(&preg, response, 10, pmatch, eflags); - if (excode == 0) { - iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL; - } else if (excode != REG_NOMATCH) { - regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER); - printf(_("Execute Error: %s\n"), errbuf); - exit(STATE_CRITICAL); - } else { - iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK; - } + if (config.test_units[loop_index].unit_value != NULL && + strcmp(config.test_units[loop_index].unit_value, "") != 0) { + pd_num_val.uom = config.test_units[loop_index].unit_value; } - /* Process this block for existence-nonexistence checks */ - /* TV: Should this be outside of this else block? */ - else { - if (eval_size > i && eval_method[i] & CRIT_PRESENT) - iresult = STATE_CRITICAL; - else if (eval_size > i && eval_method[i] & WARN_PRESENT) - iresult = STATE_WARNING; - else if (response && iresult == STATE_DEPENDENT) - iresult = STATE_OK; + xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, + pd_value_to_string(pd_result_val)); + + if (config.test_units[loop_index].unit_value != NULL && + strcmp(config.test_units[loop_index].unit_value, "") != 0) { + xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, + config.test_units[loop_index].unit_value); } - /* Result is the worst outcome of all the OIDs tested */ - result = max_state(result, iresult); - - /* Prepend a label for this OID if there is one */ - if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) - xasprintf(&outbuff, "%s%s%s %s%s%s", outbuff, (i == 0) ? " " : output_delim, labels[i], mark(iresult), show, mark(iresult)); - else - xasprintf(&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim, mark(iresult), show, mark(iresult)); - - /* Append a unit string for this OID if there is one */ - if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) - xasprintf(&outbuff, "%s %s", outbuff, unitv[i]); - - /* Write perfdata with whatever can be parsed by strtod, if possible */ - ptr = NULL; - strtod(show, &ptr); - if (ptr > show) { - if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) - temp_string = labels[i]; - else - temp_string = oidname; - if (strpbrk(temp_string, " ='\"") == NULL) { - strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); - } else { - if (strpbrk(temp_string, "'") == NULL) { - quote_string = "'"; - } else { - quote_string = "\""; - } - strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); - strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); - strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); + if (config.thresholds.warning_is_set || config.thresholds.critical_is_set) { + pd_num_val = mp_pd_set_thresholds(pd_num_val, config.thresholds); + mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); + + if (tmp_state == STATE_WARNING) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); + xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", + sc_oid_test.output); + } else if (tmp_state == STATE_CRITICAL) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); + xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", + sc_oid_test.output); } - strncat(perfstr, "=", sizeof(perfstr) - strlen(perfstr) - 1); - len = sizeof(perfstr) - strlen(perfstr) - 1; - strncat(perfstr, show, len > ptr - show ? ptr - show : len); + } - if (strcmp(type, "") != 0) { - strncat(perfstr, type, sizeof(perfstr) - strlen(perfstr) - 1); - } + mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); - if (warning_thresholds) { - strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); - if (thlds[i]->warning && thlds[i]->warning->text) - strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr) - strlen(perfstr) - 1); - } + mp_add_subcheck_to_check(&overall, sc_oid_test); + } - if (critical_thresholds) { - if (!warning_thresholds) - strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); - strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); - if (thlds[i]->critical && thlds[i]->critical->text) - strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr) - strlen(perfstr) - 1); - } + mp_exit(overall); +} - strncat(perfstr, " ", sizeof(perfstr) - strlen(perfstr) - 1); - } +/* process command-line arguments */ +static process_arguments_wrapper process_arguments(int argc, char **argv) { + static struct option longopts[] = { + STD_LONG_OPTS, + {"community", required_argument, 0, 'C'}, + {"oid", required_argument, 0, 'o'}, + {"object", required_argument, 0, 'o'}, + {"delimiter", required_argument, 0, 'd'}, + {"nulloid", required_argument, 0, 'z'}, + {"output-delimiter", required_argument, 0, 'D'}, + {"string", required_argument, 0, 's'}, + {"timeout", required_argument, 0, 't'}, + {"regex", required_argument, 0, 'r'}, + {"ereg", required_argument, 0, 'r'}, + {"eregi", required_argument, 0, 'R'}, + {"label", required_argument, 0, 'l'}, + {"units", required_argument, 0, 'u'}, + {"port", required_argument, 0, 'p'}, + {"retries", required_argument, 0, 'e'}, + {"miblist", required_argument, 0, 'm'}, + {"protocol", required_argument, 0, 'P'}, + {"context", required_argument, 0, 'N'}, + {"seclevel", required_argument, 0, 'L'}, + {"secname", required_argument, 0, 'U'}, + {"authproto", required_argument, 0, 'a'}, + {"privproto", required_argument, 0, 'x'}, + {"authpasswd", required_argument, 0, 'A'}, + {"privpasswd", required_argument, 0, 'X'}, + {"next", no_argument, 0, 'n'}, + {"offset", required_argument, 0, L_OFFSET}, + {"invert-search", no_argument, 0, L_INVERT_SEARCH}, + {"perf-oids", no_argument, 0, 'O'}, + {"ipv4", no_argument, 0, '4'}, + {"ipv6", no_argument, 0, '6'}, + {"multiplier", required_argument, 0, 'M'}, + {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, + {"connection-prefix", required_argument, 0, L_CONNECTION_PREFIX}, + {0, 0, 0, 0}}; + + if (argc < 2) { + process_arguments_wrapper result = { + .errorcode = ERROR, + }; + return result; } - /* Save state data, as all data collected now */ - if (calculate_rate) { - string_length = 1024; - state_string = malloc(string_length); - if (state_string == NULL) - die(STATE_UNKNOWN, _("Cannot malloc")); - - current_length = 0; - for (int i = 0; i < total_oids; i++) { - xasprintf(&temp_string, "%.0f", response_value[i]); - if (temp_string == NULL) - die(STATE_UNKNOWN, _("Cannot asprintf()")); - response_length = strlen(temp_string); - if (current_length + response_length > string_length) { - string_length = current_length + 1024; - state_string = realloc(state_string, string_length); - if (state_string == NULL) - die(STATE_UNKNOWN, _("Cannot realloc()")); + // Count number of OIDs here first + int option = 0; + size_t oid_counter = 0; + while (true) { + int option_char = getopt_long( + argc, argv, + "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); + + if (option_char == -1 || option_char == EOF) { + break; + } + + switch (option_char) { + case 'o': { + // we are going to parse this again, so we work on a copy of that string + char *tmp_oids = strdup(optarg); + if (tmp_oids == NULL) { + die(STATE_UNKNOWN, "strdup failed"); } - strcpy(&state_string[current_length], temp_string); - current_length = current_length + response_length; - state_string[current_length] = ':'; - current_length++; - free(temp_string); + + for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL; + ptr = strtok(NULL, ", "), oid_counter++) { + } + break; } - state_string[--current_length] = '\0'; - if (verbose > 2) - printf("State string=%s\n", state_string); - - /* This is not strictly the same as time now, but any subtle variations will cancel out */ - np_state_write_string(current_time, state_string); - if (previous_state == NULL) { - /* Or should this be highest state? */ - die(STATE_OK, _("No previous data to calculate rate - assume okay")); + case '?': /* usage */ + usage5(); + // fallthrough + case 'h': /* help */ + print_help(); + exit(STATE_UNKNOWN); + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + + default: + continue; } } - printf("%s %s -%s %s\n", label, state_text(result), outbuff, perfstr); - if (mult_resp) - printf("%s", mult_resp); + /* Check whether at least one OID was given */ + if (oid_counter == 0) { + die(STATE_UNKNOWN, _("No OIDs specified\n")); + } - return result; -} + // Allocate space for test units + check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit)); + if (tmp == NULL) { + die(STATE_UNKNOWN, "Failed to calloc"); + } -/* process command-line arguments */ -int process_arguments(int argc, char **argv) { - static struct option longopts[] = {STD_LONG_OPTS, - {"community", required_argument, 0, 'C'}, - {"oid", required_argument, 0, 'o'}, - {"object", required_argument, 0, 'o'}, - {"delimiter", required_argument, 0, 'd'}, - {"nulloid", required_argument, 0, 'z'}, - {"output-delimiter", required_argument, 0, 'D'}, - {"string", required_argument, 0, 's'}, - {"timeout", required_argument, 0, 't'}, - {"regex", required_argument, 0, 'r'}, - {"ereg", required_argument, 0, 'r'}, - {"eregi", required_argument, 0, 'R'}, - {"label", required_argument, 0, 'l'}, - {"units", required_argument, 0, 'u'}, - {"port", required_argument, 0, 'p'}, - {"retries", required_argument, 0, 'e'}, - {"miblist", required_argument, 0, 'm'}, - {"protocol", required_argument, 0, 'P'}, - {"context", required_argument, 0, 'N'}, - {"seclevel", required_argument, 0, 'L'}, - {"secname", required_argument, 0, 'U'}, - {"authproto", required_argument, 0, 'a'}, - {"privproto", required_argument, 0, 'x'}, - {"authpasswd", required_argument, 0, 'A'}, - {"privpasswd", required_argument, 0, 'X'}, - {"next", no_argument, 0, 'n'}, - {"rate", no_argument, 0, L_CALCULATE_RATE}, - {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER}, - {"offset", required_argument, 0, L_OFFSET}, - {"invert-search", no_argument, 0, L_INVERT_SEARCH}, - {"perf-oids", no_argument, 0, 'O'}, - {"ipv4", no_argument, 0, '4'}, - {"ipv6", no_argument, 0, '6'}, - {"multiplier", required_argument, 0, 'M'}, - {"fmtstr", required_argument, 0, 'f'}, - {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, - {0, 0, 0, 0}}; - - if (argc < 2) - return ERROR; - - /* reverse compatibility for very old non-POSIX usage forms */ - for (int c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) - strcpy(argv[c], "-t"); - if (strcmp("-wv", argv[c]) == 0) - strcpy(argv[c], "-w"); - if (strcmp("-cv", argv[c]) == 0) - strcpy(argv[c], "-c"); + for (size_t i = 0; i < oid_counter; i++) { + tmp[i] = check_snmp_test_unit_init(); } - size_t j = 0; - size_t jj = 0; + check_snmp_config config = check_snmp_config_init(); + config.test_units = tmp; + config.num_of_test_units = oid_counter; + + option = 0; + optind = 1; // Reset argument scanner + size_t tmp_oid_counter = 0; + size_t eval_counter = 0; + size_t unitv_counter = 0; + size_t labels_counter = 0; + unsigned char *authpasswd = NULL; + unsigned char *privpasswd = NULL; + int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; + char *port = NULL; + char *miblist = NULL; + char *connection_prefix = NULL; + // TODO error checking while (true) { - int option = 0; - int option_char = getopt_long(argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); + int option_char = getopt_long( + argc, argv, + "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); - if (option_char == -1 || option_char == EOF) + if (option_char == -1 || option_char == EOF) { break; + } switch (option_char) { case '?': /* usage */ @@ -758,65 +531,152 @@ int process_arguments(int argc, char **argv) { /* Connection info */ case 'C': /* group or community */ - community = optarg; + config.snmp_session.community = (unsigned char *)optarg; + config.snmp_session.community_len = strlen(optarg); break; case 'H': /* Host or server */ - server_address = optarg; + config.snmp_session.peername = optarg; break; - case 'p': /* TCP port number */ + case 'p': /*port number */ + // Add port to "peername" below to not rely on argument order port = optarg; break; case 'm': /* List of MIBS */ miblist = optarg; break; - case 'n': /* usesnmpgetnext */ - usesnmpgetnext = true; + case 'n': /* use_getnext instead of get */ + config.use_getnext = true; break; case 'P': /* SNMP protocol version */ - proto = optarg; + if (strcasecmp("1", optarg) == 0) { + config.snmp_session.version = SNMP_VERSION_1; + } else if (strcasecmp("2c", optarg) == 0) { + config.snmp_session.version = SNMP_VERSION_2c; + } else if (strcasecmp("3", optarg) == 0) { + config.snmp_session.version = SNMP_VERSION_3; + } else { + die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); + } break; - case 'N': /* SNMPv3 context */ - context = optarg; + case 'N': /* SNMPv3 context name */ + config.snmp_session.contextName = optarg; + config.snmp_session.contextNameLen = strlen(optarg); break; case 'L': /* security level */ - seclevel = optarg; + if (strcasecmp("noAuthNoPriv", optarg) == 0) { + config.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + } else if (strcasecmp("authNoPriv", optarg) == 0) { + config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + } else if (strcasecmp("authPriv", optarg) == 0) { + config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; + } else { + die(STATE_UNKNOWN, "invalid security level: %s", optarg); + } break; case 'U': /* security username */ - secname = optarg; + config.snmp_session.securityName = optarg; + config.snmp_session.securityNameLen = strlen(optarg); break; case 'a': /* auth protocol */ - authproto = optarg; + // SNMPv3: SHA or MD5 + // TODO Test for availability of individual protocols + if (strcasecmp("MD5", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol); + } else if (strcasecmp("SHA", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol); + } else if (strcasecmp("SHA224", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); + } else if (strcasecmp("SHA256", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); + } else if (strcasecmp("SHA384", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol); + } else if (strcasecmp("SHA512", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol); + } else { + die(STATE_UNKNOWN, "Unknown authentication protocol"); + } break; case 'x': /* priv protocol */ - privproto = optarg; + if (strcasecmp("DES", optarg) == 0) { + config.snmp_session.securityAuthProto = usmDESPrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); + } else if (strcasecmp("AES", optarg) == 0) { + config.snmp_session.securityAuthProto = usmAESPrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); + // } else if (strcasecmp("AES128", optarg)) { + // config.snmp_session.securityAuthProto = usmAES128PrivProtocol; + // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol) + // / OID_LENGTH(oid); + } else if (strcasecmp("AES192", optarg) == 0) { + config.snmp_session.securityAuthProto = usmAES192PrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES192PrivProtocol); + } else if (strcasecmp("AES256", optarg) == 0) { + config.snmp_session.securityAuthProto = usmAES256PrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES256PrivProtocol); + // } else if (strcasecmp("AES192Cisco", optarg)) { + // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol; + // config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if + // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto = + // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if + // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto + // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if + // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto + // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid); + } else { + die(STATE_UNKNOWN, "Unknow privacy protocol"); + } break; case 'A': /* auth passwd */ - authpasswd = optarg; + authpasswd = (unsigned char *)optarg; break; case 'X': /* priv passwd */ - privpasswd = optarg; + privpasswd = (unsigned char *)optarg; + break; + case 'e': + case 'E': + if (!is_integer(optarg)) { + usage2(_("Retries interval must be a positive integer"), optarg); + } else { + config.snmp_session.retries = atoi(optarg); + } break; case 't': /* timeout period */ - if (!is_integer(optarg)) + if (!is_integer(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); - else + } else { timeout_interval = atoi(optarg); + } break; /* Test parameters */ case 'c': /* critical threshold */ - critical_thresholds = optarg; - break; + { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", optarg); + } + config.thresholds.critical = tmp.range; + config.thresholds.critical_is_set = true; + } break; case 'w': /* warning threshold */ - warning_thresholds = optarg; - break; - case 'e': /* PRELIMINARY - may change */ - case 'E': /* PRELIMINARY - may change */ - if (!is_integer(optarg)) - usage2(_("Retries interval must be a positive integer"), optarg); - else - retries = atoi(optarg); - break; + { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse warning threshold range: %s", optarg); + } + config.thresholds.warning = tmp.range; + config.thresholds.warning_is_set = true; + } break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { /* @@ -824,306 +684,246 @@ int process_arguments(int argc, char **argv) { * so we have a mib variable, rather than just an SNMP OID, * so we have to actually read the mib files */ - needmibs = true; - } - for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) { - while (j >= oids_size) { - oids_size += OID_COUNT_STEP; - oids = realloc(oids, oids_size * sizeof(*oids)); - } - oids[j] = strdup(ptr); + config.need_mibs = true; } - numoids = j; - if (option_char == 'E' || option_char == 'e') { - jj++; - while (j + 1 >= eval_size) { - eval_size += OID_COUNT_STEP; - eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); - memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); - } - if (option_char == 'E') - eval_method[j + 1] |= WARN_PRESENT; - else if (option_char == 'e') - eval_method[j + 1] |= CRIT_PRESENT; + + for (char *ptr = strtok(optarg, ", "); ptr != NULL; + ptr = strtok(NULL, ", "), tmp_oid_counter++) { + config.test_units[tmp_oid_counter].oid = strdup(ptr); } break; case 'z': /* Null OID Return Check */ - if (!is_integer(optarg)) + if (!is_integer(optarg)) { usage2(_("Exit status must be a positive integer"), optarg); - else - nulloid = atoi(optarg); + } else { + config.nulloid_result = atoi(optarg); + } break; case 's': /* string or substring */ - strncpy(string_value, optarg, sizeof(string_value) - 1); - string_value[sizeof(string_value) - 1] = 0; - while (jj >= eval_size) { - eval_size += OID_COUNT_STEP; - eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); - memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); - } - eval_method[jj++] = CRIT_STRING; + strncpy(config.string_cmp_value, optarg, sizeof(config.string_cmp_value) - 1); + config.string_cmp_value[sizeof(config.string_cmp_value) - 1] = 0; + config.test_units[eval_counter++].eval_mthd.crit_string = true; break; case 'R': /* regex */ cflags = REG_ICASE; // fall through case 'r': /* regex */ + { + char regex_expect[MAX_INPUT_BUFFER] = ""; cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); regex_expect[sizeof(regex_expect) - 1] = 0; - errcode = regcomp(&preg, regex_expect, cflags); + int errcode = regcomp(&config.regex_cmp_value, regex_expect, cflags); if (errcode != 0) { - regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); - printf(_("Could Not Compile Regular Expression")); - return ERROR; + char errbuf[MAX_INPUT_BUFFER] = ""; + regerror(errcode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + printf("Could Not Compile Regular Expression: %s", errbuf); + process_arguments_wrapper result = { + .errorcode = ERROR, + }; + return result; } - while (jj >= eval_size) { - eval_size += OID_COUNT_STEP; - eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); - memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); - } - eval_method[jj++] = CRIT_REGEX; - break; - - /* Format */ - case 'd': /* delimiter */ - delimiter = strscpy(delimiter, optarg); - break; - case 'D': /* output-delimiter */ - output_delim = strscpy(output_delim, optarg); - break; + config.test_units[eval_counter++].eval_mthd.crit_regex = true; + } break; case 'l': /* label */ - nlabels++; - if (nlabels > labels_size) { - labels_size += 8; - labels = realloc(labels, labels_size * sizeof(*labels)); - if (labels == NULL) - die(STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels); + { + if (labels_counter >= config.num_of_test_units) { + break; } - labels[nlabels - 1] = optarg; - char *ptr = thisarg(optarg); - labels[nlabels - 1] = ptr; - if (ptr[0] == '\'') - labels[nlabels - 1] = ptr + 1; - while (ptr && (ptr = nextarg(ptr))) { - nlabels++; - if (nlabels > labels_size) { - labels_size += 8; - labels = realloc(labels, labels_size * sizeof(*labels)); - if (labels == NULL) - die(STATE_UNKNOWN, _("Could not reallocate labels\n")); + char *ptr = trim_whitespaces_and_check_quoting(optarg); + if (ptr[0] == '\'') { + config.test_units[labels_counter].label = ptr + 1; + } else { + config.test_units[labels_counter].label = ptr; + } + + while (ptr && (ptr = get_next_argument(ptr))) { + labels_counter++; + ptr = trim_whitespaces_and_check_quoting(ptr); + if (ptr[0] == '\'') { + config.test_units[labels_counter].label = ptr + 1; + } else { + config.test_units[labels_counter].label = ptr; } - ptr = thisarg(ptr); - if (ptr[0] == '\'') - labels[nlabels - 1] = ptr + 1; - else - labels[nlabels - 1] = ptr; } - break; + labels_counter++; + } break; case 'u': /* units */ - units = optarg; - nunits++; - if (nunits > unitv_size) { - unitv_size += 8; - unitv = realloc(unitv, unitv_size * sizeof(*unitv)); - if (unitv == NULL) - die(STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits); + { + if (unitv_counter >= config.num_of_test_units) { + break; } - unitv[nunits - 1] = optarg; - ptr = thisarg(optarg); - unitv[nunits - 1] = ptr; - if (ptr[0] == '\'') - unitv[nunits - 1] = ptr + 1; - while (ptr && (ptr = nextarg(ptr))) { - if (nunits > unitv_size) { - unitv_size += 8; - unitv = realloc(unitv, unitv_size * sizeof(*unitv)); - if (units == NULL) - die(STATE_UNKNOWN, _("Could not realloc() units\n")); + char *ptr = trim_whitespaces_and_check_quoting(optarg); + if (ptr[0] == '\'') { + config.test_units[unitv_counter].unit_value = ptr + 1; + } else { + config.test_units[unitv_counter].unit_value = ptr; + } + while (ptr && (ptr = get_next_argument(ptr))) { + unitv_counter++; + ptr = trim_whitespaces_and_check_quoting(ptr); + if (ptr[0] == '\'') { + config.test_units[unitv_counter].unit_value = ptr + 1; + } else { + config.test_units[unitv_counter].unit_value = ptr; } - nunits++; - ptr = thisarg(ptr); - if (ptr[0] == '\'') - unitv[nunits - 1] = ptr + 1; - else - unitv[nunits - 1] = ptr; } - break; - case L_CALCULATE_RATE: - if (calculate_rate == 0) - np_enable_state(NULL, 1); - calculate_rate = 1; - break; - case L_RATE_MULTIPLIER: - if (!is_integer(optarg) || ((rate_multiplier = atoi(optarg)) <= 0)) - usage2(_("Rate multiplier must be a positive integer"), optarg); - break; + unitv_counter++; + } break; case L_OFFSET: - offset = strtod(optarg, NULL); + config.offset = strtod(optarg, NULL); break; case L_INVERT_SEARCH: - invert_search = 1; + config.invert_search = false; break; case 'O': - perf_labels = 0; + config.use_perf_data_labels_from_input = true; break; case '4': + // The default, do something here to be exclusive to -6 instead of doing nothing? + connection_prefix = "udp"; break; case '6': - xasprintf(&ip_version, "udp6:"); - if (verbose > 2) - printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); + connection_prefix = "udp6"; + break; + case L_CONNECTION_PREFIX: + connection_prefix = optarg; break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { - multiplier = strtod(optarg, NULL); - } - break; - case 'f': - if (multiplier != 1.0) { - fmtstr = optarg; - fmtstr_set = true; + config.multiplier = strtod(optarg, NULL); } break; case L_IGNORE_MIB_PARSING_ERRORS: - ignore_mib_parsing_errors = true; + config.ignore_mib_parsing_errors = true; } } - if (server_address == NULL) - server_address = argv[optind]; - - if (community == NULL) - community = strdup(DEFAULT_COMMUNITY); - - return validate_arguments(); -} - -/****************************************************************************** - -@@- - -validate_arguments - -&PROTO_validate_arguments; - -Checks to see if the default miblist needs to be loaded. Also verifies -the authentication and authorization combinations based on protocol version -selected. - - - - --@@ -******************************************************************************/ + if (config.snmp_session.peername == NULL) { + config.snmp_session.peername = argv[optind]; + } -static int validate_arguments() { - /* check whether to load locally installed MIBS (CPU/disk intensive) */ - if (miblist == NULL) { - if (needmibs) { - miblist = strdup(DEFAULT_MIBLIST); + // Build true peername here if necessary + if (connection_prefix != NULL) { + // We got something in the connection prefix + if (strcasecmp(connection_prefix, "udp") == 0) { + // The default, do nothing + } else if (strcasecmp(connection_prefix, "tcp") == 0) { + // use tcp/ipv4 + xasprintf(&config.snmp_session.peername, "tcp:%s", config.snmp_session.peername); + } else if (strcasecmp(connection_prefix, "tcp6") == 0 || + strcasecmp(connection_prefix, "tcpv6") == 0 || + strcasecmp(connection_prefix, "tcpipv6") == 0 || + strcasecmp(connection_prefix, "udp6") == 0 || + strcasecmp(connection_prefix, "udpipv6") == 0 || + strcasecmp(connection_prefix, "udpv6") == 0) { + // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it + // works anyway therefore do nothing here + xasprintf(&config.snmp_session.peername, "%s:%s", connection_prefix, + config.snmp_session.peername); + } else if (strcmp(connection_prefix, "tls") == 0) { + // TODO: Anything else to do here? + xasprintf(&config.snmp_session.peername, "tls:%s", config.snmp_session.peername); + } else if (strcmp(connection_prefix, "dtls") == 0) { + // TODO: Anything else to do here? + xasprintf(&config.snmp_session.peername, "dtls:%s", config.snmp_session.peername); + } else if (strcmp(connection_prefix, "unix") == 0) { + // TODO: Check whether this is a valid path? + xasprintf(&config.snmp_session.peername, "unix:%s", config.snmp_session.peername); + } else if (strcmp(connection_prefix, "ipx") == 0) { + xasprintf(&config.snmp_session.peername, "ipx:%s", config.snmp_session.peername); } else { - miblist = ""; /* don't read any mib files for numeric oids */ + // Don't know that prefix, die here + die(STATE_UNKNOWN, "Unknown connection prefix"); } } /* Check server_address is given */ - if (server_address == NULL) + if (config.snmp_session.peername == NULL) { die(STATE_UNKNOWN, _("No host specified\n")); + } - /* Check oid is given */ - if (numoids == 0) - die(STATE_UNKNOWN, _("No OIDs specified\n")); + if (port != NULL) { + xasprintf(&config.snmp_session.peername, "%s:%s", config.snmp_session.peername, port); + } - if (proto == NULL) - xasprintf(&proto, DEFAULT_PROTOCOL); - - if ((strcmp(proto, "1") == 0) || (strcmp(proto, "2c") == 0)) { /* snmpv1 or snmpv2c */ - numauthpriv = 2; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-c"); - authpriv[1] = strdup(community); - } else if (strcmp(proto, "3") == 0) { /* snmpv3 args */ - if (!(context == NULL)) { - numcontext = 2; - contextargs = calloc(numcontext, sizeof(char *)); - contextargs[0] = strdup("-n"); - contextargs[1] = strdup(context); + /* check whether to load locally installed MIBS (CPU/disk intensive) */ + if (miblist == NULL) { + if (config.need_mibs) { + setenv("MIBLS", DEFAULT_MIBLIST, 1); + } else { + setenv("MIBLS", "NONE", 1); + miblist = ""; /* don't read any mib files for numeric oids */ } + } else { + // Blatantly stolen from snmplib/snmp_parse_args + setenv("MIBS", miblist, 1); + } - if (seclevel == NULL) - xasprintf(&seclevel, "noAuthNoPriv"); - - if (secname == NULL) + if ((config.snmp_session.version == SNMP_VERSION_1) || + (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ + /* + config.numauthpriv = 2; + config.authpriv = calloc(config.numauthpriv, sizeof(char *)); + config.authpriv[0] = strdup("-c"); + config.authpriv[1] = strdup(community); + */ + } else if (config.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ + // generate keys for priv and auth here (if demanded) + + if (config.snmp_session.securityName == NULL) { die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); + } - if (strcmp(seclevel, "noAuthNoPriv") == 0) { - numauthpriv = 4; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-l"); - authpriv[1] = strdup("noAuthNoPriv"); - authpriv[2] = strdup("-u"); - authpriv[3] = strdup(secname); - } else { - if (!((strcmp(seclevel, "authNoPriv") == 0) || (strcmp(seclevel, "authPriv") == 0))) { - usage2(_("Invalid seclevel"), seclevel); + switch (config.snmp_session.securityLevel) { + case SNMP_SEC_LEVEL_AUTHPRIV: { + if (authpasswd == NULL) { + die(STATE_UNKNOWN, + "No authentication passphrase was given, but authorization was requested"); } - - if (authproto == NULL) - xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); - - if (authpasswd == NULL) - die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); - - if (strcmp(seclevel, "authNoPriv") == 0) { - numauthpriv = 8; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-l"); - authpriv[1] = strdup("authNoPriv"); - authpriv[2] = strdup("-a"); - authpriv[3] = strdup(authproto); - authpriv[4] = strdup("-u"); - authpriv[5] = strdup(secname); - authpriv[6] = strdup("-A"); - authpriv[7] = strdup(authpasswd); - } else if (strcmp(seclevel, "authPriv") == 0) { - if (privproto == NULL) - xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL); - - if (privpasswd == NULL) - die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd"); - - numauthpriv = 12; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-l"); - authpriv[1] = strdup("authPriv"); - authpriv[2] = strdup("-a"); - authpriv[3] = strdup(authproto); - authpriv[4] = strdup("-u"); - authpriv[5] = strdup(secname); - authpriv[6] = strdup("-A"); - authpriv[7] = strdup(authpasswd); - authpriv[8] = strdup("-x"); - authpriv[9] = strdup(privproto); - authpriv[10] = strdup("-X"); - authpriv[11] = strdup(privpasswd); + // auth and priv + size_t priv_key_generated = generate_Ku( + config.snmp_session.securityPrivProto, config.snmp_session.securityPrivProtoLen, + authpasswd, strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, + &config.snmp_session.securityPrivKeyLen); + if (priv_key_generated != SNMPERR_SUCCESS) { + die(STATE_UNKNOWN, "Failed to generate privacy key"); } } - - } else { - usage2(_("Invalid SNMP version"), proto); + // fall through + case SNMP_SEC_LEVEL_AUTHNOPRIV: { + if (privpasswd == NULL) { + die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); + } + size_t auth_key_generated = generate_Ku( + config.snmp_session.securityAuthProto, config.snmp_session.securityAuthProtoLen, + privpasswd, strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, + &config.snmp_session.securityAuthKeyLen); + if (auth_key_generated != SNMPERR_SUCCESS) { + die(STATE_UNKNOWN, "Failed to generate privacy key"); + } + } break; + case SNMP_SEC_LEVEL_NOAUTH: + // No auth, no priv, not much todo + break; + } } - return OK; + process_arguments_wrapper result = { + .config = config, + .errorcode = OK, + }; + return result; } /* trim leading whitespace if there is a leading quote, make sure it balances */ - -static char *thisarg(char *str) { +char *trim_whitespaces_and_check_quoting(char *str) { str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ if (str[0] == '\'') { /* handle SIMPLE quoted strings */ - if (strlen(str) == 1 || !strstr(str + 1, "'")) + if (strlen(str) == 1 || !strstr(str + 1, "'")) { die(STATE_UNKNOWN, _("Unbalanced quotes\n")); + } } return str; } @@ -1132,23 +932,21 @@ static char *thisarg(char *str) { set the trailing quote to '\x0' if the string continues, advance beyond the comma */ -static char *nextarg(char *str) { +char *get_next_argument(char *str) { if (str[0] == '\'') { str[0] = 0; if (strlen(str) > 1) { str = strstr(str + 1, "'"); return (++str); - } else { - return NULL; } + return NULL; } if (str[0] == ',') { str[0] = 0; if (strlen(str) > 1) { return (++str); - } else { - return NULL; } + return NULL; } if ((str = strstr(str, ",")) && strlen(str) > 1) { str[0] = 0; @@ -1158,40 +956,53 @@ static char *nextarg(char *str) { } /* multiply result (values 0 < n < 1 work as divider) */ -static char *multiply(char *str) { - if (multiplier == 1) +char *multiply(char *str, double multiplier, char *fmt_str) { + + if (multiplier == 1) { return (str); + } - if (verbose > 2) + if (verbose > 2) { printf(" multiply input: %s\n", str); + } char *endptr; double val = strtod(str, &endptr); if ((val == 0.0) && (endptr == str)) { - die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str); + die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, + str); } - if (verbose > 2) + if (verbose > 2) { printf(" multiply extracted double: %f\n", val); + } val *= multiplier; char *conv = "%f"; - if (fmtstr_set) { - conv = fmtstr; + if (fmt_str != NULL) { + conv = fmt_str; } + + char *buffer = calloc(1, DEFAULT_BUFFER_SIZE); + if (buffer == NULL) { + die(STATE_UNKNOWN, "calloc failed"); + } + if (val == (int)val) { snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val); } else { - if (verbose > 2) + if (verbose > 2) { printf(" multiply using format: %s\n", conv); + } snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val); } - if (verbose > 2) + if (verbose > 2) { printf(" multiply result: %s\n", buffer); + } return buffer; } -static void print_help(void) { +void print_help(void) { print_revision(progname, NP_VERSION); printf(COPYRIGHT, copyright, email); @@ -1204,8 +1015,6 @@ static void print_help(void) { printf(UT_HELP_VRSN); printf(UT_EXTRA_OPTS); - printf(UT_IPv46); - printf(UT_HOST_PORT, 'p', DEFAULT_PORT); /* SNMP and Authentication Protocol */ @@ -1217,13 +1026,10 @@ static void print_help(void) { printf(" %s\n", _("SNMPv3 context")); printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); printf(" %s\n", _("SNMPv3 securityLevel")); - printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); - printf(" %s\n", - _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); - printf(" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512")); - printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); - printf(" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); - printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); + printf(" %s\n", "-a, --authproto=[MD5|SHA]"); + printf(" %s\n", _("SNMPv3 auth proto")); + printf(" %s\n", "-x, --privproto=[DES|AES]"); + printf(" %s\n", _("SNMPv3 priv proto (default DES)")); /* Authentication Tokens*/ printf(" %s\n", "-C, --community=STRING"); @@ -1235,15 +1041,21 @@ static void print_help(void) { printf(" %s\n", _("SNMPv3 authentication password")); printf(" %s\n", "-X, --privpasswd=PASSWORD"); printf(" %s\n", _("SNMPv3 privacy password")); + printf(" %s\n", "--connection-prefix"); + printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, " + "tcp6, tcpv6, tcpipv6, tls, dtls - " + "default is \"udp\"\n"); /* OID Stuff */ printf(" %s\n", "-o, --oid=OID(s)"); printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); printf(" %s\n", "-m, --miblist=STRING"); - printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); + printf(" %s\n", + _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); printf(" %s\n", _("for symbolic OIDs.)")); printf(" %s\n", "-d, --delimiter=STRING"); - printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER); + printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), + DEFAULT_DELIMITER); printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); printf(" %s\n", _("to be the data that should be used in the evaluation.")); printf(" %s\n", "-z, --nulloid=#"); @@ -1260,10 +1072,6 @@ static void print_help(void) { printf(" %s\n", _("Warning threshold range(s)")); printf(" %s\n", "-c, --critical=THRESHOLD(s)"); printf(" %s\n", _("Critical threshold range(s)")); - printf(" %s\n", "--rate"); - printf(" %s\n", _("Enable rate calculation. See 'Rate Calculation' below")); - printf(" %s\n", "--rate-multiplier"); - printf(" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute")); printf(" %s\n", "--offset=OFFSET"); printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); @@ -1271,9 +1079,11 @@ static void print_help(void) { printf(" %s\n", "-s, --string=STRING"); printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); printf(" %s\n", "-r, --ereg=REGEX"); - printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); + printf(" %s\n", + _("Return OK state (for that OID) if extended regular expression REGEX matches")); printf(" %s\n", "-R, --eregi=REGEX"); - printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); + printf(" %s\n", + _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); printf(" %s\n", "--invert-search"); printf(" %s\n", _("Invert search result (CRITICAL if found)")); @@ -1282,53 +1092,45 @@ static void print_help(void) { printf(" %s\n", _("Prefix label for output from plugin")); printf(" %s\n", "-u, --units=STRING"); printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); - printf(" %s\n", "-D, --output-delimiter=STRING"); - printf(" %s\n", _("Separates output on multiple OID requests")); printf(" %s\n", "-M, --multiplier=FLOAT"); printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); - printf(" %s\n", "-f, --fmtstr=STRING"); - printf(" %s\n", _("C-style format string for float values (see option -M)")); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); - printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5")); + printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: " + "timeout_interval * retries + 5")); printf(" %s\n", "-e, --retries=INTEGER"); - printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES); + printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), + DEFAULT_RETRIES); printf(" %s\n", "-O, --perf-oids"); printf(" %s\n", _("Label performance data with OIDs instead of --label's")); printf(" %s\n", "--ignore-mib-parsing-errors"); - printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files")); + printf(" %s\n", _("Do to not print errors encountered when parsing MIB files")); printf(UT_VERBOSE); printf("\n"); - printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package.")); - printf("%s\n", _("if you don't have the package installed, you will need to download it from")); + printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries.")); + printf("%s\n", + _("if you don't have the libraries installed, you will need to download them from")); printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin.")); printf("\n"); printf("%s\n", _("Notes:")); - printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); + printf(" %s\n", + _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); printf(" %s\n", _("list (lists with internal spaces must be quoted).")); printf(" -%s", UT_THRESHOLDS_NOTES); - printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); + printf(" %s\n", + _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); printf(" %s\n", _("- Note that only one string and one regex may be checked at present")); - printf(" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); + printf(" %s\n", + _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); printf(" %s\n", _("returned from the SNMP query is an unsigned integer.")); - printf("\n"); - printf("%s\n", _("Rate Calculation:")); - printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when")); - printf(" %s\n", _("calculating the counter difference since the last check. check_snmp")); - printf(" %s\n", _("saves the last state information in a file so that the rate per second")); - printf(" %s\n", _("can be calculated. Use the --rate option to save state information.")); - printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK.")); - printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so")); - printf(" %s\n", _("changing the arguments will create a new state file.")); - printf(UT_SUPPORT); } diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h new file mode 100644 index 00000000..e2e1d6c2 --- /dev/null +++ b/plugins/check_snmp.d/config.h @@ -0,0 +1,107 @@ +#pragma once + +#include "states.h" +#include "thresholds.h" +#include "utils_base.h" +#include +#include +#include + +// defines for snmp libs +#define u_char unsigned char +#define u_long unsigned long +#define u_short unsigned short +#define u_int unsigned int + +#include +#include +#include +#include + +const int DEFAULT_PROTOCOL = SNMP_VERSION_1; +const char DEFAULT_PORT[] = "161"; +const char DEFAULT_OUTPUT_DELIMITER[] = " "; +const int DEFAULT_RETRIES = 5; + +const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; + +typedef struct eval_method { + bool crit_string; + bool crit_regex; +} eval_method; + +typedef struct check_snmp_test_unit { + char *oid; + char *label; + char *unit_value; + eval_method eval_mthd; +} check_snmp_test_unit; + +check_snmp_test_unit check_snmp_test_unit_init() { + check_snmp_test_unit tmp = {}; + return tmp; +} + +typedef struct check_snmp_config { + // SNMP session to use + struct snmp_session snmp_session; + + // use getnet instead of get + bool use_getnext; + + // TODO actually make these useful + bool ignore_mib_parsing_errors; + bool need_mibs; + + check_snmp_test_unit *test_units; + size_t num_of_test_units; + mp_thresholds thresholds; + + // State if an empty value is encountered + mp_state_enum nulloid_result; + + // String evaluation stuff + bool invert_search; + regex_t regex_cmp_value; // regex to match query results against + char string_cmp_value[MAX_INPUT_BUFFER]; + + // Modify data + double multiplier; + double offset; + + // Modify output + bool use_perf_data_labels_from_input; +} check_snmp_config; + +check_snmp_config check_snmp_config_init() { + check_snmp_config tmp = { + .use_getnext = false, + + .ignore_mib_parsing_errors = false, + .need_mibs = false, + + .test_units = NULL, + .num_of_test_units = 0, + .thresholds = mp_thresholds_init(), + + .nulloid_result = STATE_UNKNOWN, // state to return if no result for query + + .invert_search = true, + .regex_cmp_value = {}, + .string_cmp_value = "", + + .multiplier = 1.0, + .offset = 0, + + .use_perf_data_labels_from_input = false, + }; + + snmp_sess_init(&tmp.snmp_session); + + tmp.snmp_session.retries = DEFAULT_RETRIES; + tmp.snmp_session.version = DEFAULT_SNMP_VERSION; + tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + tmp.snmp_session.community = (unsigned char *)"public"; + tmp.snmp_session.community_len = strlen("public"); + return tmp; +} -- cgit v1.2.3-74-g34f1 From babeb765e5725610dbf7673c91a3a5a4e5a8810f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:15:45 +0200 Subject: Fix range comparison and aesthetic improvements --- lib/utils_base.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/utils_base.c b/lib/utils_base.c index c49a473f..1e92b29b 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -222,15 +222,15 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) { bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { bool is_inside = false; - if (my_range.end_infinity == false && my_range.start_infinity == false) { + if (!my_range.end_infinity && !my_range.start_infinity) { // range: .........|---inside---|........... // value - is_inside = ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)); - } else if (my_range.start_infinity == false && my_range.end_infinity == true) { + is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) && (cmp_perfdata_value(value, my_range.end) <= 0)); + } else if (!my_range.start_infinity && my_range.end_infinity) { // range: .........|---inside--------- // value - is_inside = (cmp_perfdata_value(my_range.start, value) < 0); - } else if (my_range.start_infinity == true && my_range.end_infinity == false) { + is_inside = (cmp_perfdata_value(value, my_range.start) >= 0); + } else if (my_range.start_infinity && !my_range.end_infinity) { // range: -inside--------|.................... // value is_inside = (cmp_perfdata_value(value, my_range.end) == -1); -- cgit v1.2.3-74-g34f1 From a27862a9c774d3fc4a608f8593c83b11357cc6dc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:17:46 +0200 Subject: check_snmp: rebuild threshold parsing --- plugins/Makefile.am | 1 + plugins/check_snmp.c | 26 +++----- plugins/check_snmp.d/check_snmp_helpers.c | 103 ++++++++++++++++++++++++++++++ plugins/check_snmp.d/check_snmp_helpers.h | 8 +++ plugins/check_snmp.d/config.h | 52 ++------------- 5 files changed, 125 insertions(+), 65 deletions(-) create mode 100644 plugins/check_snmp.d/check_snmp_helpers.c create mode 100644 plugins/check_snmp.d/check_snmp_helpers.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 765e2687..38668348 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -152,6 +152,7 @@ check_ping_LDADD = $(NETLIBS) check_procs_LDADD = $(BASEOBJS) check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) check_real_LDADD = $(NETLIBS) +check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c check_snmp_LDADD = $(BASEOBJS) check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs` check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags` diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 6c9ed959..b71ee4fd 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -41,6 +41,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "../lib/utils_base.h" #include "../lib/output.h" #include "../lib/perfdata.h" +#include "check_snmp.d/check_snmp_helpers.h" #include #include @@ -366,8 +367,9 @@ int main(int argc, char **argv) { config.test_units[loop_index].unit_value); } - if (config.thresholds.warning_is_set || config.thresholds.critical_is_set) { - pd_num_val = mp_pd_set_thresholds(pd_num_val, config.thresholds); + if (config.test_units[loop_index].threshold.warning_is_set || + config.test_units[loop_index].threshold.critical_is_set) { + pd_num_val = mp_pd_set_thresholds(pd_num_val, config.test_units[loop_index].threshold); mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); if (tmp_state == STATE_WARNING) { @@ -660,23 +662,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Test parameters */ case 'c': /* critical threshold */ - { - mp_range_parsed tmp = mp_parse_range_string(optarg); - if (tmp.error != MP_PARSING_SUCCES) { - die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", optarg); - } - config.thresholds.critical = tmp.range; - config.thresholds.critical_is_set = true; - } break; + check_snmp_set_thresholds(optarg, config.test_units, oid_counter, true); + break; case 'w': /* warning threshold */ - { - mp_range_parsed tmp = mp_parse_range_string(optarg); - if (tmp.error != MP_PARSING_SUCCES) { - die(STATE_UNKNOWN, "Unable to parse warning threshold range: %s", optarg); - } - config.thresholds.warning = tmp.range; - config.thresholds.warning_is_set = true; - } break; + check_snmp_set_thresholds(optarg, config.test_units, oid_counter, false); + break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { /* diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c new file mode 100644 index 00000000..9db1d9f4 --- /dev/null +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -0,0 +1,103 @@ +#include "./check_snmp_helpers.h" +#include +#include "../../lib/utils_base.h" + +check_snmp_test_unit check_snmp_test_unit_init() { + check_snmp_test_unit tmp = { + .threshold = mp_thresholds_init(), + }; + return tmp; +} + + +int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], + size_t max_test_units, bool is_critical) { + if (strchr(threshold_string, ',') != NULL) { + // Got a comma in the string, should be multiple values + size_t tmp_counter = 0; + mp_range range_buffer; + bool first_value = true; + for (char *ptr = strtok(threshold_string, ", "); ptr != NULL; + ptr = strtok(NULL, ", "), tmp_counter++) { + + // edge case: maybe we got `,,` to skip a value + if (strlen(ptr) == 0) { + // use the previous value in this case + // or do not overwrite the loop value to be specific + if (first_value) { + die(STATE_UNKNOWN, "Empty threshold value"); + } + } else { + mp_range_parsed tmp = mp_parse_range_string(ptr); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr); + } + range_buffer = tmp.range; + } + + if (is_critical) { + tu[tmp_counter].threshold.critical = range_buffer; + tu[tmp_counter].threshold.critical_is_set = true; + } else { + tu[tmp_counter].threshold.warning = range_buffer; + tu[tmp_counter].threshold.warning_is_set = true; + } + first_value = false; + } + } else { + // Single value + mp_range_parsed tmp = mp_parse_range_string(threshold_string); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string); + } + + for (size_t i = 0; i < max_test_units; i++) { + if (is_critical) { + tu[i].threshold.critical = tmp.range; + tu[i].threshold.critical_is_set = true; + } else { + tu[i].threshold.warning = tmp.range; + tu[i].threshold.warning_is_set = true; + } + } + } + + return 0; +} + +const int DEFAULT_PROTOCOL = SNMP_VERSION_1; +const char DEFAULT_OUTPUT_DELIMITER[] = " "; + +const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; + +check_snmp_config check_snmp_config_init() { + check_snmp_config tmp = { + .use_getnext = false, + + .ignore_mib_parsing_errors = false, + .need_mibs = false, + + .test_units = NULL, + .num_of_test_units = 0, + + .nulloid_result = STATE_UNKNOWN, // state to return if no result for query + + .invert_search = true, + .regex_cmp_value = {}, + .string_cmp_value = "", + + .multiplier = 1.0, + .offset = 0, + + .use_perf_data_labels_from_input = false, + }; + + snmp_sess_init(&tmp.snmp_session); + + tmp.snmp_session.retries = DEFAULT_RETRIES; + tmp.snmp_session.version = DEFAULT_SNMP_VERSION; + tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + tmp.snmp_session.community = (unsigned char *)"public"; + tmp.snmp_session.community_len = strlen("public"); + return tmp; +} diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h new file mode 100644 index 00000000..74e7d0cd --- /dev/null +++ b/plugins/check_snmp.d/check_snmp_helpers.h @@ -0,0 +1,8 @@ +#pragma once + +#include "./config.h" + +check_snmp_test_unit check_snmp_test_unit_init(); +int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], + size_t max_test_units, bool is_critical); +check_snmp_config check_snmp_config_init(); diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index e2e1d6c2..ca624a81 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -1,11 +1,11 @@ #pragma once -#include "states.h" #include "thresholds.h" -#include "utils_base.h" +#include "states.h" #include #include #include +#include "../common.h" // defines for snmp libs #define u_char unsigned char @@ -18,12 +18,8 @@ #include #include -const int DEFAULT_PROTOCOL = SNMP_VERSION_1; -const char DEFAULT_PORT[] = "161"; -const char DEFAULT_OUTPUT_DELIMITER[] = " "; -const int DEFAULT_RETRIES = 5; - -const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; +#define DEFAULT_PORT "161" +#define DEFAULT_RETRIES 5 typedef struct eval_method { bool crit_string; @@ -35,13 +31,9 @@ typedef struct check_snmp_test_unit { char *label; char *unit_value; eval_method eval_mthd; + mp_thresholds threshold; } check_snmp_test_unit; -check_snmp_test_unit check_snmp_test_unit_init() { - check_snmp_test_unit tmp = {}; - return tmp; -} - typedef struct check_snmp_config { // SNMP session to use struct snmp_session snmp_session; @@ -55,7 +47,6 @@ typedef struct check_snmp_config { check_snmp_test_unit *test_units; size_t num_of_test_units; - mp_thresholds thresholds; // State if an empty value is encountered mp_state_enum nulloid_result; @@ -72,36 +63,3 @@ typedef struct check_snmp_config { // Modify output bool use_perf_data_labels_from_input; } check_snmp_config; - -check_snmp_config check_snmp_config_init() { - check_snmp_config tmp = { - .use_getnext = false, - - .ignore_mib_parsing_errors = false, - .need_mibs = false, - - .test_units = NULL, - .num_of_test_units = 0, - .thresholds = mp_thresholds_init(), - - .nulloid_result = STATE_UNKNOWN, // state to return if no result for query - - .invert_search = true, - .regex_cmp_value = {}, - .string_cmp_value = "", - - .multiplier = 1.0, - .offset = 0, - - .use_perf_data_labels_from_input = false, - }; - - snmp_sess_init(&tmp.snmp_session); - - tmp.snmp_session.retries = DEFAULT_RETRIES; - tmp.snmp_session.version = DEFAULT_SNMP_VERSION; - tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; - tmp.snmp_session.community = (unsigned char *)"public"; - tmp.snmp_session.community_len = strlen("public"); - return tmp; -} -- cgit v1.2.3-74-g34f1 From f976155863357e15961b280413244cc0badd89cb Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:41:02 +0200 Subject: check_snmp: Improve error handling --- plugins/check_snmp.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index b71ee4fd..badf9260 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -161,18 +161,27 @@ int main(int argc, char **argv) { struct snmp_session *active_session = snmp_open(&config.snmp_session); if (active_session == NULL) { - snmp_sess_perror("Failed to open session", &config.snmp_session); - die(STATE_UNKNOWN, "Failed to open SNMP session\n"); + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error (&config.snmp_session, &pcliberr , &psnmperr, &pperrstring); + die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); } struct snmp_pdu *response = NULL; int snmp_query_status = snmp_synch_response(active_session, pdu, &response); if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { - snmp_sess_perror("Failed to query", active_session); - // FAILED somehow - // TODO do some error analysis here - die(STATE_UNKNOWN, "SNMP query failed\n"); + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error (active_session, &pcliberr , &psnmperr, &pperrstring); + + if (psnmperr == SNMPERR_TIMEOUT) { + // We exit with critical here for some historical reason + die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); + } + die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); } snmp_close(active_session); -- cgit v1.2.3-74-g34f1 From e490c5f969b3844c1c48fcff129091fdca849d58 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:41:46 +0200 Subject: check_snmp: hopefully fix helpers --- plugins/check_snmp.d/check_snmp_helpers.c | 70 ++++++++++++++++++------------- plugins/check_snmp.d/check_snmp_helpers.h | 3 +- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 9db1d9f4..7725cc18 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -9,56 +9,66 @@ check_snmp_test_unit check_snmp_test_unit_init() { return tmp; } +int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit test_units[], + size_t max_test_units, bool is_critical) { + + if (threshold_string == NULL || strlen(threshold_string) == 0) { + // No input, do nothing + return 0; + } -int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], - size_t max_test_units, bool is_critical) { if (strchr(threshold_string, ',') != NULL) { // Got a comma in the string, should be multiple values - size_t tmp_counter = 0; - mp_range range_buffer; - bool first_value = true; + size_t tu_index = 0; + + while (threshold_string[0] == ',') { + // got commas at the beginning, so skip some values + tu_index++; + threshold_string++; + } + for (char *ptr = strtok(threshold_string, ", "); ptr != NULL; - ptr = strtok(NULL, ", "), tmp_counter++) { + ptr = strtok(NULL, ", "), tu_index++) { + + if (tu_index > max_test_units) { + // More thresholds then values, just ignore them + return 0; + } // edge case: maybe we got `,,` to skip a value if (strlen(ptr) == 0) { - // use the previous value in this case - // or do not overwrite the loop value to be specific - if (first_value) { - die(STATE_UNKNOWN, "Empty threshold value"); - } - } else { - mp_range_parsed tmp = mp_parse_range_string(ptr); - if (tmp.error != MP_PARSING_SUCCES) { - die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr); - } - range_buffer = tmp.range; + // no threshold given, do not set it then + continue; + } + + mp_range_parsed tmp = mp_parse_range_string(ptr); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr); } if (is_critical) { - tu[tmp_counter].threshold.critical = range_buffer; - tu[tmp_counter].threshold.critical_is_set = true; + test_units[tu_index].threshold.critical = tmp.range; + test_units[tu_index].threshold.critical_is_set = true; } else { - tu[tmp_counter].threshold.warning = range_buffer; - tu[tmp_counter].threshold.warning_is_set = true; + test_units[tu_index].threshold.warning = tmp.range; + test_units[tu_index].threshold.warning_is_set = true; } - first_value = false; } + } else { // Single value + // only valid for the first test unit mp_range_parsed tmp = mp_parse_range_string(threshold_string); if (tmp.error != MP_PARSING_SUCCES) { die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string); } - for (size_t i = 0; i < max_test_units; i++) { - if (is_critical) { - tu[i].threshold.critical = tmp.range; - tu[i].threshold.critical_is_set = true; - } else { - tu[i].threshold.warning = tmp.range; - tu[i].threshold.warning_is_set = true; - } + if (is_critical) { + test_units[0].threshold.critical = tmp.range; + test_units[0].threshold.critical_is_set = true; + } else { + test_units[0].threshold.warning = tmp.range; + test_units[0].threshold.warning_is_set = true; } } diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h index 74e7d0cd..28e3c4e3 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.h +++ b/plugins/check_snmp.d/check_snmp_helpers.h @@ -3,6 +3,5 @@ #include "./config.h" check_snmp_test_unit check_snmp_test_unit_init(); -int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], - size_t max_test_units, bool is_critical); +int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool); check_snmp_config check_snmp_config_init(); -- cgit v1.2.3-74-g34f1 From 015e4c098693e6234de0994879a173dd2463fefd Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:42:06 +0200 Subject: check_snmp: fix/adapt tests --- plugins/t/check_snmp.t | 103 ++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 62 deletions(-) diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t index 576cc506..99e01add 100644 --- a/plugins/t/check_snmp.t +++ b/plugins/t/check_snmp.t @@ -10,7 +10,7 @@ use NPTest; BEGIN { plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp"; - plan tests => 63; + plan tests => 42; } my $res; @@ -24,7 +24,7 @@ my $user_snmp = getTestParameter("NP_SNMP_USER", "An SNMP user", "auth_ $res = NPTest->testCmd( "./check_snmp -t 1" ); is( $res->return_code, 3, "No host name" ); -is( $res->output, "No host specified" ); +is( $res->output, "No OIDs specified" ); $res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" ); is( $res->return_code, 3, "No OIDs specified" ); @@ -32,145 +32,124 @@ is( $res->output, "No OIDs specified" ); $res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" ); is( $res->return_code, 3, "Invalid seclevel" ); -like( $res->output, "/check_snmp: Invalid seclevel - rubbish/" ); +like( $res->output, "/invalid security level: rubbish/" ); $res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" ); is( $res->return_code, 3, "Invalid protocol" ); -like( $res->output, "/check_snmp: Invalid SNMP version - 3c/" ); +like( $res->output, "/invalid SNMP version/protocol: 3c/" ); SKIP: { skip "no snmp host defined", 50 if ( ! $host_snmp ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -P 2c -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" ); - like($res->output, '/^SNMP OK - (\d+)/', "String contains SNMP OK"); - $res->output =~ /^SNMP OK - (\d+)/; + $res->output =~ /\|.*=(\d+);/; my $value = $1; cmp_ok( $value, ">", 0, "Got a time value" ); like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it"); # some more threshold tests - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 10:1"); - cmp_ok( $res->return_code, '==', 0, "Threshold test -c 10:1" ); - - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" ); - like($res->output, '/^SNMP OK - \d+/', "String contains SNMP OK"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" ); unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" ); - like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" ); - like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" ); - like($res->output, '/^SNMP OK - 1\s.*$/', "String fits SNMP OK and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " ); - like($res->output, '/^SNMP WARNING - \*1\*\s.*$/', "String matches SNMP WARNING and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" ); - like($res->output, '/^SNMP CRITICAL - \*1\*\s.*$/', "String matches SNMP CRITICAL and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" ); - like($res->output, "/^SNMP OK - 2 1/", "Got two values back" ); - like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); - like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" ); + like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" ); + like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" ); - like($res->output, "/^SNMP CRITICAL - 2 *1*/", "Got two values back" ); - like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); - like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" ); + like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" ); + like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1: -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses"); - like($res->output, '/^SNMP OK - \d+ \d+/', "String contains hrMemorySize and hrSystemProcesses"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds"); - like($res->output, '/^SNMP OK - 1\s.*$/', "String matches SNMP OK and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3"); - $res->output =~ m/^SNMP OK - (\d+\.\d{2})\s.*$/; + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -P 2c"); + $res->output =~ m/^.*Value: (\d+).*$/; my $lower = $1 - 0.05; my $higher = $1 + 0.05; - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3 -w $lower -c $higher"); - cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractionnal arguments"); + # $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -w $lower -c $higher -P 2c"); + # cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractional arguments"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2 -P 2c"); cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold"); - like($res->output, '/^SNMP WARNING - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s+\*1\*\s.*$/', "First OID returned as string, 2nd checked for thresholds"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c ''"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c '' -P 2c"); cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check"); - like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping first two thresholds, result printed rather than parsed"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,,"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,, -P 2c"); cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds"); - like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping all thresholds, result printed rather than parsed"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec'"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec' -P 2c"); cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold"); - like($res->output, '/^SNMP CRITICAL - \*\d+\* 1\/100 sec.*$/', "Timetick used as a threshold, parsed as numeric"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Timetick used as a string"); - like($res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "Timetick used as a string, result printed rather than parsed"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1 -P 2c"); cmp_ok( $res->return_code, '==', 0, "snmp response without datatype"); - like( $res->output, '/^SNMP OK - "(systemd|init)" \| $/', "snmp response without datatype" ); } SKIP: { skip "no SNMP user defined", 1 if ( ! $user_snmp ); $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv"); - like( $res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "noAuthNoPriv security level works properly" ); } # These checks need a complete command line. An invalid community is used so # the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway SKIP: { skip "no non responsive host defined", 2 if ( ! $host_nonresponsive ); - $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" ); - like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem"); + # like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem"); } SKIP: { skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); - $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); - like($res->output, '/External command error: .*(nosuchhost|Name or service not known|Unknown host).*/s', "String matches invalid host"); + like($res->output, '/.*Unknown host.*/s', "String matches invalid host"); } -- cgit v1.2.3-74-g34f1 From faf794b40139f02854b0737b2d62c5a039968762 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:43:11 +0200 Subject: check_snmp: remove leftover multiply function --- plugins/check_snmp.c | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index badf9260..3ac949a5 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -88,7 +88,6 @@ char *trim_whitespaces_and_check_quoting(char *str); char *get_next_argument(char *str); void print_usage(void); void print_help(void); -char *multiply(char *str, double multiplier, char *fmt_str); static int verbose = 0; @@ -954,53 +953,6 @@ char *get_next_argument(char *str) { return NULL; } -/* multiply result (values 0 < n < 1 work as divider) */ -char *multiply(char *str, double multiplier, char *fmt_str) { - - if (multiplier == 1) { - return (str); - } - - if (verbose > 2) { - printf(" multiply input: %s\n", str); - } - - char *endptr; - double val = strtod(str, &endptr); - if ((val == 0.0) && (endptr == str)) { - die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, - str); - } - - if (verbose > 2) { - printf(" multiply extracted double: %f\n", val); - } - - val *= multiplier; - char *conv = "%f"; - if (fmt_str != NULL) { - conv = fmt_str; - } - - char *buffer = calloc(1, DEFAULT_BUFFER_SIZE); - if (buffer == NULL) { - die(STATE_UNKNOWN, "calloc failed"); - } - - if (val == (int)val) { - snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val); - } else { - if (verbose > 2) { - printf(" multiply using format: %s\n", conv); - } - snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val); - } - if (verbose > 2) { - printf(" multiply result: %s\n", buffer); - } - return buffer; -} - void print_help(void) { print_revision(progname, NP_VERSION); -- cgit v1.2.3-74-g34f1 From 75f792bc6bdb24371197774787aa312d94fd6773 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:43:32 +0200 Subject: check_snmp: declare internal functions static --- plugins/check_snmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 3ac949a5..c65e3d82 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -84,8 +84,8 @@ typedef struct proces_arguments_wrapper { } process_arguments_wrapper; static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -char *trim_whitespaces_and_check_quoting(char *str); -char *get_next_argument(char *str); +static char *trim_whitespaces_and_check_quoting(char *str); +static char *get_next_argument(char *str); void print_usage(void); void print_help(void); -- cgit v1.2.3-74-g34f1 From 78cb7b22886556b22ea39073abcba77b0f57a957 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:12:56 +0200 Subject: check_snmp: fix typos --- plugins/check_snmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index c65e3d82..bdf4b4f6 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -105,7 +105,7 @@ int main(int argc, char **argv) { np_set_args(argc, argv); - // Initialize net-snmp before touching the sessio we are going to use + // Initialize net-snmp before touching the session we are going to use init_snmp("check_snmp"); time_t current_time; @@ -190,10 +190,10 @@ int main(int argc, char **argv) { mp_check overall = mp_check_init(); - mp_subcheck sc_succesfull_query = mp_subcheck_init(); - xasprintf(&sc_succesfull_query.output, "SNMP query was succesful"); - sc_succesfull_query = mp_set_subcheck_state(sc_succesfull_query, STATE_OK); - mp_add_subcheck_to_check(&overall, sc_succesfull_query); + mp_subcheck sc_successfull_query = mp_subcheck_init(); + xasprintf(&sc_successfull_query.output, "SNMP query was succesful"); + sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_successfull_query); // We got the the query results, now process them size_t loop_index = 0; @@ -643,7 +643,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid); } else { - die(STATE_UNKNOWN, "Unknow privacy protocol"); + die(STATE_UNKNOWN, "Unknown privacy protocol"); } break; case 'A': /* auth passwd */ -- cgit v1.2.3-74-g34f1 From 7ff0e518e4f77a8aae385511a7d46ff32e06bb9c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:14:51 +0200 Subject: Add libsnmp-dev to github action dependencies --- .github/prepare_debian.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/prepare_debian.sh b/.github/prepare_debian.sh index f7b6cf9f..b5cacd4a 100755 --- a/.github/prepare_debian.sh +++ b/.github/prepare_debian.sh @@ -24,6 +24,7 @@ apt-get -y install perl \ libpq-dev \ libradcli-dev \ libnet-snmp-perl \ + libsnmp-dev \ procps \ libdbi0-dev \ libdbd-sqlite3 \ -- cgit v1.2.3-74-g34f1 From 776c51a66f6eb9c3d6f018cb3786c6441f6b7171 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:18:15 +0200 Subject: Add netsnmp lib to specfile --- .github/monitoring-plugins.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec index 10799128..e3352988 100644 --- a/.github/monitoring-plugins.spec +++ b/.github/monitoring-plugins.spec @@ -824,6 +824,7 @@ Provides check_smtp of the Monitoring Plugins. Summary: Monitoring Plugins - check_snmp Requires: %{name} = %{version}-%{release} Requires: net-snmp +Build-Requires: net-snmp-devel %description snmp Provides check_snmp of the Monitoring Plugins. -- cgit v1.2.3-74-g34f1 From f1104f49a43285bf406d5c853b1b85b2dc62f4c0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:20:41 +0200 Subject: Fix one more typo --- plugins/check_snmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index bdf4b4f6..7851631c 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -191,7 +191,7 @@ int main(int argc, char **argv) { mp_check overall = mp_check_init(); mp_subcheck sc_successfull_query = mp_subcheck_init(); - xasprintf(&sc_successfull_query.output, "SNMP query was succesful"); + xasprintf(&sc_successfull_query.output, "SNMP query was successful"); sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); mp_add_subcheck_to_check(&overall, sc_successfull_query); -- cgit v1.2.3-74-g34f1 From 334c7e3e13342995bacaf670f49b1333654520bd Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:27:07 +0200 Subject: Add libsnmp-dev dependency to codeql gh action --- .github/workflows/codeql-analysis.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e748d2eb..6227f98f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -57,9 +57,20 @@ jobs: run: | sudo apt update sudo apt-get install -y --no-install-recommends m4 gettext automake autoconf make build-essential - sudo apt-get install -y --no-install-recommends perl autotools-dev libdbi-dev libldap2-dev libpq-dev \ - libmysqlclient-dev libradcli-dev libkrb5-dev libdbi0-dev \ - libdbd-sqlite3 libssl-dev libcurl4-openssl-dev liburiparser-dev + sudo apt-get install -y --no-install-recommends perl \ + autotools-dev \ + libdbi-dev \ + libldap2-dev \ + libpq-dev \ + libmysqlclient-dev \ + libradcli-dev \ + libkrb5-dev \ + libdbi0-dev \ + libdbd-sqlite3 \ + libssl-dev \ + libcurl4-openssl-dev \ + liburiparser-dev \ + libsnmp-dev - name: Configure build run: | -- cgit v1.2.3-74-g34f1 From d3e1c0314d94e9f97c822fc80659c7234308045f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:29:56 +0200 Subject: Fix Specfile requires --- .github/monitoring-plugins.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec index e3352988..ce22606b 100644 --- a/.github/monitoring-plugins.spec +++ b/.github/monitoring-plugins.spec @@ -88,6 +88,9 @@ BuildRequires: postgresql-devel # check_radius BuildRequires: radcli-devel +# check_snmp +BuildRequires: net-snmp-devel + %description Common files for Monitoring Plugins @@ -824,7 +827,6 @@ Provides check_smtp of the Monitoring Plugins. Summary: Monitoring Plugins - check_snmp Requires: %{name} = %{version}-%{release} Requires: net-snmp -Build-Requires: net-snmp-devel %description snmp Provides check_snmp of the Monitoring Plugins. -- cgit v1.2.3-74-g34f1 From bd6cff7f9ce4e03ee1d8d624c9e3f4c923467a73 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:54:09 +0200 Subject: check_snmp: use snmp v2c if community is given --- plugins/check_snmp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 7851631c..35e1f339 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -516,6 +516,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { char *port = NULL; char *miblist = NULL; char *connection_prefix = NULL; + bool snmp_version_set_explicitely = false; // TODO error checking while (true) { int option_char = getopt_long( @@ -567,6 +568,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } else { die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); } + snmp_version_set_explicitely = true; + break; case 'N': /* SNMPv3 context name */ config.snmp_session.contextName = optarg; @@ -858,6 +861,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { setenv("MIBS", miblist, 1); } + // Historical default is SNMP v2c + if (!snmp_version_set_explicitely && config.snmp_session.community != NULL) { + config.snmp_session.version = SNMP_VERSION_2c; + } + if ((config.snmp_session.version == SNMP_VERSION_1) || (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ /* -- cgit v1.2.3-74-g34f1 From b65f94fe0ea13dcbdc7ff5f3d1241eab2685cddc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:24:16 +0200 Subject: Remove testing for SNMPGETNEXT from autotools stuff --- configure.ac | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/configure.ac b/configure.ac index ce140218..5f00b478 100644 --- a/configure.ac +++ b/configure.ac @@ -1470,17 +1470,6 @@ AC_ARG_WITH(snmpgetnext_command, AS_IF([test -n "$PATH_TO_SNMPGET"], [ AC_DEFINE_UNQUOTED(PATH_TO_SNMPGET,"$PATH_TO_SNMPGET",[path to snmpget binary]) EXTRAS="$EXTRAS check_hpjd" - - dnl PATH_TO_SNMPGETNEXT is used unconditionally in check_snmp: - dnl - dnl https://github.com/nagios-plugins/nagios-plugins/issues/788 - dnl - AS_IF([test -n "$PATH_TO_SNMPGETNEXT"], [ - AC_DEFINE_UNQUOTED(PATH_TO_SNMPGETNEXT,"$PATH_TO_SNMPGETNEXT",[path to snmpgetnext binary]) - EXTRAS="$EXTRAS check_snmp\$(EXEEXT)" - ], [ - AC_MSG_WARN([Get snmpgetnext from https://net-snmp.sourceforge.io/ to build the check_snmp plugin]) - ]) ], [ AC_MSG_WARN([Get snmpget from https://net-snmp.sourceforge.io/ to build the check_hpjd and check_snmp plugins]) ]) -- cgit v1.2.3-74-g34f1 From f5ad4275ff9b9810d784b89a47863e41bc8c368e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:52:03 +0200 Subject: check_snmp: Test for availability of DES privacy protocol --- plugins/check_snmp.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 35e1f339..3403eee6 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -68,9 +68,15 @@ const char *email = "devel@monitoring-plugins.org"; const char DEFAULT_COMMUNITY[] = "public"; const char DEFAULT_MIBLIST[] = "ALL"; #define DEFAULT_AUTH_PROTOCOL "MD5" -#define DEFAULT_PRIV_PROTOCOL "DES" -#define DEFAULT_DELIMITER "=" -#define DEFAULT_BUFFER_SIZE 100 + +#ifdef usmDESPrivProtocol +# define DEFAULT_PRIV_PROTOCOL "DES" +#else +# define DEFAULT_PRIV_PROTOCOL "AES" +#endif + +#define DEFAULT_DELIMITER "=" +#define DEFAULT_BUFFER_SIZE 100 /* Longopts only arguments */ #define L_INVERT_SEARCH CHAR_MAX + 3 @@ -617,8 +623,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { +#ifdef usmDESPrivProtocol config.snmp_session.securityAuthProto = usmDESPrivProtocol; config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); +#else + die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform"); +#endif } else if (strcasecmp("AES", optarg) == 0) { config.snmp_session.securityAuthProto = usmAESPrivProtocol; config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); @@ -987,8 +997,13 @@ void print_help(void) { printf(" %s\n", _("SNMPv3 securityLevel")); printf(" %s\n", "-a, --authproto=[MD5|SHA]"); printf(" %s\n", _("SNMPv3 auth proto")); +#ifdef usmDESPrivProtocol printf(" %s\n", "-x, --privproto=[DES|AES]"); printf(" %s\n", _("SNMPv3 priv proto (default DES)")); +#else + printf(" %s\n", "-x, --privproto=[AES]"); + printf(" %s\n", _("SNMPv3 priv proto (default AES)")); +#endif /* Authentication Tokens*/ printf(" %s\n", "-C, --community=STRING"); -- cgit v1.2.3-74-g34f1 From 553a230a287b2b0294f0a0bbff7767493fe98657 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:52:15 +0200 Subject: check_snmp: formatting --- plugins/check_snmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 3403eee6..d0f01721 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -169,7 +169,7 @@ int main(int argc, char **argv) { int pcliberr = 0; int psnmperr = 0; char *pperrstring = NULL; - snmp_error (&config.snmp_session, &pcliberr , &psnmperr, &pperrstring); + snmp_error(&config.snmp_session, &pcliberr, &psnmperr, &pperrstring); die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); } @@ -180,7 +180,7 @@ int main(int argc, char **argv) { int pcliberr = 0; int psnmperr = 0; char *pperrstring = NULL; - snmp_error (active_session, &pcliberr , &psnmperr, &pperrstring); + snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); if (psnmperr == SNMPERR_TIMEOUT) { // We exit with critical here for some historical reason -- cgit v1.2.3-74-g34f1 From ebc2415330df963358b1e49beab0279a70ff4c84 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:12:27 +0200 Subject: check_snmp: fix DES availability detection(?) --- configure.ac | 10 ++++++++++ plugins/check_snmp.c | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 5f00b478..705183a2 100644 --- a/configure.ac +++ b/configure.ac @@ -1482,6 +1482,16 @@ else AC_MSG_WARN([Tried $PERL - install Net::SNMP perl module if you want to use the perl snmp plugins]) fi +dnl Check whether DES encryption is available (might not on RHEL) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include ]], [[oid *foo = usmDESPrivProtocol;]] + )], + [AC_DEFINE(HAVE_USM_DES_PRIV_PROTOCOL,1,Define whether we have DES Privacy Protocol)], + [] +) + AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat) AC_PATH_PROG(PATH_TO_QSTAT,qstat) AC_ARG_WITH(qstat_command, diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index d0f01721..6c672793 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -69,7 +69,7 @@ const char DEFAULT_COMMUNITY[] = "public"; const char DEFAULT_MIBLIST[] = "ALL"; #define DEFAULT_AUTH_PROTOCOL "MD5" -#ifdef usmDESPrivProtocol +#ifdef HAVE_USM_DES_PRIV_PROTOCOL # define DEFAULT_PRIV_PROTOCOL "DES" #else # define DEFAULT_PRIV_PROTOCOL "AES" @@ -623,7 +623,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { -#ifdef usmDESPrivProtocol +#ifdef HAVE_USM_DES_PRIV_PROTOCOL config.snmp_session.securityAuthProto = usmDESPrivProtocol; config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); #else @@ -997,7 +997,7 @@ void print_help(void) { printf(" %s\n", _("SNMPv3 securityLevel")); printf(" %s\n", "-a, --authproto=[MD5|SHA]"); printf(" %s\n", _("SNMPv3 auth proto")); -#ifdef usmDESPrivProtocol +#ifdef HAVE_USM_DES_PRIV_PROTOCOL printf(" %s\n", "-x, --privproto=[DES|AES]"); printf(" %s\n", _("SNMPv3 priv proto (default DES)")); #else -- cgit v1.2.3-74-g34f1 From e4c59440f0383cda12f6d4a4b1d34c38dbe10089 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:22:04 +0200 Subject: Build check_snmp unconditionally --- plugins/Makefile.am | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 38668348..f2f1777f 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -30,12 +30,13 @@ libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http che check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \ check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ check_ups check_users negate \ - urlize @EXTRAS@ + urlize @EXTRAS@ \ + check_snmp check_tcp_programs = check_ftp check_imap check_nntp check_pop \ check_udp check_clamd @check_tcp_ssl@ -EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ +EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \ check_swap check_fping check_ldap check_game check_dig \ check_nagios check_by_ssh check_dns check_nt check_ide_smart \ check_procs check_mysql_query check_apt check_dbi check_curl \ -- cgit v1.2.3-74-g34f1 From b4d84ebfee2cc93935ffead1f09a83ab0005ea10 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:39:43 +0200 Subject: check_snmp: Remove options description for input delimiter --- plugins/check_snmp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 6c672793..1f4acb5a 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -75,9 +75,6 @@ const char DEFAULT_MIBLIST[] = "ALL"; # define DEFAULT_PRIV_PROTOCOL "AES" #endif -#define DEFAULT_DELIMITER "=" -#define DEFAULT_BUFFER_SIZE 100 - /* Longopts only arguments */ #define L_INVERT_SEARCH CHAR_MAX + 3 #define L_OFFSET CHAR_MAX + 4 @@ -1027,9 +1024,6 @@ void print_help(void) { printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); printf(" %s\n", _("for symbolic OIDs.)")); - printf(" %s\n", "-d, --delimiter=STRING"); - printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), - DEFAULT_DELIMITER); printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); printf(" %s\n", _("to be the data that should be used in the evaluation.")); printf(" %s\n", "-z, --nulloid=#"); -- cgit v1.2.3-74-g34f1 From 1ca5a6040a01cb5fc4f1ceb61b133826b22e1f7b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:50:53 +0200 Subject: check_snmp: Make linter happy --- plugins/check_snmp.c | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 1f4acb5a..0c1e8ebe 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -36,7 +36,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "./runcmd.h" #include "./utils.h" #include "../lib/states.h" -#include "../lib/utils_cmd.h" + #include "../lib/thresholds.h" #include "../lib/utils_base.h" #include "../lib/output.h" @@ -159,7 +159,8 @@ int main(int argc, char **argv) { } const int timeout_safety_tolerance = 5; - alarm(timeout_interval * config.snmp_session.retries + timeout_safety_tolerance); + alarm((timeout_interval * (unsigned int)config.snmp_session.retries) + + timeout_safety_tolerance); struct snmp_session *active_session = snmp_open(&config.snmp_session); if (active_session == NULL) { @@ -254,7 +255,7 @@ int main(int argc, char **argv) { sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); } } else if (config.test_units[loop_index].eval_mthd.crit_regex) { - const int nmatch = config.regex_cmp_value.re_nsub + 1; + const size_t nmatch = config.regex_cmp_value.re_nsub + 1; regmatch_t pmatch[nmatch]; memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); @@ -287,8 +288,8 @@ int main(int argc, char **argv) { } struct counter64 tmp = *(vars->val.counter64); uint64_t counter = (tmp.high << 32) + tmp.low; - counter *= config.multiplier; - counter += config.offset; + counter *= (uint64_t)config.multiplier; + counter += (uint64_t)config.offset; pd_result_val = mp_create_pd_value(counter); } break; /* Numerical values */ @@ -299,10 +300,10 @@ int main(int argc, char **argv) { if (verbose) { printf("Debug: Got a Integer like\n"); } - unsigned long tmp = *(vars->val.integer); - tmp *= config.multiplier; + unsigned long tmp = (unsigned long)*(vars->val.integer); + tmp *= (unsigned long)config.multiplier; - tmp += config.offset; + tmp += (unsigned long)config.offset; pd_result_val = mp_create_pd_value(tmp); break; } @@ -310,17 +311,18 @@ int main(int argc, char **argv) { if (verbose) { printf("Debug: Got a Integer\n"); } - unsigned long tmp = *(vars->val.integer); - tmp *= config.multiplier; - tmp += config.offset; + long tmp = *(vars->val.integer); + tmp *= (long)config.multiplier; + tmp += (long)config.offset; + pd_result_val = mp_create_pd_value(tmp); } break; case ASN_FLOAT: { if (verbose) { printf("Debug: Got a float\n"); } - float tmp = *(vars->val.floatVal); + double tmp = *(vars->val.floatVal); tmp *= config.multiplier; tmp += config.offset; @@ -437,7 +439,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { {"ipv4", no_argument, 0, '4'}, {"ipv6", no_argument, 0, '6'}, {"multiplier", required_argument, 0, 'M'}, - {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, + {"ignore-mib-parsing-errors", no_argument, 0, L_IGNORE_MIB_PARSING_ERRORS}, {"connection-prefix", required_argument, 0, L_CONNECTION_PREFIX}, {0, 0, 0, 0}}; @@ -674,7 +676,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (!is_integer(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); } else { - timeout_interval = atoi(optarg); + timeout_interval = (unsigned int)atoi(optarg); } break; @@ -895,10 +897,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { "No authentication passphrase was given, but authorization was requested"); } // auth and priv - size_t priv_key_generated = generate_Ku( - config.snmp_session.securityPrivProto, config.snmp_session.securityPrivProtoLen, - authpasswd, strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, - &config.snmp_session.securityPrivKeyLen); + int priv_key_generated = + generate_Ku(config.snmp_session.securityPrivProto, + (unsigned int)config.snmp_session.securityPrivProtoLen, authpasswd, + strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, + &config.snmp_session.securityPrivKeyLen); + if (priv_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); } @@ -908,10 +912,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (privpasswd == NULL) { die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); } - size_t auth_key_generated = generate_Ku( - config.snmp_session.securityAuthProto, config.snmp_session.securityAuthProtoLen, - privpasswd, strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, - &config.snmp_session.securityAuthKeyLen); + int auth_key_generated = + generate_Ku(config.snmp_session.securityAuthProto, + (unsigned int)config.snmp_session.securityAuthProtoLen, privpasswd, + strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, + &config.snmp_session.securityAuthKeyLen); + if (auth_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); } -- cgit v1.2.3-74-g34f1 From 77a5db04d7ccc954096c2d86c21fa7b05948072d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:16:36 +0200 Subject: check_snmp: implement output format setting --- plugins/check_snmp.c | 58 ++++++++++++++++++++++++++++++++----------- plugins/check_snmp.d/config.h | 3 +++ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 0c1e8ebe..a939f078 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -75,12 +75,6 @@ const char DEFAULT_MIBLIST[] = "ALL"; # define DEFAULT_PRIV_PROTOCOL "AES" #endif -/* Longopts only arguments */ -#define L_INVERT_SEARCH CHAR_MAX + 3 -#define L_OFFSET CHAR_MAX + 4 -#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 -#define L_CONNECTION_PREFIX CHAR_MAX + 6 - typedef struct proces_arguments_wrapper { int errorcode; check_snmp_config config; @@ -121,6 +115,10 @@ int main(int argc, char **argv) { check_snmp_config config = paw_tmp.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + if (config.ignore_mib_parsing_errors) { char *opt_toggle_res = snmp_mib_toggle_options("e"); if (opt_toggle_res != NULL) { @@ -406,6 +404,15 @@ int main(int argc, char **argv) { /* process command-line arguments */ static process_arguments_wrapper process_arguments(int argc, char **argv) { + enum { + /* Longopts only arguments */ + invert_search_index = CHAR_MAX + 1, + offset_index, + ignore_mib_parsing_errors_index, + connection_prefix_index, + output_format_index, + }; + static struct option longopts[] = { STD_LONG_OPTS, {"community", required_argument, 0, 'C'}, @@ -433,14 +440,15 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { {"authpasswd", required_argument, 0, 'A'}, {"privpasswd", required_argument, 0, 'X'}, {"next", no_argument, 0, 'n'}, - {"offset", required_argument, 0, L_OFFSET}, - {"invert-search", no_argument, 0, L_INVERT_SEARCH}, + {"offset", required_argument, 0, offset_index}, + {"invert-search", no_argument, 0, invert_search_index}, {"perf-oids", no_argument, 0, 'O'}, {"ipv4", no_argument, 0, '4'}, {"ipv6", no_argument, 0, '6'}, {"multiplier", required_argument, 0, 'M'}, - {"ignore-mib-parsing-errors", no_argument, 0, L_IGNORE_MIB_PARSING_ERRORS}, - {"connection-prefix", required_argument, 0, L_CONNECTION_PREFIX}, + {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index}, + {"connection-prefix", required_argument, 0, connection_prefix_index}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; if (argc < 2) { @@ -780,10 +788,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } unitv_counter++; } break; - case L_OFFSET: + case offset_index: config.offset = strtod(optarg, NULL); + config.offset_set = true; break; - case L_INVERT_SEARCH: + case invert_search_index: config.invert_search = false; break; case 'O': @@ -796,16 +805,34 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { case '6': connection_prefix = "udp6"; break; - case L_CONNECTION_PREFIX: + case connection_prefix_index: connection_prefix = optarg; break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { config.multiplier = strtod(optarg, NULL); + config.multiplier_set = true; } break; - case L_IGNORE_MIB_PARSING_ERRORS: + case ignore_mib_parsing_errors_index: config.ignore_mib_parsing_errors = true; + break; + case 'f': // Deprecated format option for floating point values + 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); + } + + config.output_format_is_set = true; + config.output_format = parser.output_format; + break; + } + default: + die(STATE_UNKNOWN, "Unknown option"); } } @@ -1068,6 +1095,7 @@ void print_help(void) { printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); printf(" %s\n", "-M, --multiplier=FLOAT"); printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); + printf(UT_OUTPUT_FORMAT); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: " @@ -1115,5 +1143,5 @@ void print_usage(void) { printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n"); - printf("[-M multiplier [-f format]]\n"); + printf("[-M multiplier]\n"); } diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index ca624a81..e96dff5c 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -62,4 +62,7 @@ typedef struct check_snmp_config { // Modify output bool use_perf_data_labels_from_input; + + mp_output_format output_format; + bool output_format_is_set; } check_snmp_config; -- cgit v1.2.3-74-g34f1 From 7f1877f760a5ecdd0356010dd14c7606d44abfb0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:18:42 +0200 Subject: check_snmp: Fix number processing (offset + multiplier) --- plugins/check_snmp.c | 71 +++++++++++++++++++++++++------ plugins/check_snmp.d/check_snmp_helpers.c | 2 + plugins/check_snmp.d/config.h | 2 + 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index a939f078..0a9c6752 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -286,9 +286,21 @@ int main(int argc, char **argv) { } struct counter64 tmp = *(vars->val.counter64); uint64_t counter = (tmp.high << 32) + tmp.low; - counter *= (uint64_t)config.multiplier; - counter += (uint64_t)config.offset; - pd_result_val = mp_create_pd_value(counter); + + if (config.multiplier_set || config.offset_set) { + double processed = 0; + if (config.multiplier_set) { + processed = (double)counter * config.multiplier; + } + + if (config.offset_set) { + processed += config.offset; + } + pd_result_val = mp_create_pd_value(processed); + } else { + pd_result_val = mp_create_pd_value(counter); + } + } break; /* Numerical values */ case ASN_GAUGE: // same as ASN_UNSIGNED @@ -299,10 +311,20 @@ int main(int argc, char **argv) { printf("Debug: Got a Integer like\n"); } unsigned long tmp = (unsigned long)*(vars->val.integer); - tmp *= (unsigned long)config.multiplier; - tmp += (unsigned long)config.offset; - pd_result_val = mp_create_pd_value(tmp); + if (config.multiplier_set || config.offset_set) { + double processed = 0; + if (config.multiplier_set) { + processed = (double)tmp * config.multiplier; + } + + if (config.offset_set) { + processed += config.offset; + } + pd_result_val = mp_create_pd_value(processed); + } else { + pd_result_val = mp_create_pd_value(tmp); + } break; } case ASN_INTEGER: { @@ -311,19 +333,36 @@ int main(int argc, char **argv) { } long tmp = *(vars->val.integer); - tmp *= (long)config.multiplier; - tmp += (long)config.offset; - pd_result_val = mp_create_pd_value(tmp); + if (config.multiplier_set || config.offset_set) { + double processed = 0; + if (config.multiplier_set) { + processed = (double)tmp * config.multiplier; + } + + if (config.offset_set) { + processed += config.offset; + } + pd_result_val = mp_create_pd_value(processed); + } else { + pd_result_val = mp_create_pd_value(tmp); + } + } break; case ASN_FLOAT: { if (verbose) { printf("Debug: Got a float\n"); } double tmp = *(vars->val.floatVal); - tmp *= config.multiplier; - tmp += config.offset; + if (config.multiplier_set) { + tmp *= config.multiplier; + } + + if (config.offset_set) { + tmp += config.offset; + } + pd_result_val = mp_create_pd_value(tmp); break; } @@ -332,8 +371,14 @@ int main(int argc, char **argv) { printf("Debug: Got a double\n"); } double tmp = *(vars->val.doubleVal); - tmp *= config.multiplier; - tmp += config.offset; + if (config.multiplier_set) { + tmp *= config.multiplier; + } + + if (config.offset_set) { + tmp += config.offset; + } + pd_result_val = mp_create_pd_value(tmp); break; } diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 7725cc18..8f4bcb9c 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -97,7 +97,9 @@ check_snmp_config check_snmp_config_init() { .string_cmp_value = "", .multiplier = 1.0, + .multiplier_set = false, .offset = 0, + .offset_set = false, .use_perf_data_labels_from_input = false, }; diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index e96dff5c..e68986e2 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -58,7 +58,9 @@ typedef struct check_snmp_config { // Modify data double multiplier; + bool multiplier_set; double offset; + bool offset_set; // Modify output bool use_perf_data_labels_from_input; -- cgit v1.2.3-74-g34f1 From a7c6760cfe46fce4010056b8cdc36a1c9bd1d366 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:19:06 +0200 Subject: check_snmp: Small improvements + fix dereference bug --- plugins/check_snmp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 0a9c6752..d5abd8c2 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -214,8 +214,7 @@ int main(int argc, char **argv) { sc_oid_test.output = strdup(""); } - char oid_string[(MAX_OID_LEN * 2) + 1]; - memset(oid_string, 0, (MAX_OID_LEN * 2) + 1); + char oid_string[(MAX_OID_LEN * 2) + 1] = {}; int oid_string_result = snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length); @@ -404,9 +403,11 @@ int main(int argc, char **argv) { // Use oid for perdata label pd_num_val.label = strdup(oid_string); // TODO strdup error checking - } else if (config.test_units[loop_index].label != NULL || + } else if (config.test_units[loop_index].label != NULL && strcmp(config.test_units[loop_index].label, "") != 0) { pd_num_val.label = config.test_units[loop_index].label; + } else { + pd_num_val.label = config.test_units[loop_index].oid; } if (config.test_units[loop_index].unit_value != NULL && -- cgit v1.2.3-74-g34f1 From 41d309d438800102fc8aed736642e22da9f599fc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:22:58 +0200 Subject: check_snmp: improve string quoting in result --- plugins/check_snmp.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index d5abd8c2..3c054259 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -235,8 +235,23 @@ int main(int argc, char **argv) { if (verbose) { printf("Debug: Got a string\n"); } + char *tmp = (char *)vars->val.string; - xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + + if (strchr(tmp, '"') != NULL) { + // got double quote in the string + if (strchr(tmp, '\'') != NULL) { + // got single quote in the string too + // dont quote that at all to avoid even more confusion + xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + } else { + // quote with single quotes + xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); + } + } else { + // quote with double quotes + xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); + } if (strlen(tmp) == 0) { sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result); -- cgit v1.2.3-74-g34f1 From 888cd29202df5b99d4cd7834b11b030f2c39859f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:24:44 +0200 Subject: lib/utils_base.c: clang-format --- lib/utils_base.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/utils_base.c b/lib/utils_base.c index 1e92b29b..5ba865c9 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -33,12 +33,12 @@ #include #include -#define np_free(ptr) \ - { \ - if (ptr) { \ - free(ptr); \ - ptr = NULL; \ - } \ +#define np_free(ptr) \ + { \ + if (ptr) { \ + free(ptr); \ + ptr = NULL; \ + } \ } monitoring_plugin *this_monitoring_plugin = NULL; @@ -153,7 +153,8 @@ range *parse_range_string(char *str) { set_range_end(temp_range, end); } - if (temp_range->start_infinity == true || temp_range->end_infinity == true || temp_range->start <= temp_range->end) { + if (temp_range->start_infinity == true || temp_range->end_infinity == true || + temp_range->start <= temp_range->end) { return temp_range; } free(temp_range); @@ -205,12 +206,14 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) { printf("Threshold not set"); } else { if (my_threshold->warning) { - printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end); + printf("Warning: start=%g end=%g; ", my_threshold->warning->start, + my_threshold->warning->end); } else { printf("Warning not set; "); } if (my_threshold->critical) { - printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end); + printf("Critical: start=%g end=%g", my_threshold->critical->start, + my_threshold->critical->end); } else { printf("Critical not set"); } @@ -225,7 +228,8 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { if (!my_range.end_infinity && !my_range.start_infinity) { // range: .........|---inside---|........... // value - is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) && (cmp_perfdata_value(value, my_range.end) <= 0)); + is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) && + (cmp_perfdata_value(value, my_range.end) <= 0)); } else if (!my_range.start_infinity && my_range.end_infinity) { // range: .........|---inside--------- // value @@ -239,7 +243,8 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { is_inside = true; } - if ((is_inside && my_range.alert_on_inside_range == INSIDE) || (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) { + if ((is_inside && my_range.alert_on_inside_range == INSIDE) || + (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) { return true; } @@ -557,8 +562,8 @@ void np_enable_state(char *keyname, int expected_data_version) { this_state->state_data = NULL; /* Calculate filename */ - ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(), - this_monitoring_plugin->plugin_name, this_state->name); + ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), + (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); if (ret < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } -- cgit v1.2.3-74-g34f1 From 28bb2fa0a499b46e279244990b8268c1ed8823bc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:27:49 +0200 Subject: lib/utils_base.c: small refactoring --- lib/perfdata.h | 2 +- lib/utils_base.c | 195 ++++++++++++++++++++++++++----------------------------- 2 files changed, 94 insertions(+), 103 deletions(-) diff --git a/lib/perfdata.h b/lib/perfdata.h index 7fd908a9..c5d4a61d 100644 --- a/lib/perfdata.h +++ b/lib/perfdata.h @@ -45,7 +45,7 @@ typedef struct range_struct { double start; bool start_infinity; double end; - int end_infinity; + bool end_infinity; int alert_on; /* OUTSIDE (default) or INSIDE */ char *text; /* original unparsed text input */ } range; diff --git a/lib/utils_base.c b/lib/utils_base.c index 5ba865c9..43e88e7a 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -46,7 +46,7 @@ monitoring_plugin *this_monitoring_plugin = NULL; int timeout_state = STATE_CRITICAL; unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; -bool _np_state_read_file(FILE *); +bool _np_state_read_file(FILE *state_file); void np_init(char *plugin_name, int argc, char **argv) { if (this_monitoring_plugin == NULL) { @@ -153,7 +153,7 @@ range *parse_range_string(char *str) { set_range_end(temp_range, end); } - if (temp_range->start_infinity == true || temp_range->end_infinity == true || + if (temp_range->start_infinity || temp_range->end_infinity || temp_range->start <= temp_range->end) { return temp_range; } @@ -261,21 +261,21 @@ bool check_range(double value, range *my_range) { yes = false; } - if (my_range->end_infinity == false && my_range->start_infinity == false) { + if (!my_range->end_infinity && !my_range->start_infinity) { if ((my_range->start <= value) && (value <= my_range->end)) { return no; } return yes; } - if (my_range->start_infinity == false && my_range->end_infinity == true) { + if (!my_range->start_infinity && my_range->end_infinity) { if (my_range->start <= value) { return no; } return yes; } - if (my_range->start_infinity == true && my_range->end_infinity == false) { + if (my_range->start_infinity && !my_range->end_infinity) { if (value <= my_range->end) { return no; } @@ -287,12 +287,12 @@ bool check_range(double value, range *my_range) { /* Returns status */ int get_status(double value, thresholds *my_thresholds) { if (my_thresholds->critical != NULL) { - if (check_range(value, my_thresholds->critical) == true) { + if (check_range(value, my_thresholds->critical)) { return STATE_CRITICAL; } } if (my_thresholds->warning != NULL) { - if (check_range(value, my_thresholds->warning) == true) { + if (check_range(value, my_thresholds->warning)) { return STATE_WARNING; } } @@ -301,32 +301,31 @@ int get_status(double value, thresholds *my_thresholds) { char *np_escaped_string(const char *string) { char *data; - int i; - int j = 0; + int write_index = 0; data = strdup(string); - for (i = 0; data[i]; i++) { + for (int i = 0; data[i]; i++) { if (data[i] == '\\') { switch (data[++i]) { case 'n': - data[j++] = '\n'; + data[write_index++] = '\n'; break; case 'r': - data[j++] = '\r'; + data[write_index++] = '\r'; break; case 't': - data[j++] = '\t'; + data[write_index++] = '\t'; break; case '\\': - data[j++] = '\\'; + data[write_index++] = '\\'; break; default: - data[j++] = data[i]; + data[write_index++] = data[i]; } } else { - data[j++] = data[i]; + data[write_index++] = data[i]; } } - data[j] = '\0'; + data[write_index] = '\0'; return data; } @@ -341,33 +340,35 @@ int np_check_if_root(void) { return (geteuid() == 0); } char *np_extract_value(const char *varlist, const char *name, char sep) { char *tmp = NULL; char *value = NULL; - int i; - while (1) { + while (true) { /* Strip any leading space */ - for (; isspace(varlist[0]); varlist++) + for (; isspace(varlist[0]); varlist++) { ; + } if (strncmp(name, varlist, strlen(name)) == 0) { varlist += strlen(name); /* strip trailing spaces */ - for (; isspace(varlist[0]); varlist++) + for (; isspace(varlist[0]); varlist++) { ; + } if (varlist[0] == '=') { /* We matched the key, go past the = sign */ varlist++; /* strip leading spaces */ - for (; isspace(varlist[0]); varlist++) + for (; isspace(varlist[0]); varlist++) { ; + } if ((tmp = index(varlist, sep))) { /* Value is delimited by a comma */ if (tmp - varlist == 0) { continue; } - value = (char *)calloc(1, tmp - varlist + 1); - strncpy(value, varlist, tmp - varlist); + value = (char *)calloc(1, (unsigned long)(tmp - varlist + 1)); + strncpy(value, varlist, (unsigned long)(tmp - varlist)); value[tmp - varlist] = '\0'; } else { /* Value is delimited by a \0 */ @@ -392,7 +393,7 @@ char *np_extract_value(const char *varlist, const char *name, char sep) { /* Clean-up trailing spaces/newlines */ if (value) { - for (i = strlen(value) - 1; isspace(value[i]); i--) { + for (unsigned long i = strlen(value) - 1; isspace(value[i]); i--) { value[i] = '\0'; } } @@ -441,11 +442,7 @@ int mp_translate_state(char *state_text) { * parse of argv, so that uniqueness in parameters are reflected there. */ char *_np_state_generate_key(void) { - int i; char **argv = this_monitoring_plugin->argv; - char keyname[41]; - char *p = NULL; - unsigned char result[256]; #ifdef USE_OPENSSL @@ -458,7 +455,7 @@ char *_np_state_generate_key(void) { EVP_DigestInit(ctx, EVP_sha256()); - for (i = 0; i < this_monitoring_plugin->argc; i++) { + for (int i = 0; i < this_monitoring_plugin->argc; i++) { EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); } @@ -467,24 +464,26 @@ char *_np_state_generate_key(void) { struct sha256_ctx ctx; - for (i = 0; i < this_monitoring_plugin->argc; i++) { + for (int i = 0; i < this_monitoring_plugin->argc; i++) { sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); } sha256_finish_ctx(&ctx, result); #endif // FOUNDOPENSSL - for (i = 0; i < 20; ++i) { + char keyname[41]; + for (int i = 0; i < 20; ++i) { sprintf(&keyname[2 * i], "%02x", result[i]); } keyname[40] = '\0'; - p = strdup(keyname); - if (p == NULL) { + char *keyname_copy = strdup(keyname); + if (keyname_copy == NULL) { die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); } - return p; + + return keyname_copy; } void _cleanup_state_data(void) { @@ -525,21 +524,16 @@ char *_np_state_calculate_location_prefix(void) { * UNKNOWN if exception */ void np_enable_state(char *keyname, int expected_data_version) { - state_key *this_state = NULL; - char *temp_filename = NULL; - char *temp_keyname = NULL; - char *p = NULL; - int ret; - if (this_monitoring_plugin == NULL) { die(STATE_UNKNOWN, _("This requires np_init to be called")); } - this_state = (state_key *)calloc(1, sizeof(state_key)); + state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); if (this_state == NULL) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } + char *temp_keyname = NULL; if (keyname == NULL) { temp_keyname = _np_state_generate_key(); } else { @@ -548,13 +542,14 @@ void np_enable_state(char *keyname, int expected_data_version) { die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); } } + /* Die if invalid characters used for keyname */ - p = temp_keyname; - while (*p != '\0') { - if (!(isalnum(*p) || *p == '_')) { + char *tmp_char = temp_keyname; + while (*tmp_char != '\0') { + if (!(isalnum(*tmp_char) || *tmp_char == '_')) { die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); } - p++; + tmp_char++; } this_state->name = temp_keyname; this_state->plugin_name = this_monitoring_plugin->plugin_name; @@ -562,9 +557,11 @@ void np_enable_state(char *keyname, int expected_data_version) { this_state->state_data = NULL; /* Calculate filename */ - ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), - (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); - if (ret < 0) { + char *temp_filename = NULL; + int error = + asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), + (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); + if (error < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } @@ -581,19 +578,17 @@ void np_enable_state(char *keyname, int expected_data_version) { * if exceptional error. */ state_data *np_state_read(void) { - state_data *this_state_data = NULL; - FILE *statefile; - bool rc = false; - if (this_monitoring_plugin == NULL) { die(STATE_UNKNOWN, _("This requires np_init to be called")); } + bool error_code = false; + /* Open file. If this fails, no previous state found */ - statefile = fopen(this_monitoring_plugin->state->_filename, "r"); + FILE *statefile = fopen(this_monitoring_plugin->state->_filename, "r"); if (statefile != NULL) { - this_state_data = (state_data *)calloc(1, sizeof(state_data)); + state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); if (this_state_data == NULL) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } @@ -601,12 +596,12 @@ state_data *np_state_read(void) { this_state_data->data = NULL; this_monitoring_plugin->state->state_data = this_state_data; - rc = _np_state_read_file(statefile); + error_code = _np_state_read_file(statefile); fclose(statefile); } - if (!rc) { + if (!error_code) { _cleanup_state_data(); } @@ -616,13 +611,17 @@ state_data *np_state_read(void) { /* * Read the state file */ -bool _np_state_read_file(FILE *f) { +bool _np_state_read_file(FILE *state_file) { + time_t current_time; + time(¤t_time); + + /* Note: This introduces a limit of 1024 bytes in the string data */ + char *line = (char *)calloc(1, 1024); + if (line == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + bool status = false; - size_t pos; - char *line; - int i; - int failure = 0; - time_t current_time, data_time; enum { STATE_FILE_VERSION, STATE_DATA_VERSION, @@ -631,16 +630,9 @@ bool _np_state_read_file(FILE *f) { STATE_DATA_END } expected = STATE_FILE_VERSION; - time(¤t_time); - - /* Note: This introduces a limit of 1024 bytes in the string data */ - line = (char *)calloc(1, 1024); - if (line == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - while (!failure && (fgets(line, 1024, f)) != NULL) { - pos = strlen(line); + int failure = 0; + while (!failure && (fgets(line, 1024, state_file)) != NULL) { + size_t pos = strlen(line); if (line[pos - 1] == '\n') { line[pos - 1] = '\0'; } @@ -650,32 +642,32 @@ bool _np_state_read_file(FILE *f) { } switch (expected) { - case STATE_FILE_VERSION: - i = atoi(line); + case STATE_FILE_VERSION: { + int i = atoi(line); if (i != NP_STATE_FORMAT_VERSION) { failure++; } else { expected = STATE_DATA_VERSION; } - break; - case STATE_DATA_VERSION: - i = atoi(line); + } break; + case STATE_DATA_VERSION: { + int i = atoi(line); if (i != this_monitoring_plugin->state->data_version) { failure++; } else { expected = STATE_DATA_TIME; } - break; - case STATE_DATA_TIME: + } break; + case STATE_DATA_TIME: { /* If time > now, error */ - data_time = strtoul(line, NULL, 10); + time_t data_time = strtoul(line, NULL, 10); if (data_time > current_time) { failure++; } else { this_monitoring_plugin->state->state_data->time = data_time; expected = STATE_DATA_TEXT; } - break; + } break; case STATE_DATA_TEXT: this_monitoring_plugin->state->state_data->data = strdup(line); if (this_monitoring_plugin->state->state_data->data == NULL) { @@ -700,27 +692,24 @@ bool _np_state_read_file(FILE *f) { * Will die with UNKNOWN if errors */ void np_state_write_string(time_t data_time, char *data_string) { - FILE *fp; - char *temp_file = NULL; - int fd = 0, result = 0; time_t current_time; - char *directories = NULL; - char *p = NULL; - if (data_time == 0) { time(¤t_time); } else { current_time = data_time; } + int result = 0; + /* If file doesn't currently exist, create directories */ if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) { + char *directories = NULL; result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename); if (result < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } - for (p = directories + 1; *p; p++) { + for (char *p = directories + 1; *p; p++) { if (*p == '/') { *p = '\0'; if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { @@ -734,37 +723,39 @@ void np_state_write_string(time_t data_time, char *data_string) { np_free(directories); } + char *temp_file = NULL; result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename); if (result < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } - if ((fd = mkstemp(temp_file)) == -1) { + int temp_file_desc = 0; + if ((temp_file_desc = mkstemp(temp_file)) == -1) { np_free(temp_file); die(STATE_UNKNOWN, _("Cannot create temporary filename")); } - fp = (FILE *)fdopen(fd, "w"); - if (fp == NULL) { - close(fd); + FILE *temp_file_pointer = (FILE *)fdopen(temp_file_desc, "w"); + if (temp_file_pointer == NULL) { + close(temp_file_desc); unlink(temp_file); np_free(temp_file); die(STATE_UNKNOWN, _("Unable to open temporary state file")); } - fprintf(fp, "# NP State file\n"); - fprintf(fp, "%d\n", NP_STATE_FORMAT_VERSION); - fprintf(fp, "%d\n", this_monitoring_plugin->state->data_version); - fprintf(fp, "%lu\n", current_time); - fprintf(fp, "%s\n", data_string); + fprintf(temp_file_pointer, "# NP State file\n"); + fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); + fprintf(temp_file_pointer, "%d\n", this_monitoring_plugin->state->data_version); + fprintf(temp_file_pointer, "%lu\n", current_time); + fprintf(temp_file_pointer, "%s\n", data_string); - fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP); + fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); - fflush(fp); + fflush(temp_file_pointer); - result = fclose(fp); + result = fclose(temp_file_pointer); - fsync(fd); + fsync(temp_file_desc); if (result != 0) { unlink(temp_file); -- cgit v1.2.3-74-g34f1 From 1aefb1f9df5268ccbcd3ce38f5527ebca3896db6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:54:08 +0200 Subject: snmp: fix complaint of snmpd about paths --- plugins/tests/conf/snmpd.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tests/conf/snmpd.conf b/plugins/tests/conf/snmpd.conf index eff5b0b3..1724c027 100644 --- a/plugins/tests/conf/snmpd.conf +++ b/plugins/tests/conf/snmpd.conf @@ -19,5 +19,5 @@ syscontact Alice # Embedded Subagents ############################################################################### -perl do "tests/check_snmp_agent.pl"; +perl do "./tests/check_snmp_agent.pl"; -- cgit v1.2.3-74-g34f1 From 87195f5511bf18db2a64f71ea9783ebbfb33c3a5 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:57:06 +0200 Subject: check_snmp: refactoring + fixes This commit moves the state retention logic to check_snmp as it is only used there and I do not want it to be used at all, so it doesn't get a place in the lib. Otherwise this adapts tests and fixes the rate computing in the refactored version of check_snmp. Also fixes some bugs detected with the tests --- lib/Makefile.am | 2 +- lib/tests/test_utils.c | 190 +------ lib/utils_base.c | 344 ------------ lib/utils_base.h | 22 - plugins/Makefile.am | 14 +- plugins/check_snmp.c | 778 +++++++++++++-------------- plugins/check_snmp.d/check_snmp_helpers.c | 861 +++++++++++++++++++++++++++++- plugins/check_snmp.d/check_snmp_helpers.h | 64 +++ plugins/check_snmp.d/config.h | 25 +- plugins/tests/check_snmp.t | 159 +++--- plugins/tests/check_snmp_agent.pl | 78 ++- plugins/tests/test_check_snmp.c | 175 ++++++ plugins/tests/test_check_snmp.t | 6 + 13 files changed, 1647 insertions(+), 1071 deletions(-) create mode 100644 plugins/tests/test_check_snmp.c create mode 100755 plugins/tests/test_check_snmp.t diff --git a/lib/Makefile.am b/lib/Makefile.am index a9f3ff40..27a08278 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -4,7 +4,7 @@ SUBDIRS = . tests noinst_LIBRARIES = libmonitoringplug.a -AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ +AM_CPPFLAGS = \ -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins libmonitoringplug_a_SOURCES = utils_base.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c diff --git a/lib/tests/test_utils.c b/lib/tests/test_utils.c index c3150f00..8040dec8 100644 --- a/lib/tests/test_utils.c +++ b/lib/tests/test_utils.c @@ -28,17 +28,7 @@ #include "utils_base.c" int main(int argc, char **argv) { - char state_path[1024]; - range *range; - double temp; - thresholds *thresholds = NULL; - int i, rc; - char *temp_string; - state_key *temp_state_key = NULL; - state_data *temp_state_data; - time_t current_time; - - plan_tests(185); + plan_tests(155); ok(this_monitoring_plugin == NULL, "monitoring_plugin not initialised"); @@ -57,7 +47,7 @@ int main(int argc, char **argv) { np_set_args(argc, argv); - range = parse_range_string("6"); + range *range = parse_range_string("6"); ok(range != NULL, "'6' is valid range"); ok(range->start == 0, "Start correct"); ok(range->start_infinity == false, "Not using negative infinity"); @@ -97,7 +87,7 @@ int main(int argc, char **argv) { free(range); range = parse_range_string("12345678901234567890:"); - temp = atof("12345678901234567890"); /* Can't just use this because number too large */ + double temp = atof("12345678901234567890"); /* Can't just use this because number too large */ ok(range != NULL, "'12345678901234567890:' is valid range"); ok(range->start == temp, "Start correct"); ok(range->start_infinity == false, "Not using negative infinity"); @@ -158,32 +148,34 @@ int main(int argc, char **argv) { range = parse_range_string("2:1"); ok(range == NULL, "'2:1' rejected"); - rc = _set_thresholds(&thresholds, NULL, NULL); - ok(rc == 0, "Thresholds (NULL, NULL) set"); + thresholds *thresholds = NULL; + int returnCode; + returnCode = _set_thresholds(&thresholds, NULL, NULL); + ok(returnCode == 0, "Thresholds (NULL, NULL) set"); ok(thresholds->warning == NULL, "Warning not set"); ok(thresholds->critical == NULL, "Critical not set"); - rc = _set_thresholds(&thresholds, NULL, "80"); - ok(rc == 0, "Thresholds (NULL, '80') set"); + returnCode = _set_thresholds(&thresholds, NULL, "80"); + ok(returnCode == 0, "Thresholds (NULL, '80') set"); ok(thresholds->warning == NULL, "Warning not set"); ok(thresholds->critical->end == 80, "Critical set correctly"); - rc = _set_thresholds(&thresholds, "5:33", NULL); - ok(rc == 0, "Thresholds ('5:33', NULL) set"); + returnCode = _set_thresholds(&thresholds, "5:33", NULL); + ok(returnCode == 0, "Thresholds ('5:33', NULL) set"); ok(thresholds->warning->start == 5, "Warning start set"); ok(thresholds->warning->end == 33, "Warning end set"); ok(thresholds->critical == NULL, "Critical not set"); - rc = _set_thresholds(&thresholds, "30", "60"); - ok(rc == 0, "Thresholds ('30', '60') set"); + returnCode = _set_thresholds(&thresholds, "30", "60"); + ok(returnCode == 0, "Thresholds ('30', '60') set"); ok(thresholds->warning->end == 30, "Warning set correctly"); ok(thresholds->critical->end == 60, "Critical set correctly"); ok(get_status(15.3, thresholds) == STATE_OK, "15.3 - ok"); ok(get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning"); ok(get_status(69, thresholds) == STATE_CRITICAL, "69 - critical"); - rc = _set_thresholds(&thresholds, "-10:-2", "-30:20"); - ok(rc == 0, "Thresholds ('-30:20', '-10:-2') set"); + returnCode = _set_thresholds(&thresholds, "-10:-2", "-30:20"); + ok(returnCode == 0, "Thresholds ('-30:20', '-10:-2') set"); ok(thresholds->warning->start == -10, "Warning start set correctly"); ok(thresholds->warning->end == -2, "Warning end set correctly"); ok(thresholds->critical->start == -30, "Critical start set correctly"); @@ -304,164 +296,28 @@ int main(int argc, char **argv) { test = np_extract_ntpvar("", "foo"); ok(!test, "Empty string return NULL"); - /* This is the result of running ./test_utils */ - temp_string = (char *)_np_state_generate_key(); - ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got hash with exe and no parameters") || - diag("You are probably running in wrong directory. Must run as ./test_utils"); - - this_monitoring_plugin->argc = 4; - this_monitoring_plugin->argv[0] = "./test_utils"; - this_monitoring_plugin->argv[1] = "here"; - this_monitoring_plugin->argv[2] = "--and"; - this_monitoring_plugin->argv[3] = "now"; - temp_string = (char *)_np_state_generate_key(); - ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), "Got based on expected argv"); - - unsetenv("MP_STATE_PATH"); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory"); - - setenv("MP_STATE_PATH", "", 1); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string"); - - setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory"); - - ok(temp_state_key == NULL, "temp_state_key initially empty"); - - this_monitoring_plugin->argc = 1; - this_monitoring_plugin->argv[0] = "./test_utils"; - np_enable_state(NULL, 51); - temp_state_key = this_monitoring_plugin->state; - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got generated filename"); - - np_enable_state("allowedchars_in_keyname", 77); - temp_state_key = this_monitoring_plugin->state; - sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", (unsigned long)geteuid()); - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "allowedchars_in_keyname"), "Got key name with valid chars"); - ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename"); - - /* Don't do this test just yet. Will die */ - /* - np_enable_state("bad^chars$in@here", 77); - temp_state_key = this_monitoring_plugin->state; - ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" ); - */ - - np_enable_state("funnykeyname", 54); - temp_state_key = this_monitoring_plugin->state; - sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", (unsigned long)geteuid()); - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "funnykeyname"), "Got key name"); - - ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename"); - ok(temp_state_key->data_version == 54, "Version set"); - - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Got no state data as file does not exist"); - - /* - temp_fp = fopen("var/statefile", "r"); - if (temp_fp==NULL) - printf("Error opening. errno=%d\n", errno); - printf("temp_fp=%s\n", temp_fp); - ok( _np_state_read_file(temp_fp) == true, "Can read state file" ); - fclose(temp_fp); - */ - - temp_state_key->_filename = "var/statefile"; - temp_state_data = np_state_read(); - ok(this_monitoring_plugin->state->state_data != NULL, "Got state data now") || - diag("Are you running in right directory? Will get coredump next if not"); - ok(this_monitoring_plugin->state->state_data->time == 1234567890, "Got time"); - ok(!strcmp((char *)this_monitoring_plugin->state->state_data->data, "String to read"), "Data as expected"); - - temp_state_key->data_version = 53; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Older data version gives NULL"); - temp_state_key->data_version = 54; - - temp_state_key->_filename = "var/nonexistent"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Missing file gives NULL"); - ok(this_monitoring_plugin->state->state_data == NULL, "No state information"); - - temp_state_key->_filename = "var/oldformat"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Old file format gives NULL"); - - temp_state_key->_filename = "var/baddate"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Bad date gives NULL"); - - temp_state_key->_filename = "var/missingdataline"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Missing data line gives NULL"); - - unlink("var/generated"); - temp_state_key->_filename = "var/generated"; - current_time = 1234567890; - np_state_write_string(current_time, "String to read"); - ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected"); - - unlink("var/generated_directory/statefile"); - unlink("var/generated_directory"); - temp_state_key->_filename = "var/generated_directory/statefile"; - current_time = 1234567890; - np_state_write_string(current_time, "String to read"); - ok(system("cmp var/generated_directory/statefile var/statefile") == 0, "Have created directory"); - - /* This test to check cannot write to dir - can't automate yet */ - /* - unlink("var/generated_bad_dir"); - mkdir("var/generated_bad_dir", S_IRUSR); - np_state_write_string(current_time, "String to read"); - */ - - temp_state_key->_filename = "var/generated"; - time(¤t_time); - np_state_write_string(0, "String to read"); - temp_state_data = np_state_read(); - /* Check time is set to current_time */ - ok(system("cmp var/generated var/statefile > /dev/null") != 0, "Generated file should be different this time"); - ok(this_monitoring_plugin->state->state_data->time - current_time <= 1, "Has time generated from current time"); - - /* Don't know how to automatically test this. Need to be able to redefine die and catch the error */ - /* - temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write"; - np_state_write_string(0, "Bad file"); - */ - - np_cleanup(); - - ok(this_monitoring_plugin == NULL, "Free'd this_monitoring_plugin"); - ok(mp_suid() == false, "Test aren't suid"); /* base states with random case */ char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL}; - for (i = 0; states[i] != NULL; i++) { - /* out of the random case states, create the lower and upper versions + numeric string one */ + for (int i = 0; states[i] != NULL; i++) { + /* out of the random case states, create the lower and upper versions + numeric string one + */ char *statelower = strdup(states[i]); char *stateupper = strdup(states[i]); char statenum[2]; - char *temp_ptr; - for (temp_ptr = statelower; *temp_ptr; temp_ptr++) { - *temp_ptr = tolower(*temp_ptr); + for (char *temp_ptr = statelower; *temp_ptr; temp_ptr++) { + *temp_ptr = (char)tolower(*temp_ptr); } - for (temp_ptr = stateupper; *temp_ptr; temp_ptr++) { - *temp_ptr = toupper(*temp_ptr); + for (char *temp_ptr = stateupper; *temp_ptr; temp_ptr++) { + *temp_ptr = (char)toupper(*temp_ptr); } snprintf(statenum, 2, "%i", i); /* Base test names, we'll append the state string */ char testname[64] = "Translate state string: "; - int tlen = strlen(testname); + size_t tlen = strlen(testname); strcpy(testname + tlen, states[i]); ok(i == mp_translate_state(states[i]), testname); diff --git a/lib/utils_base.c b/lib/utils_base.c index 43e88e7a..29b393d0 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -74,14 +74,6 @@ void np_set_args(int argc, char **argv) { void np_cleanup(void) { if (this_monitoring_plugin != NULL) { - if (this_monitoring_plugin->state != NULL) { - if (this_monitoring_plugin->state->state_data) { - np_free(this_monitoring_plugin->state->state_data->data); - np_free(this_monitoring_plugin->state->state_data); - } - np_free(this_monitoring_plugin->state->name); - np_free(this_monitoring_plugin->state); - } np_free(this_monitoring_plugin->plugin_name); np_free(this_monitoring_plugin); } @@ -435,339 +427,3 @@ int mp_translate_state(char *state_text) { } return ERROR; } - -/* - * Returns a string to use as a keyname, based on an md5 hash of argv, thus - * hopefully a unique key per service/plugin invocation. Use the extra-opts - * parse of argv, so that uniqueness in parameters are reflected there. - */ -char *_np_state_generate_key(void) { - char **argv = this_monitoring_plugin->argv; - unsigned char result[256]; - -#ifdef USE_OPENSSL - /* - * This code path is chosen if openssl is available (which should be the most common - * scenario). Alternatively, the gnulib implementation/ - * - */ - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - - EVP_DigestInit(ctx, EVP_sha256()); - - for (int i = 0; i < this_monitoring_plugin->argc; i++) { - EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); - } - - EVP_DigestFinal(ctx, result, NULL); -#else - - struct sha256_ctx ctx; - - for (int i = 0; i < this_monitoring_plugin->argc; i++) { - sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); - } - - sha256_finish_ctx(&ctx, result); -#endif // FOUNDOPENSSL - - char keyname[41]; - for (int i = 0; i < 20; ++i) { - sprintf(&keyname[2 * i], "%02x", result[i]); - } - - keyname[40] = '\0'; - - char *keyname_copy = strdup(keyname); - if (keyname_copy == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - - return keyname_copy; -} - -void _cleanup_state_data(void) { - if (this_monitoring_plugin->state->state_data != NULL) { - np_free(this_monitoring_plugin->state->state_data->data); - np_free(this_monitoring_plugin->state->state_data); - } -} - -/* - * Internal function. Returns either: - * envvar NAGIOS_PLUGIN_STATE_DIRECTORY - * statically compiled shared state directory - */ -char *_np_state_calculate_location_prefix(void) { - char *env_dir; - - /* Do not allow passing MP_STATE_PATH in setuid plugins - * for security reasons */ - if (!mp_suid()) { - env_dir = getenv("MP_STATE_PATH"); - if (env_dir && env_dir[0] != '\0') { - return env_dir; - } - /* This is the former ENV, for backward-compatibility */ - env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); - if (env_dir && env_dir[0] != '\0') { - return env_dir; - } - } - - return NP_STATE_DIR_PREFIX; -} - -/* - * Initiatializer for state routines. - * Sets variables. Generates filename. Returns np_state_key. die with - * UNKNOWN if exception - */ -void np_enable_state(char *keyname, int expected_data_version) { - if (this_monitoring_plugin == NULL) { - die(STATE_UNKNOWN, _("This requires np_init to be called")); - } - - state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); - if (this_state == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - char *temp_keyname = NULL; - if (keyname == NULL) { - temp_keyname = _np_state_generate_key(); - } else { - temp_keyname = strdup(keyname); - if (temp_keyname == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - } - - /* Die if invalid characters used for keyname */ - char *tmp_char = temp_keyname; - while (*tmp_char != '\0') { - if (!(isalnum(*tmp_char) || *tmp_char == '_')) { - die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); - } - tmp_char++; - } - this_state->name = temp_keyname; - this_state->plugin_name = this_monitoring_plugin->plugin_name; - this_state->data_version = expected_data_version; - this_state->state_data = NULL; - - /* Calculate filename */ - char *temp_filename = NULL; - int error = - asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), - (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); - if (error < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - this_state->_filename = temp_filename; - - this_monitoring_plugin->state = this_state; -} - -/* - * Will return NULL if no data is available (first run). If key currently - * exists, read data. If state file format version is not expected, return - * as if no data. Get state data version number and compares to expected. - * If numerically lower, then return as no previous state. die with UNKNOWN - * if exceptional error. - */ -state_data *np_state_read(void) { - if (this_monitoring_plugin == NULL) { - die(STATE_UNKNOWN, _("This requires np_init to be called")); - } - - bool error_code = false; - - /* Open file. If this fails, no previous state found */ - FILE *statefile = fopen(this_monitoring_plugin->state->_filename, "r"); - if (statefile != NULL) { - - state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); - if (this_state_data == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - this_state_data->data = NULL; - this_monitoring_plugin->state->state_data = this_state_data; - - error_code = _np_state_read_file(statefile); - - fclose(statefile); - } - - if (!error_code) { - _cleanup_state_data(); - } - - return this_monitoring_plugin->state->state_data; -} - -/* - * Read the state file - */ -bool _np_state_read_file(FILE *state_file) { - time_t current_time; - time(¤t_time); - - /* Note: This introduces a limit of 1024 bytes in the string data */ - char *line = (char *)calloc(1, 1024); - if (line == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - bool status = false; - enum { - STATE_FILE_VERSION, - STATE_DATA_VERSION, - STATE_DATA_TIME, - STATE_DATA_TEXT, - STATE_DATA_END - } expected = STATE_FILE_VERSION; - - int failure = 0; - while (!failure && (fgets(line, 1024, state_file)) != NULL) { - size_t pos = strlen(line); - if (line[pos - 1] == '\n') { - line[pos - 1] = '\0'; - } - - if (line[0] == '#') { - continue; - } - - switch (expected) { - case STATE_FILE_VERSION: { - int i = atoi(line); - if (i != NP_STATE_FORMAT_VERSION) { - failure++; - } else { - expected = STATE_DATA_VERSION; - } - } break; - case STATE_DATA_VERSION: { - int i = atoi(line); - if (i != this_monitoring_plugin->state->data_version) { - failure++; - } else { - expected = STATE_DATA_TIME; - } - } break; - case STATE_DATA_TIME: { - /* If time > now, error */ - time_t data_time = strtoul(line, NULL, 10); - if (data_time > current_time) { - failure++; - } else { - this_monitoring_plugin->state->state_data->time = data_time; - expected = STATE_DATA_TEXT; - } - } break; - case STATE_DATA_TEXT: - this_monitoring_plugin->state->state_data->data = strdup(line); - if (this_monitoring_plugin->state->state_data->data == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - expected = STATE_DATA_END; - status = true; - break; - case STATE_DATA_END:; - } - } - - np_free(line); - return status; -} - -/* - * If time=NULL, use current time. Create state file, with state format - * version, default text. Writes version, time, and data. Avoid locking - * problems - use mv to write and then swap. Possible loss of state data if - * two things writing to same key at same time. - * Will die with UNKNOWN if errors - */ -void np_state_write_string(time_t data_time, char *data_string) { - time_t current_time; - if (data_time == 0) { - time(¤t_time); - } else { - current_time = data_time; - } - - int result = 0; - - /* If file doesn't currently exist, create directories */ - if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) { - char *directories = NULL; - result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename); - if (result < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - for (char *p = directories + 1; *p; p++) { - if (*p == '/') { - *p = '\0'; - if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { - /* Can't free this! Otherwise error message is wrong! */ - /* np_free(directories); */ - die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); - } - *p = '/'; - } - } - np_free(directories); - } - - char *temp_file = NULL; - result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename); - if (result < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - int temp_file_desc = 0; - if ((temp_file_desc = mkstemp(temp_file)) == -1) { - np_free(temp_file); - die(STATE_UNKNOWN, _("Cannot create temporary filename")); - } - - FILE *temp_file_pointer = (FILE *)fdopen(temp_file_desc, "w"); - if (temp_file_pointer == NULL) { - close(temp_file_desc); - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Unable to open temporary state file")); - } - - fprintf(temp_file_pointer, "# NP State file\n"); - fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); - fprintf(temp_file_pointer, "%d\n", this_monitoring_plugin->state->data_version); - fprintf(temp_file_pointer, "%lu\n", current_time); - fprintf(temp_file_pointer, "%s\n", data_string); - - fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); - - fflush(temp_file_pointer); - - result = fclose(temp_file_pointer); - - fsync(temp_file_desc); - - if (result != 0) { - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Error writing temp file")); - } - - if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) { - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Cannot rename state temp file")); - } - - np_free(temp_file); -} diff --git a/lib/utils_base.h b/lib/utils_base.h index 123066f8..f1c99a54 100644 --- a/lib/utils_base.h +++ b/lib/utils_base.h @@ -8,7 +8,6 @@ #include "./perfdata.h" #include "./thresholds.h" - #ifndef USE_OPENSSL # include "sha256.h" #endif @@ -26,25 +25,8 @@ #define OUTSIDE 0 #define INSIDE 1 -#define NP_STATE_FORMAT_VERSION 1 - -typedef struct state_data_struct { - time_t time; - void *data; - int length; /* Of binary data */ -} state_data; - -typedef struct state_key_struct { - char *name; - char *plugin_name; - int data_version; - char *_filename; - state_data *state_data; -} state_key; - typedef struct np_struct { char *plugin_name; - state_key *state; int argc; char **argv; } monitoring_plugin; @@ -100,10 +82,6 @@ char *np_extract_value(const char *, const char *, char); */ int mp_translate_state(char *); -void np_enable_state(char *, int); -state_data *np_state_read(void); -void np_state_write_string(time_t, char *); - void np_init(char *, int argc, char **argv); void np_set_args(int argc, char **argv); void np_cleanup(void); diff --git a/plugins/Makefile.am b/plugins/Makefile.am index f2f1777f..deae938d 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -13,8 +13,14 @@ AM_CFLAGS = -DNP_VERSION='"$(NP_VERSION)"' VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t -AM_CPPFLAGS = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl \ - @LDAPINCLUDE@ @PGINCLUDE@ @SSLINCLUDE@ +AM_CPPFLAGS = -I.. \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/gl \ + -I$(top_srcdir)/intl \ + -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ + @LDAPINCLUDE@ \ + @PGINCLUDE@ \ + @SSLINCLUDE@ localedir = $(datadir)/locale # gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this @@ -42,11 +48,13 @@ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \ check_procs check_mysql_query check_apt check_dbi check_curl \ \ tests/test_check_swap \ + tests/test_check_snmp \ tests/test_check_disk SUBDIRS = picohttpparser np_test_scripts = tests/test_check_swap.t \ + tests/test_check_snmp.t \ tests/test_check_disk.t EXTRA_DIST = t \ @@ -178,6 +186,8 @@ endif tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c +tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap +tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap tests_test_check_disk_SOURCES = tests/test_check_disk.c diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 3c054259..a5a7afe8 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -37,10 +37,8 @@ const char *email = "devel@monitoring-plugins.org"; #include "./utils.h" #include "../lib/states.h" -#include "../lib/thresholds.h" #include "../lib/utils_base.h" #include "../lib/output.h" -#include "../lib/perfdata.h" #include "check_snmp.d/check_snmp_helpers.h" #include @@ -49,6 +47,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include "check_snmp.d/config.h" +#include #include #include #include @@ -63,6 +62,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include #include "../gl/regex.h" +#include "../gl/base64.h" #include const char DEFAULT_COMMUNITY[] = "public"; @@ -86,7 +86,168 @@ static char *get_next_argument(char *str); void print_usage(void); void print_help(void); -static int verbose = 0; +int verbose = 0; + +typedef struct { + int errorcode; + char *state_string; +} gen_state_string_type; +gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) { + char *encoded_string = NULL; + gen_state_string_type result = {.errorcode = OK, .state_string = NULL}; + + if (verbose > 1) { + printf("%s:\n", __FUNCTION__); + for (size_t i = 0; i < num_of_entries; i++) { + printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp)); + switch (entries[i].type) { + case ASN_GAUGE: + printf("Type GAUGE\n"); + break; + case ASN_TIMETICKS: + printf("Type TIMETICKS\n"); + break; + case ASN_COUNTER: + printf("Type COUNTER\n"); + break; + case ASN_UINTEGER: + printf("Type UINTEGER\n"); + break; + case ASN_COUNTER64: + printf("Type COUNTER64\n"); + break; + case ASN_FLOAT: + printf("Type FLOAT\n"); + case ASN_DOUBLE: + printf("Type DOUBLE\n"); + break; + case ASN_INTEGER: + printf("Type INTEGER\n"); + break; + } + + switch (entries[i].type) { + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: + case ASN_COUNTER64: + printf("Value %llu\n", entries[i].value.uIntVal); + break; + case ASN_FLOAT: + case ASN_DOUBLE: + printf("Value %f\n", entries[i].value.doubleVal); + break; + case ASN_INTEGER: + printf("Value %lld\n", entries[i].value.intVal); + break; + } + } + } + + idx_t encoded = base64_encode_alloc((const char *)entries, + (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)), + &encoded_string); + + if (encoded > 0 && encoded_string != NULL) { + // success + if (verbose > 1) { + printf("encoded string: %s\n", encoded_string); + printf("encoded string length: %lu\n", strlen(encoded_string)); + } + result.state_string = encoded_string; + return result; + } + result.errorcode = ERROR; + return result; +} + +typedef struct { + int errorcode; + check_snmp_state_entry *state; +} recover_state_data_type; +recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) { + recover_state_data_type result = {.errorcode = OK, .state = NULL}; + + if (verbose > 1) { + printf("%s:\n", __FUNCTION__); + printf("State string: %s\n", state_string); + printf("State string length: %lu\n", state_string_length); + } + + idx_t outlen = 0; + bool decoded = + base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen); + + if (!decoded) { + if (verbose) { + printf("Failed to decode state string\n"); + } + // failure to decode + result.errorcode = ERROR; + return result; + } + + if (result.state == NULL) { + // Memory Error? + result.errorcode = ERROR; + return result; + } + + if (verbose > 1) { + printf("Recovered %lu entries of size %lu\n", + (size_t)outlen / sizeof(check_snmp_state_entry), outlen); + + for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) { + printf("Entry timestamp %lu: %s", result.state[i].timestamp, + ctime(&result.state[i].timestamp)); + switch (result.state[i].type) { + case ASN_GAUGE: + printf("Type GAUGE\n"); + break; + case ASN_TIMETICKS: + printf("Type TIMETICKS\n"); + break; + case ASN_COUNTER: + printf("Type COUNTER\n"); + break; + case ASN_UINTEGER: + printf("Type UINTEGER\n"); + break; + case ASN_COUNTER64: + printf("Type COUNTER64\n"); + break; + case ASN_FLOAT: + printf("Type FLOAT\n"); + case ASN_DOUBLE: + printf("Type DOUBLE\n"); + break; + case ASN_INTEGER: + printf("Type INTEGER\n"); + break; + } + + switch (result.state[i].type) { + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: + case ASN_COUNTER64: + printf("Value %llu\n", result.state[i].value.uIntVal); + break; + case ASN_FLOAT: + case ASN_DOUBLE: + printf("Value %f\n", result.state[i].value.doubleVal); + break; + case ASN_INTEGER: + printf("Value %lld\n", result.state[i].value.intVal); + break; + } + } + } + + return result; +} int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -97,6 +258,8 @@ int main(int argc, char **argv) { np_init((char *)progname, argc, argv); + state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv); + /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); @@ -105,9 +268,6 @@ int main(int argc, char **argv) { // Initialize net-snmp before touching the session we are going to use init_snmp("check_snmp"); - time_t current_time; - time(¤t_time); - process_arguments_wrapper paw_tmp = process_arguments(argc, argv); if (paw_tmp.errorcode == ERROR) { usage4(_("Could not parse arguments")); @@ -119,347 +279,103 @@ int main(int argc, char **argv) { mp_set_format(config.output_format); } - if (config.ignore_mib_parsing_errors) { - char *opt_toggle_res = snmp_mib_toggle_options("e"); - if (opt_toggle_res != NULL) { - die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); - } - } - - struct snmp_pdu *pdu = NULL; - if (config.use_getnext) { - pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); - } else { - pdu = snmp_pdu_create(SNMP_MSG_GET); - } - - for (size_t i = 0; i < config.num_of_test_units; i++) { - assert(config.test_units[i].oid != NULL); - if (verbose > 0) { - printf("OID %zu to parse: %s\n", i, config.test_units[i].oid); - } - - oid tmp_OID[MAX_OID_LEN]; - size_t tmp_OID_len = MAX_OID_LEN; - if (snmp_parse_oid(config.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { - // success - snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); - } else { - // failed - snmp_perror("Parsing failure"); - die(STATE_UNKNOWN, "Failed to parse OID\n"); - } - } - /* Set signal handling and alarm */ if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { usage4(_("Cannot catch SIGALRM")); } - const int timeout_safety_tolerance = 5; - alarm((timeout_interval * (unsigned int)config.snmp_session.retries) + - timeout_safety_tolerance); - - struct snmp_session *active_session = snmp_open(&config.snmp_session); - if (active_session == NULL) { - int pcliberr = 0; - int psnmperr = 0; - char *pperrstring = NULL; - snmp_error(&config.snmp_session, &pcliberr, &psnmperr, &pperrstring); - die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); - } - - struct snmp_pdu *response = NULL; - int snmp_query_status = snmp_synch_response(active_session, pdu, &response); - - if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { - int pcliberr = 0; - int psnmperr = 0; - char *pperrstring = NULL; - snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); + time_t current_time; + time(¤t_time); - if (psnmperr == SNMPERR_TIMEOUT) { - // We exit with critical here for some historical reason - die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); - } - die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); + if (verbose > 2) { + printf("current time: %s (timestamp: %lu)\n", ctime(¤t_time), current_time); } - snmp_close(active_session); - - /* disable alarm again */ - alarm(0); + snmp_responces response = do_snmp_query(config.snmp_params); mp_check overall = mp_check_init(); - mp_subcheck sc_successfull_query = mp_subcheck_init(); - xasprintf(&sc_successfull_query.output, "SNMP query was successful"); - sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); - mp_add_subcheck_to_check(&overall, sc_successfull_query); - - // We got the the query results, now process them - size_t loop_index = 0; - for (netsnmp_variable_list *vars = response->variables; vars; - vars = vars->next_variable, loop_index++) { - mp_subcheck sc_oid_test = mp_subcheck_init(); + if (response.errorcode == OK) { + mp_subcheck sc_successfull_query = mp_subcheck_init(); + xasprintf(&sc_successfull_query.output, "SNMP query was successful"); + sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_successfull_query); + } else { + // Error treatment here, either partial or whole + mp_subcheck sc_failed_query = mp_subcheck_init(); + xasprintf(&sc_failed_query.output, "SNMP query failed"); + sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_failed_query); + mp_exit(overall); + } - if (verbose > 0) { - printf("loop_index: %zu\n", loop_index); - } + check_snmp_state_entry *prev_state = NULL; + bool have_previous_state = false; - if ((config.test_units[loop_index].label != NULL) && - (strcmp(config.test_units[loop_index].label, "") != 0)) { - xasprintf(&sc_oid_test.output, "%s - ", config.test_units[loop_index].label); + if (config.evaluation_params.calculate_rate) { + state_data *previous_state = np_state_read(stateKey); + if (previous_state == NULL) { + // failed to recover state + // or no previous state + have_previous_state = false; } else { - sc_oid_test.output = strdup(""); - } - - char oid_string[(MAX_OID_LEN * 2) + 1] = {}; - - int oid_string_result = - snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length); - if (oid_string_result <= 0) { - // TODO error here - } - - if (verbose > 2) { - printf("Processing oid %s\n", oid_string); - } + // sanity check + recover_state_data_type prev_state_wrapper = + recover_state_data(previous_state->data, (idx_t)previous_state->length); - mp_perfdata_value pd_result_val = {0}; - xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); - sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); - - switch (vars->type) { - case ASN_OCTET_STR: { - if (verbose) { - printf("Debug: Got a string\n"); - } - - char *tmp = (char *)vars->val.string; - - if (strchr(tmp, '"') != NULL) { - // got double quote in the string - if (strchr(tmp, '\'') != NULL) { - // got single quote in the string too - // dont quote that at all to avoid even more confusion - xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); - } else { - // quote with single quotes - xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); - } + if (prev_state_wrapper.errorcode == OK) { + have_previous_state = true; + prev_state = prev_state_wrapper.state; } else { - // quote with double quotes - xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); - } - - if (strlen(tmp) == 0) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result); + have_previous_state = false; + prev_state = NULL; } - - // String matching test - if ((config.test_units[loop_index].eval_mthd.crit_string)) { - if (strcmp(tmp, config.string_cmp_value)) { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_CRITICAL : STATE_OK); - } else { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); - } - } else if (config.test_units[loop_index].eval_mthd.crit_regex) { - const size_t nmatch = config.regex_cmp_value.re_nsub + 1; - regmatch_t pmatch[nmatch]; - memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); - - int excode = regexec(&config.regex_cmp_value, tmp, nmatch, pmatch, 0); - if (excode == 0) { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); - } else if (excode != REG_NOMATCH) { - char errbuf[MAX_INPUT_BUFFER] = ""; - regerror(excode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); - printf(_("Execute Error: %s\n"), errbuf); - exit(STATE_CRITICAL); - } else { // REG_NOMATCH - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, config.invert_search ? STATE_CRITICAL : STATE_OK); - } - } - - mp_add_subcheck_to_check(&overall, sc_oid_test); } - continue; - case ASN_OPAQUE: - if (verbose) { - printf("Debug: Got OPAQUE\n"); - } - break; - case ASN_COUNTER64: { - if (verbose) { - printf("Debug: Got counter64\n"); - } - struct counter64 tmp = *(vars->val.counter64); - uint64_t counter = (tmp.high << 32) + tmp.low; - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)counter * config.multiplier; - } - - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(counter); - } - - } break; - /* Numerical values */ - case ASN_GAUGE: // same as ASN_UNSIGNED - case ASN_TIMETICKS: - case ASN_COUNTER: - case ASN_UINTEGER: { - if (verbose) { - printf("Debug: Got a Integer like\n"); - } - unsigned long tmp = (unsigned long)*(vars->val.integer); - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)tmp * config.multiplier; - } + } - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(tmp); - } - break; + check_snmp_state_entry *new_state = NULL; + if (config.evaluation_params.calculate_rate) { + new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry)); + if (new_state == NULL) { + die(STATE_UNKNOWN, "memory allocation failed"); } - case ASN_INTEGER: { - if (verbose) { - printf("Debug: Got a Integer\n"); - } - - long tmp = *(vars->val.integer); - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)tmp * config.multiplier; - } - - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(tmp); - } - - } break; - case ASN_FLOAT: { - if (verbose) { - printf("Debug: Got a float\n"); - } - double tmp = *(vars->val.floatVal); - - if (config.multiplier_set) { - tmp *= config.multiplier; - } - - if (config.offset_set) { - tmp += config.offset; - } + } - pd_result_val = mp_create_pd_value(tmp); - break; + // We got the the query results, now process them + for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) { + if (verbose > 0) { + printf("loop_index: %zu\n", loop_index); } - case ASN_DOUBLE: { - if (verbose) { - printf("Debug: Got a double\n"); - } - double tmp = *(vars->val.doubleVal); - if (config.multiplier_set) { - tmp *= config.multiplier; - } - - if (config.offset_set) { - tmp += config.offset; - } - pd_result_val = mp_create_pd_value(tmp); - break; + check_snmp_state_entry previous_unit_state = {}; + if (config.evaluation_params.calculate_rate && have_previous_state) { + previous_unit_state = prev_state[loop_index]; } - case ASN_IPADDRESS: - if (verbose) { - printf("Debug: Got an IP address\n"); - } - continue; - default: - if (verbose) { - printf("Debug: Got a unmatched result type: %hhu\n", vars->type); - } - // TODO: Error here? - continue; - } - - // some kind of numerical value - mp_perfdata pd_num_val = { - .value = pd_result_val, - }; - if (!config.use_perf_data_labels_from_input) { - // Use oid for perdata label - pd_num_val.label = strdup(oid_string); - // TODO strdup error checking - } else if (config.test_units[loop_index].label != NULL && - strcmp(config.test_units[loop_index].label, "") != 0) { - pd_num_val.label = config.test_units[loop_index].label; - } else { - pd_num_val.label = config.test_units[loop_index].oid; - } + check_snmp_evaluation single_eval = + evaluate_single_unit(response.response_values[loop_index], config.evaluation_params, + config.snmp_params.test_units[loop_index], current_time, + previous_unit_state, have_previous_state); - if (config.test_units[loop_index].unit_value != NULL && - strcmp(config.test_units[loop_index].unit_value, "") != 0) { - pd_num_val.uom = config.test_units[loop_index].unit_value; + if (config.evaluation_params.calculate_rate && + mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) { + new_state[loop_index] = single_eval.state; } - xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, - pd_value_to_string(pd_result_val)); + mp_add_subcheck_to_check(&overall, single_eval.sc); + } - if (config.test_units[loop_index].unit_value != NULL && - strcmp(config.test_units[loop_index].unit_value, "") != 0) { - xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, - config.test_units[loop_index].unit_value); - } + if (config.evaluation_params.calculate_rate) { + // store state + gen_state_string_type current_state_wrapper = + gen_state_string(new_state, config.snmp_params.num_of_test_units); - if (config.test_units[loop_index].threshold.warning_is_set || - config.test_units[loop_index].threshold.critical_is_set) { - pd_num_val = mp_pd_set_thresholds(pd_num_val, config.test_units[loop_index].threshold); - mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); - - if (tmp_state == STATE_WARNING) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); - xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", - sc_oid_test.output); - } else if (tmp_state == STATE_CRITICAL) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); - xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", - sc_oid_test.output); - } + if (current_state_wrapper.errorcode == OK) { + np_state_write_string(stateKey, current_time, current_state_wrapper.state_string); + } else { + die(STATE_UNKNOWN, "failed to create state string"); } - - mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); - - mp_add_subcheck_to_check(&overall, sc_oid_test); } - mp_exit(overall); } @@ -472,6 +388,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { ignore_mib_parsing_errors_index, connection_prefix_index, output_format_index, + calculate_rate, + rate_multiplier }; static struct option longopts[] = { @@ -510,6 +428,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index}, {"connection-prefix", required_argument, 0, connection_prefix_index}, {"output-format", required_argument, 0, output_format_index}, + {"rate", no_argument, 0, calculate_rate}, + {"rate-multiplier", required_argument, 0, rate_multiplier}, {0, 0, 0, 0}}; if (argc < 2) { @@ -575,8 +495,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } check_snmp_config config = check_snmp_config_init(); - config.test_units = tmp; - config.num_of_test_units = oid_counter; + config.snmp_params.test_units = tmp; + config.snmp_params.num_of_test_units = oid_counter; option = 0; optind = 1; // Reset argument scanner @@ -616,11 +536,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Connection info */ case 'C': /* group or community */ - config.snmp_session.community = (unsigned char *)optarg; - config.snmp_session.community_len = strlen(optarg); + config.snmp_params.snmp_session.community = (unsigned char *)optarg; + config.snmp_params.snmp_session.community_len = strlen(optarg); break; case 'H': /* Host or server */ - config.snmp_session.peername = optarg; + config.snmp_params.snmp_session.peername = optarg; break; case 'p': /*port number */ // Add port to "peername" below to not rely on argument order @@ -630,15 +550,15 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { miblist = optarg; break; case 'n': /* use_getnext instead of get */ - config.use_getnext = true; + config.snmp_params.use_getnext = true; break; case 'P': /* SNMP protocol version */ if (strcasecmp("1", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_1; + config.snmp_params.snmp_session.version = SNMP_VERSION_1; } else if (strcasecmp("2c", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_2c; + config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } else if (strcasecmp("3", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_3; + config.snmp_params.snmp_session.version = SNMP_VERSION_3; } else { die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); } @@ -646,45 +566,51 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'N': /* SNMPv3 context name */ - config.snmp_session.contextName = optarg; - config.snmp_session.contextNameLen = strlen(optarg); + config.snmp_params.snmp_session.contextName = optarg; + config.snmp_params.snmp_session.contextNameLen = strlen(optarg); break; case 'L': /* security level */ if (strcasecmp("noAuthNoPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; } else if (strcasecmp("authNoPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; } else if (strcasecmp("authPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; } else { die(STATE_UNKNOWN, "invalid security level: %s", optarg); } break; case 'U': /* security username */ - config.snmp_session.securityName = optarg; - config.snmp_session.securityNameLen = strlen(optarg); + config.snmp_params.snmp_session.securityName = optarg; + config.snmp_params.snmp_session.securityNameLen = strlen(optarg); break; case 'a': /* auth protocol */ // SNMPv3: SHA or MD5 // TODO Test for availability of individual protocols if (strcasecmp("MD5", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMACMD5AuthProtocol); } else if (strcasecmp("SHA", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMACSHA1AuthProtocol); } else if (strcasecmp("SHA224", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC128SHA224AuthProtocol); } else if (strcasecmp("SHA256", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC192SHA256AuthProtocol); } else if (strcasecmp("SHA384", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC256SHA384AuthProtocol); } else if (strcasecmp("SHA512", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC384SHA512AuthProtocol); } else { die(STATE_UNKNOWN, "Unknown authentication protocol"); } @@ -692,24 +618,28 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { #ifdef HAVE_USM_DES_PRIV_PROTOCOL - config.snmp_session.securityAuthProto = usmDESPrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmDESPrivProtocol); #else die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform"); #endif } else if (strcasecmp("AES", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAESPrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAESPrivProtocol); // } else if (strcasecmp("AES128", optarg)) { // config.snmp_session.securityAuthProto = usmAES128PrivProtocol; // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol) // / OID_LENGTH(oid); } else if (strcasecmp("AES192", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAES192PrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES192PrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAES192PrivProtocol); } else if (strcasecmp("AES256", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAES256PrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES256PrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAES256PrivProtocol); // } else if (strcasecmp("AES192Cisco", optarg)) { // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol; // config.snmp_session.securityAuthProtoLen = @@ -738,7 +668,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (!is_integer(optarg)) { usage2(_("Retries interval must be a positive integer"), optarg); } else { - config.snmp_session.retries = atoi(optarg); + config.snmp_params.snmp_session.retries = atoi(optarg); } break; case 't': /* timeout period */ @@ -751,10 +681,10 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Test parameters */ case 'c': /* critical threshold */ - check_snmp_set_thresholds(optarg, config.test_units, oid_counter, true); + check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true); break; case 'w': /* warning threshold */ - check_snmp_set_thresholds(optarg, config.test_units, oid_counter, false); + check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false); break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { @@ -763,25 +693,27 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { * so we have a mib variable, rather than just an SNMP OID, * so we have to actually read the mib files */ - config.need_mibs = true; + config.snmp_params.need_mibs = true; } for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), tmp_oid_counter++) { - config.test_units[tmp_oid_counter].oid = strdup(ptr); + config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr); } break; case 'z': /* Null OID Return Check */ if (!is_integer(optarg)) { usage2(_("Exit status must be a positive integer"), optarg); } else { - config.nulloid_result = atoi(optarg); + config.evaluation_params.nulloid_result = atoi(optarg); } break; case 's': /* string or substring */ - strncpy(config.string_cmp_value, optarg, sizeof(config.string_cmp_value) - 1); - config.string_cmp_value[sizeof(config.string_cmp_value) - 1] = 0; - config.test_units[eval_counter++].eval_mthd.crit_string = true; + strncpy(config.evaluation_params.string_cmp_value, optarg, + sizeof(config.evaluation_params.string_cmp_value) - 1); + config.evaluation_params + .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0; + config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true; break; case 'R': /* regex */ cflags = REG_ICASE; @@ -792,72 +724,73 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); regex_expect[sizeof(regex_expect) - 1] = 0; - int errcode = regcomp(&config.regex_cmp_value, regex_expect, cflags); + int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags); if (errcode != 0) { char errbuf[MAX_INPUT_BUFFER] = ""; - regerror(errcode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf, + MAX_INPUT_BUFFER); printf("Could Not Compile Regular Expression: %s", errbuf); process_arguments_wrapper result = { .errorcode = ERROR, }; return result; } - config.test_units[eval_counter++].eval_mthd.crit_regex = true; + config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true; } break; case 'l': /* label */ { - if (labels_counter >= config.num_of_test_units) { + if (labels_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { - config.test_units[labels_counter].label = ptr + 1; + config.snmp_params.test_units[labels_counter].label = ptr + 1; } else { - config.test_units[labels_counter].label = ptr; + config.snmp_params.test_units[labels_counter].label = ptr; } while (ptr && (ptr = get_next_argument(ptr))) { labels_counter++; ptr = trim_whitespaces_and_check_quoting(ptr); if (ptr[0] == '\'') { - config.test_units[labels_counter].label = ptr + 1; + config.snmp_params.test_units[labels_counter].label = ptr + 1; } else { - config.test_units[labels_counter].label = ptr; + config.snmp_params.test_units[labels_counter].label = ptr; } } labels_counter++; } break; case 'u': /* units */ { - if (unitv_counter >= config.num_of_test_units) { + if (unitv_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { - config.test_units[unitv_counter].unit_value = ptr + 1; + config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { - config.test_units[unitv_counter].unit_value = ptr; + config.snmp_params.test_units[unitv_counter].unit_value = ptr; } while (ptr && (ptr = get_next_argument(ptr))) { unitv_counter++; ptr = trim_whitespaces_and_check_quoting(ptr); if (ptr[0] == '\'') { - config.test_units[unitv_counter].unit_value = ptr + 1; + config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { - config.test_units[unitv_counter].unit_value = ptr; + config.snmp_params.test_units[unitv_counter].unit_value = ptr; } } unitv_counter++; } break; case offset_index: - config.offset = strtod(optarg, NULL); - config.offset_set = true; + config.evaluation_params.offset = strtod(optarg, NULL); + config.evaluation_params.offset_set = true; break; case invert_search_index: - config.invert_search = false; + config.evaluation_params.invert_search = false; break; case 'O': - config.use_perf_data_labels_from_input = true; + config.evaluation_params.use_oid_as_perf_data_label = true; break; case '4': // The default, do something here to be exclusive to -6 instead of doing nothing? @@ -871,12 +804,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { - config.multiplier = strtod(optarg, NULL); - config.multiplier_set = true; + config.evaluation_params.multiplier = strtod(optarg, NULL); + config.evaluation_params.multiplier_set = true; } break; case ignore_mib_parsing_errors_index: - config.ignore_mib_parsing_errors = true; + config.snmp_params.ignore_mib_parsing_errors = true; break; case 'f': // Deprecated format option for floating point values break; @@ -892,13 +825,22 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { config.output_format = parser.output_format; break; } + case calculate_rate: + config.evaluation_params.calculate_rate = true; + break; + case rate_multiplier: + if (!is_integer(optarg) || + ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) { + usage2(_("Rate multiplier must be a positive integer"), optarg); + } + break; default: die(STATE_UNKNOWN, "Unknown option"); } } - if (config.snmp_session.peername == NULL) { - config.snmp_session.peername = argv[optind]; + if (config.snmp_params.snmp_session.peername == NULL) { + config.snmp_params.snmp_session.peername = argv[optind]; } // Build true peername here if necessary @@ -908,7 +850,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { // The default, do nothing } else if (strcasecmp(connection_prefix, "tcp") == 0) { // use tcp/ipv4 - xasprintf(&config.snmp_session.peername, "tcp:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s", + config.snmp_params.snmp_session.peername); } else if (strcasecmp(connection_prefix, "tcp6") == 0 || strcasecmp(connection_prefix, "tcpv6") == 0 || strcasecmp(connection_prefix, "tcpipv6") == 0 || @@ -917,19 +860,23 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { strcasecmp(connection_prefix, "udpv6") == 0) { // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it // works anyway therefore do nothing here - xasprintf(&config.snmp_session.peername, "%s:%s", connection_prefix, - config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix, + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "tls") == 0) { // TODO: Anything else to do here? - xasprintf(&config.snmp_session.peername, "tls:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "dtls") == 0) { // TODO: Anything else to do here? - xasprintf(&config.snmp_session.peername, "dtls:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "unix") == 0) { // TODO: Check whether this is a valid path? - xasprintf(&config.snmp_session.peername, "unix:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "ipx") == 0) { - xasprintf(&config.snmp_session.peername, "ipx:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s", + config.snmp_params.snmp_session.peername); } else { // Don't know that prefix, die here die(STATE_UNKNOWN, "Unknown connection prefix"); @@ -937,17 +884,18 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } /* Check server_address is given */ - if (config.snmp_session.peername == NULL) { + if (config.snmp_params.snmp_session.peername == NULL) { die(STATE_UNKNOWN, _("No host specified\n")); } if (port != NULL) { - xasprintf(&config.snmp_session.peername, "%s:%s", config.snmp_session.peername, port); + xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", + config.snmp_params.snmp_session.peername, port); } /* check whether to load locally installed MIBS (CPU/disk intensive) */ if (miblist == NULL) { - if (config.need_mibs) { + if (config.snmp_params.need_mibs) { setenv("MIBLS", DEFAULT_MIBLIST, 1); } else { setenv("MIBLS", "NONE", 1); @@ -959,37 +907,37 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } // Historical default is SNMP v2c - if (!snmp_version_set_explicitely && config.snmp_session.community != NULL) { - config.snmp_session.version = SNMP_VERSION_2c; + if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) { + config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } - if ((config.snmp_session.version == SNMP_VERSION_1) || - (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ - /* - config.numauthpriv = 2; - config.authpriv = calloc(config.numauthpriv, sizeof(char *)); - config.authpriv[0] = strdup("-c"); - config.authpriv[1] = strdup(community); - */ - } else if (config.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ + if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) || + (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ + /* + config.numauthpriv = 2; + config.authpriv = calloc(config.numauthpriv, sizeof(char *)); + config.authpriv[0] = strdup("-c"); + config.authpriv[1] = strdup(community); + */ + } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ // generate keys for priv and auth here (if demanded) - if (config.snmp_session.securityName == NULL) { + if (config.snmp_params.snmp_session.securityName == NULL) { die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); } - switch (config.snmp_session.securityLevel) { + switch (config.snmp_params.snmp_session.securityLevel) { case SNMP_SEC_LEVEL_AUTHPRIV: { if (authpasswd == NULL) { die(STATE_UNKNOWN, "No authentication passphrase was given, but authorization was requested"); } // auth and priv - int priv_key_generated = - generate_Ku(config.snmp_session.securityPrivProto, - (unsigned int)config.snmp_session.securityPrivProtoLen, authpasswd, - strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, - &config.snmp_session.securityPrivKeyLen); + int priv_key_generated = generate_Ku( + config.snmp_params.snmp_session.securityPrivProto, + (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd, + strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey, + &config.snmp_params.snmp_session.securityPrivKeyLen); if (priv_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); @@ -1000,11 +948,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (privpasswd == NULL) { die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); } - int auth_key_generated = - generate_Ku(config.snmp_session.securityAuthProto, - (unsigned int)config.snmp_session.securityAuthProtoLen, privpasswd, - strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, - &config.snmp_session.securityAuthKeyLen); + int auth_key_generated = generate_Ku( + config.snmp_params.snmp_session.securityAuthProto, + (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd, + strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey, + &config.snmp_params.snmp_session.securityAuthKeyLen); if (auth_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 8f4bcb9c..9fa396d8 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -1,6 +1,15 @@ #include "./check_snmp_helpers.h" #include #include "../../lib/utils_base.h" +#include "config.h" +#include +#include "../utils.h" +#include "output.h" +#include "states.h" +#include +#include + +extern int verbose; check_snmp_test_unit check_snmp_test_unit_init() { check_snmp_test_unit tmp = { @@ -78,38 +87,848 @@ int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit const int DEFAULT_PROTOCOL = SNMP_VERSION_1; const char DEFAULT_OUTPUT_DELIMITER[] = " "; -const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; +const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192; check_snmp_config check_snmp_config_init() { check_snmp_config tmp = { - .use_getnext = false, + .snmp_params = + { + .use_getnext = false, + + .ignore_mib_parsing_errors = false, + .need_mibs = false, - .ignore_mib_parsing_errors = false, - .need_mibs = false, + .test_units = NULL, + .num_of_test_units = 0, + }, - .test_units = NULL, - .num_of_test_units = 0, + .evaluation_params = + { + .nulloid_result = STATE_UNKNOWN, // state to return if no result for query - .nulloid_result = STATE_UNKNOWN, // state to return if no result for query + .invert_search = true, + .regex_cmp_value = {}, + .string_cmp_value = "", - .invert_search = true, - .regex_cmp_value = {}, - .string_cmp_value = "", + .multiplier = 1.0, + .multiplier_set = false, + .offset = 0, + .offset_set = false, - .multiplier = 1.0, - .multiplier_set = false, - .offset = 0, - .offset_set = false, + .use_oid_as_perf_data_label = false, - .use_perf_data_labels_from_input = false, + .calculate_rate = false, + .rate_multiplier = 1, + }, }; - snmp_sess_init(&tmp.snmp_session); + snmp_sess_init(&tmp.snmp_params.snmp_session); - tmp.snmp_session.retries = DEFAULT_RETRIES; - tmp.snmp_session.version = DEFAULT_SNMP_VERSION; - tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; - tmp.snmp_session.community = (unsigned char *)"public"; - tmp.snmp_session.community_len = strlen("public"); + tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES; + tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION; + tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + tmp.snmp_params.snmp_session.community = (unsigned char *)"public"; + tmp.snmp_params.snmp_session.community_len = strlen("public"); return tmp; } + +snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) { + if (parameters.ignore_mib_parsing_errors) { + char *opt_toggle_res = snmp_mib_toggle_options("e"); + if (opt_toggle_res != NULL) { + die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); + } + } + + struct snmp_pdu *pdu = NULL; + if (parameters.use_getnext) { + pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); + } else { + pdu = snmp_pdu_create(SNMP_MSG_GET); + } + + for (size_t i = 0; i < parameters.num_of_test_units; i++) { + assert(parameters.test_units[i].oid != NULL); + if (verbose > 0) { + printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid); + } + + oid tmp_OID[MAX_OID_LEN]; + size_t tmp_OID_len = MAX_OID_LEN; + if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { + // success + snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); + } else { + // failed + snmp_perror("Parsing failure"); + die(STATE_UNKNOWN, "Failed to parse OID\n"); + } + } + + const int timeout_safety_tolerance = 5; + alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) + + timeout_safety_tolerance); + + struct snmp_session *active_session = snmp_open(¶meters.snmp_session); + if (active_session == NULL) { + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error(¶meters.snmp_session, &pcliberr, &psnmperr, &pperrstring); + die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); + } + + struct snmp_pdu *response = NULL; + int snmp_query_status = snmp_synch_response(active_session, pdu, &response); + + if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); + + if (psnmperr == SNMPERR_TIMEOUT) { + // We exit with critical here for some historical reason + die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); + } + die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); + } + + snmp_close(active_session); + + /* disable alarm again */ + alarm(0); + + snmp_responces result = { + .errorcode = OK, + .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)), + }; + + if (result.response_values == NULL) { + result.errorcode = ERROR; + return result; + } + + // We got the the query results, now process them + size_t loop_index = 0; + for (netsnmp_variable_list *vars = response->variables; vars; + vars = vars->next_variable, loop_index++) { + + for (size_t jdx = 0; jdx < vars->name_length; jdx++) { + result.response_values[loop_index].oid[jdx] = vars->name[jdx]; + } + result.response_values[loop_index].oid_length = vars->name_length; + + switch (vars->type) { + case ASN_OCTET_STR: { + result.response_values[loop_index].string_response = strdup((char *)vars->val.string); + result.response_values[loop_index].type = vars->type; + if (verbose) { + printf("Debug: Got a string as response: %s\n", vars->val.string); + } + } + continue; + case ASN_OPAQUE: + if (verbose) { + printf("Debug: Got OPAQUE\n"); + } + break; + /* Numerical values */ + case ASN_COUNTER64: { + if (verbose) { + printf("Debug: Got counter64\n"); + } + struct counter64 tmp = *(vars->val.counter64); + uint64_t counter = (tmp.high << 32) + tmp.low; + result.response_values[loop_index].value.uIntVal = counter; + result.response_values[loop_index].type = vars->type; + } break; + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + if (verbose) { + printf("Debug: Got a Integer like\n"); + } + result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_INTEGER: { + if (verbose) { + printf("Debug: Got a Integer\n"); + } + result.response_values[loop_index].value.intVal = *(vars->val.integer); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_FLOAT: { + if (verbose) { + printf("Debug: Got a float\n"); + } + result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_DOUBLE: { + if (verbose) { + printf("Debug: Got a double\n"); + } + result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal); + result.response_values[loop_index].type = vars->type; + break; + } + case ASN_IPADDRESS: + if (verbose) { + printf("Debug: Got an IP address\n"); + } + result.response_values[loop_index].type = vars->type; + + // TODO: print address here, state always ok? or regex match? + continue; + default: + if (verbose) { + printf("Debug: Got a unmatched result type: %hhu\n", vars->type); + } + // TODO: Error here? + continue; + } + } + + return result; +} + +check_snmp_evaluation evaluate_single_unit(response_value response, + check_snmp_evaluation_parameters eval_params, + check_snmp_test_unit test_unit, time_t query_timestamp, + check_snmp_state_entry prev_state, + bool have_previous_state) { + mp_subcheck sc_oid_test = mp_subcheck_init(); + + if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) { + xasprintf(&sc_oid_test.output, "%s - ", test_unit.label); + } else { + sc_oid_test.output = strdup(""); + } + + char oid_string[(MAX_OID_LEN * 2) + 1] = {}; + + int oid_string_result = + snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length); + if (oid_string_result <= 0) { + // TODO error here + die(STATE_UNKNOWN, "snprint_objid failed\n"); + } + + xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); + sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); + + if (verbose > 2) { + printf("Processing oid %s\n", oid_string); + } + + bool got_a_numerical_value = false; + mp_perfdata_value pd_result_val = {0}; + + check_snmp_state_entry result_state = { + .timestamp = query_timestamp, + .oid_length = response.oid_length, + .type = response.type, + }; + + for (size_t i = 0; i < response.oid_length; i++) { + result_state.oid[i] = response.oid[i]; + } + + if (have_previous_state) { + if (query_timestamp == prev_state.timestamp) { + // somehow we have the same timestamp again, that can't be good + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN); + xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid"); + + check_snmp_evaluation result = { + .sc = sc_oid_test, + .state = result_state, + }; + + return result; + } + } + // compute rate time difference + double timeDiff = 0; + if (have_previous_state) { + if (verbose) { + printf("Previous timestamp: %s", ctime(&prev_state.timestamp)); + printf("Current timestamp: %s", ctime(&query_timestamp)); + } + timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier; + } + + mp_perfdata pd_num_val = {}; + + switch (response.type) { + case ASN_OCTET_STR: { + char *tmp = response.string_response; + if (strchr(tmp, '"') != NULL) { + // got double quote in the string + if (strchr(tmp, '\'') != NULL) { + // got single quote in the string too + // dont quote that at all to avoid even more confusion + xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + } else { + // quote with single quotes + xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); + } + } else { + // quote with double quotes + xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); + } + + if (strlen(tmp) == 0) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result); + } + + // String matching test + if ((test_unit.eval_mthd.crit_string)) { + if (strcmp(tmp, eval_params.string_cmp_value)) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK); + } else { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL); + } + } else if (test_unit.eval_mthd.crit_regex) { + const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1; + regmatch_t pmatch[nmatch]; + memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); + + int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0); + if (excode == 0) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL); + } else if (excode != REG_NOMATCH) { + char errbuf[MAX_INPUT_BUFFER] = ""; + regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + printf(_("Execute Error: %s\n"), errbuf); + exit(STATE_CRITICAL); + } else { // REG_NOMATCH + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK); + } + } + } break; + case ASN_COUNTER64: + got_a_numerical_value = true; + + result_state.value.uIntVal = response.value.uIntVal; + result_state.type = response.type; + + // TODO: perfdata unit counter + if (eval_params.calculate_rate && have_previous_state) { + if (prev_state.value.uIntVal > response.value.uIntVal) { + // overflow + unsigned long long tmp = + (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal; + + tmp /= timeDiff; + pd_result_val = mp_create_pd_value(tmp); + } else { + pd_result_val = mp_create_pd_value( + (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff); + } + } else { + // It's only a counter if we cont compute rate + pd_num_val.uom = "c"; + pd_result_val = mp_create_pd_value(response.value.uIntVal); + } + break; + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + got_a_numerical_value = true; + long long treated_value = (long long)response.value.uIntVal; + + if (eval_params.multiplier_set || eval_params.offset_set) { + double processed = 0; + if (eval_params.offset_set) { + processed += eval_params.offset; + } + + if (eval_params.multiplier_set) { + processed = processed * eval_params.multiplier; + } + + treated_value = lround(processed); + } + + result_state.value.intVal = treated_value; + + if (eval_params.calculate_rate && have_previous_state) { + if (verbose > 2) { + printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__, + prev_state.value.intVal); + printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__, + treated_value); + } + double rate = (treated_value - prev_state.value.intVal) / timeDiff; + pd_result_val = mp_create_pd_value(rate); + } else { + pd_result_val = mp_create_pd_value(treated_value); + + if (response.type == ASN_COUNTER) { + pd_num_val.uom = "c"; + } + } + + } break; + case ASN_INTEGER: { + if (eval_params.multiplier_set || eval_params.offset_set) { + double processed = 0; + if (eval_params.multiplier_set) { + processed = (double)response.value.intVal * eval_params.multiplier; + } + + if (eval_params.offset_set) { + processed += eval_params.offset; + } + + result_state.value.doubleVal = processed; + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = + mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(processed); + } + } else { + result_state.value.intVal = response.value.intVal; + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = mp_create_pd_value( + (response.value.intVal - prev_state.value.intVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(response.value.intVal); + } + } + + got_a_numerical_value = true; + } break; + case ASN_FLOAT: // fallthrough + case ASN_DOUBLE: { + got_a_numerical_value = true; + double tmp = response.value.doubleVal; + if (eval_params.offset_set) { + tmp += eval_params.offset; + } + + if (eval_params.multiplier_set) { + tmp *= eval_params.multiplier; + } + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(tmp); + } + got_a_numerical_value = true; + + result_state.value.doubleVal = tmp; + } break; + case ASN_IPADDRESS: + // TODO + } + + if (got_a_numerical_value) { + if (eval_params.use_oid_as_perf_data_label) { + // Use oid for perdata label + pd_num_val.label = strdup(oid_string); + // TODO strdup error checking + } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) { + pd_num_val.label = strdup(test_unit.label); + } else { + pd_num_val.label = strdup(test_unit.oid); + } + + if (!(eval_params.calculate_rate && !have_previous_state)) { + // some kind of numerical value + if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { + pd_num_val.uom = test_unit.unit_value; + } + + pd_num_val.value = pd_result_val; + + xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, + pd_value_to_string(pd_result_val)); + + if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { + xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value); + } + + if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) { + pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold); + mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); + + if (tmp_state == STATE_WARNING) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); + xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", + sc_oid_test.output); + } else if (tmp_state == STATE_CRITICAL) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); + xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", + sc_oid_test.output); + } + } + + mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); + } else { + // should calculate rate, but there is no previous state, so first run + // exit with ok now + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK); + xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay", + sc_oid_test.output); + } + } + + check_snmp_evaluation result = { + .sc = sc_oid_test, + .state = result_state, + }; + + return result; +} + +char *_np_state_generate_key(int argc, char **argv); + +/* + * If time=NULL, use current time. Create state file, with state format + * version, default text. Writes version, time, and data. Avoid locking + * problems - use mv to write and then swap. Possible loss of state data if + * two things writing to same key at same time. + * Will die with UNKNOWN if errors + */ +void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) { + time_t current_time; + if (timestamp == 0) { + time(¤t_time); + } else { + current_time = timestamp; + } + + int result = 0; + + /* If file doesn't currently exist, create directories */ + if (access(stateKey._filename, F_OK) != 0) { + char *directories = NULL; + result = asprintf(&directories, "%s", stateKey._filename); + if (result < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + for (char *p = directories + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { + /* Can't free this! Otherwise error message is wrong! */ + /* np_free(directories); */ + die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); + } + *p = '/'; + } + } + + if (directories) { + free(directories); + } + } + + char *temp_file = NULL; + result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename); + if (result < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + int temp_file_desc = 0; + if ((temp_file_desc = mkstemp(temp_file)) == -1) { + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Cannot create temporary filename")); + } + + FILE *temp_file_pointer = fdopen(temp_file_desc, "w"); + if (temp_file_pointer == NULL) { + close(temp_file_desc); + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Unable to open temporary state file")); + } + + fprintf(temp_file_pointer, "# NP State file\n"); + fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); + fprintf(temp_file_pointer, "%d\n", stateKey.data_version); + fprintf(temp_file_pointer, "%lu\n", current_time); + fprintf(temp_file_pointer, "%s\n", stringToStore); + + fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); + + fflush(temp_file_pointer); + + result = fclose(temp_file_pointer); + + fsync(temp_file_desc); + + if (result != 0) { + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Error writing temp file")); + } + + if (rename(temp_file, stateKey._filename) != 0) { + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Cannot rename state temp file")); + } + + if (temp_file) { + free(temp_file); + } +} + +/* + * Read the state file + */ +bool _np_state_read_file(FILE *state_file, state_key stateKey) { + time_t current_time; + time(¤t_time); + + /* Note: This introduces a limit of 8192 bytes in the string data */ + char *line = (char *)calloc(1, 8192); + if (line == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + bool status = false; + enum { + STATE_FILE_VERSION, + STATE_DATA_VERSION, + STATE_DATA_TIME, + STATE_DATA_TEXT, + STATE_DATA_END + } expected = STATE_FILE_VERSION; + + int failure = 0; + while (!failure && (fgets(line, 8192, state_file)) != NULL) { + size_t pos = strlen(line); + if (line[pos - 1] == '\n') { + line[pos - 1] = '\0'; + } + + if (line[0] == '#') { + continue; + } + + switch (expected) { + case STATE_FILE_VERSION: { + int i = atoi(line); + if (i != NP_STATE_FORMAT_VERSION) { + failure++; + } else { + expected = STATE_DATA_VERSION; + } + } break; + case STATE_DATA_VERSION: { + int i = atoi(line); + if (i != stateKey.data_version) { + failure++; + } else { + expected = STATE_DATA_TIME; + } + } break; + case STATE_DATA_TIME: { + /* If time > now, error */ + time_t data_time = strtoul(line, NULL, 10); + if (data_time > current_time) { + failure++; + } else { + stateKey.state_data->time = data_time; + expected = STATE_DATA_TEXT; + } + } break; + case STATE_DATA_TEXT: + stateKey.state_data->data = strdup(line); + if (stateKey.state_data->data == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + stateKey.state_data->length = strlen(line); + expected = STATE_DATA_END; + status = true; + break; + case STATE_DATA_END:; + } + } + + if (line) { + free(line); + } + return status; +} +/* + * Will return NULL if no data is available (first run). If key currently + * exists, read data. If state file format version is not expected, return + * as if no data. Get state data version number and compares to expected. + * If numerically lower, then return as no previous state. die with UNKNOWN + * if exceptional error. + */ +state_data *np_state_read(state_key stateKey) { + /* Open file. If this fails, no previous state found */ + FILE *statefile = fopen(stateKey._filename, "r"); + state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); + if (statefile != NULL) { + + if (this_state_data == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + this_state_data->data = NULL; + stateKey.state_data = this_state_data; + + if (_np_state_read_file(statefile, stateKey)) { + this_state_data->errorcode = OK; + } else { + this_state_data->errorcode = ERROR; + } + + fclose(statefile); + } else { + // Failed to open state file + this_state_data->errorcode = ERROR; + } + + return stateKey.state_data; +} + +/* + * Internal function. Returns either: + * envvar NAGIOS_PLUGIN_STATE_DIRECTORY + * statically compiled shared state directory + */ +char *_np_state_calculate_location_prefix(void) { + char *env_dir; + + /* Do not allow passing MP_STATE_PATH in setuid plugins + * for security reasons */ + if (!mp_suid()) { + env_dir = getenv("MP_STATE_PATH"); + if (env_dir && env_dir[0] != '\0') { + return env_dir; + } + /* This is the former ENV, for backward-compatibility */ + env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); + if (env_dir && env_dir[0] != '\0') { + return env_dir; + } + } + + return NP_STATE_DIR_PREFIX; +} + +/* + * Initiatializer for state routines. + * Sets variables. Generates filename. Returns np_state_key. die with + * UNKNOWN if exception + */ +state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, + char **argv) { + state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); + if (this_state == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + char *temp_keyname = NULL; + if (keyname == NULL) { + temp_keyname = _np_state_generate_key(argc, argv); + } else { + temp_keyname = strdup(keyname); + if (temp_keyname == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + } + + /* Die if invalid characters used for keyname */ + char *tmp_char = temp_keyname; + while (*tmp_char != '\0') { + if (!(isalnum(*tmp_char) || *tmp_char == '_')) { + die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); + } + tmp_char++; + } + this_state->name = temp_keyname; + this_state->plugin_name = plugin_name; + this_state->data_version = expected_data_version; + this_state->state_data = NULL; + + /* Calculate filename */ + char *temp_filename = NULL; + int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), + (unsigned long)geteuid(), plugin_name, this_state->name); + if (error < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + this_state->_filename = temp_filename; + + return *this_state; +} + +/* + * Returns a string to use as a keyname, based on an md5 hash of argv, thus + * hopefully a unique key per service/plugin invocation. Use the extra-opts + * parse of argv, so that uniqueness in parameters are reflected there. + */ +char *_np_state_generate_key(int argc, char **argv) { + unsigned char result[256]; + +#ifdef USE_OPENSSL + /* + * This code path is chosen if openssl is available (which should be the most common + * scenario). Alternatively, the gnulib implementation/ + * + */ + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + + EVP_DigestInit(ctx, EVP_sha256()); + + for (int i = 0; i < argc; i++) { + EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); + } + + EVP_DigestFinal(ctx, result, NULL); +#else + + struct sha256_ctx ctx; + + for (int i = 0; i < this_monitoring_plugin->argc; i++) { + sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); + } + + sha256_finish_ctx(&ctx, result); +#endif // FOUNDOPENSSL + + char keyname[41]; + for (int i = 0; i < 20; ++i) { + sprintf(&keyname[2 * i], "%02x", result[i]); + } + + keyname[40] = '\0'; + + char *keyname_copy = strdup(keyname); + if (keyname_copy == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + + return keyname_copy; +} diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h index 28e3c4e3..0f7780b1 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.h +++ b/plugins/check_snmp.d/check_snmp_helpers.h @@ -1,7 +1,71 @@ #pragma once #include "./config.h" +#include check_snmp_test_unit check_snmp_test_unit_init(); int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool); check_snmp_config check_snmp_config_init(); + +typedef struct { + oid oid[MAX_OID_LEN]; + size_t oid_length; + unsigned char type; + union { + uint64_t uIntVal; + int64_t intVal; + double doubleVal; + } value; + char *string_response; +} response_value; + +typedef struct { + int errorcode; + response_value *response_values; +} snmp_responces; +snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters); + +// state is similar to response, but only numerics and a timestamp +typedef struct { + time_t timestamp; + oid oid[MAX_OID_LEN]; + size_t oid_length; + unsigned char type; + union { + unsigned long long uIntVal; + long long intVal; + double doubleVal; + } value; +} check_snmp_state_entry; + +typedef struct { + check_snmp_state_entry state; + mp_subcheck sc; +} check_snmp_evaluation; +check_snmp_evaluation evaluate_single_unit(response_value response, + check_snmp_evaluation_parameters eval_params, + check_snmp_test_unit test_unit, time_t query_timestamp, + check_snmp_state_entry prev_state, + bool have_previous_state); + +#define NP_STATE_FORMAT_VERSION 1 + +typedef struct state_data_struct { + time_t time; + void *data; + size_t length; /* Of binary data */ + int errorcode; +} state_data; + +typedef struct state_key_struct { + char *name; + char *plugin_name; + int data_version; + char *_filename; + state_data *state_data; +} state_key; + +state_data *np_state_read(state_key stateKey); +state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, + char **argv); +void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore); diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index e68986e2..a5d5aa52 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -1,7 +1,7 @@ #pragma once -#include "thresholds.h" -#include "states.h" +#include "../../lib/thresholds.h" +#include "../../lib/states.h" #include #include #include @@ -18,7 +18,7 @@ #include #include -#define DEFAULT_PORT "161" +#define DEFAULT_PORT "161" #define DEFAULT_RETRIES 5 typedef struct eval_method { @@ -34,10 +34,8 @@ typedef struct check_snmp_test_unit { mp_thresholds threshold; } check_snmp_test_unit; -typedef struct check_snmp_config { - // SNMP session to use +typedef struct { struct snmp_session snmp_session; - // use getnet instead of get bool use_getnext; @@ -47,7 +45,9 @@ typedef struct check_snmp_config { check_snmp_test_unit *test_units; size_t num_of_test_units; +} check_snmp_config_snmp_parameters; +typedef struct { // State if an empty value is encountered mp_state_enum nulloid_result; @@ -63,7 +63,18 @@ typedef struct check_snmp_config { bool offset_set; // Modify output - bool use_perf_data_labels_from_input; + bool use_oid_as_perf_data_label; + + // activate rate calucation + bool calculate_rate; + unsigned int rate_multiplier; +} check_snmp_evaluation_parameters; + +typedef struct check_snmp_config { + // SNMP session to use + check_snmp_config_snmp_parameters snmp_params; + + check_snmp_evaluation_parameters evaluation_params; mp_output_format output_format; bool output_format_is_set; diff --git a/plugins/tests/check_snmp.t b/plugins/tests/check_snmp.t index bfe42e16..26d67898 100755 --- a/plugins/tests/check_snmp.t +++ b/plugins/tests/check_snmp.t @@ -4,12 +4,13 @@ # use strict; +use warnings; use Test::More; use NPTest; use FindBin qw($Bin); use POSIX qw/strftime/; -my $tests = 81; +my $tests = 75; # Check that all dependent modules are available eval { require NetSNMP::OID; @@ -76,49 +77,36 @@ my $res; $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); -like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); -like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software | -.1.3.6.1.4.1.8072.3.2.67.0: -"Cisco Internetwork Operating System Software -IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version -12.2(20)EWA, RELEASE SOFTWARE (fc1) -Technical Support: http://www.cisco.com/techsupport -Copyright (c) 1986-2004 by cisco Systems, Inc. -"').'/m', "String contains all lines"); +like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines"); # sysContact.0 is "Alice" (from our snmpd.conf) $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0 -o sysContact.0 -o .1.3.6.1.4.1.8072.3.2.67.1"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); -like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); -like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software ').'"?Alice"?'.quotemeta(' Kisco Outernetwork Oserating Gystem Totware | -.1.3.6.1.4.1.8072.3.2.67.0: -"Cisco Internetwork Operating System Software -IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version -12.2(20)EWA, RELEASE SOFTWARE (fc1) -Technical Support: http://www.cisco.com/techsupport -Copyright (c) 1986-2004 by cisco Systems, Inc. -" -.1.3.6.1.4.1.8072.3.2.67.1: -"Kisco Outernetwork Oserating Gystem Totware -Copyleft (c) 2400-2689 by kisco Systrems, Inc."').'/m', "String contains all lines with multiple OIDs"); +# like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); +like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines with multiple OIDs"); +like($res->output, '/.*Alice.*/m', "String contains all lines with multiple OIDs"); +like($res->output, '/.*Kisco Outernetwork Oserating Gystem Totware.*/m', "String contains all lines with multiple OIDs"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.2"); -like($res->output, '/'.quotemeta('SNMP OK - This should not confuse check_snmp \"parser\" | -.1.3.6.1.4.1.8072.3.2.67.2: -"This should not confuse check_snmp \"parser\" -into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('This should not confuse check_snmp \"parser\" | +# .1.3.6.1.4.1.8072.3.2.67.2: +# "This should not confuse check_snmp \"parser\" +# into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.3"); -like($res->output, '/'.quotemeta('SNMP OK - It\'s getting even harder if the line | -.1.3.6.1.4.1.8072.3.2.67.3: -"It\'s getting even harder if the line -ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('It\'s getting even harder if the line | +# .1.3.6.1.4.1.8072.3.2.67.3: +# "It\'s getting even harder if the line +# ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.4"); -like($res->output, '/'.quotemeta('SNMP OK - And now have fun with with this: \"C:\\\\\" | -.1.3.6.1.4.1.8072.3.2.67.4: -"And now have fun with with this: \"C:\\\\\" -because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('And now have fun with with this: \"C:\\\\\" | +# .1.3.6.1.4.1.8072.3.2.67.4: +# "And now have fun with with this: \"C:\\\\\" +# because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*"); @@ -131,156 +119,159 @@ SKIP: { my $ts = time(); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 0, "Returns OK"); - is($res->output, "No previous data to calculate rate - assume okay"); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/"); # test rate 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 1, "WARNING - due to going above rate calculation" ); - is($res->output, "SNMP RATE WARNING - *666* | iso.3.6.1.4.1.8072.3.2.67.10=666;600 "); + like($res->output, "/.*=666.*/"); # test rate with same time $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" ); - is($res->output, "Time duration between plugin calls is invalid"); + like($res->output, "/.*Time duration between plugin calls is invalid.*/"); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK for first call" ); - is($res->output, "No previous data to calculate rate - assume okay" ); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/" ); # test rate 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets 666 | inoctets=666 ", "Check label"); + like($res->output, "/.*inoctets.*=666.*/m", "Check label"); # test rate 3 seconds later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+3))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets 333 | inoctets=333 ", "Check rate decreases due to longer interval"); + like($res->output, "/.*inoctets.*333.*/", "Check rate decreases due to longer interval"); # label performance data check $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test 67996 | test=67996c ", "Check label"); + like($res->output, "/.*test.?=67996c/", "Check label"); - $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); - is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test'test 68662 | \"test'test\"=68662c ", "Check label"); + # following test is deactivated since it was not valid due to the guidelines (no single quote in label allowed) + # $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); + # is($res->return_code, 0, "OK as no thresholds" ); + # is($res->output, "test'test 68662 | \"test'test\"=68662c ", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test\"test'" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test\"test 69328 | 'test\"test'=69328c ", "Check label"); + like($res->output, "/.*'test\"test'=68662c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test -O" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test 69994 | iso.3.6.1.4.1.8072.3.2.67.10=69994c ", "Check label"); + like($res->output, "/.*.67.10.?=69328c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - 70660 | iso.3.6.1.4.1.8072.3.2.67.10=70660c ", "Check label"); + like($res->output, "/.*8072.3.2.67.10.?=69994c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test test'" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test test 71326 | 'test test'=71326c ", "Check label"); + like($res->output, "/.*'test test'=70660c/", "Check label"); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); is($res->return_code, 0, "OK for first call" ); - is($res->output, "No previous data to calculate rate - assume okay" ); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/" ); # test 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets_per_minute 39960 | inoctets_per_minute=39960 ", "Checking multiplier"); + like($res->output, "/.*inoctets_per_minute.*=39960/", "Checking multiplier"); }; -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s '\"stringtests\"'" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s 'stringtests'" ); is($res->return_code, 0, "OK as string matches" ); -is($res->output, 'SNMP OK - "stringtests" | ', "Good string match" ); +like($res->output, '/.*stringtests.*/', "Good string match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s ring" ); is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); -is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Failed string match" ); +like($res->output, '/.*stringtests.*/', "Failed string match" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s '\"stringtests\"'" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s 'stringtests'" ); is($res->return_code, 2, "CRITICAL as string matches but inverted" ); -is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Inverted string match" ); +like($res->output, '/.*"stringtests".*/', "Inverted string match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s ring" ); is($res->return_code, 0, "OK as string doesn't match but inverted" ); -is($res->output, 'SNMP OK - "stringtests" | ', "OK as inverted string no match" ); +like($res->output, '/.*"stringtests".*/', "OK as inverted string no match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.12 -w 4:5" ); -is($res->return_code, 1, "Numeric in string test" ); -is($res->output, 'SNMP WARNING - *3.5* | iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5 ', "WARNING threshold checks for string masquerading as number" ); +# a string is a string and not a number +is($res->return_code, 0, "Numeric in string test" ); +like($res->output, '/.*3.5.*| iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5/', "WARNING threshold checks for string masquerading as number" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.13" ); is($res->return_code, 0, "Not really numeric test" ); -is($res->output, 'SNMP OK - "87.4startswithnumberbutshouldbestring" | ', "Check string with numeric start is still string" ); +like($res->output, '/.*"87.4startswithnumberbutshouldbestring".*/', "Check string with numeric start is still string" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.14" ); is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); -is($res->output, 'SNMP OK - "555\"I said\"" | ', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); +like($res->output, '/.*\'555"I said"\'.*/', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.15 -r 'CUSTOM CHECK OK'" ); is($res->return_code, 0, "String check should check whole string, not a parsed number" ); -is($res->output, 'SNMP OK - "CUSTOM CHECK OK: foo is 12345" | ', "String check with numbers returns whole string"); +like($res->output, '/.*CUSTOM CHECK OK: foo is 12345.*/', "String check with numbers returns whole string"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 0, "Negative integer check OK" ); -is($res->output, 'SNMP OK - -2 | iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3: ', "Negative integer check OK output" ); +like($res->output, '/.*-2.*| iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3:/', "Negative integer check OK output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 1, "Negative integer check WARNING" ); -is($res->output, 'SNMP WARNING - *-3* | iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3: ', "Negative integer check WARNING output" ); +like($res->output, '/.*-3.*| iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3:/', "Negative integer check WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 2, "Negative integer check CRITICAL" ); -is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3: ', "Negative integer check CRITICAL output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3:/', "Negative integer check CRITICAL output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -3: -c -6:" ); is($res->return_code, 1, "Negative integer as string, WARNING" ); -is($res->output, 'SNMP WARNING - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6: ', "Negative integer as string, WARNING output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6:/', "Negative integer as string, WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -2: -c -3:" ); is($res->return_code, 2, "Negative integer as string, CRITICAL" ); -is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3: ', "Negative integer as string, CRITICAL output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3:/', "Negative integer as string, CRITICAL output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); -is($res->return_code, 0, "Negative float OK" ); -is($res->output, 'SNMP OK - -6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); +# deactivated since the perl agent api of snmpd really does not allow floats +# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); +# is($res->return_code, 0, "Negative float OK" ); +# is($res->output, '-6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); -is($res->return_code, 1, "Negative float WARNING" ); -is($res->output, 'SNMP WARNING - *-6.6* | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;@-6.65:~;@-6.55:~ ', "Negative float WARNING output" ); +# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); +# is($res->return_code, 1, "Negative float WARNING" ); +# like($res->output, '/-6.6.*| .*67.18=-6.6;@-6.65:~;@-6.55:~/', "Negative float WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-10:20' -c '2:200000,-20:30'" ); is($res->return_code, 0, "Multiple OIDs with thresholds" ); -like($res->output, '/SNMP OK - \d+ -4 | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); +like($res->output, '/-4.*| .*67.10=\d+c;1:100000;2:200000 .*67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-1:2' -c '2:200000,-20:30'" ); is($res->return_code, 1, "Multiple OIDs with thresholds" ); -like($res->output, '/SNMP WARNING - \d+ \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); +like($res->output, '/-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1 -O" ); is($res->return_code, 2, "Multiple OIDs with some thresholds" ); -like($res->output, '/SNMP CRITICAL - \*\d+\* \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19"); is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); -is($res->output,'SNMP OK - 42 | iso.3.6.1.4.1.8072.3.2.67.19=42 ', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); +like($res->output,'/.*42.*| iso.3.6.1.4.1.8072.3.2.67.19=42/', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 -M .1"); is($res->return_code, 0, "Test multiply RC" ); -is($res->output,'SNMP OK - 4.200000 | iso.3.6.1.4.1.8072.3.2.67.19=4.200000 ' , "Test multiply .1 output" ); +like($res->output,'/.*4.200000.*| iso.3.6.1.4.1.8072.3.2.67.19=4.200000/' , "Test multiply .1 output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' "); -is($res->return_code, 0, "Test multiply RC + format" ); -is($res->output, 'SNMP OK - 4.20 | iso.3.6.1.4.1.8072.3.2.67.19=4.20 ', "Test multiply .1 output + format" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1"); +is($res->return_code, 0, "Test multiply RC" ); +like($res->output, '/.*4.20.*| iso.3.6.1.4.1.8072.3.2.67.19=4.20/', "Test multiply .1 output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' -w 1"); -is($res->return_code, 1, "Test multiply RC + format + thresholds" ); -is($res->output, 'SNMP WARNING - *4.20* | iso.3.6.1.4.1.8072.3.2.67.19=4.20;1 ', "Test multiply .1 output + format + thresholds" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -w 1"); +is($res->return_code, 1, "Test multiply RC + thresholds" ); +like($res->output, '/.*4.20.* | iso.3.6.1.4.1.8072.3.2.67.19=4.20+;1/', "Test multiply .1 output + thresholds" ); diff --git a/plugins/tests/check_snmp_agent.pl b/plugins/tests/check_snmp_agent.pl index 38912e98..608b6f92 100644 --- a/plugins/tests/check_snmp_agent.pl +++ b/plugins/tests/check_snmp_agent.pl @@ -4,9 +4,10 @@ # #use strict; # Doesn't work +use warnings; use NetSNMP::OID qw(:all); use NetSNMP::agent; -use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64); +use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64 ASN_FLOAT); #use Math::Int64 qw(uint64); # Skip that module while we don't need it sub uint64 { return $_ } @@ -22,21 +23,82 @@ IOS (tm) Catalyst 4000 "L3" Switch Software (cat4000-I9K91S-M), Version Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2004 by cisco Systems, Inc. '; -my $multilin2 = "Kisco Outernetwork Oserating Gystem Totware +my $multiline2 = "Kisco Outernetwork Oserating Gystem Totware Copyleft (c) 2400-2689 by kisco Systrems, Inc."; -my $multilin3 = 'This should not confuse check_snmp "parser" +my $multiline3 = 'This should not confuse check_snmp "parser" into thinking there is no 2nd line'; -my $multilin4 = 'It\'s getting even harder if the line +my $multiline4 = 'It\'s getting even harder if the line ends with with this: C:\\'; -my $multilin5 = 'And now have fun with with this: "C:\\" +my $multiline5 = 'And now have fun with with this: "C:\\" because we\'re not done yet!'; # Next are arrays of indexes (Type, initial value and increments) # 0..19 <---- please update comment when adding/removing fields -my @fields = (ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_UNSIGNED, ASN_UNSIGNED, ASN_COUNTER, ASN_COUNTER64, ASN_UNSIGNED, ASN_COUNTER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER ); -my @values = ($multiline, $multilin2, $multilin3, $multilin4, $multilin5, 4294965296, 1000, 4294965296, uint64("18446744073709351616"), int(rand(2**32)), 64000, "stringtests", "3.5", "87.4startswithnumberbutshouldbestring", '555"I said"', 'CUSTOM CHECK OK: foo is 12345', -2, '-4', '-6.6', 42 ); +my @fields = (ASN_OCTET_STR, # 0 + ASN_OCTET_STR, # 1 + ASN_OCTET_STR, # 2 + ASN_OCTET_STR, # 3 + ASN_OCTET_STR, # 4 + ASN_UNSIGNED, # 5 + ASN_UNSIGNED, # 6 + ASN_COUNTER, # 7 + ASN_COUNTER64, # 8 + ASN_UNSIGNED, # 9 + ASN_COUNTER, # 10 + ASN_OCTET_STR, # 11 + ASN_OCTET_STR, # 12 + ASN_OCTET_STR, # 13 + ASN_OCTET_STR, # 14 + ASN_OCTET_STR, # 15 + ASN_INTEGER, # 16 + ASN_INTEGER, # 17 + ASN_FLOAT, # 18 + ASN_INTEGER # 19 + ); +my @values = ($multiline, # 0 + $multiline2, # 1 + $multiline3, # 2 + $multiline4, # 3 + $multiline5, # 4 + 4294965296, # 5 + 1000, # 6 + 4294965296, # 7 + uint64("18446744073709351616"), # 8 + int(rand(2**32)), # 9 + 64000, # 10 + "stringtests", # 11 + "3.5", # 12 + "87.4startswithnumberbutshouldbestring", # 13 + '555"I said"', # 14 + 'CUSTOM CHECK OK: foo is 12345', # 15 + '-2', # 16 + '-4', # 17 + '-6.6', # 18 + 42 # 19 + ); # undef increments are randomized -my @incrts = (undef, undef, undef, undef, undef, 1000, -500, 1000, 100000, undef, 666, undef, undef, undef, undef, undef, -1, undef, undef, 0 ); +my @incrts = ( + undef, # 0 + undef, # 1 + undef, # 2 + undef, # 3 + undef, # 4 + 1000, # 5 + -500, # 6 + 1000, # 7 + 100000, # 8 + undef, # 9 + 666, # 10 + undef, # 11 + undef, # 12 + undef, # 13 + undef, # 14 + undef, # 15 + -1, # 16 + 0, # 17 + undef, # 18 + 0 # 19 + ); # Number of elements in our OID my $oidelts; diff --git a/plugins/tests/test_check_snmp.c b/plugins/tests/test_check_snmp.c new file mode 100644 index 00000000..d71706d0 --- /dev/null +++ b/plugins/tests/test_check_snmp.c @@ -0,0 +1,175 @@ +/***************************************************************************** + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + *****************************************************************************/ + +#include "tap.h" +#include "../../config.h" + +#include +#include +#include + +#include "utils_base.c" +#include "../check_snmp.d/check_snmp_helpers.h" + +char *_np_state_generate_key(int argc, char **argv); +char *_np_state_calculate_location_prefix(void); + +int main(int argc, char **argv) { + char *temp_string = (char *)_np_state_generate_key(argc, argv); + ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), + "Got hash with exe and no parameters") || + diag("You are probably running in wrong directory. Must run as ./test_utils"); + + int fake_argc = 4; + char *fake_argv[] = { + "./test_utils", + "here", + "--and", + "now", + }; + temp_string = (char *)_np_state_generate_key(fake_argc, fake_argv); + ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), + "Got based on expected argv"); + + unsetenv("MP_STATE_PATH"); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory"); + + setenv("MP_STATE_PATH", "", 1); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string"); + + setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory"); + + fake_argc = 1; + fake_argv[0] = "./test_utils"; + state_key temp_state_key1 = np_enable_state(NULL, 51, "check_test", fake_argc, fake_argv); + ok(!strcmp(temp_state_key1.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key1.name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), + "Got generated filename"); + + state_key temp_state_key2 = + np_enable_state("allowedchars_in_keyname", 77, "check_snmp", fake_argc, fake_argv); + + char state_path[1024]; + sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", + (unsigned long)geteuid()); + ok(!strcmp(temp_state_key2.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key2.name, "allowedchars_in_keyname"), "Got key name with valid chars"); + ok(!strcmp(temp_state_key2._filename, state_path), "Got internal filename"); + + /* Don't do this test just yet. Will die */ + /* + np_enable_state("bad^chars$in@here", 77); + temp_state_key = this_monitoring_plugin->state; + ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" + ); + */ + + state_key temp_state_key3 = + np_enable_state("funnykeyname", 54, "check_snmp", fake_argc, fake_argv); + sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", + (unsigned long)geteuid()); + ok(!strcmp(temp_state_key3.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key3.name, "funnykeyname"), "Got key name"); + + ok(!strcmp(temp_state_key3._filename, state_path), "Got internal filename"); + ok(temp_state_key3.data_version == 54, "Version set"); + + state_data *temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Got no state data as file does not exist"); + + /* + temp_fp = fopen("var/statefile", "r"); + if (temp_fp==NULL) + printf("Error opening. errno=%d\n", errno); + printf("temp_fp=%s\n", temp_fp); + ok( _np_state_read_file(temp_fp) == true, "Can read state file" ); + fclose(temp_fp); + */ + + temp_state_key3._filename = "var/statefile"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data != NULL, "Got state data now") || + diag("Are you running in right directory? Will get coredump next if not"); + ok(temp_state_data->time == 1234567890, "Got time"); + ok(!strcmp((char *)temp_state_data->data, "String to read"), "Data as expected"); + + temp_state_key3.data_version = 53; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Older data version gives NULL"); + temp_state_key3.data_version = 54; + + temp_state_key3._filename = "var/nonexistent"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Missing file gives NULL"); + + temp_state_key3._filename = "var/oldformat"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Old file format gives NULL"); + + temp_state_key3._filename = "var/baddate"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Bad date gives NULL"); + + temp_state_key3._filename = "var/missingdataline"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Missing data line gives NULL"); + + unlink("var/generated"); + temp_state_key3._filename = "var/generated"; + + time_t current_time = 1234567890; + np_state_write_string(temp_state_key3, current_time, "String to read"); + ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected"); + + unlink("var/generated_directory/statefile"); + unlink("var/generated_directory"); + temp_state_key3._filename = "var/generated_directory/statefile"; + current_time = 1234567890; + np_state_write_string(temp_state_key3, current_time, "String to read"); + ok(system("cmp var/generated_directory/statefile var/statefile") == 0, + "Have created directory"); + + /* This test to check cannot write to dir - can't automate yet */ + /* + unlink("var/generated_bad_dir"); + mkdir("var/generated_bad_dir", S_IRUSR); + np_state_write_string(current_time, "String to read"); + */ + + temp_state_key3._filename = "var/generated"; + time(¤t_time); + np_state_write_string(temp_state_key3, 0, "String to read"); + temp_state_data = np_state_read(temp_state_key3); + /* Check time is set to current_time */ + ok(system("cmp var/generated var/statefile > /dev/null") != 0, + "Generated file should be different this time"); + ok(temp_state_data->time - current_time <= 1, "Has time generated from current time"); + + /* Don't know how to automatically test this. Need to be able to redefine die and catch the + * error */ + /* + temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write"; + np_state_write_string(0, "Bad file"); + */ + + np_cleanup(); +} diff --git a/plugins/tests/test_check_snmp.t b/plugins/tests/test_check_snmp.t new file mode 100755 index 00000000..967633e9 --- /dev/null +++ b/plugins/tests/test_check_snmp.t @@ -0,0 +1,6 @@ +#!/usr/bin/perl +use Test::More; +if (! -e "./test_check_snmp") { + plan skip_all => "./test_check_snmp not compiled - please enable libtap library to test"; +} +exec "./test_check_snmp"; -- cgit v1.2.3-74-g34f1 From be9db2e02f5e3ffdf14c84beb5382336bbfca063 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:59:20 +0200 Subject: lib: code formatting, perfdata label sanity checking and so on --- lib/perfdata.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/lib/perfdata.c b/lib/perfdata.c index b87de7e0..2930a8bc 100644 --- a/lib/perfdata.c +++ b/lib/perfdata.c @@ -33,7 +33,18 @@ char *pd_value_to_string(const mp_perfdata_value pd) { char *pd_to_string(mp_perfdata pd) { assert(pd.label != NULL); char *result = NULL; - asprintf(&result, "'%s'=", pd.label); + + if (strchr(pd.label, '\'') == NULL) { + asprintf(&result, "'%s'=", pd.label); + } else { + // we have a illegal single quote in the string + // replace it silently instead of complaining + for (char *ptr = pd.label; *ptr == '\0'; ptr++) { + if (*ptr == '\'') { + *ptr = '_'; + } + } + } asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); @@ -249,7 +260,9 @@ char *mp_range_to_string(const mp_range input) { return result; } -mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { return mp_set_pd_value_double(pd, value); } +mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { + return mp_set_pd_value_double(pd, value); +} mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { pd.value.pd_double = value; @@ -257,15 +270,25 @@ mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { return pd; } -mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { return mp_set_pd_value_long_long(pd, (long long)value); } +mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { + return mp_set_pd_value_long_long(pd, (long long)value); +} -mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } +mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) { + return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); +} -mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); } +mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { + return mp_set_pd_value_long_long(pd, (long long)value); +} -mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } +mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { + return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); +} -mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { return mp_set_pd_value_long_long(pd, (long long)value); } +mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { + return mp_set_pd_value_long_long(pd, (long long)value); +} mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); @@ -290,19 +313,33 @@ mp_perfdata_value mp_create_pd_value_double(double value) { return res; } -mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); } +mp_perfdata_value mp_create_pd_value_float(float value) { + return mp_create_pd_value_double((double)value); +} -mp_perfdata_value mp_create_pd_value_char(char value) { return mp_create_pd_value_long_long((long long)value); } +mp_perfdata_value mp_create_pd_value_char(char value) { + return mp_create_pd_value_long_long((long long)value); +} -mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } +mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { + return mp_create_pd_value_u_long_long((unsigned long long)value); +} -mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); } +mp_perfdata_value mp_create_pd_value_int(int value) { + return mp_create_pd_value_long_long((long long)value); +} -mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } +mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { + return mp_create_pd_value_u_long_long((unsigned long long)value); +} -mp_perfdata_value mp_create_pd_value_long(long value) { return mp_create_pd_value_long_long((long long)value); } +mp_perfdata_value mp_create_pd_value_long(long value) { + return mp_create_pd_value_long_long((long long)value); +} -mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } +mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { + return mp_create_pd_value_u_long_long((unsigned long long)value); +} mp_perfdata_value mp_create_pd_value_long_long(long long value) { mp_perfdata_value res = {0}; @@ -368,6 +405,13 @@ mp_range_parsed mp_parse_range_string(const char *input) { } char *working_copy = strdup(input); + if (working_copy == NULL) { + // strdup error, probably + mp_range_parsed result = { + .error = MP_RANGE_PARSING_FAILURE, + }; + return result; + } input = working_copy; char *separator = index(working_copy, ':'); -- cgit v1.2.3-74-g34f1 From 2aabc4d4904a5a0994cac661a20c27ec3d1c79ee Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:04:23 +0200 Subject: Fix spelling ... --- plugins/check_snmp.d/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index a5d5aa52..e7b6d1b3 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -65,7 +65,7 @@ typedef struct { // Modify output bool use_oid_as_perf_data_label; - // activate rate calucation + // activate rate calculation bool calculate_rate; unsigned int rate_multiplier; } check_snmp_evaluation_parameters; -- cgit v1.2.3-74-g34f1 From c38276853057e2152de5106fc1de9c002b7d964b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:35:02 +0200 Subject: Little adaptions for old compilers --- plugins/check_snmp.d/check_snmp_helpers.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 9fa396d8..e18da4a3 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -273,8 +273,7 @@ snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) { } result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal); result.response_values[loop_index].type = vars->type; - break; - } + } break; case ASN_IPADDRESS: if (verbose) { printf("Debug: Got an IP address\n"); @@ -282,13 +281,13 @@ snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) { result.response_values[loop_index].type = vars->type; // TODO: print address here, state always ok? or regex match? - continue; + break; default: if (verbose) { printf("Debug: Got a unmatched result type: %hhu\n", vars->type); } // TODO: Error here? - continue; + break; } } -- cgit v1.2.3-74-g34f1 From 76e1a1fa8cbb11bc67fae55a0ae2b596faa66444 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:18:21 +0200 Subject: Activate mib parsing in Debian CI pipeline --- .github/prepare_debian.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/prepare_debian.sh b/.github/prepare_debian.sh index b5cacd4a..cffe98c5 100755 --- a/.github/prepare_debian.sh +++ b/.github/prepare_debian.sh @@ -112,6 +112,8 @@ mkdir -p /var/lib/snmp/mib_indexes sed -e 's/^agentaddress.*/agentaddress 127.0.0.1/' -i /etc/snmp/snmpd.conf service snmpd start +sed 's/^mibs ://' -i /etc/snmp/snmp.conf + # start cron, will be used by check_nagios cron -- cgit v1.2.3-74-g34f1 From 2192c6a8a108fc3586ef0bd8e990d7217d2689dc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:27:27 +0200 Subject: Add break statement to switch path --- plugins/check_snmp.d/check_snmp_helpers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index e18da4a3..ecbfc5dd 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -533,6 +533,7 @@ check_snmp_evaluation evaluate_single_unit(response_value response, } break; case ASN_IPADDRESS: // TODO + break; } if (got_a_numerical_value) { -- cgit v1.2.3-74-g34f1 From c43f845c22a9e879546472aa9051d7ca0803ef2a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:43:27 +0200 Subject: Adjust number of tests --- plugins/t/check_snmp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t index 99e01add..8d435df3 100644 --- a/plugins/t/check_snmp.t +++ b/plugins/t/check_snmp.t @@ -10,7 +10,7 @@ use NPTest; BEGIN { plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp"; - plan tests => 42; + plan tests => 62; } my $res; -- cgit v1.2.3-74-g34f1