summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-09-08 15:57:06 +0200
committerLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-09-08 15:57:06 +0200
commit87195f5511bf18db2a64f71ea9783ebbfb33c3a5 (patch)
tree491157b89647d73ed6acb0e4e2ae7cdf7fffb01c
parent1aefb1f9df5268ccbcd3ce38f5527ebca3896db6 (diff)
downloadmonitoring-plugins-87195f5511bf18db2a64f71ea9783ebbfb33c3a5.tar.gz
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
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/tests/test_utils.c190
-rw-r--r--lib/utils_base.c344
-rw-r--r--lib/utils_base.h22
-rw-r--r--plugins/Makefile.am14
-rw-r--r--plugins/check_snmp.c778
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.c861
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.h64
-rw-r--r--plugins/check_snmp.d/config.h25
-rwxr-xr-xplugins/tests/check_snmp.t159
-rw-r--r--plugins/tests/check_snmp_agent.pl78
-rw-r--r--plugins/tests/test_check_snmp.c175
-rwxr-xr-xplugins/tests/test_check_snmp.t6
13 files changed, 1647 insertions, 1071 deletions
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
4 4
5noinst_LIBRARIES = libmonitoringplug.a 5noinst_LIBRARIES = libmonitoringplug.a
6 6
7AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ 7AM_CPPFLAGS = \
8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins 8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
9 9
10libmonitoringplug_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 10libmonitoringplug_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 @@
28#include "utils_base.c" 28#include "utils_base.c"
29 29
30int main(int argc, char **argv) { 30int main(int argc, char **argv) {
31 char state_path[1024]; 31 plan_tests(155);
32 range *range;
33 double temp;
34 thresholds *thresholds = NULL;
35 int i, rc;
36 char *temp_string;
37 state_key *temp_state_key = NULL;
38 state_data *temp_state_data;
39 time_t current_time;
40
41 plan_tests(185);
42 32
43 ok(this_monitoring_plugin == NULL, "monitoring_plugin not initialised"); 33 ok(this_monitoring_plugin == NULL, "monitoring_plugin not initialised");
44 34
@@ -57,7 +47,7 @@ int main(int argc, char **argv) {
57 47
58 np_set_args(argc, argv); 48 np_set_args(argc, argv);
59 49
60 range = parse_range_string("6"); 50 range *range = parse_range_string("6");
61 ok(range != NULL, "'6' is valid range"); 51 ok(range != NULL, "'6' is valid range");
62 ok(range->start == 0, "Start correct"); 52 ok(range->start == 0, "Start correct");
63 ok(range->start_infinity == false, "Not using negative infinity"); 53 ok(range->start_infinity == false, "Not using negative infinity");
@@ -97,7 +87,7 @@ int main(int argc, char **argv) {
97 free(range); 87 free(range);
98 88
99 range = parse_range_string("12345678901234567890:"); 89 range = parse_range_string("12345678901234567890:");
100 temp = atof("12345678901234567890"); /* Can't just use this because number too large */ 90 double temp = atof("12345678901234567890"); /* Can't just use this because number too large */
101 ok(range != NULL, "'12345678901234567890:' is valid range"); 91 ok(range != NULL, "'12345678901234567890:' is valid range");
102 ok(range->start == temp, "Start correct"); 92 ok(range->start == temp, "Start correct");
103 ok(range->start_infinity == false, "Not using negative infinity"); 93 ok(range->start_infinity == false, "Not using negative infinity");
@@ -158,32 +148,34 @@ int main(int argc, char **argv) {
158 range = parse_range_string("2:1"); 148 range = parse_range_string("2:1");
159 ok(range == NULL, "'2:1' rejected"); 149 ok(range == NULL, "'2:1' rejected");
160 150
161 rc = _set_thresholds(&thresholds, NULL, NULL); 151 thresholds *thresholds = NULL;
162 ok(rc == 0, "Thresholds (NULL, NULL) set"); 152 int returnCode;
153 returnCode = _set_thresholds(&thresholds, NULL, NULL);
154 ok(returnCode == 0, "Thresholds (NULL, NULL) set");
163 ok(thresholds->warning == NULL, "Warning not set"); 155 ok(thresholds->warning == NULL, "Warning not set");
164 ok(thresholds->critical == NULL, "Critical not set"); 156 ok(thresholds->critical == NULL, "Critical not set");
165 157
166 rc = _set_thresholds(&thresholds, NULL, "80"); 158 returnCode = _set_thresholds(&thresholds, NULL, "80");
167 ok(rc == 0, "Thresholds (NULL, '80') set"); 159 ok(returnCode == 0, "Thresholds (NULL, '80') set");
168 ok(thresholds->warning == NULL, "Warning not set"); 160 ok(thresholds->warning == NULL, "Warning not set");
169 ok(thresholds->critical->end == 80, "Critical set correctly"); 161 ok(thresholds->critical->end == 80, "Critical set correctly");
170 162
171 rc = _set_thresholds(&thresholds, "5:33", NULL); 163 returnCode = _set_thresholds(&thresholds, "5:33", NULL);
172 ok(rc == 0, "Thresholds ('5:33', NULL) set"); 164 ok(returnCode == 0, "Thresholds ('5:33', NULL) set");
173 ok(thresholds->warning->start == 5, "Warning start set"); 165 ok(thresholds->warning->start == 5, "Warning start set");
174 ok(thresholds->warning->end == 33, "Warning end set"); 166 ok(thresholds->warning->end == 33, "Warning end set");
175 ok(thresholds->critical == NULL, "Critical not set"); 167 ok(thresholds->critical == NULL, "Critical not set");
176 168
177 rc = _set_thresholds(&thresholds, "30", "60"); 169 returnCode = _set_thresholds(&thresholds, "30", "60");
178 ok(rc == 0, "Thresholds ('30', '60') set"); 170 ok(returnCode == 0, "Thresholds ('30', '60') set");
179 ok(thresholds->warning->end == 30, "Warning set correctly"); 171 ok(thresholds->warning->end == 30, "Warning set correctly");
180 ok(thresholds->critical->end == 60, "Critical set correctly"); 172 ok(thresholds->critical->end == 60, "Critical set correctly");
181 ok(get_status(15.3, thresholds) == STATE_OK, "15.3 - ok"); 173 ok(get_status(15.3, thresholds) == STATE_OK, "15.3 - ok");
182 ok(get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning"); 174 ok(get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning");
183 ok(get_status(69, thresholds) == STATE_CRITICAL, "69 - critical"); 175 ok(get_status(69, thresholds) == STATE_CRITICAL, "69 - critical");
184 176
185 rc = _set_thresholds(&thresholds, "-10:-2", "-30:20"); 177 returnCode = _set_thresholds(&thresholds, "-10:-2", "-30:20");
186 ok(rc == 0, "Thresholds ('-30:20', '-10:-2') set"); 178 ok(returnCode == 0, "Thresholds ('-30:20', '-10:-2') set");
187 ok(thresholds->warning->start == -10, "Warning start set correctly"); 179 ok(thresholds->warning->start == -10, "Warning start set correctly");
188 ok(thresholds->warning->end == -2, "Warning end set correctly"); 180 ok(thresholds->warning->end == -2, "Warning end set correctly");
189 ok(thresholds->critical->start == -30, "Critical start set correctly"); 181 ok(thresholds->critical->start == -30, "Critical start set correctly");
@@ -304,164 +296,28 @@ int main(int argc, char **argv) {
304 test = np_extract_ntpvar("", "foo"); 296 test = np_extract_ntpvar("", "foo");
305 ok(!test, "Empty string return NULL"); 297 ok(!test, "Empty string return NULL");
306 298
307 /* This is the result of running ./test_utils */
308 temp_string = (char *)_np_state_generate_key();
309 ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got hash with exe and no parameters") ||
310 diag("You are probably running in wrong directory. Must run as ./test_utils");
311
312 this_monitoring_plugin->argc = 4;
313 this_monitoring_plugin->argv[0] = "./test_utils";
314 this_monitoring_plugin->argv[1] = "here";
315 this_monitoring_plugin->argv[2] = "--and";
316 this_monitoring_plugin->argv[3] = "now";
317 temp_string = (char *)_np_state_generate_key();
318 ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), "Got based on expected argv");
319
320 unsetenv("MP_STATE_PATH");
321 temp_string = (char *)_np_state_calculate_location_prefix();
322 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory");
323
324 setenv("MP_STATE_PATH", "", 1);
325 temp_string = (char *)_np_state_calculate_location_prefix();
326 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string");
327
328 setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1);
329 temp_string = (char *)_np_state_calculate_location_prefix();
330 ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory");
331
332 ok(temp_state_key == NULL, "temp_state_key initially empty");
333
334 this_monitoring_plugin->argc = 1;
335 this_monitoring_plugin->argv[0] = "./test_utils";
336 np_enable_state(NULL, 51);
337 temp_state_key = this_monitoring_plugin->state;
338 ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name");
339 ok(!strcmp(temp_state_key->name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got generated filename");
340
341 np_enable_state("allowedchars_in_keyname", 77);
342 temp_state_key = this_monitoring_plugin->state;
343 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", (unsigned long)geteuid());
344 ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name");
345 ok(!strcmp(temp_state_key->name, "allowedchars_in_keyname"), "Got key name with valid chars");
346 ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename");
347
348 /* Don't do this test just yet. Will die */
349 /*
350 np_enable_state("bad^chars$in@here", 77);
351 temp_state_key = this_monitoring_plugin->state;
352 ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" );
353 */
354
355 np_enable_state("funnykeyname", 54);
356 temp_state_key = this_monitoring_plugin->state;
357 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", (unsigned long)geteuid());
358 ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name");
359 ok(!strcmp(temp_state_key->name, "funnykeyname"), "Got key name");
360
361 ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename");
362 ok(temp_state_key->data_version == 54, "Version set");
363
364 temp_state_data = np_state_read();
365 ok(temp_state_data == NULL, "Got no state data as file does not exist");
366
367 /*
368 temp_fp = fopen("var/statefile", "r");
369 if (temp_fp==NULL)
370 printf("Error opening. errno=%d\n", errno);
371 printf("temp_fp=%s\n", temp_fp);
372 ok( _np_state_read_file(temp_fp) == true, "Can read state file" );
373 fclose(temp_fp);
374 */
375
376 temp_state_key->_filename = "var/statefile";
377 temp_state_data = np_state_read();
378 ok(this_monitoring_plugin->state->state_data != NULL, "Got state data now") ||
379 diag("Are you running in right directory? Will get coredump next if not");
380 ok(this_monitoring_plugin->state->state_data->time == 1234567890, "Got time");
381 ok(!strcmp((char *)this_monitoring_plugin->state->state_data->data, "String to read"), "Data as expected");
382
383 temp_state_key->data_version = 53;
384 temp_state_data = np_state_read();
385 ok(temp_state_data == NULL, "Older data version gives NULL");
386 temp_state_key->data_version = 54;
387
388 temp_state_key->_filename = "var/nonexistent";
389 temp_state_data = np_state_read();
390 ok(temp_state_data == NULL, "Missing file gives NULL");
391 ok(this_monitoring_plugin->state->state_data == NULL, "No state information");
392
393 temp_state_key->_filename = "var/oldformat";
394 temp_state_data = np_state_read();
395 ok(temp_state_data == NULL, "Old file format gives NULL");
396
397 temp_state_key->_filename = "var/baddate";
398 temp_state_data = np_state_read();
399 ok(temp_state_data == NULL, "Bad date gives NULL");
400
401 temp_state_key->_filename = "var/missingdataline";
402 temp_state_data = np_state_read();
403 ok(temp_state_data == NULL, "Missing data line gives NULL");
404
405 unlink("var/generated");
406 temp_state_key->_filename = "var/generated";
407 current_time = 1234567890;
408 np_state_write_string(current_time, "String to read");
409 ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected");
410
411 unlink("var/generated_directory/statefile");
412 unlink("var/generated_directory");
413 temp_state_key->_filename = "var/generated_directory/statefile";
414 current_time = 1234567890;
415 np_state_write_string(current_time, "String to read");
416 ok(system("cmp var/generated_directory/statefile var/statefile") == 0, "Have created directory");
417
418 /* This test to check cannot write to dir - can't automate yet */
419 /*
420 unlink("var/generated_bad_dir");
421 mkdir("var/generated_bad_dir", S_IRUSR);
422 np_state_write_string(current_time, "String to read");
423 */
424
425 temp_state_key->_filename = "var/generated";
426 time(&current_time);
427 np_state_write_string(0, "String to read");
428 temp_state_data = np_state_read();
429 /* Check time is set to current_time */
430 ok(system("cmp var/generated var/statefile > /dev/null") != 0, "Generated file should be different this time");
431 ok(this_monitoring_plugin->state->state_data->time - current_time <= 1, "Has time generated from current time");
432
433 /* Don't know how to automatically test this. Need to be able to redefine die and catch the error */
434 /*
435 temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write";
436 np_state_write_string(0, "Bad file");
437 */
438
439 np_cleanup();
440
441 ok(this_monitoring_plugin == NULL, "Free'd this_monitoring_plugin");
442
443 ok(mp_suid() == false, "Test aren't suid"); 299 ok(mp_suid() == false, "Test aren't suid");
444 300
445 /* base states with random case */ 301 /* base states with random case */
446 char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL}; 302 char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL};
447 303
448 for (i = 0; states[i] != NULL; i++) { 304 for (int i = 0; states[i] != NULL; i++) {
449 /* out of the random case states, create the lower and upper versions + numeric string one */ 305 /* out of the random case states, create the lower and upper versions + numeric string one
306 */
450 char *statelower = strdup(states[i]); 307 char *statelower = strdup(states[i]);
451 char *stateupper = strdup(states[i]); 308 char *stateupper = strdup(states[i]);
452 char statenum[2]; 309 char statenum[2];
453 char *temp_ptr; 310 for (char *temp_ptr = statelower; *temp_ptr; temp_ptr++) {
454 for (temp_ptr = statelower; *temp_ptr; temp_ptr++) { 311 *temp_ptr = (char)tolower(*temp_ptr);
455 *temp_ptr = tolower(*temp_ptr);
456 } 312 }
457 for (temp_ptr = stateupper; *temp_ptr; temp_ptr++) { 313 for (char *temp_ptr = stateupper; *temp_ptr; temp_ptr++) {
458 *temp_ptr = toupper(*temp_ptr); 314 *temp_ptr = (char)toupper(*temp_ptr);
459 } 315 }
460 snprintf(statenum, 2, "%i", i); 316 snprintf(statenum, 2, "%i", i);
461 317
462 /* Base test names, we'll append the state string */ 318 /* Base test names, we'll append the state string */
463 char testname[64] = "Translate state string: "; 319 char testname[64] = "Translate state string: ";
464 int tlen = strlen(testname); 320 size_t tlen = strlen(testname);
465 321
466 strcpy(testname + tlen, states[i]); 322 strcpy(testname + tlen, states[i]);
467 ok(i == mp_translate_state(states[i]), testname); 323 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) {
74 74
75void np_cleanup(void) { 75void np_cleanup(void) {
76 if (this_monitoring_plugin != NULL) { 76 if (this_monitoring_plugin != NULL) {
77 if (this_monitoring_plugin->state != NULL) {
78 if (this_monitoring_plugin->state->state_data) {
79 np_free(this_monitoring_plugin->state->state_data->data);
80 np_free(this_monitoring_plugin->state->state_data);
81 }
82 np_free(this_monitoring_plugin->state->name);
83 np_free(this_monitoring_plugin->state);
84 }
85 np_free(this_monitoring_plugin->plugin_name); 77 np_free(this_monitoring_plugin->plugin_name);
86 np_free(this_monitoring_plugin); 78 np_free(this_monitoring_plugin);
87 } 79 }
@@ -435,339 +427,3 @@ int mp_translate_state(char *state_text) {
435 } 427 }
436 return ERROR; 428 return ERROR;
437} 429}
438
439/*
440 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
441 * hopefully a unique key per service/plugin invocation. Use the extra-opts
442 * parse of argv, so that uniqueness in parameters are reflected there.
443 */
444char *_np_state_generate_key(void) {
445 char **argv = this_monitoring_plugin->argv;
446 unsigned char result[256];
447
448#ifdef USE_OPENSSL
449 /*
450 * This code path is chosen if openssl is available (which should be the most common
451 * scenario). Alternatively, the gnulib implementation/
452 *
453 */
454 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
455
456 EVP_DigestInit(ctx, EVP_sha256());
457
458 for (int i = 0; i < this_monitoring_plugin->argc; i++) {
459 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
460 }
461
462 EVP_DigestFinal(ctx, result, NULL);
463#else
464
465 struct sha256_ctx ctx;
466
467 for (int i = 0; i < this_monitoring_plugin->argc; i++) {
468 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
469 }
470
471 sha256_finish_ctx(&ctx, result);
472#endif // FOUNDOPENSSL
473
474 char keyname[41];
475 for (int i = 0; i < 20; ++i) {
476 sprintf(&keyname[2 * i], "%02x", result[i]);
477 }
478
479 keyname[40] = '\0';
480
481 char *keyname_copy = strdup(keyname);
482 if (keyname_copy == NULL) {
483 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
484 }
485
486 return keyname_copy;
487}
488
489void _cleanup_state_data(void) {
490 if (this_monitoring_plugin->state->state_data != NULL) {
491 np_free(this_monitoring_plugin->state->state_data->data);
492 np_free(this_monitoring_plugin->state->state_data);
493 }
494}
495
496/*
497 * Internal function. Returns either:
498 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
499 * statically compiled shared state directory
500 */
501char *_np_state_calculate_location_prefix(void) {
502 char *env_dir;
503
504 /* Do not allow passing MP_STATE_PATH in setuid plugins
505 * for security reasons */
506 if (!mp_suid()) {
507 env_dir = getenv("MP_STATE_PATH");
508 if (env_dir && env_dir[0] != '\0') {
509 return env_dir;
510 }
511 /* This is the former ENV, for backward-compatibility */
512 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
513 if (env_dir && env_dir[0] != '\0') {
514 return env_dir;
515 }
516 }
517
518 return NP_STATE_DIR_PREFIX;
519}
520
521/*
522 * Initiatializer for state routines.
523 * Sets variables. Generates filename. Returns np_state_key. die with
524 * UNKNOWN if exception
525 */
526void np_enable_state(char *keyname, int expected_data_version) {
527 if (this_monitoring_plugin == NULL) {
528 die(STATE_UNKNOWN, _("This requires np_init to be called"));
529 }
530
531 state_key *this_state = (state_key *)calloc(1, sizeof(state_key));
532 if (this_state == NULL) {
533 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
534 }
535
536 char *temp_keyname = NULL;
537 if (keyname == NULL) {
538 temp_keyname = _np_state_generate_key();
539 } else {
540 temp_keyname = strdup(keyname);
541 if (temp_keyname == NULL) {
542 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
543 }
544 }
545
546 /* Die if invalid characters used for keyname */
547 char *tmp_char = temp_keyname;
548 while (*tmp_char != '\0') {
549 if (!(isalnum(*tmp_char) || *tmp_char == '_')) {
550 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
551 }
552 tmp_char++;
553 }
554 this_state->name = temp_keyname;
555 this_state->plugin_name = this_monitoring_plugin->plugin_name;
556 this_state->data_version = expected_data_version;
557 this_state->state_data = NULL;
558
559 /* Calculate filename */
560 char *temp_filename = NULL;
561 int error =
562 asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(),
563 (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name);
564 if (error < 0) {
565 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
566 }
567
568 this_state->_filename = temp_filename;
569
570 this_monitoring_plugin->state = this_state;
571}
572
573/*
574 * Will return NULL if no data is available (first run). If key currently
575 * exists, read data. If state file format version is not expected, return
576 * as if no data. Get state data version number and compares to expected.
577 * If numerically lower, then return as no previous state. die with UNKNOWN
578 * if exceptional error.
579 */
580state_data *np_state_read(void) {
581 if (this_monitoring_plugin == NULL) {
582 die(STATE_UNKNOWN, _("This requires np_init to be called"));
583 }
584
585 bool error_code = false;
586
587 /* Open file. If this fails, no previous state found */
588 FILE *statefile = fopen(this_monitoring_plugin->state->_filename, "r");
589 if (statefile != NULL) {
590
591 state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data));
592 if (this_state_data == NULL) {
593 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
594 }
595
596 this_state_data->data = NULL;
597 this_monitoring_plugin->state->state_data = this_state_data;
598
599 error_code = _np_state_read_file(statefile);
600
601 fclose(statefile);
602 }
603
604 if (!error_code) {
605 _cleanup_state_data();
606 }
607
608 return this_monitoring_plugin->state->state_data;
609}
610
611/*
612 * Read the state file
613 */
614bool _np_state_read_file(FILE *state_file) {
615 time_t current_time;
616 time(&current_time);
617
618 /* Note: This introduces a limit of 1024 bytes in the string data */
619 char *line = (char *)calloc(1, 1024);
620 if (line == NULL) {
621 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
622 }
623
624 bool status = false;
625 enum {
626 STATE_FILE_VERSION,
627 STATE_DATA_VERSION,
628 STATE_DATA_TIME,
629 STATE_DATA_TEXT,
630 STATE_DATA_END
631 } expected = STATE_FILE_VERSION;
632
633 int failure = 0;
634 while (!failure && (fgets(line, 1024, state_file)) != NULL) {
635 size_t pos = strlen(line);
636 if (line[pos - 1] == '\n') {
637 line[pos - 1] = '\0';
638 }
639
640 if (line[0] == '#') {
641 continue;
642 }
643
644 switch (expected) {
645 case STATE_FILE_VERSION: {
646 int i = atoi(line);
647 if (i != NP_STATE_FORMAT_VERSION) {
648 failure++;
649 } else {
650 expected = STATE_DATA_VERSION;
651 }
652 } break;
653 case STATE_DATA_VERSION: {
654 int i = atoi(line);
655 if (i != this_monitoring_plugin->state->data_version) {
656 failure++;
657 } else {
658 expected = STATE_DATA_TIME;
659 }
660 } break;
661 case STATE_DATA_TIME: {
662 /* If time > now, error */
663 time_t data_time = strtoul(line, NULL, 10);
664 if (data_time > current_time) {
665 failure++;
666 } else {
667 this_monitoring_plugin->state->state_data->time = data_time;
668 expected = STATE_DATA_TEXT;
669 }
670 } break;
671 case STATE_DATA_TEXT:
672 this_monitoring_plugin->state->state_data->data = strdup(line);
673 if (this_monitoring_plugin->state->state_data->data == NULL) {
674 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
675 }
676 expected = STATE_DATA_END;
677 status = true;
678 break;
679 case STATE_DATA_END:;
680 }
681 }
682
683 np_free(line);
684 return status;
685}
686
687/*
688 * If time=NULL, use current time. Create state file, with state format
689 * version, default text. Writes version, time, and data. Avoid locking
690 * problems - use mv to write and then swap. Possible loss of state data if
691 * two things writing to same key at same time.
692 * Will die with UNKNOWN if errors
693 */
694void np_state_write_string(time_t data_time, char *data_string) {
695 time_t current_time;
696 if (data_time == 0) {
697 time(&current_time);
698 } else {
699 current_time = data_time;
700 }
701
702 int result = 0;
703
704 /* If file doesn't currently exist, create directories */
705 if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) {
706 char *directories = NULL;
707 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
708 if (result < 0) {
709 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
710 }
711
712 for (char *p = directories + 1; *p; p++) {
713 if (*p == '/') {
714 *p = '\0';
715 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
716 /* Can't free this! Otherwise error message is wrong! */
717 /* np_free(directories); */
718 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
719 }
720 *p = '/';
721 }
722 }
723 np_free(directories);
724 }
725
726 char *temp_file = NULL;
727 result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename);
728 if (result < 0) {
729 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
730 }
731
732 int temp_file_desc = 0;
733 if ((temp_file_desc = mkstemp(temp_file)) == -1) {
734 np_free(temp_file);
735 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
736 }
737
738 FILE *temp_file_pointer = (FILE *)fdopen(temp_file_desc, "w");
739 if (temp_file_pointer == NULL) {
740 close(temp_file_desc);
741 unlink(temp_file);
742 np_free(temp_file);
743 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
744 }
745
746 fprintf(temp_file_pointer, "# NP State file\n");
747 fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION);
748 fprintf(temp_file_pointer, "%d\n", this_monitoring_plugin->state->data_version);
749 fprintf(temp_file_pointer, "%lu\n", current_time);
750 fprintf(temp_file_pointer, "%s\n", data_string);
751
752 fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP);
753
754 fflush(temp_file_pointer);
755
756 result = fclose(temp_file_pointer);
757
758 fsync(temp_file_desc);
759
760 if (result != 0) {
761 unlink(temp_file);
762 np_free(temp_file);
763 die(STATE_UNKNOWN, _("Error writing temp file"));
764 }
765
766 if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) {
767 unlink(temp_file);
768 np_free(temp_file);
769 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
770 }
771
772 np_free(temp_file);
773}
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 @@
8#include "./perfdata.h" 8#include "./perfdata.h"
9#include "./thresholds.h" 9#include "./thresholds.h"
10 10
11
12#ifndef USE_OPENSSL 11#ifndef USE_OPENSSL
13# include "sha256.h" 12# include "sha256.h"
14#endif 13#endif
@@ -26,25 +25,8 @@
26#define OUTSIDE 0 25#define OUTSIDE 0
27#define INSIDE 1 26#define INSIDE 1
28 27
29#define NP_STATE_FORMAT_VERSION 1
30
31typedef struct state_data_struct {
32 time_t time;
33 void *data;
34 int length; /* Of binary data */
35} state_data;
36
37typedef struct state_key_struct {
38 char *name;
39 char *plugin_name;
40 int data_version;
41 char *_filename;
42 state_data *state_data;
43} state_key;
44
45typedef struct np_struct { 28typedef struct np_struct {
46 char *plugin_name; 29 char *plugin_name;
47 state_key *state;
48 int argc; 30 int argc;
49 char **argv; 31 char **argv;
50} monitoring_plugin; 32} monitoring_plugin;
@@ -100,10 +82,6 @@ char *np_extract_value(const char *, const char *, char);
100 */ 82 */
101int mp_translate_state(char *); 83int mp_translate_state(char *);
102 84
103void np_enable_state(char *, int);
104state_data *np_state_read(void);
105void np_state_write_string(time_t, char *);
106
107void np_init(char *, int argc, char **argv); 85void np_init(char *, int argc, char **argv);
108void np_set_args(int argc, char **argv); 86void np_set_args(int argc, char **argv);
109void np_cleanup(void); 87void 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)"'
13 13
14VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t 14VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t
15 15
16AM_CPPFLAGS = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl \ 16AM_CPPFLAGS = -I.. \
17 @LDAPINCLUDE@ @PGINCLUDE@ @SSLINCLUDE@ 17 -I$(top_srcdir)/lib \
18 -I$(top_srcdir)/gl \
19 -I$(top_srcdir)/intl \
20 -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
21 @LDAPINCLUDE@ \
22 @PGINCLUDE@ \
23 @SSLINCLUDE@
18 24
19localedir = $(datadir)/locale 25localedir = $(datadir)/locale
20# gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this 26# 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 \
42 check_procs check_mysql_query check_apt check_dbi check_curl \ 48 check_procs check_mysql_query check_apt check_dbi check_curl \
43 \ 49 \
44 tests/test_check_swap \ 50 tests/test_check_swap \
51 tests/test_check_snmp \
45 tests/test_check_disk 52 tests/test_check_disk
46 53
47SUBDIRS = picohttpparser 54SUBDIRS = picohttpparser
48 55
49np_test_scripts = tests/test_check_swap.t \ 56np_test_scripts = tests/test_check_swap.t \
57 tests/test_check_snmp.t \
50 tests/test_check_disk.t 58 tests/test_check_disk.t
51 59
52EXTRA_DIST = t \ 60EXTRA_DIST = t \
@@ -178,6 +186,8 @@ endif
178 186
179tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap 187tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
180tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c 188tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c
189tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
190tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c
181tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap 191tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap
182tests_test_check_disk_SOURCES = tests/test_check_disk.c 192tests_test_check_disk_SOURCES = tests/test_check_disk.c
183 193
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";
37#include "./utils.h" 37#include "./utils.h"
38#include "../lib/states.h" 38#include "../lib/states.h"
39 39
40#include "../lib/thresholds.h"
41#include "../lib/utils_base.h" 40#include "../lib/utils_base.h"
42#include "../lib/output.h" 41#include "../lib/output.h"
43#include "../lib/perfdata.h"
44#include "check_snmp.d/check_snmp_helpers.h" 42#include "check_snmp.d/check_snmp_helpers.h"
45 43
46#include <bits/getopt_core.h> 44#include <bits/getopt_core.h>
@@ -49,6 +47,7 @@ const char *email = "devel@monitoring-plugins.org";
49#include <stdint.h> 47#include <stdint.h>
50 48
51#include "check_snmp.d/config.h" 49#include "check_snmp.d/config.h"
50#include <stdlib.h>
52#include <arpa/inet.h> 51#include <arpa/inet.h>
53#include <net-snmp/library/parse.h> 52#include <net-snmp/library/parse.h>
54#include <net-snmp/net-snmp-config.h> 53#include <net-snmp/net-snmp-config.h>
@@ -63,6 +62,7 @@ const char *email = "devel@monitoring-plugins.org";
63#include <net-snmp/library/snmp_impl.h> 62#include <net-snmp/library/snmp_impl.h>
64#include <string.h> 63#include <string.h>
65#include "../gl/regex.h" 64#include "../gl/regex.h"
65#include "../gl/base64.h"
66#include <assert.h> 66#include <assert.h>
67 67
68const char DEFAULT_COMMUNITY[] = "public"; 68const char DEFAULT_COMMUNITY[] = "public";
@@ -86,7 +86,168 @@ static char *get_next_argument(char *str);
86void print_usage(void); 86void print_usage(void);
87void print_help(void); 87void print_help(void);
88 88
89static int verbose = 0; 89int verbose = 0;
90
91typedef struct {
92 int errorcode;
93 char *state_string;
94} gen_state_string_type;
95gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) {
96 char *encoded_string = NULL;
97 gen_state_string_type result = {.errorcode = OK, .state_string = NULL};
98
99 if (verbose > 1) {
100 printf("%s:\n", __FUNCTION__);
101 for (size_t i = 0; i < num_of_entries; i++) {
102 printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp));
103 switch (entries[i].type) {
104 case ASN_GAUGE:
105 printf("Type GAUGE\n");
106 break;
107 case ASN_TIMETICKS:
108 printf("Type TIMETICKS\n");
109 break;
110 case ASN_COUNTER:
111 printf("Type COUNTER\n");
112 break;
113 case ASN_UINTEGER:
114 printf("Type UINTEGER\n");
115 break;
116 case ASN_COUNTER64:
117 printf("Type COUNTER64\n");
118 break;
119 case ASN_FLOAT:
120 printf("Type FLOAT\n");
121 case ASN_DOUBLE:
122 printf("Type DOUBLE\n");
123 break;
124 case ASN_INTEGER:
125 printf("Type INTEGER\n");
126 break;
127 }
128
129 switch (entries[i].type) {
130 case ASN_GAUGE:
131 case ASN_TIMETICKS:
132 case ASN_COUNTER:
133 case ASN_UINTEGER:
134 case ASN_COUNTER64:
135 printf("Value %llu\n", entries[i].value.uIntVal);
136 break;
137 case ASN_FLOAT:
138 case ASN_DOUBLE:
139 printf("Value %f\n", entries[i].value.doubleVal);
140 break;
141 case ASN_INTEGER:
142 printf("Value %lld\n", entries[i].value.intVal);
143 break;
144 }
145 }
146 }
147
148 idx_t encoded = base64_encode_alloc((const char *)entries,
149 (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)),
150 &encoded_string);
151
152 if (encoded > 0 && encoded_string != NULL) {
153 // success
154 if (verbose > 1) {
155 printf("encoded string: %s\n", encoded_string);
156 printf("encoded string length: %lu\n", strlen(encoded_string));
157 }
158 result.state_string = encoded_string;
159 return result;
160 }
161 result.errorcode = ERROR;
162 return result;
163}
164
165typedef struct {
166 int errorcode;
167 check_snmp_state_entry *state;
168} recover_state_data_type;
169recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) {
170 recover_state_data_type result = {.errorcode = OK, .state = NULL};
171
172 if (verbose > 1) {
173 printf("%s:\n", __FUNCTION__);
174 printf("State string: %s\n", state_string);
175 printf("State string length: %lu\n", state_string_length);
176 }
177
178 idx_t outlen = 0;
179 bool decoded =
180 base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen);
181
182 if (!decoded) {
183 if (verbose) {
184 printf("Failed to decode state string\n");
185 }
186 // failure to decode
187 result.errorcode = ERROR;
188 return result;
189 }
190
191 if (result.state == NULL) {
192 // Memory Error?
193 result.errorcode = ERROR;
194 return result;
195 }
196
197 if (verbose > 1) {
198 printf("Recovered %lu entries of size %lu\n",
199 (size_t)outlen / sizeof(check_snmp_state_entry), outlen);
200
201 for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) {
202 printf("Entry timestamp %lu: %s", result.state[i].timestamp,
203 ctime(&result.state[i].timestamp));
204 switch (result.state[i].type) {
205 case ASN_GAUGE:
206 printf("Type GAUGE\n");
207 break;
208 case ASN_TIMETICKS:
209 printf("Type TIMETICKS\n");
210 break;
211 case ASN_COUNTER:
212 printf("Type COUNTER\n");
213 break;
214 case ASN_UINTEGER:
215 printf("Type UINTEGER\n");
216 break;
217 case ASN_COUNTER64:
218 printf("Type COUNTER64\n");
219 break;
220 case ASN_FLOAT:
221 printf("Type FLOAT\n");
222 case ASN_DOUBLE:
223 printf("Type DOUBLE\n");
224 break;
225 case ASN_INTEGER:
226 printf("Type INTEGER\n");
227 break;
228 }
229
230 switch (result.state[i].type) {
231 case ASN_GAUGE:
232 case ASN_TIMETICKS:
233 case ASN_COUNTER:
234 case ASN_UINTEGER:
235 case ASN_COUNTER64:
236 printf("Value %llu\n", result.state[i].value.uIntVal);
237 break;
238 case ASN_FLOAT:
239 case ASN_DOUBLE:
240 printf("Value %f\n", result.state[i].value.doubleVal);
241 break;
242 case ASN_INTEGER:
243 printf("Value %lld\n", result.state[i].value.intVal);
244 break;
245 }
246 }
247 }
248
249 return result;
250}
90 251
91int main(int argc, char **argv) { 252int main(int argc, char **argv) {
92 setlocale(LC_ALL, ""); 253 setlocale(LC_ALL, "");
@@ -97,6 +258,8 @@ int main(int argc, char **argv) {
97 258
98 np_init((char *)progname, argc, argv); 259 np_init((char *)progname, argc, argv);
99 260
261 state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv);
262
100 /* Parse extra opts if any */ 263 /* Parse extra opts if any */
101 argv = np_extra_opts(&argc, argv, progname); 264 argv = np_extra_opts(&argc, argv, progname);
102 265
@@ -105,9 +268,6 @@ int main(int argc, char **argv) {
105 // Initialize net-snmp before touching the session we are going to use 268 // Initialize net-snmp before touching the session we are going to use
106 init_snmp("check_snmp"); 269 init_snmp("check_snmp");
107 270
108 time_t current_time;
109 time(&current_time);
110
111 process_arguments_wrapper paw_tmp = process_arguments(argc, argv); 271 process_arguments_wrapper paw_tmp = process_arguments(argc, argv);
112 if (paw_tmp.errorcode == ERROR) { 272 if (paw_tmp.errorcode == ERROR) {
113 usage4(_("Could not parse arguments")); 273 usage4(_("Could not parse arguments"));
@@ -119,347 +279,103 @@ int main(int argc, char **argv) {
119 mp_set_format(config.output_format); 279 mp_set_format(config.output_format);
120 } 280 }
121 281
122 if (config.ignore_mib_parsing_errors) {
123 char *opt_toggle_res = snmp_mib_toggle_options("e");
124 if (opt_toggle_res != NULL) {
125 die(STATE_UNKNOWN, "Unable to disable MIB parsing errors");
126 }
127 }
128
129 struct snmp_pdu *pdu = NULL;
130 if (config.use_getnext) {
131 pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
132 } else {
133 pdu = snmp_pdu_create(SNMP_MSG_GET);
134 }
135
136 for (size_t i = 0; i < config.num_of_test_units; i++) {
137 assert(config.test_units[i].oid != NULL);
138 if (verbose > 0) {
139 printf("OID %zu to parse: %s\n", i, config.test_units[i].oid);
140 }
141
142 oid tmp_OID[MAX_OID_LEN];
143 size_t tmp_OID_len = MAX_OID_LEN;
144 if (snmp_parse_oid(config.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) {
145 // success
146 snmp_add_null_var(pdu, tmp_OID, tmp_OID_len);
147 } else {
148 // failed
149 snmp_perror("Parsing failure");
150 die(STATE_UNKNOWN, "Failed to parse OID\n");
151 }
152 }
153
154 /* Set signal handling and alarm */ 282 /* Set signal handling and alarm */
155 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 283 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
156 usage4(_("Cannot catch SIGALRM")); 284 usage4(_("Cannot catch SIGALRM"));
157 } 285 }
158 286
159 const int timeout_safety_tolerance = 5; 287 time_t current_time;
160 alarm((timeout_interval * (unsigned int)config.snmp_session.retries) + 288 time(&current_time);
161 timeout_safety_tolerance);
162
163 struct snmp_session *active_session = snmp_open(&config.snmp_session);
164 if (active_session == NULL) {
165 int pcliberr = 0;
166 int psnmperr = 0;
167 char *pperrstring = NULL;
168 snmp_error(&config.snmp_session, &pcliberr, &psnmperr, &pperrstring);
169 die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring);
170 }
171
172 struct snmp_pdu *response = NULL;
173 int snmp_query_status = snmp_synch_response(active_session, pdu, &response);
174
175 if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) {
176 int pcliberr = 0;
177 int psnmperr = 0;
178 char *pperrstring = NULL;
179 snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring);
180 289
181 if (psnmperr == SNMPERR_TIMEOUT) { 290 if (verbose > 2) {
182 // We exit with critical here for some historical reason 291 printf("current time: %s (timestamp: %lu)\n", ctime(&current_time), current_time);
183 die(STATE_CRITICAL, "SNMP query ran into a timeout\n");
184 }
185 die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring);
186 } 292 }
187 293
188 snmp_close(active_session); 294 snmp_responces response = do_snmp_query(config.snmp_params);
189
190 /* disable alarm again */
191 alarm(0);
192 295
193 mp_check overall = mp_check_init(); 296 mp_check overall = mp_check_init();
194 297
195 mp_subcheck sc_successfull_query = mp_subcheck_init(); 298 if (response.errorcode == OK) {
196 xasprintf(&sc_successfull_query.output, "SNMP query was successful"); 299 mp_subcheck sc_successfull_query = mp_subcheck_init();
197 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); 300 xasprintf(&sc_successfull_query.output, "SNMP query was successful");
198 mp_add_subcheck_to_check(&overall, sc_successfull_query); 301 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK);
199 302 mp_add_subcheck_to_check(&overall, sc_successfull_query);
200 // We got the the query results, now process them 303 } else {
201 size_t loop_index = 0; 304 // Error treatment here, either partial or whole
202 for (netsnmp_variable_list *vars = response->variables; vars; 305 mp_subcheck sc_failed_query = mp_subcheck_init();
203 vars = vars->next_variable, loop_index++) { 306 xasprintf(&sc_failed_query.output, "SNMP query failed");
204 mp_subcheck sc_oid_test = mp_subcheck_init(); 307 sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK);
308 mp_add_subcheck_to_check(&overall, sc_failed_query);
309 mp_exit(overall);
310 }
205 311
206 if (verbose > 0) { 312 check_snmp_state_entry *prev_state = NULL;
207 printf("loop_index: %zu\n", loop_index); 313 bool have_previous_state = false;
208 }
209 314
210 if ((config.test_units[loop_index].label != NULL) && 315 if (config.evaluation_params.calculate_rate) {
211 (strcmp(config.test_units[loop_index].label, "") != 0)) { 316 state_data *previous_state = np_state_read(stateKey);
212 xasprintf(&sc_oid_test.output, "%s - ", config.test_units[loop_index].label); 317 if (previous_state == NULL) {
318 // failed to recover state
319 // or no previous state
320 have_previous_state = false;
213 } else { 321 } else {
214 sc_oid_test.output = strdup(""); 322 // sanity check
215 } 323 recover_state_data_type prev_state_wrapper =
216 324 recover_state_data(previous_state->data, (idx_t)previous_state->length);
217 char oid_string[(MAX_OID_LEN * 2) + 1] = {};
218
219 int oid_string_result =
220 snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length);
221 if (oid_string_result <= 0) {
222 // TODO error here
223 }
224
225 if (verbose > 2) {
226 printf("Processing oid %s\n", oid_string);
227 }
228 325
229 mp_perfdata_value pd_result_val = {0}; 326 if (prev_state_wrapper.errorcode == OK) {
230 xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); 327 have_previous_state = true;
231 sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); 328 prev_state = prev_state_wrapper.state;
232
233 switch (vars->type) {
234 case ASN_OCTET_STR: {
235 if (verbose) {
236 printf("Debug: Got a string\n");
237 }
238
239 char *tmp = (char *)vars->val.string;
240
241 if (strchr(tmp, '"') != NULL) {
242 // got double quote in the string
243 if (strchr(tmp, '\'') != NULL) {
244 // got single quote in the string too
245 // dont quote that at all to avoid even more confusion
246 xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp);
247 } else {
248 // quote with single quotes
249 xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp);
250 }
251 } else { 329 } else {
252 // quote with double quotes 330 have_previous_state = false;
253 xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); 331 prev_state = NULL;
254 }
255
256 if (strlen(tmp) == 0) {
257 sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result);
258 } 332 }
259
260 // String matching test
261 if ((config.test_units[loop_index].eval_mthd.crit_string)) {
262 if (strcmp(tmp, config.string_cmp_value)) {
263 sc_oid_test = mp_set_subcheck_state(
264 sc_oid_test, (config.invert_search) ? STATE_CRITICAL : STATE_OK);
265 } else {
266 sc_oid_test = mp_set_subcheck_state(
267 sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL);
268 }
269 } else if (config.test_units[loop_index].eval_mthd.crit_regex) {
270 const size_t nmatch = config.regex_cmp_value.re_nsub + 1;
271 regmatch_t pmatch[nmatch];
272 memset(pmatch, '\0', sizeof(regmatch_t) * nmatch);
273
274 int excode = regexec(&config.regex_cmp_value, tmp, nmatch, pmatch, 0);
275 if (excode == 0) {
276 sc_oid_test = mp_set_subcheck_state(
277 sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL);
278 } else if (excode != REG_NOMATCH) {
279 char errbuf[MAX_INPUT_BUFFER] = "";
280 regerror(excode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER);
281 printf(_("Execute Error: %s\n"), errbuf);
282 exit(STATE_CRITICAL);
283 } else { // REG_NOMATCH
284 sc_oid_test = mp_set_subcheck_state(
285 sc_oid_test, config.invert_search ? STATE_CRITICAL : STATE_OK);
286 }
287 }
288
289 mp_add_subcheck_to_check(&overall, sc_oid_test);
290 } 333 }
291 continue; 334 }
292 case ASN_OPAQUE:
293 if (verbose) {
294 printf("Debug: Got OPAQUE\n");
295 }
296 break;
297 case ASN_COUNTER64: {
298 if (verbose) {
299 printf("Debug: Got counter64\n");
300 }
301 struct counter64 tmp = *(vars->val.counter64);
302 uint64_t counter = (tmp.high << 32) + tmp.low;
303
304 if (config.multiplier_set || config.offset_set) {
305 double processed = 0;
306 if (config.multiplier_set) {
307 processed = (double)counter * config.multiplier;
308 }
309
310 if (config.offset_set) {
311 processed += config.offset;
312 }
313 pd_result_val = mp_create_pd_value(processed);
314 } else {
315 pd_result_val = mp_create_pd_value(counter);
316 }
317
318 } break;
319 /* Numerical values */
320 case ASN_GAUGE: // same as ASN_UNSIGNED
321 case ASN_TIMETICKS:
322 case ASN_COUNTER:
323 case ASN_UINTEGER: {
324 if (verbose) {
325 printf("Debug: Got a Integer like\n");
326 }
327 unsigned long tmp = (unsigned long)*(vars->val.integer);
328
329 if (config.multiplier_set || config.offset_set) {
330 double processed = 0;
331 if (config.multiplier_set) {
332 processed = (double)tmp * config.multiplier;
333 }
334 335
335 if (config.offset_set) { 336 check_snmp_state_entry *new_state = NULL;
336 processed += config.offset; 337 if (config.evaluation_params.calculate_rate) {
337 } 338 new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry));
338 pd_result_val = mp_create_pd_value(processed); 339 if (new_state == NULL) {
339 } else { 340 die(STATE_UNKNOWN, "memory allocation failed");
340 pd_result_val = mp_create_pd_value(tmp);
341 }
342 break;
343 } 341 }
344 case ASN_INTEGER: { 342 }
345 if (verbose) {
346 printf("Debug: Got a Integer\n");
347 }
348
349 long tmp = *(vars->val.integer);
350
351 if (config.multiplier_set || config.offset_set) {
352 double processed = 0;
353 if (config.multiplier_set) {
354 processed = (double)tmp * config.multiplier;
355 }
356
357 if (config.offset_set) {
358 processed += config.offset;
359 }
360 pd_result_val = mp_create_pd_value(processed);
361 } else {
362 pd_result_val = mp_create_pd_value(tmp);
363 }
364
365 } break;
366 case ASN_FLOAT: {
367 if (verbose) {
368 printf("Debug: Got a float\n");
369 }
370 double tmp = *(vars->val.floatVal);
371
372 if (config.multiplier_set) {
373 tmp *= config.multiplier;
374 }
375
376 if (config.offset_set) {
377 tmp += config.offset;
378 }
379 343
380 pd_result_val = mp_create_pd_value(tmp); 344 // We got the the query results, now process them
381 break; 345 for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) {
346 if (verbose > 0) {
347 printf("loop_index: %zu\n", loop_index);
382 } 348 }
383 case ASN_DOUBLE: {
384 if (verbose) {
385 printf("Debug: Got a double\n");
386 }
387 double tmp = *(vars->val.doubleVal);
388 if (config.multiplier_set) {
389 tmp *= config.multiplier;
390 }
391
392 if (config.offset_set) {
393 tmp += config.offset;
394 }
395 349
396 pd_result_val = mp_create_pd_value(tmp); 350 check_snmp_state_entry previous_unit_state = {};
397 break; 351 if (config.evaluation_params.calculate_rate && have_previous_state) {
352 previous_unit_state = prev_state[loop_index];
398 } 353 }
399 case ASN_IPADDRESS:
400 if (verbose) {
401 printf("Debug: Got an IP address\n");
402 }
403 continue;
404 default:
405 if (verbose) {
406 printf("Debug: Got a unmatched result type: %hhu\n", vars->type);
407 }
408 // TODO: Error here?
409 continue;
410 }
411
412 // some kind of numerical value
413 mp_perfdata pd_num_val = {
414 .value = pd_result_val,
415 };
416 354
417 if (!config.use_perf_data_labels_from_input) { 355 check_snmp_evaluation single_eval =
418 // Use oid for perdata label 356 evaluate_single_unit(response.response_values[loop_index], config.evaluation_params,
419 pd_num_val.label = strdup(oid_string); 357 config.snmp_params.test_units[loop_index], current_time,
420 // TODO strdup error checking 358 previous_unit_state, have_previous_state);
421 } else if (config.test_units[loop_index].label != NULL &&
422 strcmp(config.test_units[loop_index].label, "") != 0) {
423 pd_num_val.label = config.test_units[loop_index].label;
424 } else {
425 pd_num_val.label = config.test_units[loop_index].oid;
426 }
427 359
428 if (config.test_units[loop_index].unit_value != NULL && 360 if (config.evaluation_params.calculate_rate &&
429 strcmp(config.test_units[loop_index].unit_value, "") != 0) { 361 mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) {
430 pd_num_val.uom = config.test_units[loop_index].unit_value; 362 new_state[loop_index] = single_eval.state;
431 } 363 }
432 364
433 xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, 365 mp_add_subcheck_to_check(&overall, single_eval.sc);
434 pd_value_to_string(pd_result_val)); 366 }
435 367
436 if (config.test_units[loop_index].unit_value != NULL && 368 if (config.evaluation_params.calculate_rate) {
437 strcmp(config.test_units[loop_index].unit_value, "") != 0) { 369 // store state
438 xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, 370 gen_state_string_type current_state_wrapper =
439 config.test_units[loop_index].unit_value); 371 gen_state_string(new_state, config.snmp_params.num_of_test_units);
440 }
441 372
442 if (config.test_units[loop_index].threshold.warning_is_set || 373 if (current_state_wrapper.errorcode == OK) {
443 config.test_units[loop_index].threshold.critical_is_set) { 374 np_state_write_string(stateKey, current_time, current_state_wrapper.state_string);
444 pd_num_val = mp_pd_set_thresholds(pd_num_val, config.test_units[loop_index].threshold); 375 } else {
445 mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); 376 die(STATE_UNKNOWN, "failed to create state string");
446
447 if (tmp_state == STATE_WARNING) {
448 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING);
449 xasprintf(&sc_oid_test.output, "%s - number violates warning threshold",
450 sc_oid_test.output);
451 } else if (tmp_state == STATE_CRITICAL) {
452 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL);
453 xasprintf(&sc_oid_test.output, "%s - number violates critical threshold",
454 sc_oid_test.output);
455 }
456 } 377 }
457
458 mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val);
459
460 mp_add_subcheck_to_check(&overall, sc_oid_test);
461 } 378 }
462
463 mp_exit(overall); 379 mp_exit(overall);
464} 380}
465 381
@@ -472,6 +388,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
472 ignore_mib_parsing_errors_index, 388 ignore_mib_parsing_errors_index,
473 connection_prefix_index, 389 connection_prefix_index,
474 output_format_index, 390 output_format_index,
391 calculate_rate,
392 rate_multiplier
475 }; 393 };
476 394
477 static struct option longopts[] = { 395 static struct option longopts[] = {
@@ -510,6 +428,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
510 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index}, 428 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index},
511 {"connection-prefix", required_argument, 0, connection_prefix_index}, 429 {"connection-prefix", required_argument, 0, connection_prefix_index},
512 {"output-format", required_argument, 0, output_format_index}, 430 {"output-format", required_argument, 0, output_format_index},
431 {"rate", no_argument, 0, calculate_rate},
432 {"rate-multiplier", required_argument, 0, rate_multiplier},
513 {0, 0, 0, 0}}; 433 {0, 0, 0, 0}};
514 434
515 if (argc < 2) { 435 if (argc < 2) {
@@ -575,8 +495,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
575 } 495 }
576 496
577 check_snmp_config config = check_snmp_config_init(); 497 check_snmp_config config = check_snmp_config_init();
578 config.test_units = tmp; 498 config.snmp_params.test_units = tmp;
579 config.num_of_test_units = oid_counter; 499 config.snmp_params.num_of_test_units = oid_counter;
580 500
581 option = 0; 501 option = 0;
582 optind = 1; // Reset argument scanner 502 optind = 1; // Reset argument scanner
@@ -616,11 +536,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
616 536
617 /* Connection info */ 537 /* Connection info */
618 case 'C': /* group or community */ 538 case 'C': /* group or community */
619 config.snmp_session.community = (unsigned char *)optarg; 539 config.snmp_params.snmp_session.community = (unsigned char *)optarg;
620 config.snmp_session.community_len = strlen(optarg); 540 config.snmp_params.snmp_session.community_len = strlen(optarg);
621 break; 541 break;
622 case 'H': /* Host or server */ 542 case 'H': /* Host or server */
623 config.snmp_session.peername = optarg; 543 config.snmp_params.snmp_session.peername = optarg;
624 break; 544 break;
625 case 'p': /*port number */ 545 case 'p': /*port number */
626 // Add port to "peername" below to not rely on argument order 546 // 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) {
630 miblist = optarg; 550 miblist = optarg;
631 break; 551 break;
632 case 'n': /* use_getnext instead of get */ 552 case 'n': /* use_getnext instead of get */
633 config.use_getnext = true; 553 config.snmp_params.use_getnext = true;
634 break; 554 break;
635 case 'P': /* SNMP protocol version */ 555 case 'P': /* SNMP protocol version */
636 if (strcasecmp("1", optarg) == 0) { 556 if (strcasecmp("1", optarg) == 0) {
637 config.snmp_session.version = SNMP_VERSION_1; 557 config.snmp_params.snmp_session.version = SNMP_VERSION_1;
638 } else if (strcasecmp("2c", optarg) == 0) { 558 } else if (strcasecmp("2c", optarg) == 0) {
639 config.snmp_session.version = SNMP_VERSION_2c; 559 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
640 } else if (strcasecmp("3", optarg) == 0) { 560 } else if (strcasecmp("3", optarg) == 0) {
641 config.snmp_session.version = SNMP_VERSION_3; 561 config.snmp_params.snmp_session.version = SNMP_VERSION_3;
642 } else { 562 } else {
643 die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); 563 die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg);
644 } 564 }
@@ -646,45 +566,51 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
646 566
647 break; 567 break;
648 case 'N': /* SNMPv3 context name */ 568 case 'N': /* SNMPv3 context name */
649 config.snmp_session.contextName = optarg; 569 config.snmp_params.snmp_session.contextName = optarg;
650 config.snmp_session.contextNameLen = strlen(optarg); 570 config.snmp_params.snmp_session.contextNameLen = strlen(optarg);
651 break; 571 break;
652 case 'L': /* security level */ 572 case 'L': /* security level */
653 if (strcasecmp("noAuthNoPriv", optarg) == 0) { 573 if (strcasecmp("noAuthNoPriv", optarg) == 0) {
654 config.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; 574 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
655 } else if (strcasecmp("authNoPriv", optarg) == 0) { 575 } else if (strcasecmp("authNoPriv", optarg) == 0) {
656 config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; 576 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
657 } else if (strcasecmp("authPriv", optarg) == 0) { 577 } else if (strcasecmp("authPriv", optarg) == 0) {
658 config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; 578 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
659 } else { 579 } else {
660 die(STATE_UNKNOWN, "invalid security level: %s", optarg); 580 die(STATE_UNKNOWN, "invalid security level: %s", optarg);
661 } 581 }
662 break; 582 break;
663 case 'U': /* security username */ 583 case 'U': /* security username */
664 config.snmp_session.securityName = optarg; 584 config.snmp_params.snmp_session.securityName = optarg;
665 config.snmp_session.securityNameLen = strlen(optarg); 585 config.snmp_params.snmp_session.securityNameLen = strlen(optarg);
666 break; 586 break;
667 case 'a': /* auth protocol */ 587 case 'a': /* auth protocol */
668 // SNMPv3: SHA or MD5 588 // SNMPv3: SHA or MD5
669 // TODO Test for availability of individual protocols 589 // TODO Test for availability of individual protocols
670 if (strcasecmp("MD5", optarg) == 0) { 590 if (strcasecmp("MD5", optarg) == 0) {
671 config.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; 591 config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol;
672 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol); 592 config.snmp_params.snmp_session.securityAuthProtoLen =
593 OID_LENGTH(usmHMACMD5AuthProtocol);
673 } else if (strcasecmp("SHA", optarg) == 0) { 594 } else if (strcasecmp("SHA", optarg) == 0) {
674 config.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; 595 config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol;
675 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol); 596 config.snmp_params.snmp_session.securityAuthProtoLen =
597 OID_LENGTH(usmHMACSHA1AuthProtocol);
676 } else if (strcasecmp("SHA224", optarg) == 0) { 598 } else if (strcasecmp("SHA224", optarg) == 0) {
677 config.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; 599 config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol;
678 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); 600 config.snmp_params.snmp_session.securityAuthProtoLen =
601 OID_LENGTH(usmHMAC128SHA224AuthProtocol);
679 } else if (strcasecmp("SHA256", optarg) == 0) { 602 } else if (strcasecmp("SHA256", optarg) == 0) {
680 config.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; 603 config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol;
681 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); 604 config.snmp_params.snmp_session.securityAuthProtoLen =
605 OID_LENGTH(usmHMAC192SHA256AuthProtocol);
682 } else if (strcasecmp("SHA384", optarg) == 0) { 606 } else if (strcasecmp("SHA384", optarg) == 0) {
683 config.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; 607 config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol;
684 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol); 608 config.snmp_params.snmp_session.securityAuthProtoLen =
609 OID_LENGTH(usmHMAC256SHA384AuthProtocol);
685 } else if (strcasecmp("SHA512", optarg) == 0) { 610 } else if (strcasecmp("SHA512", optarg) == 0) {
686 config.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; 611 config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol;
687 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol); 612 config.snmp_params.snmp_session.securityAuthProtoLen =
613 OID_LENGTH(usmHMAC384SHA512AuthProtocol);
688 } else { 614 } else {
689 die(STATE_UNKNOWN, "Unknown authentication protocol"); 615 die(STATE_UNKNOWN, "Unknown authentication protocol");
690 } 616 }
@@ -692,24 +618,28 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
692 case 'x': /* priv protocol */ 618 case 'x': /* priv protocol */
693 if (strcasecmp("DES", optarg) == 0) { 619 if (strcasecmp("DES", optarg) == 0) {
694#ifdef HAVE_USM_DES_PRIV_PROTOCOL 620#ifdef HAVE_USM_DES_PRIV_PROTOCOL
695 config.snmp_session.securityAuthProto = usmDESPrivProtocol; 621 config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol;
696 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); 622 config.snmp_params.snmp_session.securityAuthProtoLen =
623 OID_LENGTH(usmDESPrivProtocol);
697#else 624#else
698 die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform"); 625 die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform");
699#endif 626#endif
700 } else if (strcasecmp("AES", optarg) == 0) { 627 } else if (strcasecmp("AES", optarg) == 0) {
701 config.snmp_session.securityAuthProto = usmAESPrivProtocol; 628 config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol;
702 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); 629 config.snmp_params.snmp_session.securityAuthProtoLen =
630 OID_LENGTH(usmAESPrivProtocol);
703 // } else if (strcasecmp("AES128", optarg)) { 631 // } else if (strcasecmp("AES128", optarg)) {
704 // config.snmp_session.securityAuthProto = usmAES128PrivProtocol; 632 // config.snmp_session.securityAuthProto = usmAES128PrivProtocol;
705 // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol) 633 // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol)
706 // / OID_LENGTH(oid); 634 // / OID_LENGTH(oid);
707 } else if (strcasecmp("AES192", optarg) == 0) { 635 } else if (strcasecmp("AES192", optarg) == 0) {
708 config.snmp_session.securityAuthProto = usmAES192PrivProtocol; 636 config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol;
709 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES192PrivProtocol); 637 config.snmp_params.snmp_session.securityAuthProtoLen =
638 OID_LENGTH(usmAES192PrivProtocol);
710 } else if (strcasecmp("AES256", optarg) == 0) { 639 } else if (strcasecmp("AES256", optarg) == 0) {
711 config.snmp_session.securityAuthProto = usmAES256PrivProtocol; 640 config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol;
712 config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES256PrivProtocol); 641 config.snmp_params.snmp_session.securityAuthProtoLen =
642 OID_LENGTH(usmAES256PrivProtocol);
713 // } else if (strcasecmp("AES192Cisco", optarg)) { 643 // } else if (strcasecmp("AES192Cisco", optarg)) {
714 // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol; 644 // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol;
715 // config.snmp_session.securityAuthProtoLen = 645 // config.snmp_session.securityAuthProtoLen =
@@ -738,7 +668,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
738 if (!is_integer(optarg)) { 668 if (!is_integer(optarg)) {
739 usage2(_("Retries interval must be a positive integer"), optarg); 669 usage2(_("Retries interval must be a positive integer"), optarg);
740 } else { 670 } else {
741 config.snmp_session.retries = atoi(optarg); 671 config.snmp_params.snmp_session.retries = atoi(optarg);
742 } 672 }
743 break; 673 break;
744 case 't': /* timeout period */ 674 case 't': /* timeout period */
@@ -751,10 +681,10 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
751 681
752 /* Test parameters */ 682 /* Test parameters */
753 case 'c': /* critical threshold */ 683 case 'c': /* critical threshold */
754 check_snmp_set_thresholds(optarg, config.test_units, oid_counter, true); 684 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true);
755 break; 685 break;
756 case 'w': /* warning threshold */ 686 case 'w': /* warning threshold */
757 check_snmp_set_thresholds(optarg, config.test_units, oid_counter, false); 687 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false);
758 break; 688 break;
759 case 'o': /* object identifier */ 689 case 'o': /* object identifier */
760 if (strspn(optarg, "0123456789.,") != strlen(optarg)) { 690 if (strspn(optarg, "0123456789.,") != strlen(optarg)) {
@@ -763,25 +693,27 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
763 * so we have a mib variable, rather than just an SNMP OID, 693 * so we have a mib variable, rather than just an SNMP OID,
764 * so we have to actually read the mib files 694 * so we have to actually read the mib files
765 */ 695 */
766 config.need_mibs = true; 696 config.snmp_params.need_mibs = true;
767 } 697 }
768 698
769 for (char *ptr = strtok(optarg, ", "); ptr != NULL; 699 for (char *ptr = strtok(optarg, ", "); ptr != NULL;
770 ptr = strtok(NULL, ", "), tmp_oid_counter++) { 700 ptr = strtok(NULL, ", "), tmp_oid_counter++) {
771 config.test_units[tmp_oid_counter].oid = strdup(ptr); 701 config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr);
772 } 702 }
773 break; 703 break;
774 case 'z': /* Null OID Return Check */ 704 case 'z': /* Null OID Return Check */
775 if (!is_integer(optarg)) { 705 if (!is_integer(optarg)) {
776 usage2(_("Exit status must be a positive integer"), optarg); 706 usage2(_("Exit status must be a positive integer"), optarg);
777 } else { 707 } else {
778 config.nulloid_result = atoi(optarg); 708 config.evaluation_params.nulloid_result = atoi(optarg);
779 } 709 }
780 break; 710 break;
781 case 's': /* string or substring */ 711 case 's': /* string or substring */
782 strncpy(config.string_cmp_value, optarg, sizeof(config.string_cmp_value) - 1); 712 strncpy(config.evaluation_params.string_cmp_value, optarg,
783 config.string_cmp_value[sizeof(config.string_cmp_value) - 1] = 0; 713 sizeof(config.evaluation_params.string_cmp_value) - 1);
784 config.test_units[eval_counter++].eval_mthd.crit_string = true; 714 config.evaluation_params
715 .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0;
716 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true;
785 break; 717 break;
786 case 'R': /* regex */ 718 case 'R': /* regex */
787 cflags = REG_ICASE; 719 cflags = REG_ICASE;
@@ -792,72 +724,73 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
792 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 724 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
793 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); 725 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1);
794 regex_expect[sizeof(regex_expect) - 1] = 0; 726 regex_expect[sizeof(regex_expect) - 1] = 0;
795 int errcode = regcomp(&config.regex_cmp_value, regex_expect, cflags); 727 int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags);
796 if (errcode != 0) { 728 if (errcode != 0) {
797 char errbuf[MAX_INPUT_BUFFER] = ""; 729 char errbuf[MAX_INPUT_BUFFER] = "";
798 regerror(errcode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); 730 regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf,
731 MAX_INPUT_BUFFER);
799 printf("Could Not Compile Regular Expression: %s", errbuf); 732 printf("Could Not Compile Regular Expression: %s", errbuf);
800 process_arguments_wrapper result = { 733 process_arguments_wrapper result = {
801 .errorcode = ERROR, 734 .errorcode = ERROR,
802 }; 735 };
803 return result; 736 return result;
804 } 737 }
805 config.test_units[eval_counter++].eval_mthd.crit_regex = true; 738 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true;
806 } break; 739 } break;
807 case 'l': /* label */ 740 case 'l': /* label */
808 { 741 {
809 if (labels_counter >= config.num_of_test_units) { 742 if (labels_counter >= config.snmp_params.num_of_test_units) {
810 break; 743 break;
811 } 744 }
812 char *ptr = trim_whitespaces_and_check_quoting(optarg); 745 char *ptr = trim_whitespaces_and_check_quoting(optarg);
813 if (ptr[0] == '\'') { 746 if (ptr[0] == '\'') {
814 config.test_units[labels_counter].label = ptr + 1; 747 config.snmp_params.test_units[labels_counter].label = ptr + 1;
815 } else { 748 } else {
816 config.test_units[labels_counter].label = ptr; 749 config.snmp_params.test_units[labels_counter].label = ptr;
817 } 750 }
818 751
819 while (ptr && (ptr = get_next_argument(ptr))) { 752 while (ptr && (ptr = get_next_argument(ptr))) {
820 labels_counter++; 753 labels_counter++;
821 ptr = trim_whitespaces_and_check_quoting(ptr); 754 ptr = trim_whitespaces_and_check_quoting(ptr);
822 if (ptr[0] == '\'') { 755 if (ptr[0] == '\'') {
823 config.test_units[labels_counter].label = ptr + 1; 756 config.snmp_params.test_units[labels_counter].label = ptr + 1;
824 } else { 757 } else {
825 config.test_units[labels_counter].label = ptr; 758 config.snmp_params.test_units[labels_counter].label = ptr;
826 } 759 }
827 } 760 }
828 labels_counter++; 761 labels_counter++;
829 } break; 762 } break;
830 case 'u': /* units */ 763 case 'u': /* units */
831 { 764 {
832 if (unitv_counter >= config.num_of_test_units) { 765 if (unitv_counter >= config.snmp_params.num_of_test_units) {
833 break; 766 break;
834 } 767 }
835 char *ptr = trim_whitespaces_and_check_quoting(optarg); 768 char *ptr = trim_whitespaces_and_check_quoting(optarg);
836 if (ptr[0] == '\'') { 769 if (ptr[0] == '\'') {
837 config.test_units[unitv_counter].unit_value = ptr + 1; 770 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
838 } else { 771 } else {
839 config.test_units[unitv_counter].unit_value = ptr; 772 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
840 } 773 }
841 while (ptr && (ptr = get_next_argument(ptr))) { 774 while (ptr && (ptr = get_next_argument(ptr))) {
842 unitv_counter++; 775 unitv_counter++;
843 ptr = trim_whitespaces_and_check_quoting(ptr); 776 ptr = trim_whitespaces_and_check_quoting(ptr);
844 if (ptr[0] == '\'') { 777 if (ptr[0] == '\'') {
845 config.test_units[unitv_counter].unit_value = ptr + 1; 778 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
846 } else { 779 } else {
847 config.test_units[unitv_counter].unit_value = ptr; 780 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
848 } 781 }
849 } 782 }
850 unitv_counter++; 783 unitv_counter++;
851 } break; 784 } break;
852 case offset_index: 785 case offset_index:
853 config.offset = strtod(optarg, NULL); 786 config.evaluation_params.offset = strtod(optarg, NULL);
854 config.offset_set = true; 787 config.evaluation_params.offset_set = true;
855 break; 788 break;
856 case invert_search_index: 789 case invert_search_index:
857 config.invert_search = false; 790 config.evaluation_params.invert_search = false;
858 break; 791 break;
859 case 'O': 792 case 'O':
860 config.use_perf_data_labels_from_input = true; 793 config.evaluation_params.use_oid_as_perf_data_label = true;
861 break; 794 break;
862 case '4': 795 case '4':
863 // The default, do something here to be exclusive to -6 instead of doing nothing? 796 // 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) {
871 break; 804 break;
872 case 'M': 805 case 'M':
873 if (strspn(optarg, "0123456789.,") == strlen(optarg)) { 806 if (strspn(optarg, "0123456789.,") == strlen(optarg)) {
874 config.multiplier = strtod(optarg, NULL); 807 config.evaluation_params.multiplier = strtod(optarg, NULL);
875 config.multiplier_set = true; 808 config.evaluation_params.multiplier_set = true;
876 } 809 }
877 break; 810 break;
878 case ignore_mib_parsing_errors_index: 811 case ignore_mib_parsing_errors_index:
879 config.ignore_mib_parsing_errors = true; 812 config.snmp_params.ignore_mib_parsing_errors = true;
880 break; 813 break;
881 case 'f': // Deprecated format option for floating point values 814 case 'f': // Deprecated format option for floating point values
882 break; 815 break;
@@ -892,13 +825,22 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
892 config.output_format = parser.output_format; 825 config.output_format = parser.output_format;
893 break; 826 break;
894 } 827 }
828 case calculate_rate:
829 config.evaluation_params.calculate_rate = true;
830 break;
831 case rate_multiplier:
832 if (!is_integer(optarg) ||
833 ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) {
834 usage2(_("Rate multiplier must be a positive integer"), optarg);
835 }
836 break;
895 default: 837 default:
896 die(STATE_UNKNOWN, "Unknown option"); 838 die(STATE_UNKNOWN, "Unknown option");
897 } 839 }
898 } 840 }
899 841
900 if (config.snmp_session.peername == NULL) { 842 if (config.snmp_params.snmp_session.peername == NULL) {
901 config.snmp_session.peername = argv[optind]; 843 config.snmp_params.snmp_session.peername = argv[optind];
902 } 844 }
903 845
904 // Build true peername here if necessary 846 // Build true peername here if necessary
@@ -908,7 +850,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
908 // The default, do nothing 850 // The default, do nothing
909 } else if (strcasecmp(connection_prefix, "tcp") == 0) { 851 } else if (strcasecmp(connection_prefix, "tcp") == 0) {
910 // use tcp/ipv4 852 // use tcp/ipv4
911 xasprintf(&config.snmp_session.peername, "tcp:%s", config.snmp_session.peername); 853 xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s",
854 config.snmp_params.snmp_session.peername);
912 } else if (strcasecmp(connection_prefix, "tcp6") == 0 || 855 } else if (strcasecmp(connection_prefix, "tcp6") == 0 ||
913 strcasecmp(connection_prefix, "tcpv6") == 0 || 856 strcasecmp(connection_prefix, "tcpv6") == 0 ||
914 strcasecmp(connection_prefix, "tcpipv6") == 0 || 857 strcasecmp(connection_prefix, "tcpipv6") == 0 ||
@@ -917,19 +860,23 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
917 strcasecmp(connection_prefix, "udpv6") == 0) { 860 strcasecmp(connection_prefix, "udpv6") == 0) {
918 // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it 861 // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it
919 // works anyway therefore do nothing here 862 // works anyway therefore do nothing here
920 xasprintf(&config.snmp_session.peername, "%s:%s", connection_prefix, 863 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix,
921 config.snmp_session.peername); 864 config.snmp_params.snmp_session.peername);
922 } else if (strcmp(connection_prefix, "tls") == 0) { 865 } else if (strcmp(connection_prefix, "tls") == 0) {
923 // TODO: Anything else to do here? 866 // TODO: Anything else to do here?
924 xasprintf(&config.snmp_session.peername, "tls:%s", config.snmp_session.peername); 867 xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s",
868 config.snmp_params.snmp_session.peername);
925 } else if (strcmp(connection_prefix, "dtls") == 0) { 869 } else if (strcmp(connection_prefix, "dtls") == 0) {
926 // TODO: Anything else to do here? 870 // TODO: Anything else to do here?
927 xasprintf(&config.snmp_session.peername, "dtls:%s", config.snmp_session.peername); 871 xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s",
872 config.snmp_params.snmp_session.peername);
928 } else if (strcmp(connection_prefix, "unix") == 0) { 873 } else if (strcmp(connection_prefix, "unix") == 0) {
929 // TODO: Check whether this is a valid path? 874 // TODO: Check whether this is a valid path?
930 xasprintf(&config.snmp_session.peername, "unix:%s", config.snmp_session.peername); 875 xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s",
876 config.snmp_params.snmp_session.peername);
931 } else if (strcmp(connection_prefix, "ipx") == 0) { 877 } else if (strcmp(connection_prefix, "ipx") == 0) {
932 xasprintf(&config.snmp_session.peername, "ipx:%s", config.snmp_session.peername); 878 xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s",
879 config.snmp_params.snmp_session.peername);
933 } else { 880 } else {
934 // Don't know that prefix, die here 881 // Don't know that prefix, die here
935 die(STATE_UNKNOWN, "Unknown connection prefix"); 882 die(STATE_UNKNOWN, "Unknown connection prefix");
@@ -937,17 +884,18 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
937 } 884 }
938 885
939 /* Check server_address is given */ 886 /* Check server_address is given */
940 if (config.snmp_session.peername == NULL) { 887 if (config.snmp_params.snmp_session.peername == NULL) {
941 die(STATE_UNKNOWN, _("No host specified\n")); 888 die(STATE_UNKNOWN, _("No host specified\n"));
942 } 889 }
943 890
944 if (port != NULL) { 891 if (port != NULL) {
945 xasprintf(&config.snmp_session.peername, "%s:%s", config.snmp_session.peername, port); 892 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s",
893 config.snmp_params.snmp_session.peername, port);
946 } 894 }
947 895
948 /* check whether to load locally installed MIBS (CPU/disk intensive) */ 896 /* check whether to load locally installed MIBS (CPU/disk intensive) */
949 if (miblist == NULL) { 897 if (miblist == NULL) {
950 if (config.need_mibs) { 898 if (config.snmp_params.need_mibs) {
951 setenv("MIBLS", DEFAULT_MIBLIST, 1); 899 setenv("MIBLS", DEFAULT_MIBLIST, 1);
952 } else { 900 } else {
953 setenv("MIBLS", "NONE", 1); 901 setenv("MIBLS", "NONE", 1);
@@ -959,37 +907,37 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
959 } 907 }
960 908
961 // Historical default is SNMP v2c 909 // Historical default is SNMP v2c
962 if (!snmp_version_set_explicitely && config.snmp_session.community != NULL) { 910 if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) {
963 config.snmp_session.version = SNMP_VERSION_2c; 911 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
964 } 912 }
965 913
966 if ((config.snmp_session.version == SNMP_VERSION_1) || 914 if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) ||
967 (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ 915 (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */
968 /* 916 /*
969 config.numauthpriv = 2; 917 config.numauthpriv = 2;
970 config.authpriv = calloc(config.numauthpriv, sizeof(char *)); 918 config.authpriv = calloc(config.numauthpriv, sizeof(char *));
971 config.authpriv[0] = strdup("-c"); 919 config.authpriv[0] = strdup("-c");
972 config.authpriv[1] = strdup(community); 920 config.authpriv[1] = strdup(community);
973 */ 921 */
974 } else if (config.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ 922 } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */
975 // generate keys for priv and auth here (if demanded) 923 // generate keys for priv and auth here (if demanded)
976 924
977 if (config.snmp_session.securityName == NULL) { 925 if (config.snmp_params.snmp_session.securityName == NULL) {
978 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); 926 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
979 } 927 }
980 928
981 switch (config.snmp_session.securityLevel) { 929 switch (config.snmp_params.snmp_session.securityLevel) {
982 case SNMP_SEC_LEVEL_AUTHPRIV: { 930 case SNMP_SEC_LEVEL_AUTHPRIV: {
983 if (authpasswd == NULL) { 931 if (authpasswd == NULL) {
984 die(STATE_UNKNOWN, 932 die(STATE_UNKNOWN,
985 "No authentication passphrase was given, but authorization was requested"); 933 "No authentication passphrase was given, but authorization was requested");
986 } 934 }
987 // auth and priv 935 // auth and priv
988 int priv_key_generated = 936 int priv_key_generated = generate_Ku(
989 generate_Ku(config.snmp_session.securityPrivProto, 937 config.snmp_params.snmp_session.securityPrivProto,
990 (unsigned int)config.snmp_session.securityPrivProtoLen, authpasswd, 938 (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd,
991 strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, 939 strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey,
992 &config.snmp_session.securityPrivKeyLen); 940 &config.snmp_params.snmp_session.securityPrivKeyLen);
993 941
994 if (priv_key_generated != SNMPERR_SUCCESS) { 942 if (priv_key_generated != SNMPERR_SUCCESS) {
995 die(STATE_UNKNOWN, "Failed to generate privacy key"); 943 die(STATE_UNKNOWN, "Failed to generate privacy key");
@@ -1000,11 +948,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) {
1000 if (privpasswd == NULL) { 948 if (privpasswd == NULL) {
1001 die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); 949 die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested");
1002 } 950 }
1003 int auth_key_generated = 951 int auth_key_generated = generate_Ku(
1004 generate_Ku(config.snmp_session.securityAuthProto, 952 config.snmp_params.snmp_session.securityAuthProto,
1005 (unsigned int)config.snmp_session.securityAuthProtoLen, privpasswd, 953 (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd,
1006 strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, 954 strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey,
1007 &config.snmp_session.securityAuthKeyLen); 955 &config.snmp_params.snmp_session.securityAuthKeyLen);
1008 956
1009 if (auth_key_generated != SNMPERR_SUCCESS) { 957 if (auth_key_generated != SNMPERR_SUCCESS) {
1010 die(STATE_UNKNOWN, "Failed to generate privacy key"); 958 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 @@
1#include "./check_snmp_helpers.h" 1#include "./check_snmp_helpers.h"
2#include <string.h> 2#include <string.h>
3#include "../../lib/utils_base.h" 3#include "../../lib/utils_base.h"
4#include "config.h"
5#include <assert.h>
6#include "../utils.h"
7#include "output.h"
8#include "states.h"
9#include <sys/stat.h>
10#include <ctype.h>
11
12extern int verbose;
4 13
5check_snmp_test_unit check_snmp_test_unit_init() { 14check_snmp_test_unit check_snmp_test_unit_init() {
6 check_snmp_test_unit tmp = { 15 check_snmp_test_unit tmp = {
@@ -78,38 +87,848 @@ int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit
78const int DEFAULT_PROTOCOL = SNMP_VERSION_1; 87const int DEFAULT_PROTOCOL = SNMP_VERSION_1;
79const char DEFAULT_OUTPUT_DELIMITER[] = " "; 88const char DEFAULT_OUTPUT_DELIMITER[] = " ";
80 89
81const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; 90const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192;
82 91
83check_snmp_config check_snmp_config_init() { 92check_snmp_config check_snmp_config_init() {
84 check_snmp_config tmp = { 93 check_snmp_config tmp = {
85 .use_getnext = false, 94 .snmp_params =
95 {
96 .use_getnext = false,
97
98 .ignore_mib_parsing_errors = false,
99 .need_mibs = false,
86 100
87 .ignore_mib_parsing_errors = false, 101 .test_units = NULL,
88 .need_mibs = false, 102 .num_of_test_units = 0,
103 },
89 104
90 .test_units = NULL, 105 .evaluation_params =
91 .num_of_test_units = 0, 106 {
107 .nulloid_result = STATE_UNKNOWN, // state to return if no result for query
92 108
93 .nulloid_result = STATE_UNKNOWN, // state to return if no result for query 109 .invert_search = true,
110 .regex_cmp_value = {},
111 .string_cmp_value = "",
94 112
95 .invert_search = true, 113 .multiplier = 1.0,
96 .regex_cmp_value = {}, 114 .multiplier_set = false,
97 .string_cmp_value = "", 115 .offset = 0,
116 .offset_set = false,
98 117
99 .multiplier = 1.0, 118 .use_oid_as_perf_data_label = false,
100 .multiplier_set = false,
101 .offset = 0,
102 .offset_set = false,
103 119
104 .use_perf_data_labels_from_input = false, 120 .calculate_rate = false,
121 .rate_multiplier = 1,
122 },
105 }; 123 };
106 124
107 snmp_sess_init(&tmp.snmp_session); 125 snmp_sess_init(&tmp.snmp_params.snmp_session);
108 126
109 tmp.snmp_session.retries = DEFAULT_RETRIES; 127 tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES;
110 tmp.snmp_session.version = DEFAULT_SNMP_VERSION; 128 tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION;
111 tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; 129 tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
112 tmp.snmp_session.community = (unsigned char *)"public"; 130 tmp.snmp_params.snmp_session.community = (unsigned char *)"public";
113 tmp.snmp_session.community_len = strlen("public"); 131 tmp.snmp_params.snmp_session.community_len = strlen("public");
114 return tmp; 132 return tmp;
115} 133}
134
135snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) {
136 if (parameters.ignore_mib_parsing_errors) {
137 char *opt_toggle_res = snmp_mib_toggle_options("e");
138 if (opt_toggle_res != NULL) {
139 die(STATE_UNKNOWN, "Unable to disable MIB parsing errors");
140 }
141 }
142
143 struct snmp_pdu *pdu = NULL;
144 if (parameters.use_getnext) {
145 pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
146 } else {
147 pdu = snmp_pdu_create(SNMP_MSG_GET);
148 }
149
150 for (size_t i = 0; i < parameters.num_of_test_units; i++) {
151 assert(parameters.test_units[i].oid != NULL);
152 if (verbose > 0) {
153 printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid);
154 }
155
156 oid tmp_OID[MAX_OID_LEN];
157 size_t tmp_OID_len = MAX_OID_LEN;
158 if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) {
159 // success
160 snmp_add_null_var(pdu, tmp_OID, tmp_OID_len);
161 } else {
162 // failed
163 snmp_perror("Parsing failure");
164 die(STATE_UNKNOWN, "Failed to parse OID\n");
165 }
166 }
167
168 const int timeout_safety_tolerance = 5;
169 alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) +
170 timeout_safety_tolerance);
171
172 struct snmp_session *active_session = snmp_open(&parameters.snmp_session);
173 if (active_session == NULL) {
174 int pcliberr = 0;
175 int psnmperr = 0;
176 char *pperrstring = NULL;
177 snmp_error(&parameters.snmp_session, &pcliberr, &psnmperr, &pperrstring);
178 die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring);
179 }
180
181 struct snmp_pdu *response = NULL;
182 int snmp_query_status = snmp_synch_response(active_session, pdu, &response);
183
184 if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) {
185 int pcliberr = 0;
186 int psnmperr = 0;
187 char *pperrstring = NULL;
188 snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring);
189
190 if (psnmperr == SNMPERR_TIMEOUT) {
191 // We exit with critical here for some historical reason
192 die(STATE_CRITICAL, "SNMP query ran into a timeout\n");
193 }
194 die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring);
195 }
196
197 snmp_close(active_session);
198
199 /* disable alarm again */
200 alarm(0);
201
202 snmp_responces result = {
203 .errorcode = OK,
204 .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)),
205 };
206
207 if (result.response_values == NULL) {
208 result.errorcode = ERROR;
209 return result;
210 }
211
212 // We got the the query results, now process them
213 size_t loop_index = 0;
214 for (netsnmp_variable_list *vars = response->variables; vars;
215 vars = vars->next_variable, loop_index++) {
216
217 for (size_t jdx = 0; jdx < vars->name_length; jdx++) {
218 result.response_values[loop_index].oid[jdx] = vars->name[jdx];
219 }
220 result.response_values[loop_index].oid_length = vars->name_length;
221
222 switch (vars->type) {
223 case ASN_OCTET_STR: {
224 result.response_values[loop_index].string_response = strdup((char *)vars->val.string);
225 result.response_values[loop_index].type = vars->type;
226 if (verbose) {
227 printf("Debug: Got a string as response: %s\n", vars->val.string);
228 }
229 }
230 continue;
231 case ASN_OPAQUE:
232 if (verbose) {
233 printf("Debug: Got OPAQUE\n");
234 }
235 break;
236 /* Numerical values */
237 case ASN_COUNTER64: {
238 if (verbose) {
239 printf("Debug: Got counter64\n");
240 }
241 struct counter64 tmp = *(vars->val.counter64);
242 uint64_t counter = (tmp.high << 32) + tmp.low;
243 result.response_values[loop_index].value.uIntVal = counter;
244 result.response_values[loop_index].type = vars->type;
245 } break;
246 case ASN_GAUGE: // same as ASN_UNSIGNED
247 case ASN_TIMETICKS:
248 case ASN_COUNTER:
249 case ASN_UINTEGER: {
250 if (verbose) {
251 printf("Debug: Got a Integer like\n");
252 }
253 result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer);
254 result.response_values[loop_index].type = vars->type;
255 } break;
256 case ASN_INTEGER: {
257 if (verbose) {
258 printf("Debug: Got a Integer\n");
259 }
260 result.response_values[loop_index].value.intVal = *(vars->val.integer);
261 result.response_values[loop_index].type = vars->type;
262 } break;
263 case ASN_FLOAT: {
264 if (verbose) {
265 printf("Debug: Got a float\n");
266 }
267 result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal);
268 result.response_values[loop_index].type = vars->type;
269 } break;
270 case ASN_DOUBLE: {
271 if (verbose) {
272 printf("Debug: Got a double\n");
273 }
274 result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal);
275 result.response_values[loop_index].type = vars->type;
276 break;
277 }
278 case ASN_IPADDRESS:
279 if (verbose) {
280 printf("Debug: Got an IP address\n");
281 }
282 result.response_values[loop_index].type = vars->type;
283
284 // TODO: print address here, state always ok? or regex match?
285 continue;
286 default:
287 if (verbose) {
288 printf("Debug: Got a unmatched result type: %hhu\n", vars->type);
289 }
290 // TODO: Error here?
291 continue;
292 }
293 }
294
295 return result;
296}
297
298check_snmp_evaluation evaluate_single_unit(response_value response,
299 check_snmp_evaluation_parameters eval_params,
300 check_snmp_test_unit test_unit, time_t query_timestamp,
301 check_snmp_state_entry prev_state,
302 bool have_previous_state) {
303 mp_subcheck sc_oid_test = mp_subcheck_init();
304
305 if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) {
306 xasprintf(&sc_oid_test.output, "%s - ", test_unit.label);
307 } else {
308 sc_oid_test.output = strdup("");
309 }
310
311 char oid_string[(MAX_OID_LEN * 2) + 1] = {};
312
313 int oid_string_result =
314 snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length);
315 if (oid_string_result <= 0) {
316 // TODO error here
317 die(STATE_UNKNOWN, "snprint_objid failed\n");
318 }
319
320 xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string);
321 sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK);
322
323 if (verbose > 2) {
324 printf("Processing oid %s\n", oid_string);
325 }
326
327 bool got_a_numerical_value = false;
328 mp_perfdata_value pd_result_val = {0};
329
330 check_snmp_state_entry result_state = {
331 .timestamp = query_timestamp,
332 .oid_length = response.oid_length,
333 .type = response.type,
334 };
335
336 for (size_t i = 0; i < response.oid_length; i++) {
337 result_state.oid[i] = response.oid[i];
338 }
339
340 if (have_previous_state) {
341 if (query_timestamp == prev_state.timestamp) {
342 // somehow we have the same timestamp again, that can't be good
343 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN);
344 xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid");
345
346 check_snmp_evaluation result = {
347 .sc = sc_oid_test,
348 .state = result_state,
349 };
350
351 return result;
352 }
353 }
354 // compute rate time difference
355 double timeDiff = 0;
356 if (have_previous_state) {
357 if (verbose) {
358 printf("Previous timestamp: %s", ctime(&prev_state.timestamp));
359 printf("Current timestamp: %s", ctime(&query_timestamp));
360 }
361 timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier;
362 }
363
364 mp_perfdata pd_num_val = {};
365
366 switch (response.type) {
367 case ASN_OCTET_STR: {
368 char *tmp = response.string_response;
369 if (strchr(tmp, '"') != NULL) {
370 // got double quote in the string
371 if (strchr(tmp, '\'') != NULL) {
372 // got single quote in the string too
373 // dont quote that at all to avoid even more confusion
374 xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp);
375 } else {
376 // quote with single quotes
377 xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp);
378 }
379 } else {
380 // quote with double quotes
381 xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp);
382 }
383
384 if (strlen(tmp) == 0) {
385 sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result);
386 }
387
388 // String matching test
389 if ((test_unit.eval_mthd.crit_string)) {
390 if (strcmp(tmp, eval_params.string_cmp_value)) {
391 sc_oid_test = mp_set_subcheck_state(
392 sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK);
393 } else {
394 sc_oid_test = mp_set_subcheck_state(
395 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
396 }
397 } else if (test_unit.eval_mthd.crit_regex) {
398 const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1;
399 regmatch_t pmatch[nmatch];
400 memset(pmatch, '\0', sizeof(regmatch_t) * nmatch);
401
402 int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0);
403 if (excode == 0) {
404 sc_oid_test = mp_set_subcheck_state(
405 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
406 } else if (excode != REG_NOMATCH) {
407 char errbuf[MAX_INPUT_BUFFER] = "";
408 regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER);
409 printf(_("Execute Error: %s\n"), errbuf);
410 exit(STATE_CRITICAL);
411 } else { // REG_NOMATCH
412 sc_oid_test = mp_set_subcheck_state(
413 sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK);
414 }
415 }
416 } break;
417 case ASN_COUNTER64:
418 got_a_numerical_value = true;
419
420 result_state.value.uIntVal = response.value.uIntVal;
421 result_state.type = response.type;
422
423 // TODO: perfdata unit counter
424 if (eval_params.calculate_rate && have_previous_state) {
425 if (prev_state.value.uIntVal > response.value.uIntVal) {
426 // overflow
427 unsigned long long tmp =
428 (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal;
429
430 tmp /= timeDiff;
431 pd_result_val = mp_create_pd_value(tmp);
432 } else {
433 pd_result_val = mp_create_pd_value(
434 (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff);
435 }
436 } else {
437 // It's only a counter if we cont compute rate
438 pd_num_val.uom = "c";
439 pd_result_val = mp_create_pd_value(response.value.uIntVal);
440 }
441 break;
442 case ASN_GAUGE: // same as ASN_UNSIGNED
443 case ASN_TIMETICKS:
444 case ASN_COUNTER:
445 case ASN_UINTEGER: {
446 got_a_numerical_value = true;
447 long long treated_value = (long long)response.value.uIntVal;
448
449 if (eval_params.multiplier_set || eval_params.offset_set) {
450 double processed = 0;
451 if (eval_params.offset_set) {
452 processed += eval_params.offset;
453 }
454
455 if (eval_params.multiplier_set) {
456 processed = processed * eval_params.multiplier;
457 }
458
459 treated_value = lround(processed);
460 }
461
462 result_state.value.intVal = treated_value;
463
464 if (eval_params.calculate_rate && have_previous_state) {
465 if (verbose > 2) {
466 printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__,
467 prev_state.value.intVal);
468 printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__,
469 treated_value);
470 }
471 double rate = (treated_value - prev_state.value.intVal) / timeDiff;
472 pd_result_val = mp_create_pd_value(rate);
473 } else {
474 pd_result_val = mp_create_pd_value(treated_value);
475
476 if (response.type == ASN_COUNTER) {
477 pd_num_val.uom = "c";
478 }
479 }
480
481 } break;
482 case ASN_INTEGER: {
483 if (eval_params.multiplier_set || eval_params.offset_set) {
484 double processed = 0;
485 if (eval_params.multiplier_set) {
486 processed = (double)response.value.intVal * eval_params.multiplier;
487 }
488
489 if (eval_params.offset_set) {
490 processed += eval_params.offset;
491 }
492
493 result_state.value.doubleVal = processed;
494
495 if (eval_params.calculate_rate && have_previous_state) {
496 pd_result_val =
497 mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff);
498 } else {
499 pd_result_val = mp_create_pd_value(processed);
500 }
501 } else {
502 result_state.value.intVal = response.value.intVal;
503
504 if (eval_params.calculate_rate && have_previous_state) {
505 pd_result_val = mp_create_pd_value(
506 (response.value.intVal - prev_state.value.intVal) / timeDiff);
507 } else {
508 pd_result_val = mp_create_pd_value(response.value.intVal);
509 }
510 }
511
512 got_a_numerical_value = true;
513 } break;
514 case ASN_FLOAT: // fallthrough
515 case ASN_DOUBLE: {
516 got_a_numerical_value = true;
517 double tmp = response.value.doubleVal;
518 if (eval_params.offset_set) {
519 tmp += eval_params.offset;
520 }
521
522 if (eval_params.multiplier_set) {
523 tmp *= eval_params.multiplier;
524 }
525
526 if (eval_params.calculate_rate && have_previous_state) {
527 pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff);
528 } else {
529 pd_result_val = mp_create_pd_value(tmp);
530 }
531 got_a_numerical_value = true;
532
533 result_state.value.doubleVal = tmp;
534 } break;
535 case ASN_IPADDRESS:
536 // TODO
537 }
538
539 if (got_a_numerical_value) {
540 if (eval_params.use_oid_as_perf_data_label) {
541 // Use oid for perdata label
542 pd_num_val.label = strdup(oid_string);
543 // TODO strdup error checking
544 } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) {
545 pd_num_val.label = strdup(test_unit.label);
546 } else {
547 pd_num_val.label = strdup(test_unit.oid);
548 }
549
550 if (!(eval_params.calculate_rate && !have_previous_state)) {
551 // some kind of numerical value
552 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
553 pd_num_val.uom = test_unit.unit_value;
554 }
555
556 pd_num_val.value = pd_result_val;
557
558 xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output,
559 pd_value_to_string(pd_result_val));
560
561 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
562 xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value);
563 }
564
565 if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) {
566 pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold);
567 mp_state_enum tmp_state = mp_get_pd_status(pd_num_val);
568
569 if (tmp_state == STATE_WARNING) {
570 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING);
571 xasprintf(&sc_oid_test.output, "%s - number violates warning threshold",
572 sc_oid_test.output);
573 } else if (tmp_state == STATE_CRITICAL) {
574 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL);
575 xasprintf(&sc_oid_test.output, "%s - number violates critical threshold",
576 sc_oid_test.output);
577 }
578 }
579
580 mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val);
581 } else {
582 // should calculate rate, but there is no previous state, so first run
583 // exit with ok now
584 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK);
585 xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay",
586 sc_oid_test.output);
587 }
588 }
589
590 check_snmp_evaluation result = {
591 .sc = sc_oid_test,
592 .state = result_state,
593 };
594
595 return result;
596}
597
598char *_np_state_generate_key(int argc, char **argv);
599
600/*
601 * If time=NULL, use current time. Create state file, with state format
602 * version, default text. Writes version, time, and data. Avoid locking
603 * problems - use mv to write and then swap. Possible loss of state data if
604 * two things writing to same key at same time.
605 * Will die with UNKNOWN if errors
606 */
607void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) {
608 time_t current_time;
609 if (timestamp == 0) {
610 time(&current_time);
611 } else {
612 current_time = timestamp;
613 }
614
615 int result = 0;
616
617 /* If file doesn't currently exist, create directories */
618 if (access(stateKey._filename, F_OK) != 0) {
619 char *directories = NULL;
620 result = asprintf(&directories, "%s", stateKey._filename);
621 if (result < 0) {
622 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
623 }
624
625 for (char *p = directories + 1; *p; p++) {
626 if (*p == '/') {
627 *p = '\0';
628 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
629 /* Can't free this! Otherwise error message is wrong! */
630 /* np_free(directories); */
631 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
632 }
633 *p = '/';
634 }
635 }
636
637 if (directories) {
638 free(directories);
639 }
640 }
641
642 char *temp_file = NULL;
643 result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename);
644 if (result < 0) {
645 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
646 }
647
648 int temp_file_desc = 0;
649 if ((temp_file_desc = mkstemp(temp_file)) == -1) {
650 if (temp_file) {
651 free(temp_file);
652 }
653 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
654 }
655
656 FILE *temp_file_pointer = fdopen(temp_file_desc, "w");
657 if (temp_file_pointer == NULL) {
658 close(temp_file_desc);
659 unlink(temp_file);
660 if (temp_file) {
661 free(temp_file);
662 }
663 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
664 }
665
666 fprintf(temp_file_pointer, "# NP State file\n");
667 fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION);
668 fprintf(temp_file_pointer, "%d\n", stateKey.data_version);
669 fprintf(temp_file_pointer, "%lu\n", current_time);
670 fprintf(temp_file_pointer, "%s\n", stringToStore);
671
672 fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP);
673
674 fflush(temp_file_pointer);
675
676 result = fclose(temp_file_pointer);
677
678 fsync(temp_file_desc);
679
680 if (result != 0) {
681 unlink(temp_file);
682 if (temp_file) {
683 free(temp_file);
684 }
685 die(STATE_UNKNOWN, _("Error writing temp file"));
686 }
687
688 if (rename(temp_file, stateKey._filename) != 0) {
689 unlink(temp_file);
690 if (temp_file) {
691 free(temp_file);
692 }
693 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
694 }
695
696 if (temp_file) {
697 free(temp_file);
698 }
699}
700
701/*
702 * Read the state file
703 */
704bool _np_state_read_file(FILE *state_file, state_key stateKey) {
705 time_t current_time;
706 time(&current_time);
707
708 /* Note: This introduces a limit of 8192 bytes in the string data */
709 char *line = (char *)calloc(1, 8192);
710 if (line == NULL) {
711 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
712 }
713
714 bool status = false;
715 enum {
716 STATE_FILE_VERSION,
717 STATE_DATA_VERSION,
718 STATE_DATA_TIME,
719 STATE_DATA_TEXT,
720 STATE_DATA_END
721 } expected = STATE_FILE_VERSION;
722
723 int failure = 0;
724 while (!failure && (fgets(line, 8192, state_file)) != NULL) {
725 size_t pos = strlen(line);
726 if (line[pos - 1] == '\n') {
727 line[pos - 1] = '\0';
728 }
729
730 if (line[0] == '#') {
731 continue;
732 }
733
734 switch (expected) {
735 case STATE_FILE_VERSION: {
736 int i = atoi(line);
737 if (i != NP_STATE_FORMAT_VERSION) {
738 failure++;
739 } else {
740 expected = STATE_DATA_VERSION;
741 }
742 } break;
743 case STATE_DATA_VERSION: {
744 int i = atoi(line);
745 if (i != stateKey.data_version) {
746 failure++;
747 } else {
748 expected = STATE_DATA_TIME;
749 }
750 } break;
751 case STATE_DATA_TIME: {
752 /* If time > now, error */
753 time_t data_time = strtoul(line, NULL, 10);
754 if (data_time > current_time) {
755 failure++;
756 } else {
757 stateKey.state_data->time = data_time;
758 expected = STATE_DATA_TEXT;
759 }
760 } break;
761 case STATE_DATA_TEXT:
762 stateKey.state_data->data = strdup(line);
763 if (stateKey.state_data->data == NULL) {
764 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
765 }
766 stateKey.state_data->length = strlen(line);
767 expected = STATE_DATA_END;
768 status = true;
769 break;
770 case STATE_DATA_END:;
771 }
772 }
773
774 if (line) {
775 free(line);
776 }
777 return status;
778}
779/*
780 * Will return NULL if no data is available (first run). If key currently
781 * exists, read data. If state file format version is not expected, return
782 * as if no data. Get state data version number and compares to expected.
783 * If numerically lower, then return as no previous state. die with UNKNOWN
784 * if exceptional error.
785 */
786state_data *np_state_read(state_key stateKey) {
787 /* Open file. If this fails, no previous state found */
788 FILE *statefile = fopen(stateKey._filename, "r");
789 state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data));
790 if (statefile != NULL) {
791
792 if (this_state_data == NULL) {
793 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
794 }
795
796 this_state_data->data = NULL;
797 stateKey.state_data = this_state_data;
798
799 if (_np_state_read_file(statefile, stateKey)) {
800 this_state_data->errorcode = OK;
801 } else {
802 this_state_data->errorcode = ERROR;
803 }
804
805 fclose(statefile);
806 } else {
807 // Failed to open state file
808 this_state_data->errorcode = ERROR;
809 }
810
811 return stateKey.state_data;
812}
813
814/*
815 * Internal function. Returns either:
816 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
817 * statically compiled shared state directory
818 */
819char *_np_state_calculate_location_prefix(void) {
820 char *env_dir;
821
822 /* Do not allow passing MP_STATE_PATH in setuid plugins
823 * for security reasons */
824 if (!mp_suid()) {
825 env_dir = getenv("MP_STATE_PATH");
826 if (env_dir && env_dir[0] != '\0') {
827 return env_dir;
828 }
829 /* This is the former ENV, for backward-compatibility */
830 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
831 if (env_dir && env_dir[0] != '\0') {
832 return env_dir;
833 }
834 }
835
836 return NP_STATE_DIR_PREFIX;
837}
838
839/*
840 * Initiatializer for state routines.
841 * Sets variables. Generates filename. Returns np_state_key. die with
842 * UNKNOWN if exception
843 */
844state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc,
845 char **argv) {
846 state_key *this_state = (state_key *)calloc(1, sizeof(state_key));
847 if (this_state == NULL) {
848 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
849 }
850
851 char *temp_keyname = NULL;
852 if (keyname == NULL) {
853 temp_keyname = _np_state_generate_key(argc, argv);
854 } else {
855 temp_keyname = strdup(keyname);
856 if (temp_keyname == NULL) {
857 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
858 }
859 }
860
861 /* Die if invalid characters used for keyname */
862 char *tmp_char = temp_keyname;
863 while (*tmp_char != '\0') {
864 if (!(isalnum(*tmp_char) || *tmp_char == '_')) {
865 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
866 }
867 tmp_char++;
868 }
869 this_state->name = temp_keyname;
870 this_state->plugin_name = plugin_name;
871 this_state->data_version = expected_data_version;
872 this_state->state_data = NULL;
873
874 /* Calculate filename */
875 char *temp_filename = NULL;
876 int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(),
877 (unsigned long)geteuid(), plugin_name, this_state->name);
878 if (error < 0) {
879 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
880 }
881
882 this_state->_filename = temp_filename;
883
884 return *this_state;
885}
886
887/*
888 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
889 * hopefully a unique key per service/plugin invocation. Use the extra-opts
890 * parse of argv, so that uniqueness in parameters are reflected there.
891 */
892char *_np_state_generate_key(int argc, char **argv) {
893 unsigned char result[256];
894
895#ifdef USE_OPENSSL
896 /*
897 * This code path is chosen if openssl is available (which should be the most common
898 * scenario). Alternatively, the gnulib implementation/
899 *
900 */
901 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
902
903 EVP_DigestInit(ctx, EVP_sha256());
904
905 for (int i = 0; i < argc; i++) {
906 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
907 }
908
909 EVP_DigestFinal(ctx, result, NULL);
910#else
911
912 struct sha256_ctx ctx;
913
914 for (int i = 0; i < this_monitoring_plugin->argc; i++) {
915 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
916 }
917
918 sha256_finish_ctx(&ctx, result);
919#endif // FOUNDOPENSSL
920
921 char keyname[41];
922 for (int i = 0; i < 20; ++i) {
923 sprintf(&keyname[2 * i], "%02x", result[i]);
924 }
925
926 keyname[40] = '\0';
927
928 char *keyname_copy = strdup(keyname);
929 if (keyname_copy == NULL) {
930 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
931 }
932
933 return keyname_copy;
934}
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 @@
1#pragma once 1#pragma once
2 2
3#include "./config.h" 3#include "./config.h"
4#include <net-snmp/library/asn1.h>
4 5
5check_snmp_test_unit check_snmp_test_unit_init(); 6check_snmp_test_unit check_snmp_test_unit_init();
6int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool); 7int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool);
7check_snmp_config check_snmp_config_init(); 8check_snmp_config check_snmp_config_init();
9
10typedef struct {
11 oid oid[MAX_OID_LEN];
12 size_t oid_length;
13 unsigned char type;
14 union {
15 uint64_t uIntVal;
16 int64_t intVal;
17 double doubleVal;
18 } value;
19 char *string_response;
20} response_value;
21
22typedef struct {
23 int errorcode;
24 response_value *response_values;
25} snmp_responces;
26snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters);
27
28// state is similar to response, but only numerics and a timestamp
29typedef struct {
30 time_t timestamp;
31 oid oid[MAX_OID_LEN];
32 size_t oid_length;
33 unsigned char type;
34 union {
35 unsigned long long uIntVal;
36 long long intVal;
37 double doubleVal;
38 } value;
39} check_snmp_state_entry;
40
41typedef struct {
42 check_snmp_state_entry state;
43 mp_subcheck sc;
44} check_snmp_evaluation;
45check_snmp_evaluation evaluate_single_unit(response_value response,
46 check_snmp_evaluation_parameters eval_params,
47 check_snmp_test_unit test_unit, time_t query_timestamp,
48 check_snmp_state_entry prev_state,
49 bool have_previous_state);
50
51#define NP_STATE_FORMAT_VERSION 1
52
53typedef struct state_data_struct {
54 time_t time;
55 void *data;
56 size_t length; /* Of binary data */
57 int errorcode;
58} state_data;
59
60typedef struct state_key_struct {
61 char *name;
62 char *plugin_name;
63 int data_version;
64 char *_filename;
65 state_data *state_data;
66} state_key;
67
68state_data *np_state_read(state_key stateKey);
69state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc,
70 char **argv);
71void 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 @@
1#pragma once 1#pragma once
2 2
3#include "thresholds.h" 3#include "../../lib/thresholds.h"
4#include "states.h" 4#include "../../lib/states.h"
5#include <stdlib.h> 5#include <stdlib.h>
6#include <stdbool.h> 6#include <stdbool.h>
7#include <regex.h> 7#include <regex.h>
@@ -18,7 +18,7 @@
18#include <net-snmp/library/snmp.h> 18#include <net-snmp/library/snmp.h>
19#include <net-snmp/session_api.h> 19#include <net-snmp/session_api.h>
20 20
21#define DEFAULT_PORT "161" 21#define DEFAULT_PORT "161"
22#define DEFAULT_RETRIES 5 22#define DEFAULT_RETRIES 5
23 23
24typedef struct eval_method { 24typedef struct eval_method {
@@ -34,10 +34,8 @@ typedef struct check_snmp_test_unit {
34 mp_thresholds threshold; 34 mp_thresholds threshold;
35} check_snmp_test_unit; 35} check_snmp_test_unit;
36 36
37typedef struct check_snmp_config { 37typedef struct {
38 // SNMP session to use
39 struct snmp_session snmp_session; 38 struct snmp_session snmp_session;
40
41 // use getnet instead of get 39 // use getnet instead of get
42 bool use_getnext; 40 bool use_getnext;
43 41
@@ -47,7 +45,9 @@ typedef struct check_snmp_config {
47 45
48 check_snmp_test_unit *test_units; 46 check_snmp_test_unit *test_units;
49 size_t num_of_test_units; 47 size_t num_of_test_units;
48} check_snmp_config_snmp_parameters;
50 49
50typedef struct {
51 // State if an empty value is encountered 51 // State if an empty value is encountered
52 mp_state_enum nulloid_result; 52 mp_state_enum nulloid_result;
53 53
@@ -63,7 +63,18 @@ typedef struct check_snmp_config {
63 bool offset_set; 63 bool offset_set;
64 64
65 // Modify output 65 // Modify output
66 bool use_perf_data_labels_from_input; 66 bool use_oid_as_perf_data_label;
67
68 // activate rate calucation
69 bool calculate_rate;
70 unsigned int rate_multiplier;
71} check_snmp_evaluation_parameters;
72
73typedef struct check_snmp_config {
74 // SNMP session to use
75 check_snmp_config_snmp_parameters snmp_params;
76
77 check_snmp_evaluation_parameters evaluation_params;
67 78
68 mp_output_format output_format; 79 mp_output_format output_format;
69 bool output_format_is_set; 80 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 @@
4# 4#
5 5
6use strict; 6use strict;
7use warnings;
7use Test::More; 8use Test::More;
8use NPTest; 9use NPTest;
9use FindBin qw($Bin); 10use FindBin qw($Bin);
10use POSIX qw/strftime/; 11use POSIX qw/strftime/;
11 12
12my $tests = 81; 13my $tests = 75;
13# Check that all dependent modules are available 14# Check that all dependent modules are available
14eval { 15eval {
15 require NetSNMP::OID; 16 require NetSNMP::OID;
@@ -76,49 +77,36 @@ my $res;
76 77
77$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"); 78$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");
78cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); 79cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" );
79like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); 80like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines");
80like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software |
81.1.3.6.1.4.1.8072.3.2.67.0:
82"Cisco Internetwork Operating System Software
83IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version
8412.2(20)EWA, RELEASE SOFTWARE (fc1)
85Technical Support: http://www.cisco.com/techsupport
86Copyright (c) 1986-2004 by cisco Systems, Inc.
87"').'/m', "String contains all lines");
88 81
89# sysContact.0 is "Alice" (from our snmpd.conf) 82# sysContact.0 is "Alice" (from our snmpd.conf)
90$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"); 83$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");
91cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); 84cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
92like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); 85# like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
93like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software ').'"?Alice"?'.quotemeta(' Kisco Outernetwork Oserating Gystem Totware | 86like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines with multiple OIDs");
94.1.3.6.1.4.1.8072.3.2.67.0: 87like($res->output, '/.*Alice.*/m', "String contains all lines with multiple OIDs");
95"Cisco Internetwork Operating System Software 88like($res->output, '/.*Kisco Outernetwork Oserating Gystem Totware.*/m', "String contains all lines with multiple OIDs");
96IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version
9712.2(20)EWA, RELEASE SOFTWARE (fc1)
98Technical Support: http://www.cisco.com/techsupport
99Copyright (c) 1986-2004 by cisco Systems, Inc.
100"
101.1.3.6.1.4.1.8072.3.2.67.1:
102"Kisco Outernetwork Oserating Gystem Totware
103Copyleft (c) 2400-2689 by kisco Systrems, Inc."').'/m', "String contains all lines with multiple OIDs");
104 89
105$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"); 90$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");
106like($res->output, '/'.quotemeta('SNMP OK - This should not confuse check_snmp \"parser\" | 91cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
107.1.3.6.1.4.1.8072.3.2.67.2: 92# like($res->output, '/'.quotemeta('This should not confuse check_snmp \"parser\" |
108"This should not confuse check_snmp \"parser\" 93# .1.3.6.1.4.1.8072.3.2.67.2:
109into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); 94# "This should not confuse check_snmp \"parser\"
95# into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1");
110 96
111$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"); 97$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");
112like($res->output, '/'.quotemeta('SNMP OK - It\'s getting even harder if the line | 98cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
113.1.3.6.1.4.1.8072.3.2.67.3: 99# like($res->output, '/'.quotemeta('It\'s getting even harder if the line |
114"It\'s getting even harder if the line 100# .1.3.6.1.4.1.8072.3.2.67.3:
115ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); 101# "It\'s getting even harder if the line
102# ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2");
116 103
117$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"); 104$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");
118like($res->output, '/'.quotemeta('SNMP OK - And now have fun with with this: \"C:\\\\\" | 105cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
119.1.3.6.1.4.1.8072.3.2.67.4: 106# like($res->output, '/'.quotemeta('And now have fun with with this: \"C:\\\\\" |
120"And now have fun with with this: \"C:\\\\\" 107# .1.3.6.1.4.1.8072.3.2.67.4:
121because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); 108# "And now have fun with with this: \"C:\\\\\"
109# because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3");
122 110
123system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*"); 111system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*");
124 112
@@ -131,156 +119,159 @@ SKIP: {
131 my $ts = time(); 119 my $ts = time();
132 $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" ); 120 $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" );
133 is($res->return_code, 0, "Returns OK"); 121 is($res->return_code, 0, "Returns OK");
134 is($res->output, "No previous data to calculate rate - assume okay"); 122 like($res->output, "/.*No previous data to calculate rate - assume okay.*/");
135 123
136 # test rate 1 second later 124 # test rate 1 second later
137 $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" ); 125 $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" );
138 is($res->return_code, 1, "WARNING - due to going above rate calculation" ); 126 is($res->return_code, 1, "WARNING - due to going above rate calculation" );
139 is($res->output, "SNMP RATE WARNING - *666* | iso.3.6.1.4.1.8072.3.2.67.10=666;600 "); 127 like($res->output, "/.*=666.*/");
140 128
141 # test rate with same time 129 # test rate with same time
142 $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" ); 130 $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" );
143 is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" ); 131 is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" );
144 is($res->output, "Time duration between plugin calls is invalid"); 132 like($res->output, "/.*Time duration between plugin calls is invalid.*/");
145 133
146 134
147 $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" ); 135 $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" );
148 is($res->return_code, 0, "OK for first call" ); 136 is($res->return_code, 0, "OK for first call" );
149 is($res->output, "No previous data to calculate rate - assume okay" ); 137 like($res->output, "/.*No previous data to calculate rate - assume okay.*/" );
150 138
151 # test rate 1 second later 139 # test rate 1 second later
152 $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" ); 140 $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" );
153 is($res->return_code, 0, "OK as no thresholds" ); 141 is($res->return_code, 0, "OK as no thresholds" );
154 is($res->output, "SNMP RATE OK - inoctets 666 | inoctets=666 ", "Check label"); 142 like($res->output, "/.*inoctets.*=666.*/m", "Check label");
155 143
156 # test rate 3 seconds later 144 # test rate 3 seconds later
157 $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" ); 145 $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" );
158 is($res->return_code, 0, "OK as no thresholds" ); 146 is($res->return_code, 0, "OK as no thresholds" );
159 is($res->output, "SNMP RATE OK - inoctets 333 | inoctets=333 ", "Check rate decreases due to longer interval"); 147 like($res->output, "/.*inoctets.*333.*/", "Check rate decreases due to longer interval");
160 148
161 149
162 # label performance data check 150 # label performance data check
163 $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" ); 151 $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" );
164 is($res->return_code, 0, "OK as no thresholds" ); 152 is($res->return_code, 0, "OK as no thresholds" );
165 is($res->output, "SNMP OK - test 67996 | test=67996c ", "Check label"); 153 like($res->output, "/.*test.?=67996c/", "Check label");
166 154
167 $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\"" ); 155 # following test is deactivated since it was not valid due to the guidelines (no single quote in label allowed)
168 is($res->return_code, 0, "OK as no thresholds" ); 156 # $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\"" );
169 is($res->output, "SNMP OK - test'test 68662 | \"test'test\"=68662c ", "Check label"); 157 # is($res->return_code, 0, "OK as no thresholds" );
158 # is($res->output, "test'test 68662 | \"test'test\"=68662c ", "Check label");
170 159
171 $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'" ); 160 $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'" );
172 is($res->return_code, 0, "OK as no thresholds" ); 161 is($res->return_code, 0, "OK as no thresholds" );
173 is($res->output, "SNMP OK - test\"test 69328 | 'test\"test'=69328c ", "Check label"); 162 like($res->output, "/.*'test\"test'=68662c.*/", "Check label");
174 163
175 $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" ); 164 $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" );
176 is($res->return_code, 0, "OK as no thresholds" ); 165 is($res->return_code, 0, "OK as no thresholds" );
177 is($res->output, "SNMP OK - test 69994 | iso.3.6.1.4.1.8072.3.2.67.10=69994c ", "Check label"); 166 like($res->output, "/.*.67.10.?=69328c.*/", "Check label");
178 167
179 $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" ); 168 $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" );
180 is($res->return_code, 0, "OK as no thresholds" ); 169 is($res->return_code, 0, "OK as no thresholds" );
181 is($res->output, "SNMP OK - 70660 | iso.3.6.1.4.1.8072.3.2.67.10=70660c ", "Check label"); 170 like($res->output, "/.*8072.3.2.67.10.?=69994c.*/", "Check label");
182 171
183 $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'" ); 172 $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'" );
184 is($res->return_code, 0, "OK as no thresholds" ); 173 is($res->return_code, 0, "OK as no thresholds" );
185 is($res->output, "SNMP OK - test test 71326 | 'test test'=71326c ", "Check label"); 174 like($res->output, "/.*'test test'=70660c/", "Check label");
186 175
187 176
188 $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" ); 177 $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" );
189 is($res->return_code, 0, "OK for first call" ); 178 is($res->return_code, 0, "OK for first call" );
190 is($res->output, "No previous data to calculate rate - assume okay" ); 179 like($res->output, "/.*No previous data to calculate rate - assume okay.*/" );
191 180
192 # test 1 second later 181 # test 1 second later
193 $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" ); 182 $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" );
194 is($res->return_code, 0, "OK as no thresholds" ); 183 is($res->return_code, 0, "OK as no thresholds" );
195 is($res->output, "SNMP RATE OK - inoctets_per_minute 39960 | inoctets_per_minute=39960 ", "Checking multiplier"); 184 like($res->output, "/.*inoctets_per_minute.*=39960/", "Checking multiplier");
196}; 185};
197 186
198 187
199 188
200$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\"'" ); 189$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'" );
201is($res->return_code, 0, "OK as string matches" ); 190is($res->return_code, 0, "OK as string matches" );
202is($res->output, 'SNMP OK - "stringtests" | ', "Good string match" ); 191like($res->output, '/.*stringtests.*/', "Good string match" );
203 192
204$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" ); 193$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" );
205is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); 194is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" );
206is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Failed string match" ); 195like($res->output, '/.*stringtests.*/', "Failed string match" );
207 196
208$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\"'" ); 197$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'" );
209is($res->return_code, 2, "CRITICAL as string matches but inverted" ); 198is($res->return_code, 2, "CRITICAL as string matches but inverted" );
210is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Inverted string match" ); 199like($res->output, '/.*"stringtests".*/', "Inverted string match" );
211 200
212$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" ); 201$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" );
213is($res->return_code, 0, "OK as string doesn't match but inverted" ); 202is($res->return_code, 0, "OK as string doesn't match but inverted" );
214is($res->output, 'SNMP OK - "stringtests" | ', "OK as inverted string no match" ); 203like($res->output, '/.*"stringtests".*/', "OK as inverted string no match" );
215 204
216$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" ); 205$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" );
217is($res->return_code, 1, "Numeric in string test" ); 206# a string is a string and not a number
218is($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" ); 207is($res->return_code, 0, "Numeric in string test" );
208like($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" );
219 209
220$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" ); 210$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" );
221is($res->return_code, 0, "Not really numeric test" ); 211is($res->return_code, 0, "Not really numeric test" );
222is($res->output, 'SNMP OK - "87.4startswithnumberbutshouldbestring" | ', "Check string with numeric start is still string" ); 212like($res->output, '/.*"87.4startswithnumberbutshouldbestring".*/', "Check string with numeric start is still string" );
223 213
224$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" ); 214$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" );
225is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); 215is($res->return_code, 0, "Not really numeric test (trying best to fool it)" );
226is($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)" ); 216like($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)" );
227 217
228$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'" ); 218$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'" );
229is($res->return_code, 0, "String check should check whole string, not a parsed number" ); 219is($res->return_code, 0, "String check should check whole string, not a parsed number" );
230is($res->output, 'SNMP OK - "CUSTOM CHECK OK: foo is 12345" | ', "String check with numbers returns whole string"); 220like($res->output, '/.*CUSTOM CHECK OK: foo is 12345.*/', "String check with numbers returns whole string");
231 221
232$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:" ); 222$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:" );
233is($res->return_code, 0, "Negative integer check OK" ); 223is($res->return_code, 0, "Negative integer check OK" );
234is($res->output, 'SNMP OK - -2 | iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3: ', "Negative integer check OK output" ); 224like($res->output, '/.*-2.*| iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3:/', "Negative integer check OK output" );
235 225
236$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:" ); 226$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:" );
237is($res->return_code, 1, "Negative integer check WARNING" ); 227is($res->return_code, 1, "Negative integer check WARNING" );
238is($res->output, 'SNMP WARNING - *-3* | iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3: ', "Negative integer check WARNING output" ); 228like($res->output, '/.*-3.*| iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3:/', "Negative integer check WARNING output" );
239 229
240$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:" ); 230$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:" );
241is($res->return_code, 2, "Negative integer check CRITICAL" ); 231is($res->return_code, 2, "Negative integer check CRITICAL" );
242is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3: ', "Negative integer check CRITICAL output" ); 232like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3:/', "Negative integer check CRITICAL output" );
243 233
244$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:" ); 234$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:" );
245is($res->return_code, 1, "Negative integer as string, WARNING" ); 235is($res->return_code, 1, "Negative integer as string, WARNING" );
246is($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" ); 236like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6:/', "Negative integer as string, WARNING output" );
247 237
248$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:" ); 238$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:" );
249is($res->return_code, 2, "Negative integer as string, CRITICAL" ); 239is($res->return_code, 2, "Negative integer as string, CRITICAL" );
250is($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" ); 240like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3:/', "Negative integer as string, CRITICAL output" );
251 241
252$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'" ); 242# deactivated since the perl agent api of snmpd really does not allow floats
253is($res->return_code, 0, "Negative float OK" ); 243# $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'" );
254is($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" ); 244# is($res->return_code, 0, "Negative float OK" );
245# is($res->output, '-6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" );
255 246
256$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'" ); 247# $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'" );
257is($res->return_code, 1, "Negative float WARNING" ); 248# is($res->return_code, 1, "Negative float WARNING" );
258is($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" ); 249# like($res->output, '/-6.6.*| .*67.18=-6.6;@-6.65:~;@-6.55:~/', "Negative float WARNING output" );
259 250
260$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'" ); 251$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'" );
261is($res->return_code, 0, "Multiple OIDs with thresholds" ); 252is($res->return_code, 0, "Multiple OIDs with thresholds" );
262like($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" ); 253like($res->output, '/-4.*| .*67.10=\d+c;1:100000;2:200000 .*67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" );
263 254
264$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'" ); 255$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'" );
265is($res->return_code, 1, "Multiple OIDs with thresholds" ); 256is($res->return_code, 1, "Multiple OIDs with thresholds" );
266like($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" ); 257like($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" );
267 258
268$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" ); 259$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" );
269is($res->return_code, 2, "Multiple OIDs with some thresholds" ); 260is($res->return_code, 2, "Multiple OIDs with some thresholds" );
270like($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" ); 261like($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" );
271 262
272$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"); 263$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");
273is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); 264is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" );
274is($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" ); 265like($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" );
275 266
276$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"); 267$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");
277is($res->return_code, 0, "Test multiply RC" ); 268is($res->return_code, 0, "Test multiply RC" );
278is($res->output,'SNMP OK - 4.200000 | iso.3.6.1.4.1.8072.3.2.67.19=4.200000 ' , "Test multiply .1 output" ); 269like($res->output,'/.*4.200000.*| iso.3.6.1.4.1.8072.3.2.67.19=4.200000/' , "Test multiply .1 output" );
279 270
280$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' "); 271$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");
281is($res->return_code, 0, "Test multiply RC + format" ); 272is($res->return_code, 0, "Test multiply RC" );
282is($res->output, 'SNMP OK - 4.20 | iso.3.6.1.4.1.8072.3.2.67.19=4.20 ', "Test multiply .1 output + format" ); 273like($res->output, '/.*4.20.*| iso.3.6.1.4.1.8072.3.2.67.19=4.20/', "Test multiply .1 output" );
283 274
284$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"); 275$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");
285is($res->return_code, 1, "Test multiply RC + format + thresholds" ); 276is($res->return_code, 1, "Test multiply RC + thresholds" );
286is($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" ); 277like($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 @@
4# 4#
5 5
6#use strict; # Doesn't work 6#use strict; # Doesn't work
7use warnings;
7use NetSNMP::OID qw(:all); 8use NetSNMP::OID qw(:all);
8use NetSNMP::agent; 9use NetSNMP::agent;
9use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64); 10use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64 ASN_FLOAT);
10#use Math::Int64 qw(uint64); # Skip that module while we don't need it 11#use Math::Int64 qw(uint64); # Skip that module while we don't need it
11sub uint64 { return $_ } 12sub uint64 { return $_ }
12 13
@@ -22,21 +23,82 @@ IOS (tm) Catalyst 4000 "L3" Switch Software (cat4000-I9K91S-M), Version
22Technical Support: http://www.cisco.com/techsupport 23Technical Support: http://www.cisco.com/techsupport
23Copyright (c) 1986-2004 by cisco Systems, Inc. 24Copyright (c) 1986-2004 by cisco Systems, Inc.
24'; 25';
25my $multilin2 = "Kisco Outernetwork Oserating Gystem Totware 26my $multiline2 = "Kisco Outernetwork Oserating Gystem Totware
26Copyleft (c) 2400-2689 by kisco Systrems, Inc."; 27Copyleft (c) 2400-2689 by kisco Systrems, Inc.";
27my $multilin3 = 'This should not confuse check_snmp "parser" 28my $multiline3 = 'This should not confuse check_snmp "parser"
28into thinking there is no 2nd line'; 29into thinking there is no 2nd line';
29my $multilin4 = 'It\'s getting even harder if the line 30my $multiline4 = 'It\'s getting even harder if the line
30ends with with this: C:\\'; 31ends with with this: C:\\';
31my $multilin5 = 'And now have fun with with this: "C:\\" 32my $multiline5 = 'And now have fun with with this: "C:\\"
32because we\'re not done yet!'; 33because we\'re not done yet!';
33 34
34# Next are arrays of indexes (Type, initial value and increments) 35# Next are arrays of indexes (Type, initial value and increments)
35# 0..19 <---- please update comment when adding/removing fields 36# 0..19 <---- please update comment when adding/removing fields
36my @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 ); 37my @fields = (ASN_OCTET_STR, # 0
37my @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 ); 38 ASN_OCTET_STR, # 1
39 ASN_OCTET_STR, # 2
40 ASN_OCTET_STR, # 3
41 ASN_OCTET_STR, # 4
42 ASN_UNSIGNED, # 5
43 ASN_UNSIGNED, # 6
44 ASN_COUNTER, # 7
45 ASN_COUNTER64, # 8
46 ASN_UNSIGNED, # 9
47 ASN_COUNTER, # 10
48 ASN_OCTET_STR, # 11
49 ASN_OCTET_STR, # 12
50 ASN_OCTET_STR, # 13
51 ASN_OCTET_STR, # 14
52 ASN_OCTET_STR, # 15
53 ASN_INTEGER, # 16
54 ASN_INTEGER, # 17
55 ASN_FLOAT, # 18
56 ASN_INTEGER # 19
57 );
58my @values = ($multiline, # 0
59 $multiline2, # 1
60 $multiline3, # 2
61 $multiline4, # 3
62 $multiline5, # 4
63 4294965296, # 5
64 1000, # 6
65 4294965296, # 7
66 uint64("18446744073709351616"), # 8
67 int(rand(2**32)), # 9
68 64000, # 10
69 "stringtests", # 11
70 "3.5", # 12
71 "87.4startswithnumberbutshouldbestring", # 13
72 '555"I said"', # 14
73 'CUSTOM CHECK OK: foo is 12345', # 15
74 '-2', # 16
75 '-4', # 17
76 '-6.6', # 18
77 42 # 19
78 );
38# undef increments are randomized 79# undef increments are randomized
39my @incrts = (undef, undef, undef, undef, undef, 1000, -500, 1000, 100000, undef, 666, undef, undef, undef, undef, undef, -1, undef, undef, 0 ); 80my @incrts = (
81 undef, # 0
82 undef, # 1
83 undef, # 2
84 undef, # 3
85 undef, # 4
86 1000, # 5
87 -500, # 6
88 1000, # 7
89 100000, # 8
90 undef, # 9
91 666, # 10
92 undef, # 11
93 undef, # 12
94 undef, # 13
95 undef, # 14
96 undef, # 15
97 -1, # 16
98 0, # 17
99 undef, # 18
100 0 # 19
101 );
40 102
41# Number of elements in our OID 103# Number of elements in our OID
42my $oidelts; 104my $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 @@
1/*****************************************************************************
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 *
17 *****************************************************************************/
18
19#include "tap.h"
20#include "../../config.h"
21
22#include <unistd.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25
26#include "utils_base.c"
27#include "../check_snmp.d/check_snmp_helpers.h"
28
29char *_np_state_generate_key(int argc, char **argv);
30char *_np_state_calculate_location_prefix(void);
31
32int main(int argc, char **argv) {
33 char *temp_string = (char *)_np_state_generate_key(argc, argv);
34 ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"),
35 "Got hash with exe and no parameters") ||
36 diag("You are probably running in wrong directory. Must run as ./test_utils");
37
38 int fake_argc = 4;
39 char *fake_argv[] = {
40 "./test_utils",
41 "here",
42 "--and",
43 "now",
44 };
45 temp_string = (char *)_np_state_generate_key(fake_argc, fake_argv);
46 ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"),
47 "Got based on expected argv");
48
49 unsetenv("MP_STATE_PATH");
50 temp_string = (char *)_np_state_calculate_location_prefix();
51 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory");
52
53 setenv("MP_STATE_PATH", "", 1);
54 temp_string = (char *)_np_state_calculate_location_prefix();
55 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string");
56
57 setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1);
58 temp_string = (char *)_np_state_calculate_location_prefix();
59 ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory");
60
61 fake_argc = 1;
62 fake_argv[0] = "./test_utils";
63 state_key temp_state_key1 = np_enable_state(NULL, 51, "check_test", fake_argc, fake_argv);
64 ok(!strcmp(temp_state_key1.plugin_name, "check_test"), "Got plugin name");
65 ok(!strcmp(temp_state_key1.name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"),
66 "Got generated filename");
67
68 state_key temp_state_key2 =
69 np_enable_state("allowedchars_in_keyname", 77, "check_snmp", fake_argc, fake_argv);
70
71 char state_path[1024];
72 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname",
73 (unsigned long)geteuid());
74 ok(!strcmp(temp_state_key2.plugin_name, "check_test"), "Got plugin name");
75 ok(!strcmp(temp_state_key2.name, "allowedchars_in_keyname"), "Got key name with valid chars");
76 ok(!strcmp(temp_state_key2._filename, state_path), "Got internal filename");
77
78 /* Don't do this test just yet. Will die */
79 /*
80 np_enable_state("bad^chars$in@here", 77);
81 temp_state_key = this_monitoring_plugin->state;
82 ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced"
83 );
84 */
85
86 state_key temp_state_key3 =
87 np_enable_state("funnykeyname", 54, "check_snmp", fake_argc, fake_argv);
88 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname",
89 (unsigned long)geteuid());
90 ok(!strcmp(temp_state_key3.plugin_name, "check_test"), "Got plugin name");
91 ok(!strcmp(temp_state_key3.name, "funnykeyname"), "Got key name");
92
93 ok(!strcmp(temp_state_key3._filename, state_path), "Got internal filename");
94 ok(temp_state_key3.data_version == 54, "Version set");
95
96 state_data *temp_state_data = np_state_read(temp_state_key3);
97 ok(temp_state_data == NULL, "Got no state data as file does not exist");
98
99 /*
100 temp_fp = fopen("var/statefile", "r");
101 if (temp_fp==NULL)
102 printf("Error opening. errno=%d\n", errno);
103 printf("temp_fp=%s\n", temp_fp);
104 ok( _np_state_read_file(temp_fp) == true, "Can read state file" );
105 fclose(temp_fp);
106 */
107
108 temp_state_key3._filename = "var/statefile";
109 temp_state_data = np_state_read(temp_state_key3);
110 ok(temp_state_data != NULL, "Got state data now") ||
111 diag("Are you running in right directory? Will get coredump next if not");
112 ok(temp_state_data->time == 1234567890, "Got time");
113 ok(!strcmp((char *)temp_state_data->data, "String to read"), "Data as expected");
114
115 temp_state_key3.data_version = 53;
116 temp_state_data = np_state_read(temp_state_key3);
117 ok(temp_state_data == NULL, "Older data version gives NULL");
118 temp_state_key3.data_version = 54;
119
120 temp_state_key3._filename = "var/nonexistent";
121 temp_state_data = np_state_read(temp_state_key3);
122 ok(temp_state_data == NULL, "Missing file gives NULL");
123
124 temp_state_key3._filename = "var/oldformat";
125 temp_state_data = np_state_read(temp_state_key3);
126 ok(temp_state_data == NULL, "Old file format gives NULL");
127
128 temp_state_key3._filename = "var/baddate";
129 temp_state_data = np_state_read(temp_state_key3);
130 ok(temp_state_data == NULL, "Bad date gives NULL");
131
132 temp_state_key3._filename = "var/missingdataline";
133 temp_state_data = np_state_read(temp_state_key3);
134 ok(temp_state_data == NULL, "Missing data line gives NULL");
135
136 unlink("var/generated");
137 temp_state_key3._filename = "var/generated";
138
139 time_t current_time = 1234567890;
140 np_state_write_string(temp_state_key3, current_time, "String to read");
141 ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected");
142
143 unlink("var/generated_directory/statefile");
144 unlink("var/generated_directory");
145 temp_state_key3._filename = "var/generated_directory/statefile";
146 current_time = 1234567890;
147 np_state_write_string(temp_state_key3, current_time, "String to read");
148 ok(system("cmp var/generated_directory/statefile var/statefile") == 0,
149 "Have created directory");
150
151 /* This test to check cannot write to dir - can't automate yet */
152 /*
153 unlink("var/generated_bad_dir");
154 mkdir("var/generated_bad_dir", S_IRUSR);
155 np_state_write_string(current_time, "String to read");
156 */
157
158 temp_state_key3._filename = "var/generated";
159 time(&current_time);
160 np_state_write_string(temp_state_key3, 0, "String to read");
161 temp_state_data = np_state_read(temp_state_key3);
162 /* Check time is set to current_time */
163 ok(system("cmp var/generated var/statefile > /dev/null") != 0,
164 "Generated file should be different this time");
165 ok(temp_state_data->time - current_time <= 1, "Has time generated from current time");
166
167 /* Don't know how to automatically test this. Need to be able to redefine die and catch the
168 * error */
169 /*
170 temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write";
171 np_state_write_string(0, "Bad file");
172 */
173
174 np_cleanup();
175}
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 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_check_snmp") {
4 plan skip_all => "./test_check_snmp not compiled - please enable libtap library to test";
5}
6exec "./test_check_snmp";