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/check_snmp.c | 1654 ++++++++++++++++++++++---------------------------- 1 file changed, 728 insertions(+), 926 deletions(-) (limited to 'plugins/check_snmp.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); } -- 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 (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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 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(-) (limited to 'plugins/check_snmp.c') 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 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(+) (limited to 'plugins/check_snmp.c') 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 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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(-) (limited to 'plugins/check_snmp.c') 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 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 (limited to 'plugins/check_snmp.c') 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