/***************************************************************************** * * Monitoring check_snmp plugin * * License: GPL * Copyright (c) 1999-2024 Monitoring Plugins Development Team * * Description: * * This file contains the check_snmp plugin * * Check status of remote machines and obtain system information via SNMP * * * 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 . * * *****************************************************************************/ 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 "../lib/states.h" #include "../lib/utils_base.h" #include "../lib/output.h" #include "check_snmp.d/check_snmp_helpers.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 #include "../gl/regex.h" #include "../gl/base64.h" #include const char DEFAULT_COMMUNITY[] = "public"; const char DEFAULT_MIBLIST[] = "ALL"; #define DEFAULT_AUTH_PROTOCOL "MD5" #ifdef HAVE_USM_DES_PRIV_PROTOCOL # define DEFAULT_PRIV_PROTOCOL "DES" #else # define DEFAULT_PRIV_PROTOCOL "AES" #endif typedef struct proces_arguments_wrapper { int errorcode; check_snmp_config config; } process_arguments_wrapper; static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static char *trim_whitespaces_and_check_quoting(char *str); static char *get_next_argument(char *str); void print_usage(void); void print_help(void); 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, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); timeout_interval = DEFAULT_SOCKET_TIMEOUT; 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); np_set_args(argc, argv); // Initialize net-snmp before touching the session we are going to use init_snmp("check_snmp"); process_arguments_wrapper paw_tmp = process_arguments(argc, argv); if (paw_tmp.errorcode == ERROR) { usage4(_("Could not parse arguments")); } check_snmp_config config = paw_tmp.config; if (config.output_format_is_set) { mp_set_format(config.output_format); } /* Set signal handling and alarm */ if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { usage4(_("Cannot catch SIGALRM")); } time_t current_time; time(¤t_time); if (verbose > 2) { printf("current time: %s (timestamp: %lu)\n", ctime(¤t_time), current_time); } snmp_responces response = do_snmp_query(config.snmp_params); mp_check overall = mp_check_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); } check_snmp_state_entry *prev_state = NULL; bool have_previous_state = false; 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 { // sanity check recover_state_data_type prev_state_wrapper = recover_state_data(previous_state->data, (idx_t)previous_state->length); if (prev_state_wrapper.errorcode == OK) { have_previous_state = true; prev_state = prev_state_wrapper.state; } else { have_previous_state = false; prev_state = NULL; } } } 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"); } } // 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); } check_snmp_state_entry previous_unit_state = {}; if (config.evaluation_params.calculate_rate && have_previous_state) { previous_unit_state = prev_state[loop_index]; } 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.evaluation_params.calculate_rate && mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) { new_state[loop_index] = single_eval.state; } mp_add_subcheck_to_check(&overall, single_eval.sc); } 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 (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_exit(overall); } /* 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, calculate_rate, rate_multiplier }; 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, 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, 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) { process_arguments_wrapper result = { .errorcode = ERROR, }; return result; } // 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"); } for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL; ptr = strtok(NULL, ", "), oid_counter++) { } break; } 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; } } /* Check whether at least one OID was given */ if (oid_counter == 0) { die(STATE_UNKNOWN, _("No OIDs specified\n")); } // 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"); } for (size_t i = 0; i < oid_counter; i++) { tmp[i] = check_snmp_test_unit_init(); } check_snmp_config config = check_snmp_config_init(); config.snmp_params.test_units = tmp; config.snmp_params.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; bool snmp_version_set_explicitely = false; // TODO error checking 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 '?': /* usage */ usage5(); case 'h': /* help */ print_help(); exit(STATE_UNKNOWN); case 'V': /* version */ print_revision(progname, NP_VERSION); exit(STATE_UNKNOWN); case 'v': /* verbose */ verbose++; break; /* Connection info */ case 'C': /* group or community */ 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_params.snmp_session.peername = optarg; break; 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': /* use_getnext instead of get */ config.snmp_params.use_getnext = true; break; case 'P': /* SNMP protocol version */ if (strcasecmp("1", optarg) == 0) { config.snmp_params.snmp_session.version = SNMP_VERSION_1; } else if (strcasecmp("2c", optarg) == 0) { config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } else if (strcasecmp("3", optarg) == 0) { config.snmp_params.snmp_session.version = SNMP_VERSION_3; } else { die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); } snmp_version_set_explicitely = true; break; case 'N': /* SNMPv3 context name */ 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_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; } else if (strcasecmp("authNoPriv", optarg) == 0) { config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; } else if (strcasecmp("authPriv", optarg) == 0) { 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_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_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; config.snmp_params.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol); } else if (strcasecmp("SHA", optarg) == 0) { config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; config.snmp_params.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol); } else if (strcasecmp("SHA224", optarg) == 0) { config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; config.snmp_params.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); } else if (strcasecmp("SHA256", optarg) == 0) { config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; config.snmp_params.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); } else if (strcasecmp("SHA384", optarg) == 0) { config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; config.snmp_params.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol); } else if (strcasecmp("SHA512", optarg) == 0) { config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; config.snmp_params.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol); } else { die(STATE_UNKNOWN, "Unknown authentication protocol"); } break; case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { #ifdef HAVE_USM_DES_PRIV_PROTOCOL 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_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_params.snmp_session.securityAuthProto = usmAES192PrivProtocol; config.snmp_params.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES192PrivProtocol); } else if (strcasecmp("AES256", optarg) == 0) { 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 = // 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, "Unknown privacy protocol"); } break; case 'A': /* auth passwd */ authpasswd = (unsigned char *)optarg; break; case 'X': /* priv passwd */ 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_params.snmp_session.retries = atoi(optarg); } break; case 't': /* timeout period */ if (!is_integer(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); } else { timeout_interval = (unsigned int)atoi(optarg); } break; /* Test parameters */ case 'c': /* critical threshold */ check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true); break; case 'w': /* warning threshold */ check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false); break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { /* * we have something other than digits, periods and comas, * so we have a mib variable, rather than just an SNMP OID, * so we have to actually read the mib files */ config.snmp_params.need_mibs = true; } for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), tmp_oid_counter++) { 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.evaluation_params.nulloid_result = atoi(optarg); } break; case 's': /* string or substring */ 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; // 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; int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags); if (errcode != 0) { char 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.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true; } break; case 'l': /* label */ { if (labels_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { config.snmp_params.test_units[labels_counter].label = ptr + 1; } else { 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.snmp_params.test_units[labels_counter].label = ptr + 1; } else { config.snmp_params.test_units[labels_counter].label = ptr; } } labels_counter++; } break; case 'u': /* units */ { if (unitv_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { 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.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { config.snmp_params.test_units[unitv_counter].unit_value = ptr; } } unitv_counter++; } break; case offset_index: config.evaluation_params.offset = strtod(optarg, NULL); config.evaluation_params.offset_set = true; break; case invert_search_index: config.evaluation_params.invert_search = false; break; case 'O': 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? connection_prefix = "udp"; break; case '6': connection_prefix = "udp6"; break; case connection_prefix_index: connection_prefix = optarg; break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { config.evaluation_params.multiplier = strtod(optarg, NULL); config.evaluation_params.multiplier_set = true; } break; case ignore_mib_parsing_errors_index: config.snmp_params.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; } 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_params.snmp_session.peername == NULL) { config.snmp_params.snmp_session.peername = argv[optind]; } // 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_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 || 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_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_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_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_params.snmp_session.peername, "unix:%s", config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "ipx") == 0) { 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"); } } /* Check server_address is given */ if (config.snmp_params.snmp_session.peername == NULL) { die(STATE_UNKNOWN, _("No host specified\n")); } if (port != NULL) { 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.snmp_params.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); } // Historical default is SNMP v2c if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) { config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } 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_params.snmp_session.securityName == NULL) { die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); } 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_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"); } } // fall through case SNMP_SEC_LEVEL_AUTHNOPRIV: { if (privpasswd == NULL) { die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); } 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"); } } break; case SNMP_SEC_LEVEL_NOAUTH: // No auth, no priv, not much todo break; } } process_arguments_wrapper result = { .config = config, .errorcode = OK, }; return result; } /* trim leading whitespace if there is a leading quote, make sure it balances */ 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, "'")) { die(STATE_UNKNOWN, _("Unbalanced quotes\n")); } } return str; } /* if there's a leading quote, advance to the trailing quote set the trailing quote to '\x0' if the string continues, advance beyond the comma */ char *get_next_argument(char *str) { if (str[0] == '\'') { str[0] = 0; if (strlen(str) > 1) { str = strstr(str + 1, "'"); return (++str); } return NULL; } if (str[0] == ',') { str[0] = 0; if (strlen(str) > 1) { return (++str); } return NULL; } if ((str = strstr(str, ",")) && strlen(str) > 1) { str[0] = 0; return (++str); } return NULL; } void print_help(void) { print_revision(progname, NP_VERSION); printf(COPYRIGHT, copyright, email); printf("%s\n", _("Check status of remote machines and obtain system information via SNMP")); printf("\n\n"); print_usage(); printf(UT_HELP_VRSN); printf(UT_EXTRA_OPTS); printf(UT_HOST_PORT, 'p', DEFAULT_PORT); /* SNMP and Authentication Protocol */ printf(" %s\n", "-n, --next"); printf(" %s\n", _("Use SNMP GETNEXT instead of SNMP GET")); printf(" %s\n", "-P, --protocol=[1|2c|3]"); printf(" %s\n", _("SNMP protocol version")); printf(" %s\n", "-N, --context=CONTEXT"); printf(" %s\n", _("SNMPv3 context")); printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); printf(" %s\n", _("SNMPv3 securityLevel")); printf(" %s\n", "-a, --authproto=[MD5|SHA]"); printf(" %s\n", _("SNMPv3 auth proto")); #ifdef HAVE_USM_DES_PRIV_PROTOCOL 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"); printf(" %s ", _("Optional community string for SNMP communication")); printf("(%s \"%s\")\n", _("default is"), DEFAULT_COMMUNITY); printf(" %s\n", "-U, --secname=USERNAME"); printf(" %s\n", _("SNMPv3 username")); printf(" %s\n", "-A, --authpasswd=PASSWORD"); 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", _("for symbolic OIDs.)")); 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=#"); printf(" %s\n", _("If the check returns a 0 length string or NULL value")); printf(" %s\n", _("This option allows you to choose what status you want it to exit")); printf(" %s\n", _("Excluding this option renders the default exit of 3(STATE_UNKNOWN)")); printf(" %s\n", _("0 = OK")); printf(" %s\n", _("1 = WARNING")); printf(" %s\n", _("2 = CRITICAL")); printf(" %s\n", _("3 = UNKNOWN")); /* Tests Against Integers */ printf(" %s\n", "-w, --warning=THRESHOLD(s)"); printf(" %s\n", _("Warning threshold range(s)")); printf(" %s\n", "-c, --critical=THRESHOLD(s)"); printf(" %s\n", _("Critical threshold range(s)")); printf(" %s\n", "--offset=OFFSET"); printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); /* Tests Against Strings */ 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", "-R, --eregi=REGEX"); 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)")); /* Output Formatting */ printf(" %s\n", "-l, --label=STRING"); 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", "-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: " "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\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", _("Do to not print errors encountered when parsing MIB files")); printf(UT_VERBOSE); printf("\n"); 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", _("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", _("- 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", _("returned from the SNMP query is an unsigned integer.")); printf(UT_SUPPORT); } void print_usage(void) { printf("%s\n", _("Usage:")); printf("%s -H -o [-w warn_range] [-c crit_range]\n", progname); printf("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n"); 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]\n"); }