summaryrefslogtreecommitdiffstats
path: root/lib
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 /lib
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
Diffstat (limited to 'lib')
-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
4 files changed, 24 insertions, 534 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);