mirror of
https://github.com/monitoring-plugins/monitoring-plugins.git
synced 2026-02-03 18:49:29 -05:00
516 lines
12 KiB
C
516 lines
12 KiB
C
#include "./perfdata.h"
|
|
#include "../plugins/common.h"
|
|
#include "../plugins/utils.h"
|
|
#include "utils_base.h"
|
|
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
|
|
char *pd_value_to_string(const mp_perfdata_value pd) {
|
|
char *result = NULL;
|
|
|
|
assert(pd.type != PD_TYPE_NONE);
|
|
|
|
switch (pd.type) {
|
|
case PD_TYPE_INT:
|
|
asprintf(&result, "%lli", pd.pd_int);
|
|
break;
|
|
case PD_TYPE_UINT:
|
|
asprintf(&result, "%llu", pd.pd_int);
|
|
break;
|
|
case PD_TYPE_DOUBLE:
|
|
asprintf(&result, "%f", pd.pd_double);
|
|
break;
|
|
default:
|
|
// die here
|
|
die(STATE_UNKNOWN, "Invalid mp_perfdata mode\n");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
char *pd_to_string(mp_perfdata pd) {
|
|
assert(pd.label != NULL);
|
|
char *result = NULL;
|
|
asprintf(&result, "%s=", pd.label);
|
|
|
|
asprintf(&result, "%s%s", result, pd_value_to_string(pd.value));
|
|
|
|
if (pd.uom != NULL) {
|
|
asprintf(&result, "%s%s", result, pd.uom);
|
|
}
|
|
|
|
if (pd.warn_present) {
|
|
asprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn));
|
|
} else {
|
|
asprintf(&result, "%s;", result);
|
|
}
|
|
|
|
if (pd.crit_present) {
|
|
asprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit));
|
|
} else {
|
|
asprintf(&result, "%s;", result);
|
|
}
|
|
if (pd.min_present) {
|
|
asprintf(&result, "%s;%s", result, pd_value_to_string(pd.min));
|
|
} else {
|
|
asprintf(&result, "%s;", result);
|
|
}
|
|
|
|
if (pd.max_present) {
|
|
asprintf(&result, "%s;%s", result, pd_value_to_string(pd.max));
|
|
}
|
|
|
|
/*printf("pd_to_string: %s\n", result); */
|
|
|
|
return result;
|
|
}
|
|
|
|
char *pd_list_to_string(const pd_list pd) {
|
|
char *result = pd_to_string(pd.data);
|
|
|
|
for (pd_list *elem = pd.next; elem != NULL; elem = elem->next) {
|
|
asprintf(&result, "%s %s", result, pd_to_string(elem->data));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
mp_perfdata perfdata_init() {
|
|
mp_perfdata pd = {};
|
|
return pd;
|
|
}
|
|
|
|
pd_list *pd_list_init() {
|
|
pd_list *tmp = (pd_list *)calloc(1, sizeof(pd_list));
|
|
if (tmp == NULL) {
|
|
die(STATE_UNKNOWN, "calloc failed\n");
|
|
}
|
|
tmp->next = NULL;
|
|
return tmp;
|
|
}
|
|
|
|
mp_range mp_range_init() {
|
|
mp_range result = {
|
|
.alert_on_inside_range = OUTSIDE,
|
|
.start = {},
|
|
.start_infinity = true,
|
|
.end = {},
|
|
.end_infinity = true,
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
mp_range mp_range_set_start(mp_range input, mp_perfdata_value perf_val) {
|
|
input.start = perf_val;
|
|
input.start_infinity = false;
|
|
return input;
|
|
}
|
|
|
|
mp_range mp_range_set_end(mp_range input, mp_perfdata_value perf_val) {
|
|
input.end = perf_val;
|
|
input.end_infinity = false;
|
|
return input;
|
|
}
|
|
|
|
void pd_list_append(pd_list pdl[1], const mp_perfdata pd) {
|
|
assert(pdl != NULL);
|
|
|
|
if (pdl->data.value.type == PD_TYPE_NONE) {
|
|
// first entry is still empty
|
|
pdl->data = pd;
|
|
} else {
|
|
// find last element in the list
|
|
pd_list *curr = pdl;
|
|
pd_list *next = pdl->next;
|
|
|
|
while (next != NULL) {
|
|
curr = next;
|
|
next = next->next;
|
|
}
|
|
|
|
if (curr->data.value.type == PD_TYPE_NONE) {
|
|
// still empty
|
|
curr->data = pd;
|
|
} else {
|
|
// new a new one
|
|
curr->next = pd_list_init();
|
|
curr->next->data = pd;
|
|
}
|
|
}
|
|
}
|
|
|
|
void pd_list_free(pd_list pdl[1]) {
|
|
while (pdl != NULL) {
|
|
pd_list *old = pdl;
|
|
pdl = pdl->next;
|
|
free(old);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* returns -1 if a < b, 0 if a == b, 1 if a > b
|
|
*/
|
|
int cmp_perfdata_value(const mp_perfdata_value a, const mp_perfdata_value b) {
|
|
// Test if types are different
|
|
if (a.type == b.type) {
|
|
|
|
switch (a.type) {
|
|
case PD_TYPE_UINT:
|
|
if (a.pd_uint < b.pd_uint) {
|
|
return -1;
|
|
} else if (a.pd_uint == b.pd_uint) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
case PD_TYPE_INT:
|
|
if (a.pd_int < b.pd_int) {
|
|
return -1;
|
|
} else if (a.pd_int == b.pd_int) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
case PD_TYPE_DOUBLE:
|
|
if (a.pd_int < b.pd_int) {
|
|
return -1;
|
|
} else if (a.pd_int == b.pd_int) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
default:
|
|
die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
|
|
}
|
|
}
|
|
|
|
// Get dirty here
|
|
long double floating_a = 0;
|
|
|
|
switch (a.type) {
|
|
case PD_TYPE_UINT:
|
|
floating_a = a.pd_uint;
|
|
break;
|
|
case PD_TYPE_INT:
|
|
floating_a = a.pd_int;
|
|
break;
|
|
case PD_TYPE_DOUBLE:
|
|
floating_a = a.pd_double;
|
|
break;
|
|
default:
|
|
die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
|
|
}
|
|
|
|
long double floating_b = 0;
|
|
switch (b.type) {
|
|
case PD_TYPE_UINT:
|
|
floating_b = b.pd_uint;
|
|
break;
|
|
case PD_TYPE_INT:
|
|
floating_b = b.pd_int;
|
|
break;
|
|
case PD_TYPE_DOUBLE:
|
|
floating_b = b.pd_double;
|
|
break;
|
|
default:
|
|
die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
|
|
}
|
|
|
|
if (floating_a < floating_b) {
|
|
return -1;
|
|
}
|
|
if (floating_a == floating_b) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
char *mp_range_to_string(const mp_range input) {
|
|
char *result = "";
|
|
if (input.alert_on_inside_range == INSIDE) {
|
|
asprintf(&result, "@");
|
|
}
|
|
|
|
if (input.start_infinity) {
|
|
asprintf(&result, "%s~:", result);
|
|
} else {
|
|
asprintf(&result, "%s%s:", result, pd_value_to_string(input.start));
|
|
}
|
|
|
|
if (!input.end_infinity) {
|
|
asprintf(&result, "%s%s", result, pd_value_to_string(input.end));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { return mp_set_pd_value_double(pd, value); }
|
|
|
|
mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
|
|
pd.value.pd_double = value;
|
|
pd.value.type = PD_TYPE_DOUBLE;
|
|
return pd;
|
|
}
|
|
|
|
mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); }
|
|
|
|
mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); }
|
|
|
|
mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { return mp_set_pd_value_long_long(pd, (long long)value); }
|
|
|
|
mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) {
|
|
return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
|
|
}
|
|
|
|
mp_perfdata mp_set_pd_value_long_long(mp_perfdata pd, long long value) {
|
|
pd.value.pd_int = value;
|
|
pd.value.type = PD_TYPE_INT;
|
|
return pd;
|
|
}
|
|
|
|
mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata pd, unsigned long long value) {
|
|
pd.value.pd_uint = value;
|
|
pd.value.type = PD_TYPE_UINT;
|
|
return pd;
|
|
}
|
|
|
|
mp_perfdata_value mp_create_pd_value_double(double value) {
|
|
mp_perfdata_value res = {0};
|
|
res.type = PD_TYPE_DOUBLE;
|
|
res.pd_double = value;
|
|
return res;
|
|
}
|
|
|
|
mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); }
|
|
|
|
mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); }
|
|
|
|
mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
|
|
|
|
mp_perfdata_value mp_create_pd_value_long(long value) { return mp_create_pd_value_long_long((long long)value); }
|
|
|
|
mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
|
|
|
|
mp_perfdata_value mp_create_pd_value_long_long(long long value) {
|
|
mp_perfdata_value res = {0};
|
|
res.type = PD_TYPE_INT;
|
|
res.pd_int = value;
|
|
return res;
|
|
}
|
|
|
|
mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long value) {
|
|
mp_perfdata_value res = {0};
|
|
res.type = PD_TYPE_UINT;
|
|
res.pd_uint = value;
|
|
return res;
|
|
}
|
|
|
|
char *fmt_range(range foo) { return foo.text; }
|
|
|
|
typedef struct integer_parser_wrapper {
|
|
int error;
|
|
mp_perfdata_value value;
|
|
} integer_parser_wrapper;
|
|
|
|
typedef struct double_parser_wrapper {
|
|
int error;
|
|
mp_perfdata_value value;
|
|
} double_parser_wrapper;
|
|
|
|
typedef struct perfdata_value_parser_wrapper {
|
|
int error;
|
|
mp_perfdata_value value;
|
|
} perfdata_value_parser_wrapper;
|
|
|
|
double_parser_wrapper parse_double(const char *input);
|
|
integer_parser_wrapper parse_integer(const char *input);
|
|
perfdata_value_parser_wrapper parse_pd_value(const char *input);
|
|
|
|
mp_range_parsed mp_parse_range_string(const char *input) {
|
|
if (input == NULL) {
|
|
mp_range_parsed result = {
|
|
.error = MP_RANGE_PARSING_FAILURE,
|
|
};
|
|
return result;
|
|
}
|
|
|
|
if (strlen(input) == 0) {
|
|
mp_range_parsed result = {
|
|
.error = MP_RANGE_PARSING_FAILURE,
|
|
};
|
|
return result;
|
|
}
|
|
|
|
mp_range_parsed result = {
|
|
.range = mp_range_init(),
|
|
.error = MP_PARSING_SUCCES,
|
|
};
|
|
|
|
if (input[0] == '@') {
|
|
// found an '@' at beginning, so invert the range logic
|
|
result.range.alert_on_inside_range = INSIDE;
|
|
|
|
// advance the pointer one symbol
|
|
input++;
|
|
}
|
|
|
|
char *working_copy = strdup(input);
|
|
input = working_copy;
|
|
|
|
char *separator = index(working_copy, ':');
|
|
if (separator != NULL) {
|
|
// Found a separator
|
|
// set the separator to 0, so we have two different strings
|
|
*separator = '\0';
|
|
|
|
if (input[0] == '~') {
|
|
// the beginning starts with '~', so it might be infinity
|
|
if (&input[1] != separator) {
|
|
// the next symbol after '~' is not the separator!
|
|
// so input is probably wrong
|
|
result.error = MP_RANGE_PARSING_FAILURE;
|
|
free(working_copy);
|
|
return result;
|
|
}
|
|
|
|
result.range.start_infinity = true;
|
|
} else {
|
|
// No '~' at the beginning, so this should be a number
|
|
result.range.start_infinity = false;
|
|
perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
|
|
|
|
if (parsed_pd.error != MP_PARSING_SUCCES) {
|
|
result.error = parsed_pd.error;
|
|
free(working_copy);
|
|
return result;
|
|
}
|
|
|
|
result.range.start = parsed_pd.value;
|
|
result.range.start_infinity = false;
|
|
}
|
|
// got the first part now
|
|
// advance the pointer
|
|
input = separator + 1;
|
|
}
|
|
|
|
// End part or no separator
|
|
if (input[0] == '\0') {
|
|
// the end is infinite
|
|
result.range.end_infinity = true;
|
|
} else {
|
|
perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
|
|
|
|
if (parsed_pd.error != MP_PARSING_SUCCES) {
|
|
result.error = parsed_pd.error;
|
|
return result;
|
|
}
|
|
result.range.end = parsed_pd.value;
|
|
result.range.end_infinity = false;
|
|
}
|
|
free(working_copy);
|
|
return result;
|
|
}
|
|
|
|
double_parser_wrapper parse_double(const char *input) {
|
|
double_parser_wrapper result = {
|
|
.error = MP_PARSING_SUCCES,
|
|
};
|
|
|
|
if (input == NULL) {
|
|
result.error = MP_PARSING_FAILURE;
|
|
return result;
|
|
}
|
|
|
|
char *endptr = NULL;
|
|
errno = 0;
|
|
double tmp = strtod(input, &endptr);
|
|
|
|
if (input == endptr) {
|
|
// man 3 strtod says, no conversion performed
|
|
result.error = MP_PARSING_FAILURE;
|
|
return result;
|
|
}
|
|
|
|
if (errno) {
|
|
// some other error
|
|
// TODO maybe differentiate a little bit
|
|
result.error = MP_PARSING_FAILURE;
|
|
return result;
|
|
}
|
|
|
|
result.value = mp_create_pd_value(tmp);
|
|
return result;
|
|
}
|
|
|
|
integer_parser_wrapper parse_integer(const char *input) {
|
|
integer_parser_wrapper result = {
|
|
.error = MP_PARSING_SUCCES,
|
|
};
|
|
|
|
if (input == NULL) {
|
|
result.error = MP_PARSING_FAILURE;
|
|
return result;
|
|
}
|
|
|
|
char *endptr = NULL;
|
|
errno = 0;
|
|
long long tmp = strtoll(input, &endptr, 0);
|
|
|
|
// validating *sigh*
|
|
if (*endptr != '\0') {
|
|
// something went wrong in strtoll
|
|
if (tmp == LLONG_MIN) {
|
|
// underflow
|
|
result.error = MP_RANGE_PARSING_UNDERFLOW;
|
|
return result;
|
|
}
|
|
|
|
if (tmp == LLONG_MAX) {
|
|
// overflow
|
|
result.error = MP_RANGE_PARSING_OVERFLOW;
|
|
return result;
|
|
}
|
|
|
|
// still wrong, but not sure why, probably invalid characters
|
|
if (errno == EINVAL) {
|
|
result.error = MP_RANGE_PARSING_INVALID_CHAR;
|
|
return result;
|
|
}
|
|
|
|
// some other error, do catch all here
|
|
result.error = MP_RANGE_PARSING_FAILURE;
|
|
return result;
|
|
}
|
|
|
|
// no error, should be fine
|
|
result.value = mp_create_pd_value(tmp);
|
|
return result;
|
|
}
|
|
|
|
perfdata_value_parser_wrapper parse_pd_value(const char *input) {
|
|
// try integer first
|
|
integer_parser_wrapper tmp_int = parse_integer(input);
|
|
|
|
if (tmp_int.error == MP_PARSING_SUCCES) {
|
|
perfdata_value_parser_wrapper result = {
|
|
.error = tmp_int.error,
|
|
.value = tmp_int.value,
|
|
};
|
|
return result;
|
|
}
|
|
|
|
double_parser_wrapper tmp_double = parse_double(input);
|
|
perfdata_value_parser_wrapper result = {};
|
|
if (tmp_double.error == MP_PARSING_SUCCES) {
|
|
result.error = tmp_double.error;
|
|
result.value = tmp_double.value;
|
|
} else {
|
|
result.error = tmp_double.error;
|
|
}
|
|
return result;
|
|
}
|