From 4966b920a2392484a6ae83a4a499c24a2942c832 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:29:08 +0200 Subject: General smal improvements to the lib logic --- lib/utils_base.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/utils_base.h') diff --git a/lib/utils_base.h b/lib/utils_base.h index 123066f8..8fb114c2 100644 --- a/lib/utils_base.h +++ b/lib/utils_base.h @@ -7,6 +7,7 @@ #include "./perfdata.h" #include "./thresholds.h" +#include "states.h" #ifndef USE_OPENSSL @@ -55,7 +56,7 @@ void set_thresholds(thresholds **, char *, char *); void print_thresholds(const char *, thresholds *); bool check_range(double, range *); bool mp_check_range(mp_perfdata_value, mp_range); -int get_status(double, thresholds *); +mp_state_enum get_status(double, thresholds *); /* Handle timeouts */ extern int timeout_state; @@ -107,6 +108,6 @@ 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); -const char *state_text(int); +const char *state_text(mp_state_enum); #endif /* _UTILS_BASE_ */ -- 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 'lib/utils_base.h') diff --git a/lib/Makefile.am b/lib/Makefile.am index a9f3ff40..27a08278 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -4,7 +4,7 @@ SUBDIRS = . tests noinst_LIBRARIES = libmonitoringplug.a -AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ +AM_CPPFLAGS = \ -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins libmonitoringplug_a_SOURCES = utils_base.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c diff --git a/lib/tests/test_utils.c b/lib/tests/test_utils.c index c3150f00..8040dec8 100644 --- a/lib/tests/test_utils.c +++ b/lib/tests/test_utils.c @@ -28,17 +28,7 @@ #include "utils_base.c" int main(int argc, char **argv) { - char state_path[1024]; - range *range; - double temp; - thresholds *thresholds = NULL; - int i, rc; - char *temp_string; - state_key *temp_state_key = NULL; - state_data *temp_state_data; - time_t current_time; - - plan_tests(185); + plan_tests(155); ok(this_monitoring_plugin == NULL, "monitoring_plugin not initialised"); @@ -57,7 +47,7 @@ int main(int argc, char **argv) { np_set_args(argc, argv); - range = parse_range_string("6"); + range *range = parse_range_string("6"); ok(range != NULL, "'6' is valid range"); ok(range->start == 0, "Start correct"); ok(range->start_infinity == false, "Not using negative infinity"); @@ -97,7 +87,7 @@ int main(int argc, char **argv) { free(range); range = parse_range_string("12345678901234567890:"); - temp = atof("12345678901234567890"); /* Can't just use this because number too large */ + double temp = atof("12345678901234567890"); /* Can't just use this because number too large */ ok(range != NULL, "'12345678901234567890:' is valid range"); ok(range->start == temp, "Start correct"); ok(range->start_infinity == false, "Not using negative infinity"); @@ -158,32 +148,34 @@ int main(int argc, char **argv) { range = parse_range_string("2:1"); ok(range == NULL, "'2:1' rejected"); - rc = _set_thresholds(&thresholds, NULL, NULL); - ok(rc == 0, "Thresholds (NULL, NULL) set"); + thresholds *thresholds = NULL; + int returnCode; + returnCode = _set_thresholds(&thresholds, NULL, NULL); + ok(returnCode == 0, "Thresholds (NULL, NULL) set"); ok(thresholds->warning == NULL, "Warning not set"); ok(thresholds->critical == NULL, "Critical not set"); - rc = _set_thresholds(&thresholds, NULL, "80"); - ok(rc == 0, "Thresholds (NULL, '80') set"); + returnCode = _set_thresholds(&thresholds, NULL, "80"); + ok(returnCode == 0, "Thresholds (NULL, '80') set"); ok(thresholds->warning == NULL, "Warning not set"); ok(thresholds->critical->end == 80, "Critical set correctly"); - rc = _set_thresholds(&thresholds, "5:33", NULL); - ok(rc == 0, "Thresholds ('5:33', NULL) set"); + returnCode = _set_thresholds(&thresholds, "5:33", NULL); + ok(returnCode == 0, "Thresholds ('5:33', NULL) set"); ok(thresholds->warning->start == 5, "Warning start set"); ok(thresholds->warning->end == 33, "Warning end set"); ok(thresholds->critical == NULL, "Critical not set"); - rc = _set_thresholds(&thresholds, "30", "60"); - ok(rc == 0, "Thresholds ('30', '60') set"); + returnCode = _set_thresholds(&thresholds, "30", "60"); + ok(returnCode == 0, "Thresholds ('30', '60') set"); ok(thresholds->warning->end == 30, "Warning set correctly"); ok(thresholds->critical->end == 60, "Critical set correctly"); ok(get_status(15.3, thresholds) == STATE_OK, "15.3 - ok"); ok(get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning"); ok(get_status(69, thresholds) == STATE_CRITICAL, "69 - critical"); - rc = _set_thresholds(&thresholds, "-10:-2", "-30:20"); - ok(rc == 0, "Thresholds ('-30:20', '-10:-2') set"); + returnCode = _set_thresholds(&thresholds, "-10:-2", "-30:20"); + ok(returnCode == 0, "Thresholds ('-30:20', '-10:-2') set"); ok(thresholds->warning->start == -10, "Warning start set correctly"); ok(thresholds->warning->end == -2, "Warning end set correctly"); ok(thresholds->critical->start == -30, "Critical start set correctly"); @@ -304,164 +296,28 @@ int main(int argc, char **argv) { test = np_extract_ntpvar("", "foo"); ok(!test, "Empty string return NULL"); - /* This is the result of running ./test_utils */ - temp_string = (char *)_np_state_generate_key(); - ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got hash with exe and no parameters") || - diag("You are probably running in wrong directory. Must run as ./test_utils"); - - this_monitoring_plugin->argc = 4; - this_monitoring_plugin->argv[0] = "./test_utils"; - this_monitoring_plugin->argv[1] = "here"; - this_monitoring_plugin->argv[2] = "--and"; - this_monitoring_plugin->argv[3] = "now"; - temp_string = (char *)_np_state_generate_key(); - ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), "Got based on expected argv"); - - unsetenv("MP_STATE_PATH"); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory"); - - setenv("MP_STATE_PATH", "", 1); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string"); - - setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory"); - - ok(temp_state_key == NULL, "temp_state_key initially empty"); - - this_monitoring_plugin->argc = 1; - this_monitoring_plugin->argv[0] = "./test_utils"; - np_enable_state(NULL, 51); - temp_state_key = this_monitoring_plugin->state; - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got generated filename"); - - np_enable_state("allowedchars_in_keyname", 77); - temp_state_key = this_monitoring_plugin->state; - sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", (unsigned long)geteuid()); - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "allowedchars_in_keyname"), "Got key name with valid chars"); - ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename"); - - /* Don't do this test just yet. Will die */ - /* - np_enable_state("bad^chars$in@here", 77); - temp_state_key = this_monitoring_plugin->state; - ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" ); - */ - - np_enable_state("funnykeyname", 54); - temp_state_key = this_monitoring_plugin->state; - sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", (unsigned long)geteuid()); - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "funnykeyname"), "Got key name"); - - ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename"); - ok(temp_state_key->data_version == 54, "Version set"); - - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Got no state data as file does not exist"); - - /* - temp_fp = fopen("var/statefile", "r"); - if (temp_fp==NULL) - printf("Error opening. errno=%d\n", errno); - printf("temp_fp=%s\n", temp_fp); - ok( _np_state_read_file(temp_fp) == true, "Can read state file" ); - fclose(temp_fp); - */ - - temp_state_key->_filename = "var/statefile"; - temp_state_data = np_state_read(); - ok(this_monitoring_plugin->state->state_data != NULL, "Got state data now") || - diag("Are you running in right directory? Will get coredump next if not"); - ok(this_monitoring_plugin->state->state_data->time == 1234567890, "Got time"); - ok(!strcmp((char *)this_monitoring_plugin->state->state_data->data, "String to read"), "Data as expected"); - - temp_state_key->data_version = 53; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Older data version gives NULL"); - temp_state_key->data_version = 54; - - temp_state_key->_filename = "var/nonexistent"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Missing file gives NULL"); - ok(this_monitoring_plugin->state->state_data == NULL, "No state information"); - - temp_state_key->_filename = "var/oldformat"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Old file format gives NULL"); - - temp_state_key->_filename = "var/baddate"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Bad date gives NULL"); - - temp_state_key->_filename = "var/missingdataline"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Missing data line gives NULL"); - - unlink("var/generated"); - temp_state_key->_filename = "var/generated"; - current_time = 1234567890; - np_state_write_string(current_time, "String to read"); - ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected"); - - unlink("var/generated_directory/statefile"); - unlink("var/generated_directory"); - temp_state_key->_filename = "var/generated_directory/statefile"; - current_time = 1234567890; - np_state_write_string(current_time, "String to read"); - ok(system("cmp var/generated_directory/statefile var/statefile") == 0, "Have created directory"); - - /* This test to check cannot write to dir - can't automate yet */ - /* - unlink("var/generated_bad_dir"); - mkdir("var/generated_bad_dir", S_IRUSR); - np_state_write_string(current_time, "String to read"); - */ - - temp_state_key->_filename = "var/generated"; - time(¤t_time); - np_state_write_string(0, "String to read"); - temp_state_data = np_state_read(); - /* Check time is set to current_time */ - ok(system("cmp var/generated var/statefile > /dev/null") != 0, "Generated file should be different this time"); - ok(this_monitoring_plugin->state->state_data->time - current_time <= 1, "Has time generated from current time"); - - /* Don't know how to automatically test this. Need to be able to redefine die and catch the error */ - /* - temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write"; - np_state_write_string(0, "Bad file"); - */ - - np_cleanup(); - - ok(this_monitoring_plugin == NULL, "Free'd this_monitoring_plugin"); - ok(mp_suid() == false, "Test aren't suid"); /* base states with random case */ char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL}; - for (i = 0; states[i] != NULL; i++) { - /* out of the random case states, create the lower and upper versions + numeric string one */ + for (int i = 0; states[i] != NULL; i++) { + /* out of the random case states, create the lower and upper versions + numeric string one + */ char *statelower = strdup(states[i]); char *stateupper = strdup(states[i]); char statenum[2]; - char *temp_ptr; - for (temp_ptr = statelower; *temp_ptr; temp_ptr++) { - *temp_ptr = tolower(*temp_ptr); + for (char *temp_ptr = statelower; *temp_ptr; temp_ptr++) { + *temp_ptr = (char)tolower(*temp_ptr); } - for (temp_ptr = stateupper; *temp_ptr; temp_ptr++) { - *temp_ptr = toupper(*temp_ptr); + for (char *temp_ptr = stateupper; *temp_ptr; temp_ptr++) { + *temp_ptr = (char)toupper(*temp_ptr); } snprintf(statenum, 2, "%i", i); /* Base test names, we'll append the state string */ char testname[64] = "Translate state string: "; - int tlen = strlen(testname); + size_t tlen = strlen(testname); strcpy(testname + tlen, states[i]); ok(i == mp_translate_state(states[i]), testname); diff --git a/lib/utils_base.c b/lib/utils_base.c index 43e88e7a..29b393d0 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -74,14 +74,6 @@ void np_set_args(int argc, char **argv) { void np_cleanup(void) { if (this_monitoring_plugin != NULL) { - if (this_monitoring_plugin->state != NULL) { - if (this_monitoring_plugin->state->state_data) { - np_free(this_monitoring_plugin->state->state_data->data); - np_free(this_monitoring_plugin->state->state_data); - } - np_free(this_monitoring_plugin->state->name); - np_free(this_monitoring_plugin->state); - } np_free(this_monitoring_plugin->plugin_name); np_free(this_monitoring_plugin); } @@ -435,339 +427,3 @@ int mp_translate_state(char *state_text) { } return ERROR; } - -/* - * Returns a string to use as a keyname, based on an md5 hash of argv, thus - * hopefully a unique key per service/plugin invocation. Use the extra-opts - * parse of argv, so that uniqueness in parameters are reflected there. - */ -char *_np_state_generate_key(void) { - char **argv = this_monitoring_plugin->argv; - unsigned char result[256]; - -#ifdef USE_OPENSSL - /* - * This code path is chosen if openssl is available (which should be the most common - * scenario). Alternatively, the gnulib implementation/ - * - */ - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - - EVP_DigestInit(ctx, EVP_sha256()); - - for (int i = 0; i < this_monitoring_plugin->argc; i++) { - EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); - } - - EVP_DigestFinal(ctx, result, NULL); -#else - - struct sha256_ctx ctx; - - for (int i = 0; i < this_monitoring_plugin->argc; i++) { - sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); - } - - sha256_finish_ctx(&ctx, result); -#endif // FOUNDOPENSSL - - char keyname[41]; - for (int i = 0; i < 20; ++i) { - sprintf(&keyname[2 * i], "%02x", result[i]); - } - - keyname[40] = '\0'; - - char *keyname_copy = strdup(keyname); - if (keyname_copy == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - - return keyname_copy; -} - -void _cleanup_state_data(void) { - if (this_monitoring_plugin->state->state_data != NULL) { - np_free(this_monitoring_plugin->state->state_data->data); - np_free(this_monitoring_plugin->state->state_data); - } -} - -/* - * Internal function. Returns either: - * envvar NAGIOS_PLUGIN_STATE_DIRECTORY - * statically compiled shared state directory - */ -char *_np_state_calculate_location_prefix(void) { - char *env_dir; - - /* Do not allow passing MP_STATE_PATH in setuid plugins - * for security reasons */ - if (!mp_suid()) { - env_dir = getenv("MP_STATE_PATH"); - if (env_dir && env_dir[0] != '\0') { - return env_dir; - } - /* This is the former ENV, for backward-compatibility */ - env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); - if (env_dir && env_dir[0] != '\0') { - return env_dir; - } - } - - return NP_STATE_DIR_PREFIX; -} - -/* - * Initiatializer for state routines. - * Sets variables. Generates filename. Returns np_state_key. die with - * UNKNOWN if exception - */ -void np_enable_state(char *keyname, int expected_data_version) { - if (this_monitoring_plugin == NULL) { - die(STATE_UNKNOWN, _("This requires np_init to be called")); - } - - state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); - if (this_state == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - char *temp_keyname = NULL; - if (keyname == NULL) { - temp_keyname = _np_state_generate_key(); - } else { - temp_keyname = strdup(keyname); - if (temp_keyname == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - } - - /* Die if invalid characters used for keyname */ - char *tmp_char = temp_keyname; - while (*tmp_char != '\0') { - if (!(isalnum(*tmp_char) || *tmp_char == '_')) { - die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); - } - tmp_char++; - } - this_state->name = temp_keyname; - this_state->plugin_name = this_monitoring_plugin->plugin_name; - this_state->data_version = expected_data_version; - this_state->state_data = NULL; - - /* Calculate filename */ - char *temp_filename = NULL; - int error = - asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), - (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); - if (error < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - this_state->_filename = temp_filename; - - this_monitoring_plugin->state = this_state; -} - -/* - * Will return NULL if no data is available (first run). If key currently - * exists, read data. If state file format version is not expected, return - * as if no data. Get state data version number and compares to expected. - * If numerically lower, then return as no previous state. die with UNKNOWN - * if exceptional error. - */ -state_data *np_state_read(void) { - if (this_monitoring_plugin == NULL) { - die(STATE_UNKNOWN, _("This requires np_init to be called")); - } - - bool error_code = false; - - /* Open file. If this fails, no previous state found */ - FILE *statefile = fopen(this_monitoring_plugin->state->_filename, "r"); - if (statefile != NULL) { - - state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); - if (this_state_data == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - this_state_data->data = NULL; - this_monitoring_plugin->state->state_data = this_state_data; - - error_code = _np_state_read_file(statefile); - - fclose(statefile); - } - - if (!error_code) { - _cleanup_state_data(); - } - - return this_monitoring_plugin->state->state_data; -} - -/* - * Read the state file - */ -bool _np_state_read_file(FILE *state_file) { - time_t current_time; - time(¤t_time); - - /* Note: This introduces a limit of 1024 bytes in the string data */ - char *line = (char *)calloc(1, 1024); - if (line == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - bool status = false; - enum { - STATE_FILE_VERSION, - STATE_DATA_VERSION, - STATE_DATA_TIME, - STATE_DATA_TEXT, - STATE_DATA_END - } expected = STATE_FILE_VERSION; - - int failure = 0; - while (!failure && (fgets(line, 1024, state_file)) != NULL) { - size_t pos = strlen(line); - if (line[pos - 1] == '\n') { - line[pos - 1] = '\0'; - } - - if (line[0] == '#') { - continue; - } - - switch (expected) { - case STATE_FILE_VERSION: { - int i = atoi(line); - if (i != NP_STATE_FORMAT_VERSION) { - failure++; - } else { - expected = STATE_DATA_VERSION; - } - } break; - case STATE_DATA_VERSION: { - int i = atoi(line); - if (i != this_monitoring_plugin->state->data_version) { - failure++; - } else { - expected = STATE_DATA_TIME; - } - } break; - case STATE_DATA_TIME: { - /* If time > now, error */ - time_t data_time = strtoul(line, NULL, 10); - if (data_time > current_time) { - failure++; - } else { - this_monitoring_plugin->state->state_data->time = data_time; - expected = STATE_DATA_TEXT; - } - } break; - case STATE_DATA_TEXT: - this_monitoring_plugin->state->state_data->data = strdup(line); - if (this_monitoring_plugin->state->state_data->data == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - expected = STATE_DATA_END; - status = true; - break; - case STATE_DATA_END:; - } - } - - np_free(line); - return status; -} - -/* - * If time=NULL, use current time. Create state file, with state format - * version, default text. Writes version, time, and data. Avoid locking - * problems - use mv to write and then swap. Possible loss of state data if - * two things writing to same key at same time. - * Will die with UNKNOWN if errors - */ -void np_state_write_string(time_t data_time, char *data_string) { - time_t current_time; - if (data_time == 0) { - time(¤t_time); - } else { - current_time = data_time; - } - - int result = 0; - - /* If file doesn't currently exist, create directories */ - if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) { - char *directories = NULL; - result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename); - if (result < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - for (char *p = directories + 1; *p; p++) { - if (*p == '/') { - *p = '\0'; - if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { - /* Can't free this! Otherwise error message is wrong! */ - /* np_free(directories); */ - die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); - } - *p = '/'; - } - } - np_free(directories); - } - - char *temp_file = NULL; - result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename); - if (result < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - int temp_file_desc = 0; - if ((temp_file_desc = mkstemp(temp_file)) == -1) { - np_free(temp_file); - die(STATE_UNKNOWN, _("Cannot create temporary filename")); - } - - FILE *temp_file_pointer = (FILE *)fdopen(temp_file_desc, "w"); - if (temp_file_pointer == NULL) { - close(temp_file_desc); - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Unable to open temporary state file")); - } - - fprintf(temp_file_pointer, "# NP State file\n"); - fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); - fprintf(temp_file_pointer, "%d\n", this_monitoring_plugin->state->data_version); - fprintf(temp_file_pointer, "%lu\n", current_time); - fprintf(temp_file_pointer, "%s\n", data_string); - - fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); - - fflush(temp_file_pointer); - - result = fclose(temp_file_pointer); - - fsync(temp_file_desc); - - if (result != 0) { - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Error writing temp file")); - } - - if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) { - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Cannot rename state temp file")); - } - - np_free(temp_file); -} diff --git a/lib/utils_base.h b/lib/utils_base.h index 123066f8..f1c99a54 100644 --- a/lib/utils_base.h +++ b/lib/utils_base.h @@ -8,7 +8,6 @@ #include "./perfdata.h" #include "./thresholds.h" - #ifndef USE_OPENSSL # include "sha256.h" #endif @@ -26,25 +25,8 @@ #define OUTSIDE 0 #define INSIDE 1 -#define NP_STATE_FORMAT_VERSION 1 - -typedef struct state_data_struct { - time_t time; - void *data; - int length; /* Of binary data */ -} state_data; - -typedef struct state_key_struct { - char *name; - char *plugin_name; - int data_version; - char *_filename; - state_data *state_data; -} state_key; - typedef struct np_struct { char *plugin_name; - state_key *state; int argc; char **argv; } monitoring_plugin; @@ -100,10 +82,6 @@ char *np_extract_value(const char *, const char *, char); */ int mp_translate_state(char *); -void np_enable_state(char *, int); -state_data *np_state_read(void); -void np_state_write_string(time_t, char *); - void np_init(char *, int argc, char **argv); void np_set_args(int argc, char **argv); void np_cleanup(void); diff --git a/plugins/Makefile.am b/plugins/Makefile.am index f2f1777f..deae938d 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -13,8 +13,14 @@ AM_CFLAGS = -DNP_VERSION='"$(NP_VERSION)"' VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t -AM_CPPFLAGS = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl \ - @LDAPINCLUDE@ @PGINCLUDE@ @SSLINCLUDE@ +AM_CPPFLAGS = -I.. \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/gl \ + -I$(top_srcdir)/intl \ + -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ + @LDAPINCLUDE@ \ + @PGINCLUDE@ \ + @SSLINCLUDE@ localedir = $(datadir)/locale # gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this @@ -42,11 +48,13 @@ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \ check_procs check_mysql_query check_apt check_dbi check_curl \ \ tests/test_check_swap \ + tests/test_check_snmp \ tests/test_check_disk SUBDIRS = picohttpparser np_test_scripts = tests/test_check_swap.t \ + tests/test_check_snmp.t \ tests/test_check_disk.t EXTRA_DIST = t \ @@ -178,6 +186,8 @@ endif tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c +tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap +tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap tests_test_check_disk_SOURCES = tests/test_check_disk.c diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 3c054259..a5a7afe8 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -37,10 +37,8 @@ const char *email = "devel@monitoring-plugins.org"; #include "./utils.h" #include "../lib/states.h" -#include "../lib/thresholds.h" #include "../lib/utils_base.h" #include "../lib/output.h" -#include "../lib/perfdata.h" #include "check_snmp.d/check_snmp_helpers.h" #include @@ -49,6 +47,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include "check_snmp.d/config.h" +#include #include #include #include @@ -63,6 +62,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include #include "../gl/regex.h" +#include "../gl/base64.h" #include const char DEFAULT_COMMUNITY[] = "public"; @@ -86,7 +86,168 @@ static char *get_next_argument(char *str); void print_usage(void); void print_help(void); -static int verbose = 0; +int verbose = 0; + +typedef struct { + int errorcode; + char *state_string; +} gen_state_string_type; +gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) { + char *encoded_string = NULL; + gen_state_string_type result = {.errorcode = OK, .state_string = NULL}; + + if (verbose > 1) { + printf("%s:\n", __FUNCTION__); + for (size_t i = 0; i < num_of_entries; i++) { + printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp)); + switch (entries[i].type) { + case ASN_GAUGE: + printf("Type GAUGE\n"); + break; + case ASN_TIMETICKS: + printf("Type TIMETICKS\n"); + break; + case ASN_COUNTER: + printf("Type COUNTER\n"); + break; + case ASN_UINTEGER: + printf("Type UINTEGER\n"); + break; + case ASN_COUNTER64: + printf("Type COUNTER64\n"); + break; + case ASN_FLOAT: + printf("Type FLOAT\n"); + case ASN_DOUBLE: + printf("Type DOUBLE\n"); + break; + case ASN_INTEGER: + printf("Type INTEGER\n"); + break; + } + + switch (entries[i].type) { + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: + case ASN_COUNTER64: + printf("Value %llu\n", entries[i].value.uIntVal); + break; + case ASN_FLOAT: + case ASN_DOUBLE: + printf("Value %f\n", entries[i].value.doubleVal); + break; + case ASN_INTEGER: + printf("Value %lld\n", entries[i].value.intVal); + break; + } + } + } + + idx_t encoded = base64_encode_alloc((const char *)entries, + (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)), + &encoded_string); + + if (encoded > 0 && encoded_string != NULL) { + // success + if (verbose > 1) { + printf("encoded string: %s\n", encoded_string); + printf("encoded string length: %lu\n", strlen(encoded_string)); + } + result.state_string = encoded_string; + return result; + } + result.errorcode = ERROR; + return result; +} + +typedef struct { + int errorcode; + check_snmp_state_entry *state; +} recover_state_data_type; +recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) { + recover_state_data_type result = {.errorcode = OK, .state = NULL}; + + if (verbose > 1) { + printf("%s:\n", __FUNCTION__); + printf("State string: %s\n", state_string); + printf("State string length: %lu\n", state_string_length); + } + + idx_t outlen = 0; + bool decoded = + base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen); + + if (!decoded) { + if (verbose) { + printf("Failed to decode state string\n"); + } + // failure to decode + result.errorcode = ERROR; + return result; + } + + if (result.state == NULL) { + // Memory Error? + result.errorcode = ERROR; + return result; + } + + if (verbose > 1) { + printf("Recovered %lu entries of size %lu\n", + (size_t)outlen / sizeof(check_snmp_state_entry), outlen); + + for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) { + printf("Entry timestamp %lu: %s", result.state[i].timestamp, + ctime(&result.state[i].timestamp)); + switch (result.state[i].type) { + case ASN_GAUGE: + printf("Type GAUGE\n"); + break; + case ASN_TIMETICKS: + printf("Type TIMETICKS\n"); + break; + case ASN_COUNTER: + printf("Type COUNTER\n"); + break; + case ASN_UINTEGER: + printf("Type UINTEGER\n"); + break; + case ASN_COUNTER64: + printf("Type COUNTER64\n"); + break; + case ASN_FLOAT: + printf("Type FLOAT\n"); + case ASN_DOUBLE: + printf("Type DOUBLE\n"); + break; + case ASN_INTEGER: + printf("Type INTEGER\n"); + break; + } + + switch (result.state[i].type) { + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: + case ASN_COUNTER64: + printf("Value %llu\n", result.state[i].value.uIntVal); + break; + case ASN_FLOAT: + case ASN_DOUBLE: + printf("Value %f\n", result.state[i].value.doubleVal); + break; + case ASN_INTEGER: + printf("Value %lld\n", result.state[i].value.intVal); + break; + } + } + } + + return result; +} int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -97,6 +258,8 @@ int main(int argc, char **argv) { np_init((char *)progname, argc, argv); + state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv); + /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); @@ -105,9 +268,6 @@ int main(int argc, char **argv) { // Initialize net-snmp before touching the session we are going to use init_snmp("check_snmp"); - time_t current_time; - time(¤t_time); - process_arguments_wrapper paw_tmp = process_arguments(argc, argv); if (paw_tmp.errorcode == ERROR) { usage4(_("Could not parse arguments")); @@ -119,347 +279,103 @@ int main(int argc, char **argv) { mp_set_format(config.output_format); } - if (config.ignore_mib_parsing_errors) { - char *opt_toggle_res = snmp_mib_toggle_options("e"); - if (opt_toggle_res != NULL) { - die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); - } - } - - struct snmp_pdu *pdu = NULL; - if (config.use_getnext) { - pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); - } else { - pdu = snmp_pdu_create(SNMP_MSG_GET); - } - - for (size_t i = 0; i < config.num_of_test_units; i++) { - assert(config.test_units[i].oid != NULL); - if (verbose > 0) { - printf("OID %zu to parse: %s\n", i, config.test_units[i].oid); - } - - oid tmp_OID[MAX_OID_LEN]; - size_t tmp_OID_len = MAX_OID_LEN; - if (snmp_parse_oid(config.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { - // success - snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); - } else { - // failed - snmp_perror("Parsing failure"); - die(STATE_UNKNOWN, "Failed to parse OID\n"); - } - } - /* Set signal handling and alarm */ if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { usage4(_("Cannot catch SIGALRM")); } - const int timeout_safety_tolerance = 5; - alarm((timeout_interval * (unsigned int)config.snmp_session.retries) + - timeout_safety_tolerance); - - struct snmp_session *active_session = snmp_open(&config.snmp_session); - if (active_session == NULL) { - int pcliberr = 0; - int psnmperr = 0; - char *pperrstring = NULL; - snmp_error(&config.snmp_session, &pcliberr, &psnmperr, &pperrstring); - die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); - } - - struct snmp_pdu *response = NULL; - int snmp_query_status = snmp_synch_response(active_session, pdu, &response); - - if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { - int pcliberr = 0; - int psnmperr = 0; - char *pperrstring = NULL; - snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); + time_t current_time; + time(¤t_time); - if (psnmperr == SNMPERR_TIMEOUT) { - // We exit with critical here for some historical reason - die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); - } - die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); + if (verbose > 2) { + printf("current time: %s (timestamp: %lu)\n", ctime(¤t_time), current_time); } - snmp_close(active_session); - - /* disable alarm again */ - alarm(0); + snmp_responces response = do_snmp_query(config.snmp_params); mp_check overall = mp_check_init(); - mp_subcheck sc_successfull_query = mp_subcheck_init(); - xasprintf(&sc_successfull_query.output, "SNMP query was successful"); - sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); - mp_add_subcheck_to_check(&overall, sc_successfull_query); - - // We got the the query results, now process them - size_t loop_index = 0; - for (netsnmp_variable_list *vars = response->variables; vars; - vars = vars->next_variable, loop_index++) { - mp_subcheck sc_oid_test = mp_subcheck_init(); + if (response.errorcode == OK) { + mp_subcheck sc_successfull_query = mp_subcheck_init(); + xasprintf(&sc_successfull_query.output, "SNMP query was successful"); + sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_successfull_query); + } else { + // Error treatment here, either partial or whole + mp_subcheck sc_failed_query = mp_subcheck_init(); + xasprintf(&sc_failed_query.output, "SNMP query failed"); + sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_failed_query); + mp_exit(overall); + } - if (verbose > 0) { - printf("loop_index: %zu\n", loop_index); - } + check_snmp_state_entry *prev_state = NULL; + bool have_previous_state = false; - if ((config.test_units[loop_index].label != NULL) && - (strcmp(config.test_units[loop_index].label, "") != 0)) { - xasprintf(&sc_oid_test.output, "%s - ", config.test_units[loop_index].label); + if (config.evaluation_params.calculate_rate) { + state_data *previous_state = np_state_read(stateKey); + if (previous_state == NULL) { + // failed to recover state + // or no previous state + have_previous_state = false; } else { - sc_oid_test.output = strdup(""); - } - - char oid_string[(MAX_OID_LEN * 2) + 1] = {}; - - int oid_string_result = - snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length); - if (oid_string_result <= 0) { - // TODO error here - } - - if (verbose > 2) { - printf("Processing oid %s\n", oid_string); - } + // sanity check + recover_state_data_type prev_state_wrapper = + recover_state_data(previous_state->data, (idx_t)previous_state->length); - mp_perfdata_value pd_result_val = {0}; - xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); - sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); - - switch (vars->type) { - case ASN_OCTET_STR: { - if (verbose) { - printf("Debug: Got a string\n"); - } - - char *tmp = (char *)vars->val.string; - - if (strchr(tmp, '"') != NULL) { - // got double quote in the string - if (strchr(tmp, '\'') != NULL) { - // got single quote in the string too - // dont quote that at all to avoid even more confusion - xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); - } else { - // quote with single quotes - xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); - } + if (prev_state_wrapper.errorcode == OK) { + have_previous_state = true; + prev_state = prev_state_wrapper.state; } else { - // quote with double quotes - xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); - } - - if (strlen(tmp) == 0) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result); + have_previous_state = false; + prev_state = NULL; } - - // String matching test - if ((config.test_units[loop_index].eval_mthd.crit_string)) { - if (strcmp(tmp, config.string_cmp_value)) { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_CRITICAL : STATE_OK); - } else { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); - } - } else if (config.test_units[loop_index].eval_mthd.crit_regex) { - const size_t nmatch = config.regex_cmp_value.re_nsub + 1; - regmatch_t pmatch[nmatch]; - memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); - - int excode = regexec(&config.regex_cmp_value, tmp, nmatch, pmatch, 0); - if (excode == 0) { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); - } else if (excode != REG_NOMATCH) { - char errbuf[MAX_INPUT_BUFFER] = ""; - regerror(excode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); - printf(_("Execute Error: %s\n"), errbuf); - exit(STATE_CRITICAL); - } else { // REG_NOMATCH - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, config.invert_search ? STATE_CRITICAL : STATE_OK); - } - } - - mp_add_subcheck_to_check(&overall, sc_oid_test); } - continue; - case ASN_OPAQUE: - if (verbose) { - printf("Debug: Got OPAQUE\n"); - } - break; - case ASN_COUNTER64: { - if (verbose) { - printf("Debug: Got counter64\n"); - } - struct counter64 tmp = *(vars->val.counter64); - uint64_t counter = (tmp.high << 32) + tmp.low; - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)counter * config.multiplier; - } - - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(counter); - } - - } break; - /* Numerical values */ - case ASN_GAUGE: // same as ASN_UNSIGNED - case ASN_TIMETICKS: - case ASN_COUNTER: - case ASN_UINTEGER: { - if (verbose) { - printf("Debug: Got a Integer like\n"); - } - unsigned long tmp = (unsigned long)*(vars->val.integer); - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)tmp * config.multiplier; - } + } - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(tmp); - } - break; + check_snmp_state_entry *new_state = NULL; + if (config.evaluation_params.calculate_rate) { + new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry)); + if (new_state == NULL) { + die(STATE_UNKNOWN, "memory allocation failed"); } - case ASN_INTEGER: { - if (verbose) { - printf("Debug: Got a Integer\n"); - } - - long tmp = *(vars->val.integer); - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)tmp * config.multiplier; - } - - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(tmp); - } - - } break; - case ASN_FLOAT: { - if (verbose) { - printf("Debug: Got a float\n"); - } - double tmp = *(vars->val.floatVal); - - if (config.multiplier_set) { - tmp *= config.multiplier; - } - - if (config.offset_set) { - tmp += config.offset; - } + } - pd_result_val = mp_create_pd_value(tmp); - break; + // We got the the query results, now process them + for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) { + if (verbose > 0) { + printf("loop_index: %zu\n", loop_index); } - case ASN_DOUBLE: { - if (verbose) { - printf("Debug: Got a double\n"); - } - double tmp = *(vars->val.doubleVal); - if (config.multiplier_set) { - tmp *= config.multiplier; - } - - if (config.offset_set) { - tmp += config.offset; - } - pd_result_val = mp_create_pd_value(tmp); - break; + check_snmp_state_entry previous_unit_state = {}; + if (config.evaluation_params.calculate_rate && have_previous_state) { + previous_unit_state = prev_state[loop_index]; } - case ASN_IPADDRESS: - if (verbose) { - printf("Debug: Got an IP address\n"); - } - continue; - default: - if (verbose) { - printf("Debug: Got a unmatched result type: %hhu\n", vars->type); - } - // TODO: Error here? - continue; - } - - // some kind of numerical value - mp_perfdata pd_num_val = { - .value = pd_result_val, - }; - if (!config.use_perf_data_labels_from_input) { - // Use oid for perdata label - pd_num_val.label = strdup(oid_string); - // TODO strdup error checking - } else if (config.test_units[loop_index].label != NULL && - strcmp(config.test_units[loop_index].label, "") != 0) { - pd_num_val.label = config.test_units[loop_index].label; - } else { - pd_num_val.label = config.test_units[loop_index].oid; - } + check_snmp_evaluation single_eval = + evaluate_single_unit(response.response_values[loop_index], config.evaluation_params, + config.snmp_params.test_units[loop_index], current_time, + previous_unit_state, have_previous_state); - if (config.test_units[loop_index].unit_value != NULL && - strcmp(config.test_units[loop_index].unit_value, "") != 0) { - pd_num_val.uom = config.test_units[loop_index].unit_value; + if (config.evaluation_params.calculate_rate && + mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) { + new_state[loop_index] = single_eval.state; } - xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, - pd_value_to_string(pd_result_val)); + mp_add_subcheck_to_check(&overall, single_eval.sc); + } - if (config.test_units[loop_index].unit_value != NULL && - strcmp(config.test_units[loop_index].unit_value, "") != 0) { - xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, - config.test_units[loop_index].unit_value); - } + if (config.evaluation_params.calculate_rate) { + // store state + gen_state_string_type current_state_wrapper = + gen_state_string(new_state, config.snmp_params.num_of_test_units); - if (config.test_units[loop_index].threshold.warning_is_set || - config.test_units[loop_index].threshold.critical_is_set) { - pd_num_val = mp_pd_set_thresholds(pd_num_val, config.test_units[loop_index].threshold); - mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); - - if (tmp_state == STATE_WARNING) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); - xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", - sc_oid_test.output); - } else if (tmp_state == STATE_CRITICAL) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); - xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", - sc_oid_test.output); - } + if (current_state_wrapper.errorcode == OK) { + np_state_write_string(stateKey, current_time, current_state_wrapper.state_string); + } else { + die(STATE_UNKNOWN, "failed to create state string"); } - - mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); - - mp_add_subcheck_to_check(&overall, sc_oid_test); } - mp_exit(overall); } @@ -472,6 +388,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { ignore_mib_parsing_errors_index, connection_prefix_index, output_format_index, + calculate_rate, + rate_multiplier }; static struct option longopts[] = { @@ -510,6 +428,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index}, {"connection-prefix", required_argument, 0, connection_prefix_index}, {"output-format", required_argument, 0, output_format_index}, + {"rate", no_argument, 0, calculate_rate}, + {"rate-multiplier", required_argument, 0, rate_multiplier}, {0, 0, 0, 0}}; if (argc < 2) { @@ -575,8 +495,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } check_snmp_config config = check_snmp_config_init(); - config.test_units = tmp; - config.num_of_test_units = oid_counter; + config.snmp_params.test_units = tmp; + config.snmp_params.num_of_test_units = oid_counter; option = 0; optind = 1; // Reset argument scanner @@ -616,11 +536,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Connection info */ case 'C': /* group or community */ - config.snmp_session.community = (unsigned char *)optarg; - config.snmp_session.community_len = strlen(optarg); + config.snmp_params.snmp_session.community = (unsigned char *)optarg; + config.snmp_params.snmp_session.community_len = strlen(optarg); break; case 'H': /* Host or server */ - config.snmp_session.peername = optarg; + config.snmp_params.snmp_session.peername = optarg; break; case 'p': /*port number */ // Add port to "peername" below to not rely on argument order @@ -630,15 +550,15 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { miblist = optarg; break; case 'n': /* use_getnext instead of get */ - config.use_getnext = true; + config.snmp_params.use_getnext = true; break; case 'P': /* SNMP protocol version */ if (strcasecmp("1", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_1; + config.snmp_params.snmp_session.version = SNMP_VERSION_1; } else if (strcasecmp("2c", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_2c; + config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } else if (strcasecmp("3", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_3; + config.snmp_params.snmp_session.version = SNMP_VERSION_3; } else { die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); } @@ -646,45 +566,51 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'N': /* SNMPv3 context name */ - config.snmp_session.contextName = optarg; - config.snmp_session.contextNameLen = strlen(optarg); + config.snmp_params.snmp_session.contextName = optarg; + config.snmp_params.snmp_session.contextNameLen = strlen(optarg); break; case 'L': /* security level */ if (strcasecmp("noAuthNoPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; } else if (strcasecmp("authNoPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; } else if (strcasecmp("authPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; } else { die(STATE_UNKNOWN, "invalid security level: %s", optarg); } break; case 'U': /* security username */ - config.snmp_session.securityName = optarg; - config.snmp_session.securityNameLen = strlen(optarg); + config.snmp_params.snmp_session.securityName = optarg; + config.snmp_params.snmp_session.securityNameLen = strlen(optarg); break; case 'a': /* auth protocol */ // SNMPv3: SHA or MD5 // TODO Test for availability of individual protocols if (strcasecmp("MD5", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMACMD5AuthProtocol); } else if (strcasecmp("SHA", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMACSHA1AuthProtocol); } else if (strcasecmp("SHA224", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC128SHA224AuthProtocol); } else if (strcasecmp("SHA256", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC192SHA256AuthProtocol); } else if (strcasecmp("SHA384", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC256SHA384AuthProtocol); } else if (strcasecmp("SHA512", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC384SHA512AuthProtocol); } else { die(STATE_UNKNOWN, "Unknown authentication protocol"); } @@ -692,24 +618,28 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { #ifdef HAVE_USM_DES_PRIV_PROTOCOL - config.snmp_session.securityAuthProto = usmDESPrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmDESPrivProtocol); #else die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform"); #endif } else if (strcasecmp("AES", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAESPrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAESPrivProtocol); // } else if (strcasecmp("AES128", optarg)) { // config.snmp_session.securityAuthProto = usmAES128PrivProtocol; // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol) // / OID_LENGTH(oid); } else if (strcasecmp("AES192", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAES192PrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES192PrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAES192PrivProtocol); } else if (strcasecmp("AES256", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAES256PrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES256PrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAES256PrivProtocol); // } else if (strcasecmp("AES192Cisco", optarg)) { // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol; // config.snmp_session.securityAuthProtoLen = @@ -738,7 +668,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (!is_integer(optarg)) { usage2(_("Retries interval must be a positive integer"), optarg); } else { - config.snmp_session.retries = atoi(optarg); + config.snmp_params.snmp_session.retries = atoi(optarg); } break; case 't': /* timeout period */ @@ -751,10 +681,10 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Test parameters */ case 'c': /* critical threshold */ - check_snmp_set_thresholds(optarg, config.test_units, oid_counter, true); + check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true); break; case 'w': /* warning threshold */ - check_snmp_set_thresholds(optarg, config.test_units, oid_counter, false); + check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false); break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { @@ -763,25 +693,27 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { * so we have a mib variable, rather than just an SNMP OID, * so we have to actually read the mib files */ - config.need_mibs = true; + config.snmp_params.need_mibs = true; } for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), tmp_oid_counter++) { - config.test_units[tmp_oid_counter].oid = strdup(ptr); + config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr); } break; case 'z': /* Null OID Return Check */ if (!is_integer(optarg)) { usage2(_("Exit status must be a positive integer"), optarg); } else { - config.nulloid_result = atoi(optarg); + config.evaluation_params.nulloid_result = atoi(optarg); } break; case 's': /* string or substring */ - strncpy(config.string_cmp_value, optarg, sizeof(config.string_cmp_value) - 1); - config.string_cmp_value[sizeof(config.string_cmp_value) - 1] = 0; - config.test_units[eval_counter++].eval_mthd.crit_string = true; + strncpy(config.evaluation_params.string_cmp_value, optarg, + sizeof(config.evaluation_params.string_cmp_value) - 1); + config.evaluation_params + .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0; + config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true; break; case 'R': /* regex */ cflags = REG_ICASE; @@ -792,72 +724,73 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); regex_expect[sizeof(regex_expect) - 1] = 0; - int errcode = regcomp(&config.regex_cmp_value, regex_expect, cflags); + int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags); if (errcode != 0) { char errbuf[MAX_INPUT_BUFFER] = ""; - regerror(errcode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf, + MAX_INPUT_BUFFER); printf("Could Not Compile Regular Expression: %s", errbuf); process_arguments_wrapper result = { .errorcode = ERROR, }; return result; } - config.test_units[eval_counter++].eval_mthd.crit_regex = true; + config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true; } break; case 'l': /* label */ { - if (labels_counter >= config.num_of_test_units) { + if (labels_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { - config.test_units[labels_counter].label = ptr + 1; + config.snmp_params.test_units[labels_counter].label = ptr + 1; } else { - config.test_units[labels_counter].label = ptr; + config.snmp_params.test_units[labels_counter].label = ptr; } while (ptr && (ptr = get_next_argument(ptr))) { labels_counter++; ptr = trim_whitespaces_and_check_quoting(ptr); if (ptr[0] == '\'') { - config.test_units[labels_counter].label = ptr + 1; + config.snmp_params.test_units[labels_counter].label = ptr + 1; } else { - config.test_units[labels_counter].label = ptr; + config.snmp_params.test_units[labels_counter].label = ptr; } } labels_counter++; } break; case 'u': /* units */ { - if (unitv_counter >= config.num_of_test_units) { + if (unitv_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { - config.test_units[unitv_counter].unit_value = ptr + 1; + config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { - config.test_units[unitv_counter].unit_value = ptr; + config.snmp_params.test_units[unitv_counter].unit_value = ptr; } while (ptr && (ptr = get_next_argument(ptr))) { unitv_counter++; ptr = trim_whitespaces_and_check_quoting(ptr); if (ptr[0] == '\'') { - config.test_units[unitv_counter].unit_value = ptr + 1; + config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { - config.test_units[unitv_counter].unit_value = ptr; + config.snmp_params.test_units[unitv_counter].unit_value = ptr; } } unitv_counter++; } break; case offset_index: - config.offset = strtod(optarg, NULL); - config.offset_set = true; + config.evaluation_params.offset = strtod(optarg, NULL); + config.evaluation_params.offset_set = true; break; case invert_search_index: - config.invert_search = false; + config.evaluation_params.invert_search = false; break; case 'O': - config.use_perf_data_labels_from_input = true; + config.evaluation_params.use_oid_as_perf_data_label = true; break; case '4': // The default, do something here to be exclusive to -6 instead of doing nothing? @@ -871,12 +804,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { - config.multiplier = strtod(optarg, NULL); - config.multiplier_set = true; + config.evaluation_params.multiplier = strtod(optarg, NULL); + config.evaluation_params.multiplier_set = true; } break; case ignore_mib_parsing_errors_index: - config.ignore_mib_parsing_errors = true; + config.snmp_params.ignore_mib_parsing_errors = true; break; case 'f': // Deprecated format option for floating point values break; @@ -892,13 +825,22 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { config.output_format = parser.output_format; break; } + case calculate_rate: + config.evaluation_params.calculate_rate = true; + break; + case rate_multiplier: + if (!is_integer(optarg) || + ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) { + usage2(_("Rate multiplier must be a positive integer"), optarg); + } + break; default: die(STATE_UNKNOWN, "Unknown option"); } } - if (config.snmp_session.peername == NULL) { - config.snmp_session.peername = argv[optind]; + if (config.snmp_params.snmp_session.peername == NULL) { + config.snmp_params.snmp_session.peername = argv[optind]; } // Build true peername here if necessary @@ -908,7 +850,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { // The default, do nothing } else if (strcasecmp(connection_prefix, "tcp") == 0) { // use tcp/ipv4 - xasprintf(&config.snmp_session.peername, "tcp:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s", + config.snmp_params.snmp_session.peername); } else if (strcasecmp(connection_prefix, "tcp6") == 0 || strcasecmp(connection_prefix, "tcpv6") == 0 || strcasecmp(connection_prefix, "tcpipv6") == 0 || @@ -917,19 +860,23 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { strcasecmp(connection_prefix, "udpv6") == 0) { // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it // works anyway therefore do nothing here - xasprintf(&config.snmp_session.peername, "%s:%s", connection_prefix, - config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix, + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "tls") == 0) { // TODO: Anything else to do here? - xasprintf(&config.snmp_session.peername, "tls:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "dtls") == 0) { // TODO: Anything else to do here? - xasprintf(&config.snmp_session.peername, "dtls:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "unix") == 0) { // TODO: Check whether this is a valid path? - xasprintf(&config.snmp_session.peername, "unix:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "ipx") == 0) { - xasprintf(&config.snmp_session.peername, "ipx:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s", + config.snmp_params.snmp_session.peername); } else { // Don't know that prefix, die here die(STATE_UNKNOWN, "Unknown connection prefix"); @@ -937,17 +884,18 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } /* Check server_address is given */ - if (config.snmp_session.peername == NULL) { + if (config.snmp_params.snmp_session.peername == NULL) { die(STATE_UNKNOWN, _("No host specified\n")); } if (port != NULL) { - xasprintf(&config.snmp_session.peername, "%s:%s", config.snmp_session.peername, port); + xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", + config.snmp_params.snmp_session.peername, port); } /* check whether to load locally installed MIBS (CPU/disk intensive) */ if (miblist == NULL) { - if (config.need_mibs) { + if (config.snmp_params.need_mibs) { setenv("MIBLS", DEFAULT_MIBLIST, 1); } else { setenv("MIBLS", "NONE", 1); @@ -959,37 +907,37 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } // Historical default is SNMP v2c - if (!snmp_version_set_explicitely && config.snmp_session.community != NULL) { - config.snmp_session.version = SNMP_VERSION_2c; + if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) { + config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } - if ((config.snmp_session.version == SNMP_VERSION_1) || - (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ - /* - config.numauthpriv = 2; - config.authpriv = calloc(config.numauthpriv, sizeof(char *)); - config.authpriv[0] = strdup("-c"); - config.authpriv[1] = strdup(community); - */ - } else if (config.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ + if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) || + (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ + /* + config.numauthpriv = 2; + config.authpriv = calloc(config.numauthpriv, sizeof(char *)); + config.authpriv[0] = strdup("-c"); + config.authpriv[1] = strdup(community); + */ + } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ // generate keys for priv and auth here (if demanded) - if (config.snmp_session.securityName == NULL) { + if (config.snmp_params.snmp_session.securityName == NULL) { die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); } - switch (config.snmp_session.securityLevel) { + switch (config.snmp_params.snmp_session.securityLevel) { case SNMP_SEC_LEVEL_AUTHPRIV: { if (authpasswd == NULL) { die(STATE_UNKNOWN, "No authentication passphrase was given, but authorization was requested"); } // auth and priv - int priv_key_generated = - generate_Ku(config.snmp_session.securityPrivProto, - (unsigned int)config.snmp_session.securityPrivProtoLen, authpasswd, - strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, - &config.snmp_session.securityPrivKeyLen); + int priv_key_generated = generate_Ku( + config.snmp_params.snmp_session.securityPrivProto, + (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd, + strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey, + &config.snmp_params.snmp_session.securityPrivKeyLen); if (priv_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); @@ -1000,11 +948,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (privpasswd == NULL) { die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); } - int auth_key_generated = - generate_Ku(config.snmp_session.securityAuthProto, - (unsigned int)config.snmp_session.securityAuthProtoLen, privpasswd, - strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, - &config.snmp_session.securityAuthKeyLen); + int auth_key_generated = generate_Ku( + config.snmp_params.snmp_session.securityAuthProto, + (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd, + strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey, + &config.snmp_params.snmp_session.securityAuthKeyLen); if (auth_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 8f4bcb9c..9fa396d8 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -1,6 +1,15 @@ #include "./check_snmp_helpers.h" #include #include "../../lib/utils_base.h" +#include "config.h" +#include +#include "../utils.h" +#include "output.h" +#include "states.h" +#include +#include + +extern int verbose; check_snmp_test_unit check_snmp_test_unit_init() { check_snmp_test_unit tmp = { @@ -78,38 +87,848 @@ int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit const int DEFAULT_PROTOCOL = SNMP_VERSION_1; const char DEFAULT_OUTPUT_DELIMITER[] = " "; -const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; +const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192; check_snmp_config check_snmp_config_init() { check_snmp_config tmp = { - .use_getnext = false, + .snmp_params = + { + .use_getnext = false, + + .ignore_mib_parsing_errors = false, + .need_mibs = false, - .ignore_mib_parsing_errors = false, - .need_mibs = false, + .test_units = NULL, + .num_of_test_units = 0, + }, - .test_units = NULL, - .num_of_test_units = 0, + .evaluation_params = + { + .nulloid_result = STATE_UNKNOWN, // state to return if no result for query - .nulloid_result = STATE_UNKNOWN, // state to return if no result for query + .invert_search = true, + .regex_cmp_value = {}, + .string_cmp_value = "", - .invert_search = true, - .regex_cmp_value = {}, - .string_cmp_value = "", + .multiplier = 1.0, + .multiplier_set = false, + .offset = 0, + .offset_set = false, - .multiplier = 1.0, - .multiplier_set = false, - .offset = 0, - .offset_set = false, + .use_oid_as_perf_data_label = false, - .use_perf_data_labels_from_input = false, + .calculate_rate = false, + .rate_multiplier = 1, + }, }; - snmp_sess_init(&tmp.snmp_session); + snmp_sess_init(&tmp.snmp_params.snmp_session); - tmp.snmp_session.retries = DEFAULT_RETRIES; - tmp.snmp_session.version = DEFAULT_SNMP_VERSION; - tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; - tmp.snmp_session.community = (unsigned char *)"public"; - tmp.snmp_session.community_len = strlen("public"); + tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES; + tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION; + tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + tmp.snmp_params.snmp_session.community = (unsigned char *)"public"; + tmp.snmp_params.snmp_session.community_len = strlen("public"); return tmp; } + +snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) { + if (parameters.ignore_mib_parsing_errors) { + char *opt_toggle_res = snmp_mib_toggle_options("e"); + if (opt_toggle_res != NULL) { + die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); + } + } + + struct snmp_pdu *pdu = NULL; + if (parameters.use_getnext) { + pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); + } else { + pdu = snmp_pdu_create(SNMP_MSG_GET); + } + + for (size_t i = 0; i < parameters.num_of_test_units; i++) { + assert(parameters.test_units[i].oid != NULL); + if (verbose > 0) { + printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid); + } + + oid tmp_OID[MAX_OID_LEN]; + size_t tmp_OID_len = MAX_OID_LEN; + if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { + // success + snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); + } else { + // failed + snmp_perror("Parsing failure"); + die(STATE_UNKNOWN, "Failed to parse OID\n"); + } + } + + const int timeout_safety_tolerance = 5; + alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) + + timeout_safety_tolerance); + + struct snmp_session *active_session = snmp_open(¶meters.snmp_session); + if (active_session == NULL) { + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error(¶meters.snmp_session, &pcliberr, &psnmperr, &pperrstring); + die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); + } + + struct snmp_pdu *response = NULL; + int snmp_query_status = snmp_synch_response(active_session, pdu, &response); + + if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); + + if (psnmperr == SNMPERR_TIMEOUT) { + // We exit with critical here for some historical reason + die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); + } + die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); + } + + snmp_close(active_session); + + /* disable alarm again */ + alarm(0); + + snmp_responces result = { + .errorcode = OK, + .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)), + }; + + if (result.response_values == NULL) { + result.errorcode = ERROR; + return result; + } + + // We got the the query results, now process them + size_t loop_index = 0; + for (netsnmp_variable_list *vars = response->variables; vars; + vars = vars->next_variable, loop_index++) { + + for (size_t jdx = 0; jdx < vars->name_length; jdx++) { + result.response_values[loop_index].oid[jdx] = vars->name[jdx]; + } + result.response_values[loop_index].oid_length = vars->name_length; + + switch (vars->type) { + case ASN_OCTET_STR: { + result.response_values[loop_index].string_response = strdup((char *)vars->val.string); + result.response_values[loop_index].type = vars->type; + if (verbose) { + printf("Debug: Got a string as response: %s\n", vars->val.string); + } + } + continue; + case ASN_OPAQUE: + if (verbose) { + printf("Debug: Got OPAQUE\n"); + } + break; + /* Numerical values */ + case ASN_COUNTER64: { + if (verbose) { + printf("Debug: Got counter64\n"); + } + struct counter64 tmp = *(vars->val.counter64); + uint64_t counter = (tmp.high << 32) + tmp.low; + result.response_values[loop_index].value.uIntVal = counter; + result.response_values[loop_index].type = vars->type; + } break; + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + if (verbose) { + printf("Debug: Got a Integer like\n"); + } + result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_INTEGER: { + if (verbose) { + printf("Debug: Got a Integer\n"); + } + result.response_values[loop_index].value.intVal = *(vars->val.integer); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_FLOAT: { + if (verbose) { + printf("Debug: Got a float\n"); + } + result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_DOUBLE: { + if (verbose) { + printf("Debug: Got a double\n"); + } + result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal); + result.response_values[loop_index].type = vars->type; + break; + } + case ASN_IPADDRESS: + if (verbose) { + printf("Debug: Got an IP address\n"); + } + result.response_values[loop_index].type = vars->type; + + // TODO: print address here, state always ok? or regex match? + continue; + default: + if (verbose) { + printf("Debug: Got a unmatched result type: %hhu\n", vars->type); + } + // TODO: Error here? + continue; + } + } + + return result; +} + +check_snmp_evaluation evaluate_single_unit(response_value response, + check_snmp_evaluation_parameters eval_params, + check_snmp_test_unit test_unit, time_t query_timestamp, + check_snmp_state_entry prev_state, + bool have_previous_state) { + mp_subcheck sc_oid_test = mp_subcheck_init(); + + if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) { + xasprintf(&sc_oid_test.output, "%s - ", test_unit.label); + } else { + sc_oid_test.output = strdup(""); + } + + char oid_string[(MAX_OID_LEN * 2) + 1] = {}; + + int oid_string_result = + snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length); + if (oid_string_result <= 0) { + // TODO error here + die(STATE_UNKNOWN, "snprint_objid failed\n"); + } + + xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); + sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); + + if (verbose > 2) { + printf("Processing oid %s\n", oid_string); + } + + bool got_a_numerical_value = false; + mp_perfdata_value pd_result_val = {0}; + + check_snmp_state_entry result_state = { + .timestamp = query_timestamp, + .oid_length = response.oid_length, + .type = response.type, + }; + + for (size_t i = 0; i < response.oid_length; i++) { + result_state.oid[i] = response.oid[i]; + } + + if (have_previous_state) { + if (query_timestamp == prev_state.timestamp) { + // somehow we have the same timestamp again, that can't be good + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN); + xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid"); + + check_snmp_evaluation result = { + .sc = sc_oid_test, + .state = result_state, + }; + + return result; + } + } + // compute rate time difference + double timeDiff = 0; + if (have_previous_state) { + if (verbose) { + printf("Previous timestamp: %s", ctime(&prev_state.timestamp)); + printf("Current timestamp: %s", ctime(&query_timestamp)); + } + timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier; + } + + mp_perfdata pd_num_val = {}; + + switch (response.type) { + case ASN_OCTET_STR: { + char *tmp = response.string_response; + if (strchr(tmp, '"') != NULL) { + // got double quote in the string + if (strchr(tmp, '\'') != NULL) { + // got single quote in the string too + // dont quote that at all to avoid even more confusion + xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + } else { + // quote with single quotes + xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); + } + } else { + // quote with double quotes + xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); + } + + if (strlen(tmp) == 0) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result); + } + + // String matching test + if ((test_unit.eval_mthd.crit_string)) { + if (strcmp(tmp, eval_params.string_cmp_value)) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK); + } else { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL); + } + } else if (test_unit.eval_mthd.crit_regex) { + const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1; + regmatch_t pmatch[nmatch]; + memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); + + int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0); + if (excode == 0) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL); + } else if (excode != REG_NOMATCH) { + char errbuf[MAX_INPUT_BUFFER] = ""; + regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + printf(_("Execute Error: %s\n"), errbuf); + exit(STATE_CRITICAL); + } else { // REG_NOMATCH + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK); + } + } + } break; + case ASN_COUNTER64: + got_a_numerical_value = true; + + result_state.value.uIntVal = response.value.uIntVal; + result_state.type = response.type; + + // TODO: perfdata unit counter + if (eval_params.calculate_rate && have_previous_state) { + if (prev_state.value.uIntVal > response.value.uIntVal) { + // overflow + unsigned long long tmp = + (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal; + + tmp /= timeDiff; + pd_result_val = mp_create_pd_value(tmp); + } else { + pd_result_val = mp_create_pd_value( + (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff); + } + } else { + // It's only a counter if we cont compute rate + pd_num_val.uom = "c"; + pd_result_val = mp_create_pd_value(response.value.uIntVal); + } + break; + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + got_a_numerical_value = true; + long long treated_value = (long long)response.value.uIntVal; + + if (eval_params.multiplier_set || eval_params.offset_set) { + double processed = 0; + if (eval_params.offset_set) { + processed += eval_params.offset; + } + + if (eval_params.multiplier_set) { + processed = processed * eval_params.multiplier; + } + + treated_value = lround(processed); + } + + result_state.value.intVal = treated_value; + + if (eval_params.calculate_rate && have_previous_state) { + if (verbose > 2) { + printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__, + prev_state.value.intVal); + printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__, + treated_value); + } + double rate = (treated_value - prev_state.value.intVal) / timeDiff; + pd_result_val = mp_create_pd_value(rate); + } else { + pd_result_val = mp_create_pd_value(treated_value); + + if (response.type == ASN_COUNTER) { + pd_num_val.uom = "c"; + } + } + + } break; + case ASN_INTEGER: { + if (eval_params.multiplier_set || eval_params.offset_set) { + double processed = 0; + if (eval_params.multiplier_set) { + processed = (double)response.value.intVal * eval_params.multiplier; + } + + if (eval_params.offset_set) { + processed += eval_params.offset; + } + + result_state.value.doubleVal = processed; + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = + mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(processed); + } + } else { + result_state.value.intVal = response.value.intVal; + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = mp_create_pd_value( + (response.value.intVal - prev_state.value.intVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(response.value.intVal); + } + } + + got_a_numerical_value = true; + } break; + case ASN_FLOAT: // fallthrough + case ASN_DOUBLE: { + got_a_numerical_value = true; + double tmp = response.value.doubleVal; + if (eval_params.offset_set) { + tmp += eval_params.offset; + } + + if (eval_params.multiplier_set) { + tmp *= eval_params.multiplier; + } + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(tmp); + } + got_a_numerical_value = true; + + result_state.value.doubleVal = tmp; + } break; + case ASN_IPADDRESS: + // TODO + } + + if (got_a_numerical_value) { + if (eval_params.use_oid_as_perf_data_label) { + // Use oid for perdata label + pd_num_val.label = strdup(oid_string); + // TODO strdup error checking + } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) { + pd_num_val.label = strdup(test_unit.label); + } else { + pd_num_val.label = strdup(test_unit.oid); + } + + if (!(eval_params.calculate_rate && !have_previous_state)) { + // some kind of numerical value + if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { + pd_num_val.uom = test_unit.unit_value; + } + + pd_num_val.value = pd_result_val; + + xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, + pd_value_to_string(pd_result_val)); + + if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { + xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value); + } + + if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) { + pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold); + mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); + + if (tmp_state == STATE_WARNING) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); + xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", + sc_oid_test.output); + } else if (tmp_state == STATE_CRITICAL) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); + xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", + sc_oid_test.output); + } + } + + mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); + } else { + // should calculate rate, but there is no previous state, so first run + // exit with ok now + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK); + xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay", + sc_oid_test.output); + } + } + + check_snmp_evaluation result = { + .sc = sc_oid_test, + .state = result_state, + }; + + return result; +} + +char *_np_state_generate_key(int argc, char **argv); + +/* + * If time=NULL, use current time. Create state file, with state format + * version, default text. Writes version, time, and data. Avoid locking + * problems - use mv to write and then swap. Possible loss of state data if + * two things writing to same key at same time. + * Will die with UNKNOWN if errors + */ +void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) { + time_t current_time; + if (timestamp == 0) { + time(¤t_time); + } else { + current_time = timestamp; + } + + int result = 0; + + /* If file doesn't currently exist, create directories */ + if (access(stateKey._filename, F_OK) != 0) { + char *directories = NULL; + result = asprintf(&directories, "%s", stateKey._filename); + if (result < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + for (char *p = directories + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { + /* Can't free this! Otherwise error message is wrong! */ + /* np_free(directories); */ + die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); + } + *p = '/'; + } + } + + if (directories) { + free(directories); + } + } + + char *temp_file = NULL; + result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename); + if (result < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + int temp_file_desc = 0; + if ((temp_file_desc = mkstemp(temp_file)) == -1) { + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Cannot create temporary filename")); + } + + FILE *temp_file_pointer = fdopen(temp_file_desc, "w"); + if (temp_file_pointer == NULL) { + close(temp_file_desc); + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Unable to open temporary state file")); + } + + fprintf(temp_file_pointer, "# NP State file\n"); + fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); + fprintf(temp_file_pointer, "%d\n", stateKey.data_version); + fprintf(temp_file_pointer, "%lu\n", current_time); + fprintf(temp_file_pointer, "%s\n", stringToStore); + + fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); + + fflush(temp_file_pointer); + + result = fclose(temp_file_pointer); + + fsync(temp_file_desc); + + if (result != 0) { + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Error writing temp file")); + } + + if (rename(temp_file, stateKey._filename) != 0) { + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Cannot rename state temp file")); + } + + if (temp_file) { + free(temp_file); + } +} + +/* + * Read the state file + */ +bool _np_state_read_file(FILE *state_file, state_key stateKey) { + time_t current_time; + time(¤t_time); + + /* Note: This introduces a limit of 8192 bytes in the string data */ + char *line = (char *)calloc(1, 8192); + if (line == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + bool status = false; + enum { + STATE_FILE_VERSION, + STATE_DATA_VERSION, + STATE_DATA_TIME, + STATE_DATA_TEXT, + STATE_DATA_END + } expected = STATE_FILE_VERSION; + + int failure = 0; + while (!failure && (fgets(line, 8192, state_file)) != NULL) { + size_t pos = strlen(line); + if (line[pos - 1] == '\n') { + line[pos - 1] = '\0'; + } + + if (line[0] == '#') { + continue; + } + + switch (expected) { + case STATE_FILE_VERSION: { + int i = atoi(line); + if (i != NP_STATE_FORMAT_VERSION) { + failure++; + } else { + expected = STATE_DATA_VERSION; + } + } break; + case STATE_DATA_VERSION: { + int i = atoi(line); + if (i != stateKey.data_version) { + failure++; + } else { + expected = STATE_DATA_TIME; + } + } break; + case STATE_DATA_TIME: { + /* If time > now, error */ + time_t data_time = strtoul(line, NULL, 10); + if (data_time > current_time) { + failure++; + } else { + stateKey.state_data->time = data_time; + expected = STATE_DATA_TEXT; + } + } break; + case STATE_DATA_TEXT: + stateKey.state_data->data = strdup(line); + if (stateKey.state_data->data == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + stateKey.state_data->length = strlen(line); + expected = STATE_DATA_END; + status = true; + break; + case STATE_DATA_END:; + } + } + + if (line) { + free(line); + } + return status; +} +/* + * Will return NULL if no data is available (first run). If key currently + * exists, read data. If state file format version is not expected, return + * as if no data. Get state data version number and compares to expected. + * If numerically lower, then return as no previous state. die with UNKNOWN + * if exceptional error. + */ +state_data *np_state_read(state_key stateKey) { + /* Open file. If this fails, no previous state found */ + FILE *statefile = fopen(stateKey._filename, "r"); + state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); + if (statefile != NULL) { + + if (this_state_data == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + this_state_data->data = NULL; + stateKey.state_data = this_state_data; + + if (_np_state_read_file(statefile, stateKey)) { + this_state_data->errorcode = OK; + } else { + this_state_data->errorcode = ERROR; + } + + fclose(statefile); + } else { + // Failed to open state file + this_state_data->errorcode = ERROR; + } + + return stateKey.state_data; +} + +/* + * Internal function. Returns either: + * envvar NAGIOS_PLUGIN_STATE_DIRECTORY + * statically compiled shared state directory + */ +char *_np_state_calculate_location_prefix(void) { + char *env_dir; + + /* Do not allow passing MP_STATE_PATH in setuid plugins + * for security reasons */ + if (!mp_suid()) { + env_dir = getenv("MP_STATE_PATH"); + if (env_dir && env_dir[0] != '\0') { + return env_dir; + } + /* This is the former ENV, for backward-compatibility */ + env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); + if (env_dir && env_dir[0] != '\0') { + return env_dir; + } + } + + return NP_STATE_DIR_PREFIX; +} + +/* + * Initiatializer for state routines. + * Sets variables. Generates filename. Returns np_state_key. die with + * UNKNOWN if exception + */ +state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, + char **argv) { + state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); + if (this_state == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + char *temp_keyname = NULL; + if (keyname == NULL) { + temp_keyname = _np_state_generate_key(argc, argv); + } else { + temp_keyname = strdup(keyname); + if (temp_keyname == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + } + + /* Die if invalid characters used for keyname */ + char *tmp_char = temp_keyname; + while (*tmp_char != '\0') { + if (!(isalnum(*tmp_char) || *tmp_char == '_')) { + die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); + } + tmp_char++; + } + this_state->name = temp_keyname; + this_state->plugin_name = plugin_name; + this_state->data_version = expected_data_version; + this_state->state_data = NULL; + + /* Calculate filename */ + char *temp_filename = NULL; + int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), + (unsigned long)geteuid(), plugin_name, this_state->name); + if (error < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + this_state->_filename = temp_filename; + + return *this_state; +} + +/* + * Returns a string to use as a keyname, based on an md5 hash of argv, thus + * hopefully a unique key per service/plugin invocation. Use the extra-opts + * parse of argv, so that uniqueness in parameters are reflected there. + */ +char *_np_state_generate_key(int argc, char **argv) { + unsigned char result[256]; + +#ifdef USE_OPENSSL + /* + * This code path is chosen if openssl is available (which should be the most common + * scenario). Alternatively, the gnulib implementation/ + * + */ + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + + EVP_DigestInit(ctx, EVP_sha256()); + + for (int i = 0; i < argc; i++) { + EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); + } + + EVP_DigestFinal(ctx, result, NULL); +#else + + struct sha256_ctx ctx; + + for (int i = 0; i < this_monitoring_plugin->argc; i++) { + sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); + } + + sha256_finish_ctx(&ctx, result); +#endif // FOUNDOPENSSL + + char keyname[41]; + for (int i = 0; i < 20; ++i) { + sprintf(&keyname[2 * i], "%02x", result[i]); + } + + keyname[40] = '\0'; + + char *keyname_copy = strdup(keyname); + if (keyname_copy == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + + return keyname_copy; +} diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h index 28e3c4e3..0f7780b1 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.h +++ b/plugins/check_snmp.d/check_snmp_helpers.h @@ -1,7 +1,71 @@ #pragma once #include "./config.h" +#include check_snmp_test_unit check_snmp_test_unit_init(); int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool); check_snmp_config check_snmp_config_init(); + +typedef struct { + oid oid[MAX_OID_LEN]; + size_t oid_length; + unsigned char type; + union { + uint64_t uIntVal; + int64_t intVal; + double doubleVal; + } value; + char *string_response; +} response_value; + +typedef struct { + int errorcode; + response_value *response_values; +} snmp_responces; +snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters); + +// state is similar to response, but only numerics and a timestamp +typedef struct { + time_t timestamp; + oid oid[MAX_OID_LEN]; + size_t oid_length; + unsigned char type; + union { + unsigned long long uIntVal; + long long intVal; + double doubleVal; + } value; +} check_snmp_state_entry; + +typedef struct { + check_snmp_state_entry state; + mp_subcheck sc; +} check_snmp_evaluation; +check_snmp_evaluation evaluate_single_unit(response_value response, + check_snmp_evaluation_parameters eval_params, + check_snmp_test_unit test_unit, time_t query_timestamp, + check_snmp_state_entry prev_state, + bool have_previous_state); + +#define NP_STATE_FORMAT_VERSION 1 + +typedef struct state_data_struct { + time_t time; + void *data; + size_t length; /* Of binary data */ + int errorcode; +} state_data; + +typedef struct state_key_struct { + char *name; + char *plugin_name; + int data_version; + char *_filename; + state_data *state_data; +} state_key; + +state_data *np_state_read(state_key stateKey); +state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, + char **argv); +void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore); diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index e68986e2..a5d5aa52 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -1,7 +1,7 @@ #pragma once -#include "thresholds.h" -#include "states.h" +#include "../../lib/thresholds.h" +#include "../../lib/states.h" #include #include #include @@ -18,7 +18,7 @@ #include #include -#define DEFAULT_PORT "161" +#define DEFAULT_PORT "161" #define DEFAULT_RETRIES 5 typedef struct eval_method { @@ -34,10 +34,8 @@ typedef struct check_snmp_test_unit { mp_thresholds threshold; } check_snmp_test_unit; -typedef struct check_snmp_config { - // SNMP session to use +typedef struct { struct snmp_session snmp_session; - // use getnet instead of get bool use_getnext; @@ -47,7 +45,9 @@ typedef struct check_snmp_config { check_snmp_test_unit *test_units; size_t num_of_test_units; +} check_snmp_config_snmp_parameters; +typedef struct { // State if an empty value is encountered mp_state_enum nulloid_result; @@ -63,7 +63,18 @@ typedef struct check_snmp_config { bool offset_set; // Modify output - bool use_perf_data_labels_from_input; + bool use_oid_as_perf_data_label; + + // activate rate calucation + bool calculate_rate; + unsigned int rate_multiplier; +} check_snmp_evaluation_parameters; + +typedef struct check_snmp_config { + // SNMP session to use + check_snmp_config_snmp_parameters snmp_params; + + check_snmp_evaluation_parameters evaluation_params; mp_output_format output_format; bool output_format_is_set; diff --git a/plugins/tests/check_snmp.t b/plugins/tests/check_snmp.t index bfe42e16..26d67898 100755 --- a/plugins/tests/check_snmp.t +++ b/plugins/tests/check_snmp.t @@ -4,12 +4,13 @@ # use strict; +use warnings; use Test::More; use NPTest; use FindBin qw($Bin); use POSIX qw/strftime/; -my $tests = 81; +my $tests = 75; # Check that all dependent modules are available eval { require NetSNMP::OID; @@ -76,49 +77,36 @@ my $res; $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); -like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); -like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software | -.1.3.6.1.4.1.8072.3.2.67.0: -"Cisco Internetwork Operating System Software -IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version -12.2(20)EWA, RELEASE SOFTWARE (fc1) -Technical Support: http://www.cisco.com/techsupport -Copyright (c) 1986-2004 by cisco Systems, Inc. -"').'/m', "String contains all lines"); +like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines"); # sysContact.0 is "Alice" (from our snmpd.conf) $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0 -o sysContact.0 -o .1.3.6.1.4.1.8072.3.2.67.1"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); -like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); -like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software ').'"?Alice"?'.quotemeta(' Kisco Outernetwork Oserating Gystem Totware | -.1.3.6.1.4.1.8072.3.2.67.0: -"Cisco Internetwork Operating System Software -IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version -12.2(20)EWA, RELEASE SOFTWARE (fc1) -Technical Support: http://www.cisco.com/techsupport -Copyright (c) 1986-2004 by cisco Systems, Inc. -" -.1.3.6.1.4.1.8072.3.2.67.1: -"Kisco Outernetwork Oserating Gystem Totware -Copyleft (c) 2400-2689 by kisco Systrems, Inc."').'/m', "String contains all lines with multiple OIDs"); +# like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); +like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines with multiple OIDs"); +like($res->output, '/.*Alice.*/m', "String contains all lines with multiple OIDs"); +like($res->output, '/.*Kisco Outernetwork Oserating Gystem Totware.*/m', "String contains all lines with multiple OIDs"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.2"); -like($res->output, '/'.quotemeta('SNMP OK - This should not confuse check_snmp \"parser\" | -.1.3.6.1.4.1.8072.3.2.67.2: -"This should not confuse check_snmp \"parser\" -into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('This should not confuse check_snmp \"parser\" | +# .1.3.6.1.4.1.8072.3.2.67.2: +# "This should not confuse check_snmp \"parser\" +# into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.3"); -like($res->output, '/'.quotemeta('SNMP OK - It\'s getting even harder if the line | -.1.3.6.1.4.1.8072.3.2.67.3: -"It\'s getting even harder if the line -ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('It\'s getting even harder if the line | +# .1.3.6.1.4.1.8072.3.2.67.3: +# "It\'s getting even harder if the line +# ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.4"); -like($res->output, '/'.quotemeta('SNMP OK - And now have fun with with this: \"C:\\\\\" | -.1.3.6.1.4.1.8072.3.2.67.4: -"And now have fun with with this: \"C:\\\\\" -because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('And now have fun with with this: \"C:\\\\\" | +# .1.3.6.1.4.1.8072.3.2.67.4: +# "And now have fun with with this: \"C:\\\\\" +# because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*"); @@ -131,156 +119,159 @@ SKIP: { my $ts = time(); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 0, "Returns OK"); - is($res->output, "No previous data to calculate rate - assume okay"); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/"); # test rate 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 1, "WARNING - due to going above rate calculation" ); - is($res->output, "SNMP RATE WARNING - *666* | iso.3.6.1.4.1.8072.3.2.67.10=666;600 "); + like($res->output, "/.*=666.*/"); # test rate with same time $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" ); - is($res->output, "Time duration between plugin calls is invalid"); + like($res->output, "/.*Time duration between plugin calls is invalid.*/"); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK for first call" ); - is($res->output, "No previous data to calculate rate - assume okay" ); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/" ); # test rate 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets 666 | inoctets=666 ", "Check label"); + like($res->output, "/.*inoctets.*=666.*/m", "Check label"); # test rate 3 seconds later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+3))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets 333 | inoctets=333 ", "Check rate decreases due to longer interval"); + like($res->output, "/.*inoctets.*333.*/", "Check rate decreases due to longer interval"); # label performance data check $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test 67996 | test=67996c ", "Check label"); + like($res->output, "/.*test.?=67996c/", "Check label"); - $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); - is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test'test 68662 | \"test'test\"=68662c ", "Check label"); + # following test is deactivated since it was not valid due to the guidelines (no single quote in label allowed) + # $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); + # is($res->return_code, 0, "OK as no thresholds" ); + # is($res->output, "test'test 68662 | \"test'test\"=68662c ", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test\"test'" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test\"test 69328 | 'test\"test'=69328c ", "Check label"); + like($res->output, "/.*'test\"test'=68662c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test -O" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test 69994 | iso.3.6.1.4.1.8072.3.2.67.10=69994c ", "Check label"); + like($res->output, "/.*.67.10.?=69328c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - 70660 | iso.3.6.1.4.1.8072.3.2.67.10=70660c ", "Check label"); + like($res->output, "/.*8072.3.2.67.10.?=69994c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test test'" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test test 71326 | 'test test'=71326c ", "Check label"); + like($res->output, "/.*'test test'=70660c/", "Check label"); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); is($res->return_code, 0, "OK for first call" ); - is($res->output, "No previous data to calculate rate - assume okay" ); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/" ); # test 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets_per_minute 39960 | inoctets_per_minute=39960 ", "Checking multiplier"); + like($res->output, "/.*inoctets_per_minute.*=39960/", "Checking multiplier"); }; -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s '\"stringtests\"'" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s 'stringtests'" ); is($res->return_code, 0, "OK as string matches" ); -is($res->output, 'SNMP OK - "stringtests" | ', "Good string match" ); +like($res->output, '/.*stringtests.*/', "Good string match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s ring" ); is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); -is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Failed string match" ); +like($res->output, '/.*stringtests.*/', "Failed string match" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s '\"stringtests\"'" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s 'stringtests'" ); is($res->return_code, 2, "CRITICAL as string matches but inverted" ); -is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Inverted string match" ); +like($res->output, '/.*"stringtests".*/', "Inverted string match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s ring" ); is($res->return_code, 0, "OK as string doesn't match but inverted" ); -is($res->output, 'SNMP OK - "stringtests" | ', "OK as inverted string no match" ); +like($res->output, '/.*"stringtests".*/', "OK as inverted string no match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.12 -w 4:5" ); -is($res->return_code, 1, "Numeric in string test" ); -is($res->output, 'SNMP WARNING - *3.5* | iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5 ', "WARNING threshold checks for string masquerading as number" ); +# a string is a string and not a number +is($res->return_code, 0, "Numeric in string test" ); +like($res->output, '/.*3.5.*| iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5/', "WARNING threshold checks for string masquerading as number" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.13" ); is($res->return_code, 0, "Not really numeric test" ); -is($res->output, 'SNMP OK - "87.4startswithnumberbutshouldbestring" | ', "Check string with numeric start is still string" ); +like($res->output, '/.*"87.4startswithnumberbutshouldbestring".*/', "Check string with numeric start is still string" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.14" ); is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); -is($res->output, 'SNMP OK - "555\"I said\"" | ', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); +like($res->output, '/.*\'555"I said"\'.*/', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.15 -r 'CUSTOM CHECK OK'" ); is($res->return_code, 0, "String check should check whole string, not a parsed number" ); -is($res->output, 'SNMP OK - "CUSTOM CHECK OK: foo is 12345" | ', "String check with numbers returns whole string"); +like($res->output, '/.*CUSTOM CHECK OK: foo is 12345.*/', "String check with numbers returns whole string"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 0, "Negative integer check OK" ); -is($res->output, 'SNMP OK - -2 | iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3: ', "Negative integer check OK output" ); +like($res->output, '/.*-2.*| iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3:/', "Negative integer check OK output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 1, "Negative integer check WARNING" ); -is($res->output, 'SNMP WARNING - *-3* | iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3: ', "Negative integer check WARNING output" ); +like($res->output, '/.*-3.*| iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3:/', "Negative integer check WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 2, "Negative integer check CRITICAL" ); -is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3: ', "Negative integer check CRITICAL output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3:/', "Negative integer check CRITICAL output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -3: -c -6:" ); is($res->return_code, 1, "Negative integer as string, WARNING" ); -is($res->output, 'SNMP WARNING - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6: ', "Negative integer as string, WARNING output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6:/', "Negative integer as string, WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -2: -c -3:" ); is($res->return_code, 2, "Negative integer as string, CRITICAL" ); -is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3: ', "Negative integer as string, CRITICAL output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3:/', "Negative integer as string, CRITICAL output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); -is($res->return_code, 0, "Negative float OK" ); -is($res->output, 'SNMP OK - -6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); +# deactivated since the perl agent api of snmpd really does not allow floats +# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); +# is($res->return_code, 0, "Negative float OK" ); +# is($res->output, '-6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); -is($res->return_code, 1, "Negative float WARNING" ); -is($res->output, 'SNMP WARNING - *-6.6* | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;@-6.65:~;@-6.55:~ ', "Negative float WARNING output" ); +# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); +# is($res->return_code, 1, "Negative float WARNING" ); +# like($res->output, '/-6.6.*| .*67.18=-6.6;@-6.65:~;@-6.55:~/', "Negative float WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-10:20' -c '2:200000,-20:30'" ); is($res->return_code, 0, "Multiple OIDs with thresholds" ); -like($res->output, '/SNMP OK - \d+ -4 | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); +like($res->output, '/-4.*| .*67.10=\d+c;1:100000;2:200000 .*67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-1:2' -c '2:200000,-20:30'" ); is($res->return_code, 1, "Multiple OIDs with thresholds" ); -like($res->output, '/SNMP WARNING - \d+ \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); +like($res->output, '/-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1 -O" ); is($res->return_code, 2, "Multiple OIDs with some thresholds" ); -like($res->output, '/SNMP CRITICAL - \*\d+\* \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19"); is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); -is($res->output,'SNMP OK - 42 | iso.3.6.1.4.1.8072.3.2.67.19=42 ', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); +like($res->output,'/.*42.*| iso.3.6.1.4.1.8072.3.2.67.19=42/', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 -M .1"); is($res->return_code, 0, "Test multiply RC" ); -is($res->output,'SNMP OK - 4.200000 | iso.3.6.1.4.1.8072.3.2.67.19=4.200000 ' , "Test multiply .1 output" ); +like($res->output,'/.*4.200000.*| iso.3.6.1.4.1.8072.3.2.67.19=4.200000/' , "Test multiply .1 output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' "); -is($res->return_code, 0, "Test multiply RC + format" ); -is($res->output, 'SNMP OK - 4.20 | iso.3.6.1.4.1.8072.3.2.67.19=4.20 ', "Test multiply .1 output + format" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1"); +is($res->return_code, 0, "Test multiply RC" ); +like($res->output, '/.*4.20.*| iso.3.6.1.4.1.8072.3.2.67.19=4.20/', "Test multiply .1 output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' -w 1"); -is($res->return_code, 1, "Test multiply RC + format + thresholds" ); -is($res->output, 'SNMP WARNING - *4.20* | iso.3.6.1.4.1.8072.3.2.67.19=4.20;1 ', "Test multiply .1 output + format + thresholds" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -w 1"); +is($res->return_code, 1, "Test multiply RC + thresholds" ); +like($res->output, '/.*4.20.* | iso.3.6.1.4.1.8072.3.2.67.19=4.20+;1/', "Test multiply .1 output + thresholds" ); diff --git a/plugins/tests/check_snmp_agent.pl b/plugins/tests/check_snmp_agent.pl index 38912e98..608b6f92 100644 --- a/plugins/tests/check_snmp_agent.pl +++ b/plugins/tests/check_snmp_agent.pl @@ -4,9 +4,10 @@ # #use strict; # Doesn't work +use warnings; use NetSNMP::OID qw(:all); use NetSNMP::agent; -use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64); +use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64 ASN_FLOAT); #use Math::Int64 qw(uint64); # Skip that module while we don't need it sub uint64 { return $_ } @@ -22,21 +23,82 @@ IOS (tm) Catalyst 4000 "L3" Switch Software (cat4000-I9K91S-M), Version Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2004 by cisco Systems, Inc. '; -my $multilin2 = "Kisco Outernetwork Oserating Gystem Totware +my $multiline2 = "Kisco Outernetwork Oserating Gystem Totware Copyleft (c) 2400-2689 by kisco Systrems, Inc."; -my $multilin3 = 'This should not confuse check_snmp "parser" +my $multiline3 = 'This should not confuse check_snmp "parser" into thinking there is no 2nd line'; -my $multilin4 = 'It\'s getting even harder if the line +my $multiline4 = 'It\'s getting even harder if the line ends with with this: C:\\'; -my $multilin5 = 'And now have fun with with this: "C:\\" +my $multiline5 = 'And now have fun with with this: "C:\\" because we\'re not done yet!'; # Next are arrays of indexes (Type, initial value and increments) # 0..19 <---- please update comment when adding/removing fields -my @fields = (ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_UNSIGNED, ASN_UNSIGNED, ASN_COUNTER, ASN_COUNTER64, ASN_UNSIGNED, ASN_COUNTER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER ); -my @values = ($multiline, $multilin2, $multilin3, $multilin4, $multilin5, 4294965296, 1000, 4294965296, uint64("18446744073709351616"), int(rand(2**32)), 64000, "stringtests", "3.5", "87.4startswithnumberbutshouldbestring", '555"I said"', 'CUSTOM CHECK OK: foo is 12345', -2, '-4', '-6.6', 42 ); +my @fields = (ASN_OCTET_STR, # 0 + ASN_OCTET_STR, # 1 + ASN_OCTET_STR, # 2 + ASN_OCTET_STR, # 3 + ASN_OCTET_STR, # 4 + ASN_UNSIGNED, # 5 + ASN_UNSIGNED, # 6 + ASN_COUNTER, # 7 + ASN_COUNTER64, # 8 + ASN_UNSIGNED, # 9 + ASN_COUNTER, # 10 + ASN_OCTET_STR, # 11 + ASN_OCTET_STR, # 12 + ASN_OCTET_STR, # 13 + ASN_OCTET_STR, # 14 + ASN_OCTET_STR, # 15 + ASN_INTEGER, # 16 + ASN_INTEGER, # 17 + ASN_FLOAT, # 18 + ASN_INTEGER # 19 + ); +my @values = ($multiline, # 0 + $multiline2, # 1 + $multiline3, # 2 + $multiline4, # 3 + $multiline5, # 4 + 4294965296, # 5 + 1000, # 6 + 4294965296, # 7 + uint64("18446744073709351616"), # 8 + int(rand(2**32)), # 9 + 64000, # 10 + "stringtests", # 11 + "3.5", # 12 + "87.4startswithnumberbutshouldbestring", # 13 + '555"I said"', # 14 + 'CUSTOM CHECK OK: foo is 12345', # 15 + '-2', # 16 + '-4', # 17 + '-6.6', # 18 + 42 # 19 + ); # undef increments are randomized -my @incrts = (undef, undef, undef, undef, undef, 1000, -500, 1000, 100000, undef, 666, undef, undef, undef, undef, undef, -1, undef, undef, 0 ); +my @incrts = ( + undef, # 0 + undef, # 1 + undef, # 2 + undef, # 3 + undef, # 4 + 1000, # 5 + -500, # 6 + 1000, # 7 + 100000, # 8 + undef, # 9 + 666, # 10 + undef, # 11 + undef, # 12 + undef, # 13 + undef, # 14 + undef, # 15 + -1, # 16 + 0, # 17 + undef, # 18 + 0 # 19 + ); # Number of elements in our OID my $oidelts; diff --git a/plugins/tests/test_check_snmp.c b/plugins/tests/test_check_snmp.c new file mode 100644 index 00000000..d71706d0 --- /dev/null +++ b/plugins/tests/test_check_snmp.c @@ -0,0 +1,175 @@ +/***************************************************************************** + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + *****************************************************************************/ + +#include "tap.h" +#include "../../config.h" + +#include +#include +#include + +#include "utils_base.c" +#include "../check_snmp.d/check_snmp_helpers.h" + +char *_np_state_generate_key(int argc, char **argv); +char *_np_state_calculate_location_prefix(void); + +int main(int argc, char **argv) { + char *temp_string = (char *)_np_state_generate_key(argc, argv); + ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), + "Got hash with exe and no parameters") || + diag("You are probably running in wrong directory. Must run as ./test_utils"); + + int fake_argc = 4; + char *fake_argv[] = { + "./test_utils", + "here", + "--and", + "now", + }; + temp_string = (char *)_np_state_generate_key(fake_argc, fake_argv); + ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), + "Got based on expected argv"); + + unsetenv("MP_STATE_PATH"); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory"); + + setenv("MP_STATE_PATH", "", 1); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string"); + + setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory"); + + fake_argc = 1; + fake_argv[0] = "./test_utils"; + state_key temp_state_key1 = np_enable_state(NULL, 51, "check_test", fake_argc, fake_argv); + ok(!strcmp(temp_state_key1.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key1.name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), + "Got generated filename"); + + state_key temp_state_key2 = + np_enable_state("allowedchars_in_keyname", 77, "check_snmp", fake_argc, fake_argv); + + char state_path[1024]; + sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", + (unsigned long)geteuid()); + ok(!strcmp(temp_state_key2.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key2.name, "allowedchars_in_keyname"), "Got key name with valid chars"); + ok(!strcmp(temp_state_key2._filename, state_path), "Got internal filename"); + + /* Don't do this test just yet. Will die */ + /* + np_enable_state("bad^chars$in@here", 77); + temp_state_key = this_monitoring_plugin->state; + ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" + ); + */ + + state_key temp_state_key3 = + np_enable_state("funnykeyname", 54, "check_snmp", fake_argc, fake_argv); + sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", + (unsigned long)geteuid()); + ok(!strcmp(temp_state_key3.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key3.name, "funnykeyname"), "Got key name"); + + ok(!strcmp(temp_state_key3._filename, state_path), "Got internal filename"); + ok(temp_state_key3.data_version == 54, "Version set"); + + state_data *temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Got no state data as file does not exist"); + + /* + temp_fp = fopen("var/statefile", "r"); + if (temp_fp==NULL) + printf("Error opening. errno=%d\n", errno); + printf("temp_fp=%s\n", temp_fp); + ok( _np_state_read_file(temp_fp) == true, "Can read state file" ); + fclose(temp_fp); + */ + + temp_state_key3._filename = "var/statefile"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data != NULL, "Got state data now") || + diag("Are you running in right directory? Will get coredump next if not"); + ok(temp_state_data->time == 1234567890, "Got time"); + ok(!strcmp((char *)temp_state_data->data, "String to read"), "Data as expected"); + + temp_state_key3.data_version = 53; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Older data version gives NULL"); + temp_state_key3.data_version = 54; + + temp_state_key3._filename = "var/nonexistent"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Missing file gives NULL"); + + temp_state_key3._filename = "var/oldformat"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Old file format gives NULL"); + + temp_state_key3._filename = "var/baddate"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Bad date gives NULL"); + + temp_state_key3._filename = "var/missingdataline"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Missing data line gives NULL"); + + unlink("var/generated"); + temp_state_key3._filename = "var/generated"; + + time_t current_time = 1234567890; + np_state_write_string(temp_state_key3, current_time, "String to read"); + ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected"); + + unlink("var/generated_directory/statefile"); + unlink("var/generated_directory"); + temp_state_key3._filename = "var/generated_directory/statefile"; + current_time = 1234567890; + np_state_write_string(temp_state_key3, current_time, "String to read"); + ok(system("cmp var/generated_directory/statefile var/statefile") == 0, + "Have created directory"); + + /* This test to check cannot write to dir - can't automate yet */ + /* + unlink("var/generated_bad_dir"); + mkdir("var/generated_bad_dir", S_IRUSR); + np_state_write_string(current_time, "String to read"); + */ + + temp_state_key3._filename = "var/generated"; + time(¤t_time); + np_state_write_string(temp_state_key3, 0, "String to read"); + temp_state_data = np_state_read(temp_state_key3); + /* Check time is set to current_time */ + ok(system("cmp var/generated var/statefile > /dev/null") != 0, + "Generated file should be different this time"); + ok(temp_state_data->time - current_time <= 1, "Has time generated from current time"); + + /* Don't know how to automatically test this. Need to be able to redefine die and catch the + * error */ + /* + temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write"; + np_state_write_string(0, "Bad file"); + */ + + np_cleanup(); +} diff --git a/plugins/tests/test_check_snmp.t b/plugins/tests/test_check_snmp.t new file mode 100755 index 00000000..967633e9 --- /dev/null +++ b/plugins/tests/test_check_snmp.t @@ -0,0 +1,6 @@ +#!/usr/bin/perl +use Test::More; +if (! -e "./test_check_snmp") { + plan skip_all => "./test_check_snmp not compiled - please enable libtap library to test"; +} +exec "./test_check_snmp"; -- cgit v1.2.3-74-g34f1 From f855c5b5bbbc6d5436741fd8108be64825a3c76b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:06:55 +0200 Subject: general refactorin in lib, more local variables, real booleans --- lib/extra_opts.c | 39 ++++++++------ lib/maxfd.c | 1 - lib/parse_ini.c | 141 ++++++++++++++++++++++++++++--------------------- lib/tests/test_cmd.c | 14 +++-- lib/tests/test_ini1.c | 3 +- lib/tests/test_opts1.c | 17 +++--- lib/tests/test_opts2.c | 17 +++--- lib/tests/test_tcp.c | 5 +- lib/utils_base.c | 2 +- lib/utils_base.h | 2 +- lib/utils_cmd.c | 107 +++++++++++++++++-------------------- lib/utils_cmd.h | 8 +-- lib/utils_tcp.c | 20 ++++--- 13 files changed, 196 insertions(+), 180 deletions(-) (limited to 'lib/utils_base.h') diff --git a/lib/extra_opts.c b/lib/extra_opts.c index 857b34b4..3fe69014 100644 --- a/lib/extra_opts.c +++ b/lib/extra_opts.c @@ -29,26 +29,30 @@ bool is_option2(char *str) { if (!str) { return false; - } else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) { + } + + if (strspn(str, "-") == 1 || strspn(str, "-") == 2) { return true; - } else { - return false; } + + return false; } /* this is the externally visible function used by plugins */ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { - np_arg_list *extra_args = NULL, *ea1 = NULL, *ea_tmp = NULL; - char **argv_new = NULL; - char *argptr = NULL; - int i, j, optfound, argc_new, ea_num = *argc; - if (*argc < 2) { /* No arguments provided */ return argv; } - for (i = 1; i < *argc; i++) { + np_arg_list *extra_args = NULL; + np_arg_list *ea1 = NULL; + np_arg_list *ea_tmp = NULL; + char *argptr = NULL; + int optfound; + size_t ea_num = (size_t)*argc; + + for (int i = 1; i < *argc; i++) { argptr = NULL; optfound = 0; @@ -57,9 +61,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* It is a single argument with value */ argptr = argv[i] + 13; /* Delete the extra opts argument */ - for (j = i; j < *argc; j++) { + for (int j = i; j < *argc; j++) { argv[j] = argv[j + 1]; } + i--; *argc -= 1; } else if (strcmp(argv[i], "--extra-opts") == 0) { @@ -67,9 +72,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* It is a argument with separate value */ argptr = argv[i + 1]; /* Delete the extra-opts argument/value */ - for (j = i; j < *argc - 1; j++) { + for (int j = i; j < *argc - 1; j++) { argv[j] = argv[j + 2]; } + i -= 2; *argc -= 2; ea_num--; @@ -77,9 +83,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* It has no value */ optfound = 1; /* Delete the extra opts argument */ - for (j = i; j < *argc; j++) { + for (int j = i; j < *argc; j++) { argv[j] = argv[j + 1]; } + i--; *argc -= 1; } @@ -115,20 +122,20 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { } } /* lather, rince, repeat */ - if (ea_num == *argc && extra_args == NULL) { + if (ea_num == (size_t)*argc && extra_args == NULL) { /* No extra-opts */ return argv; } /* done processing arguments. now create a new argv array... */ - argv_new = (char **)malloc((ea_num + 1) * sizeof(char **)); + char **argv_new = (char **)malloc((ea_num + 1) * sizeof(char **)); if (argv_new == NULL) { die(STATE_UNKNOWN, _("malloc() failed!\n")); } /* starting with program name */ argv_new[0] = argv[0]; - argc_new = 1; + int argc_new = 1; /* then parsed ini opts (frying them up in the same run) */ while (extra_args) { argv_new[argc_new++] = extra_args->arg; @@ -137,7 +144,7 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { free(ea1); } /* finally the rest of the argv array */ - for (i = 1; i < *argc; i++) { + for (int i = 1; i < *argc; i++) { argv_new[argc_new++] = argv[i]; } *argc = argc_new; diff --git a/lib/maxfd.c b/lib/maxfd.c index 9b58d8e3..a0f79949 100644 --- a/lib/maxfd.c +++ b/lib/maxfd.c @@ -19,7 +19,6 @@ *****************************************************************************/ #include "./maxfd.h" -#include long mp_open_max(void) { long maxfd = 0L; diff --git a/lib/parse_ini.c b/lib/parse_ini.c index 4c3c1b93..db337622 100644 --- a/lib/parse_ini.c +++ b/lib/parse_ini.c @@ -59,10 +59,10 @@ static char *default_ini_path_names[] = { } while ((c) != EOF && (c) != (n)) /* internal function that returns the constructed defaults options */ -static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts); +static bool read_defaults(FILE *defaults_file, const char *stanza, np_arg_list **opts); /* internal function that converts a single line into options format */ -static int add_option(FILE *f, np_arg_list **optlst); +static int add_option(FILE *filePointer, np_arg_list **optlst); /* internal functions to find default file */ static char *default_file(void); @@ -74,7 +74,8 @@ static char *default_file_in_path(void); * into its separate parts. */ static void parse_locator(const char *locator, const char *def_stanza, np_ini_info *i) { - size_t locator_len = 0, stanza_len = 0; + size_t locator_len = 0; + size_t stanza_len = 0; /* if locator is NULL we'll use default values */ if (locator != NULL) { @@ -112,33 +113,34 @@ static void parse_locator(const char *locator, const char *def_stanza, np_ini_in * This is the externally visible function used by extra_opts. */ np_arg_list *np_get_defaults(const char *locator, const char *default_section) { - FILE *inifile = NULL; - np_arg_list *defaults = NULL; - np_ini_info i; int is_suid_plugin = mp_suid(); if (is_suid_plugin && idpriv_temp_drop() == -1) { die(STATE_UNKNOWN, _("Cannot drop privileges: %s\n"), strerror(errno)); } - parse_locator(locator, default_section, &i); - inifile = strcmp(i.file, "-") == 0 ? stdin : fopen(i.file, "r"); + FILE *inifile = NULL; + np_ini_info ini_info; + parse_locator(locator, default_section, &ini_info); + inifile = strcmp(ini_info.file, "-") == 0 ? stdin : fopen(ini_info.file, "r"); if (inifile == NULL) { die(STATE_UNKNOWN, _("Can't read config file: %s\n"), strerror(errno)); } - if (!read_defaults(inifile, i.stanza, &defaults)) { - die(STATE_UNKNOWN, _("Invalid section '%s' in config file '%s'\n"), i.stanza, i.file); + + np_arg_list *defaults = NULL; + if (!read_defaults(inifile, ini_info.stanza, &defaults)) { + die(STATE_UNKNOWN, _("Invalid section '%s' in config file '%s'\n"), ini_info.stanza, ini_info.file); } - if (i.file_string_on_heap) { - free(i.file); + if (ini_info.file_string_on_heap) { + free(ini_info.file); } if (inifile != stdin) { fclose(inifile); } - free(i.stanza); + free(ini_info.stanza); if (is_suid_plugin && idpriv_temp_restore() == -1) { die(STATE_UNKNOWN, _("Cannot restore privileges: %s\n"), strerror(errno)); } @@ -153,59 +155,58 @@ np_arg_list *np_get_defaults(const char *locator, const char *default_section) { * be extra careful about user-supplied input (i.e. avoiding possible * format string vulnerabilities, etc). */ -static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { - int c = 0; +static bool read_defaults(FILE *defaults_file, const char *stanza, np_arg_list **opts) { bool status = false; - size_t i, stanza_len; enum { NOSTANZA, WRONGSTANZA, RIGHTSTANZA } stanzastate = NOSTANZA; - stanza_len = strlen(stanza); + size_t stanza_len = strlen(stanza); /* our little stanza-parsing state machine */ - while ((c = fgetc(f)) != EOF) { + int current_char = 0; + while ((current_char = fgetc(defaults_file)) != EOF) { /* gobble up leading whitespace */ - if (isspace(c)) { + if (isspace(current_char)) { continue; } - switch (c) { + switch (current_char) { /* globble up comment lines */ case ';': case '#': - GOBBLE_TO(f, c, '\n'); + GOBBLE_TO(defaults_file, current_char, '\n'); break; /* start of a stanza, check to see if it matches */ - case '[': + case '[': { stanzastate = WRONGSTANZA; + size_t i; for (i = 0; i < stanza_len; i++) { - c = fgetc(f); + current_char = fgetc(defaults_file); /* strip leading whitespace */ if (i == 0) { - for (; isspace(c); c = fgetc(f)) { - continue; + for (; isspace(current_char); current_char = fgetc(defaults_file)) { } } /* nope, read to the end of the line */ - if (c != stanza[i]) { - GOBBLE_TO(f, c, '\n'); + if (current_char != stanza[i]) { + GOBBLE_TO(defaults_file, current_char, '\n'); break; } } + /* if it matched up to here and the next char is ']'... */ if (i == stanza_len) { - c = fgetc(f); + current_char = fgetc(defaults_file); /* strip trailing whitespace */ - for (; isspace(c); c = fgetc(f)) { - continue; + for (; isspace(current_char); current_char = fgetc(defaults_file)) { } - if (c == ']') { + if (current_char == ']') { stanzastate = RIGHTSTANZA; } } - break; + } break; /* otherwise, we're in the body of a stanza or a parse error */ default: switch (stanzastate) { @@ -216,12 +217,12 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { die(STATE_UNKNOWN, "%s\n", _("Config file error")); /* we're in a stanza, but for a different plugin */ case WRONGSTANZA: - GOBBLE_TO(f, c, '\n'); + GOBBLE_TO(defaults_file, current_char, '\n'); break; /* okay, this is where we start taking the config */ case RIGHTSTANZA: - ungetc(c, f); - if (add_option(f, opts)) { + ungetc(current_char, defaults_file); + if (add_option(defaults_file, opts)) { die(STATE_UNKNOWN, "%s\n", _("Config file error")); } status = true; @@ -240,13 +241,12 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { * --option[=value] * appending it to the linked list optbuf. */ -static int add_option(FILE *f, np_arg_list **optlst) { - np_arg_list *opttmp = *optlst, *optnew; - char *linebuf = NULL, *lineend = NULL, *optptr = NULL, *optend = NULL; - char *eqptr = NULL, *valptr = NULL, *valend = NULL; - short done_reading = 0, equals = 0, value = 0; - size_t cfg_len = 0, read_sz = 8, linebuf_sz = 0, read_pos = 0; - size_t opt_len = 0, val_len = 0; +static int add_option(FILE *filePointer, np_arg_list **optlst) { + char *linebuf = NULL; + bool done_reading = false; + const size_t read_sz = 8; + size_t linebuf_sz = 0; + size_t read_pos = 0; /* read one line from the file */ while (!done_reading) { @@ -258,24 +258,29 @@ static int add_option(FILE *f, np_arg_list **optlst) { die(STATE_UNKNOWN, _("malloc() failed!\n")); } } - if (fgets(&linebuf[read_pos], (int)read_sz, f) == NULL) { - done_reading = 1; + + if (fgets(&linebuf[read_pos], (int)read_sz, filePointer) == NULL) { + done_reading = true; } else { read_pos = strlen(linebuf); if (linebuf[read_pos - 1] == '\n') { linebuf[--read_pos] = '\0'; - done_reading = 1; + done_reading = true; } } } - lineend = &linebuf[read_pos]; + + char *lineend = &linebuf[read_pos]; /* all that to read one line, isn't C fun? :) now comes the parsing :/ */ /* skip leading whitespace */ + char *optptr = NULL; for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++) { - continue; } + /* continue to '=' or EOL, watching for spaces that might precede it */ + char *eqptr = NULL; + char *optend = NULL; for (eqptr = optptr; eqptr < lineend && *eqptr != '='; eqptr++) { if (isspace(*eqptr) && optend == NULL) { optend = eqptr; @@ -283,55 +288,67 @@ static int add_option(FILE *f, np_arg_list **optlst) { optend = NULL; } } + if (optend == NULL) { optend = eqptr; } + --optend; + /* ^[[:space:]]*=foo is a syntax error */ if (optptr == eqptr) { die(STATE_UNKNOWN, "%s\n", _("Config file error")); } + /* continue from '=' to start of value or EOL */ + char *valptr = NULL; for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr); valptr++) { - continue; } + /* continue to the end of value */ + char *valend = NULL; for (valend = valptr; valend < lineend; valend++) { - continue; } + --valend; + /* finally trim off trailing spaces */ for (; isspace(*valend); valend--) { - continue; } + /* calculate the length of "--foo" */ - opt_len = (size_t)(1 + optend - optptr); + size_t opt_len = (size_t)(1 + optend - optptr); /* 1-character params needs only one dash */ + size_t cfg_len = 0; if (opt_len == 1) { cfg_len = 1 + (opt_len); } else { cfg_len = 2 + (opt_len); } + + size_t val_len = 0; + bool equals = false; + bool value = false; /* if valptrnext = NULL; read_pos = 0; @@ -357,6 +374,7 @@ static int add_option(FILE *f, np_arg_list **optlst) { if (*optlst == NULL) { *optlst = optnew; } else { + np_arg_list *opttmp = *optlst; while (opttmp->next != NULL) { opttmp = opttmp->next; } @@ -384,8 +402,11 @@ static char *default_file(void) { } static char *default_file_in_path(void) { - char *config_path, **file; - char *dir, *ini_file, *tokens; + char *config_path; + char **file; + char *dir; + char *ini_file; + char *tokens; if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL) { return NULL; diff --git a/lib/tests/test_cmd.c b/lib/tests/test_cmd.c index ade0da90..d51016cc 100644 --- a/lib/tests/test_cmd.c +++ b/lib/tests/test_cmd.c @@ -38,31 +38,29 @@ char *get_command(char *const *line) { } int main(int argc, char **argv) { - char **command_line = malloc(sizeof(char *) * COMMAND_LINE); - char *command = NULL; - char *perl; - output chld_out, chld_err; - int c; - int result = UNSET; - plan_tests(51); diag("Running plain echo command, set one"); /* ensure everything is empty before we begin */ + + output chld_out; memset(&chld_out, 0, sizeof(output)); + output chld_err; memset(&chld_err, 0, sizeof(output)); ok(chld_out.lines == 0, "(initialised) Checking stdout is reset"); ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); + int result = UNSET; ok(result == UNSET, "(initialised) Checking exit code is reset"); + char **command_line = malloc(sizeof(char *) * COMMAND_LINE); command_line[0] = strdup("/bin/echo"); command_line[1] = strdup("this"); command_line[2] = strdup("is"); command_line[3] = strdup("test"); command_line[4] = strdup("one"); - command = get_command(command_line); + char *command = get_command(command_line); result = cmd_run_array(command_line, &chld_out, &chld_err, 0); ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); diff --git a/lib/tests/test_ini1.c b/lib/tests/test_ini1.c index 3792d142..de983764 100644 --- a/lib/tests/test_ini1.c +++ b/lib/tests/test_ini1.c @@ -50,11 +50,10 @@ char *list2str(np_arg_list *optlst) { } int main(int argc, char **argv) { - char *optstr = NULL; plan_tests(12); - optstr = list2str(np_get_defaults("section@./config-tiny.ini", "check_disk")); + char *optstr = list2str(np_get_defaults("section@./config-tiny.ini", "check_disk")); ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), "config-tiny.ini's section as expected"); my_free(optstr); diff --git a/lib/tests/test_opts1.c b/lib/tests/test_opts1.c index 99da5596..fa95c4d4 100644 --- a/lib/tests/test_opts1.c +++ b/lib/tests/test_opts1.c @@ -40,15 +40,16 @@ void my_free(int *argc, char **newargv, char **argv) { #else void my_free(int *argc, char **newargv, char **argv) { /* Free stuff (and print while we're at it) */ - int i, freeflag = 1; + bool freeflag = true; printf(" Arg(%i): ", *argc + 1); printf("'%s' ", newargv[0]); - for (i = 1; i < *argc; i++) { + + for (int i = 1; i < *argc; i++) { printf("'%s' ", newargv[i]); /* Stop freeing when we get to the start of the original array */ if (freeflag) { if (newargv[i] == argv[1]) { - freeflag = 0; + freeflag = false; } else { free(newargv[i]); } @@ -64,13 +65,12 @@ void my_free(int *argc, char **newargv, char **argv) { #endif int array_diff(int i1, char **a1, int i2, char **a2) { - int i; - if (i1 != i2) { printf(" Argument count doesn't match!\n"); return 0; } - for (i = 0; i <= i1; i++) { + + for (int i = 0; i <= i1; i++) { if (a1[i] == NULL && a2[i] == NULL) { continue; } @@ -87,11 +87,10 @@ int array_diff(int i1, char **a1, int i2, char **a2) { } int main(int argc, char **argv) { - char **argv_new = NULL; - int i, argc_test; - plan_tests(5); + char **argv_new = NULL; + int argc_test; { char *argv_test[] = {"prog_name", (char *)NULL}; argc_test = 1; diff --git a/lib/tests/test_opts2.c b/lib/tests/test_opts2.c index d1b0aca3..3dd1b039 100644 --- a/lib/tests/test_opts2.c +++ b/lib/tests/test_opts2.c @@ -23,15 +23,16 @@ void my_free(int *argc, char **newargv, char **argv) { /* Free stuff (and print while we're at it) */ - int i, freeflag = 1; + bool freeflag = true; + printf(" Arg(%i): ", *argc + 1); printf("'%s' ", newargv[0]); - for (i = 1; i < *argc; i++) { + for (int i = 1; i < *argc; i++) { printf("'%s' ", newargv[i]); /* Stop freeing when we get to the start of the original array */ if (freeflag) { if (newargv[i] == argv[1]) { - freeflag = 0; + freeflag = false; } else { free(newargv[i]); } @@ -46,13 +47,12 @@ void my_free(int *argc, char **newargv, char **argv) { } int array_diff(int i1, char **a1, int i2, char **a2) { - int i; - if (i1 != i2) { printf(" Argument count doesn't match!\n"); return 0; } - for (i = 0; i <= i1; i++) { + + for (int i = 0; i <= i1; i++) { if (a1[i] == NULL && a2[i] == NULL) { continue; } @@ -69,11 +69,10 @@ int array_diff(int i1, char **a1, int i2, char **a2) { } int main(int argc, char **argv) { - char **argv_new = NULL; - int i, argc_test; - plan_tests(5); + char **argv_new = NULL; + int argc_test; { char *argv_test[] = {"prog_name", "arg1", "--extra-opts", "--arg3", "val2", (char *)NULL}; argc_test = 5; diff --git a/lib/tests/test_tcp.c b/lib/tests/test_tcp.c index de3a2102..37c818c9 100644 --- a/lib/tests/test_tcp.c +++ b/lib/tests/test_tcp.c @@ -21,11 +21,10 @@ #include "tap.h" int main(void) { - char **server_expect; - int server_expect_count = 3; - plan_tests(9); + char **server_expect; + const int server_expect_count = 3; server_expect = malloc(sizeof(char *) * server_expect_count); server_expect[0] = strdup("AA"); diff --git a/lib/utils_base.c b/lib/utils_base.c index 69024bc9..28e6dc47 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -44,7 +44,7 @@ monitoring_plugin *this_monitoring_plugin = NULL; -int timeout_state = STATE_CRITICAL; +mp_state_enum timeout_state = STATE_CRITICAL; unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; bool _np_state_read_file(FILE *state_file); diff --git a/lib/utils_base.h b/lib/utils_base.h index f31299c4..27884bf0 100644 --- a/lib/utils_base.h +++ b/lib/utils_base.h @@ -41,7 +41,7 @@ bool mp_check_range(mp_perfdata_value, mp_range); mp_state_enum get_status(double, thresholds *); /* Handle timeouts */ -extern int timeout_state; +extern mp_state_enum timeout_state; extern unsigned int timeout_interval; /* All possible characters in a threshold range */ diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index d1feaa33..35b83297 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c @@ -40,7 +40,6 @@ /** includes **/ #include "common.h" -#include "utils.h" #include "utils_cmd.h" /* This variable must be global, since there's no way the caller * can forcibly slay a dead or ungainly running program otherwise. @@ -62,9 +61,6 @@ static pid_t *_cmd_pids = NULL; # include #endif -/* used in _cmd_open to pass the environment to commands */ -extern char **environ; - /** macros **/ #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) @@ -80,14 +76,12 @@ extern char **environ; #endif /** prototypes **/ -static int _cmd_open(char *const *, int *, int *) __attribute__((__nonnull__(1, 2, 3))); - -static int _cmd_fetch_output(int, output *, int) __attribute__((__nonnull__(2))); +static int _cmd_open(char *const *argv, int *pfd, int *pfderr) + __attribute__((__nonnull__(1, 2, 3))); -static int _cmd_close(int); +static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) __attribute__((__nonnull__(2))); -/* prototype imported from utils.h */ -extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 2, 3))); +static int _cmd_close(int fileDescriptor); /* this function is NOT async-safe. It is exported so multithreaded * plugins (or other apps) can call it prior to running any commands @@ -110,7 +104,6 @@ void cmd_init(void) { /* Start running a command, array style */ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { - pid_t pid; #ifdef RLIMIT_CORE struct rlimit limit; #endif @@ -123,6 +116,7 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { setenv("LC_ALL", "C", 1); + pid_t pid; if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) { return -1; /* errno set by the failing function */ } @@ -171,22 +165,23 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { return pfd[0]; } -static int _cmd_close(int fd) { - int status; +static int _cmd_close(int fileDescriptor) { pid_t pid; /* make sure the provided fd was opened */ long maxfd = mp_open_max(); - if (fd < 0 || fd > maxfd || !_cmd_pids || (pid = _cmd_pids[fd]) == 0) { + if (fileDescriptor < 0 || fileDescriptor > maxfd || !_cmd_pids || + (pid = _cmd_pids[fileDescriptor]) == 0) { return -1; } - _cmd_pids[fd] = 0; - if (close(fd) == -1) { + _cmd_pids[fileDescriptor] = 0; + if (close(fileDescriptor) == -1) { return -1; } /* EINTR is ok (sort of), everything else is bad */ + int status; while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { return -1; @@ -197,68 +192,67 @@ static int _cmd_close(int fd) { return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; } -static int _cmd_fetch_output(int fd, output *op, int flags) { - size_t len = 0, i = 0, lineno = 0; - size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ - char *buf = NULL; - int ret; +static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) { char tmpbuf[4096]; - - op->buf = NULL; - op->buflen = 0; - while ((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) { - len = (size_t)ret; - op->buf = realloc(op->buf, op->buflen + len + 1); - memcpy(op->buf + op->buflen, tmpbuf, len); - op->buflen += len; - i++; + cmd_output->buf = NULL; + cmd_output->buflen = 0; + ssize_t ret; + while ((ret = read(fileDescriptor, tmpbuf, sizeof(tmpbuf))) > 0) { + size_t len = (size_t)ret; + cmd_output->buf = realloc(cmd_output->buf, cmd_output->buflen + len + 1); + memcpy(cmd_output->buf + cmd_output->buflen, tmpbuf, len); + cmd_output->buflen += len; } if (ret < 0) { - printf("read() returned %d: %s\n", ret, strerror(errno)); + printf("read() returned %zd: %s\n", ret, strerror(errno)); return ret; } /* some plugins may want to keep output unbroken, and some commands * will yield no output, so return here for those */ - if (flags & CMD_NO_ARRAYS || !op->buf || !op->buflen) { - return op->buflen; + if (flags & CMD_NO_ARRAYS || !cmd_output->buf || !cmd_output->buflen) { + return cmd_output->buflen; } /* and some may want both */ + char *buf = NULL; if (flags & CMD_NO_ASSOC) { - buf = malloc(op->buflen); - memcpy(buf, op->buf, op->buflen); + buf = malloc(cmd_output->buflen); + memcpy(buf, cmd_output->buf, cmd_output->buflen); } else { - buf = op->buf; + buf = cmd_output->buf; } - op->line = NULL; - op->lens = NULL; - i = 0; - while (i < op->buflen) { + cmd_output->line = NULL; + cmd_output->lens = NULL; + size_t i = 0; + size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ + size_t rsf = 6; + size_t lineno = 0; + while (i < cmd_output->buflen) { /* make sure we have enough memory */ if (lineno >= ary_size) { /* ary_size must never be zero */ do { - ary_size = op->buflen >> --rsf; + ary_size = cmd_output->buflen >> --rsf; } while (!ary_size); - op->line = realloc(op->line, ary_size * sizeof(char *)); - op->lens = realloc(op->lens, ary_size * sizeof(size_t)); + cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *)); + cmd_output->lens = realloc(cmd_output->lens, ary_size * sizeof(size_t)); } /* set the pointer to the string */ - op->line[lineno] = &buf[i]; + cmd_output->line[lineno] = &buf[i]; /* hop to next newline or end of buffer */ - while (buf[i] != '\n' && i < op->buflen) { + while (buf[i] != '\n' && i < cmd_output->buflen) { i++; } buf[i] = '\0'; /* calculate the string length using pointer difference */ - op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno]; + cmd_output->lens[lineno] = (size_t)&buf[i] - (size_t)cmd_output->line[lineno]; lineno++; i++; @@ -268,12 +262,6 @@ static int _cmd_fetch_output(int fd, output *op, int flags) { } int cmd_run(const char *cmdstring, output *out, output *err, int flags) { - int i = 0, argc; - size_t cmdlen; - char **argv = NULL; - char *cmd = NULL; - char *str = NULL; - if (cmdstring == NULL) { return -1; } @@ -288,7 +276,8 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { /* make copy of command string so strtok() doesn't silently modify it */ /* (the calling program may want to access it later) */ - cmdlen = strlen(cmdstring); + size_t cmdlen = strlen(cmdstring); + char *cmd = NULL; if ((cmd = malloc(cmdlen + 1)) == NULL) { return -1; } @@ -307,8 +296,8 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { /* each arg must be whitespace-separated, so args can be a maximum * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ - argc = (cmdlen >> 1) + 2; - argv = calloc((size_t)argc, sizeof(char *)); + int argc = (cmdlen >> 1) + 2; + char **argv = calloc((size_t)argc, sizeof(char *)); if (argv == NULL) { printf("%s\n", _("Could not malloc argv array in popen()")); @@ -316,8 +305,9 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { } /* get command arguments (stupidly, but fairly quickly) */ + int i = 0; while (cmd) { - str = cmd; + char *str = cmd; str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ @@ -347,8 +337,6 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { } int cmd_run_array(char *const *argv, output *out, output *err, int flags) { - int fd, pfd_out[2], pfd_err[2]; - /* initialize the structs */ if (out) { memset(out, 0, sizeof(output)); @@ -357,6 +345,9 @@ int cmd_run_array(char *const *argv, output *out, output *err, int flags) { memset(err, 0, sizeof(output)); } + int fd; + int pfd_out[2]; + int pfd_err[2]; if ((fd = _cmd_open(argv, pfd_out, pfd_err)) == -1) { die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), argv[0]); } diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h index 728ece23..3672cdc9 100644 --- a/lib/utils_cmd.h +++ b/lib/utils_cmd.h @@ -5,17 +5,17 @@ * Header file for Monitoring Plugins utils_cmd.c * */ +#include "../config.h" +#include /** types **/ -struct output { +typedef struct { char *buf; /* output buffer */ size_t buflen; /* output buffer content length */ char **line; /* array of lines (points to buf) */ size_t *lens; /* string lengths */ size_t lines; /* lines of output */ -}; - -typedef struct output output; +} output; /** prototypes **/ int cmd_run(const char *, output *, output *, int); diff --git a/lib/utils_tcp.c b/lib/utils_tcp.c index 1482458b..a82d5a3f 100644 --- a/lib/utils_tcp.c +++ b/lib/utils_tcp.c @@ -26,8 +26,10 @@ * *****************************************************************************/ -#include "common.h" +#include "../config.h" #include "utils_tcp.h" +#include +#include #define VERBOSE(message) \ do { \ @@ -37,9 +39,9 @@ enum np_match_result np_expect_match(char *status, char **server_expect, int expect_count, int flags) { - int i, match = 0, partial = 0; - - for (i = 0; i < expect_count; i++) { + int match = 0; + int partial = 0; + for (int i = 0; i < expect_count; i++) { if (flags & NP_MATCH_VERBOSE) { printf("looking for [%s] %s [%s]\n", server_expect[i], (flags & NP_MATCH_EXACT) ? "in beginning of" : "anywhere in", status); @@ -50,7 +52,9 @@ enum np_match_result np_expect_match(char *status, char **server_expect, int exp VERBOSE("found it"); match++; continue; - } else if (strncmp(status, server_expect[i], strlen(status)) == 0) { + } + + if (strncmp(status, server_expect[i], strlen(status)) == 0) { VERBOSE("found a substring"); partial++; continue; @@ -66,9 +70,9 @@ enum np_match_result np_expect_match(char *status, char **server_expect, int exp if ((flags & NP_MATCH_ALL && match == expect_count) || (!(flags & NP_MATCH_ALL) && match >= 1)) { return NP_MATCH_SUCCESS; - } else if (partial > 0 || !(flags & NP_MATCH_EXACT)) { + } + if (partial > 0 || !(flags & NP_MATCH_EXACT)) { return NP_MATCH_RETRY; - } else { - return NP_MATCH_FAILURE; } + return NP_MATCH_FAILURE; } -- cgit v1.2.3-74-g34f1