summaryrefslogtreecommitdiffstats
path: root/lib/utils_base.c
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/utils_base.c
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/utils_base.c')
-rw-r--r--lib/utils_base.c344
1 files changed, 0 insertions, 344 deletions
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}