mirror of
https://github.com/monitoring-plugins/monitoring-plugins.git
synced 2026-02-03 18:49:29 -05:00
649 lines
14 KiB
C
649 lines
14 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;
|
|
|
|
if (strchr(pd.label, '\'') == NULL) {
|
|
asprintf(&result, "'%s'=", pd.label);
|
|
} else {
|
|
// we have a illegal single quote in the string
|
|
// replace it silently instead of complaining
|
|
for (char *ptr = pd.label; *ptr == '\0'; ptr++) {
|
|
if (*ptr == '\'') {
|
|
*ptr = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
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_char(mp_perfdata pd, char value) {
|
|
return mp_set_pd_value_long_long(pd, (long long)value);
|
|
}
|
|
|
|
mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) {
|
|
return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
|
|
}
|
|
|
|
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_char(char value) {
|
|
return mp_create_pd_value_long_long((long long)value);
|
|
}
|
|
|
|
mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) {
|
|
return mp_create_pd_value_u_long_long((unsigned long long)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);
|
|
if (working_copy == NULL) {
|
|
// strdup error, probably
|
|
mp_range_parsed result = {
|
|
.error = MP_RANGE_PARSING_FAILURE,
|
|
};
|
|
return result;
|
|
}
|
|
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;
|
|
}
|
|
|
|
mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value) {
|
|
perfdata.max = value;
|
|
perfdata.max_present = true;
|
|
return perfdata;
|
|
}
|
|
|
|
mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value) {
|
|
perfdata.min = value;
|
|
perfdata.min_present = true;
|
|
return perfdata;
|
|
}
|
|
|
|
double mp_get_pd_value(mp_perfdata_value value) {
|
|
assert(value.type != PD_TYPE_NONE);
|
|
switch (value.type) {
|
|
case PD_TYPE_DOUBLE:
|
|
return value.pd_double;
|
|
case PD_TYPE_INT:
|
|
return (double)value.pd_int;
|
|
case PD_TYPE_UINT:
|
|
return (double)value.pd_uint;
|
|
default:
|
|
return 0; // just to make the compiler happy
|
|
}
|
|
}
|
|
|
|
mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right) {
|
|
if (left.type == right.type) {
|
|
switch (left.type) {
|
|
case PD_TYPE_DOUBLE:
|
|
left.pd_double *= right.pd_double;
|
|
return left;
|
|
case PD_TYPE_INT:
|
|
left.pd_int *= right.pd_int;
|
|
return left;
|
|
case PD_TYPE_UINT:
|
|
left.pd_uint *= right.pd_uint;
|
|
return left;
|
|
default:
|
|
// what to here?
|
|
return left;
|
|
}
|
|
}
|
|
|
|
// Different types, oh boy, just do the lazy thing for now and switch to double
|
|
switch (left.type) {
|
|
case PD_TYPE_INT:
|
|
left.pd_double = (double)left.pd_int;
|
|
left.type = PD_TYPE_DOUBLE;
|
|
break;
|
|
case PD_TYPE_UINT:
|
|
left.pd_double = (double)left.pd_uint;
|
|
left.type = PD_TYPE_DOUBLE;
|
|
break;
|
|
}
|
|
|
|
switch (right.type) {
|
|
case PD_TYPE_INT:
|
|
right.pd_double = (double)right.pd_int;
|
|
right.type = PD_TYPE_DOUBLE;
|
|
break;
|
|
case PD_TYPE_UINT:
|
|
right.pd_double = (double)right.pd_uint;
|
|
right.type = PD_TYPE_DOUBLE;
|
|
break;
|
|
}
|
|
|
|
left.pd_double *= right.pd_double;
|
|
return left;
|
|
}
|
|
|
|
mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor) {
|
|
if (!range.end_infinity) {
|
|
range.end = mp_pd_value_multiply(range.end, factor);
|
|
}
|
|
if (!range.start_infinity) {
|
|
range.start = mp_pd_value_multiply(range.start, factor);
|
|
}
|
|
return range;
|
|
}
|