Merge pull request #2064 from RincewindsHat/feature/new_output_infra

Feature/new output infra
This commit is contained in:
Lorenz Kästle 2025-02-21 14:33:24 +01:00 committed by GitHub
commit 75658bd04d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 5602 additions and 197 deletions

View file

@ -61,7 +61,8 @@ apt-get -y install perl \
libmariadb-dev \
cron \
iputils-ping \
iproute2
iproute2 \
libjson-perl
# remove ipv6 interface from hosts
sed '/^::1/d' /etc/hosts > /tmp/hosts

2
.gitignore vendored
View file

@ -103,6 +103,8 @@ NP-VERSION-FILE
/lib/libmonitoringplug.a
/lib/Makefile
/lib/Makefile.in
/lib/vendor/cJSON/.deps
/lib/vendor/cJSON/.dirstamp
# /lib/tests/
/lib/tests/base64.Po

View file

@ -7,8 +7,21 @@ noinst_LIBRARIES = libmonitoringplug.a
AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
-I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c
EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h utils_cmd.h parse_ini.h extra_opts.h maxfd.h
libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c
EXTRA_DIST = utils_base.h \
utils_disk.h \
utils_tcp.h \
utils_cmd.h \
parse_ini.h \
extra_opts.h \
maxfd.h \
perfdata.h \
output.h \
thresholds.h \
states.h \
vendor/cJSON/cJSON.h \
monitoringplug.h
if USE_PARSE_INI
libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c
@ -16,4 +29,3 @@ endif USE_PARSE_INI
test test-debug:
cd tests && make $@

7
lib/monitoringplug.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include "./states.h"
#include "./utils_base.h"
#include "./thresholds.h"
#include "./maxfd.h"
#include "./output.h"

535
lib/output.c Normal file
View file

@ -0,0 +1,535 @@
#include "./output.h"
#include "./utils_base.h"
#include "../plugins/utils.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
// #include <cjson/cJSON.h>
#include "./vendor/cJSON/cJSON.h"
#include "perfdata.h"
#include "states.h"
// == Prototypes ==
static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation);
static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
// == Implementation ==
/*
* Generate output string for a mp_subcheck object
*/
static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
char *result = strdup("");
int added = 0;
if (check.perfdata != NULL) {
added = asprintf(&result, "%s", pd_list_to_string(*check.perfdata));
}
if (check.subchecks == NULL) {
// No subchecks, return here
return result;
}
mp_subcheck_list *subchecks = check.subchecks;
while (subchecks != NULL) {
if (added > 0) {
added = asprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck));
} else {
// TODO free previous result here?
added = asprintf(&result, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
}
subchecks = subchecks->next;
}
return result;
}
/*
* Initialiser for a mp_check object. Always use this to get a new one!
* It sets useful defaults
*/
mp_check mp_check_init(void) {
mp_check check = {0};
check.format = MP_FORMAT_DEFAULT;
return check;
}
/*
* Initialiser for a mp_subcheck object. Always use this to get a new one!
* It sets useful defaults
*/
mp_subcheck mp_subcheck_init(void) {
mp_subcheck tmp = {0};
tmp.default_state = STATE_UNKNOWN; // Default state is unknown
tmp.state_set_explicitly = false;
return tmp;
}
/*
* Add a subcheck to a (the one and only) check object
*/
int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck subcheck) {
assert(subcheck.output != NULL); // There must be output in a subcheck
mp_subcheck_list *tmp = NULL;
if (check->subchecks == NULL) {
check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
if (check->subchecks == NULL) {
die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
}
check->subchecks->subcheck = subcheck;
check->subchecks->next = NULL;
} else {
// Search for the end
tmp = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
if (tmp == NULL) {
die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
}
tmp->subcheck = subcheck;
tmp->next = check->subchecks;
check->subchecks = tmp;
}
return 0;
}
/*
* Add a mp_perfdata data point to a mp_subcheck object
*/
void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata perfData) {
if (check->perfdata == NULL) {
check->perfdata = pd_list_init();
}
pd_list_append(check->perfdata, perfData);
}
/*
* Add a mp_subcheck object to another one. The seconde mp_subcheck (argument) is the lower in the
* hierarchy
*/
int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
if (subcheck.output == NULL) {
die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL");
}
mp_subcheck_list *tmp = NULL;
if (check->subchecks == NULL) {
check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
if (check->subchecks == NULL) {
die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
}
tmp = check->subchecks;
} else {
// Search for the end
tmp = check->subchecks;
while (tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
if (tmp->next == NULL) {
die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
}
tmp = tmp->next;
}
tmp->subcheck = subcheck;
return 0;
}
/*
* Add a manual summary to a mp_check object, effectively replacing
* the autogenerated one
*/
void mp_add_summary(mp_check check[static 1], char *summary) { check->summary = summary; }
/*
* Generate the summary string of a mp_check object based on it's subchecks
*/
char *get_subcheck_summary(mp_check check) {
mp_subcheck_list *subchecks = check.subchecks;
unsigned int ok = 0;
unsigned int warning = 0;
unsigned int critical = 0;
unsigned int unknown = 0;
while (subchecks != NULL) {
switch (subchecks->subcheck.state) {
case STATE_OK:
ok++;
break;
case STATE_WARNING:
warning++;
break;
case STATE_CRITICAL:
critical++;
break;
case STATE_UNKNOWN:
unknown++;
break;
default:
die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary");
}
subchecks = subchecks->next;
}
char *result = NULL;
asprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown);
return result;
}
/*
* Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states
*/
mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) {
if (check.state_set_explicitly) {
return check.state;
}
mp_subcheck_list *scl = check.subchecks;
mp_state_enum result = check.default_state;
while (scl != NULL) {
result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
scl = scl->next;
}
return result;
}
/*
* Generate the result state of a mp_check object based on it's own state and it's subchecks states
*/
mp_state_enum mp_compute_check_state(const mp_check check) {
assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
mp_subcheck_list *scl = check.subchecks;
mp_state_enum result = STATE_OK;
while (scl != NULL) {
result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
scl = scl->next;
}
return result;
}
/*
* Generate output string for a mp_check object
* Non static to be available for testing functions
*/
char *mp_fmt_output(mp_check check) {
char *result = NULL;
switch (check.format) {
case MP_FORMAT_MULTI_LINE: {
if (check.summary == NULL) {
check.summary = get_subcheck_summary(check);
}
asprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary);
mp_subcheck_list *subchecks = check.subchecks;
while (subchecks != NULL) {
asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1));
subchecks = subchecks->next;
}
char *pd_string = NULL;
subchecks = check.subchecks;
while (subchecks != NULL) {
if (pd_string == NULL) {
asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
} else {
asprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck));
}
subchecks = subchecks->next;
}
if (pd_string != NULL && strlen(pd_string) > 0) {
asprintf(&result, "%s|%s", result, pd_string);
}
break;
}
case MP_FORMAT_TEST_JSON: {
cJSON *resultObject = cJSON_CreateObject();
if (resultObject == NULL) {
die(STATE_UNKNOWN, "cJSON_CreateObject failed");
}
cJSON *resultState = cJSON_CreateString(state_text(mp_compute_check_state(check)));
cJSON_AddItemToObject(resultObject, "state", resultState);
if (check.summary == NULL) {
check.summary = get_subcheck_summary(check);
}
cJSON *summary = cJSON_CreateString(check.summary);
cJSON_AddItemToObject(resultObject, "summary", summary);
if (check.subchecks != NULL) {
cJSON *subchecks = cJSON_CreateArray();
mp_subcheck_list *sc = check.subchecks;
while (sc != NULL) {
cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
cJSON_AddItemToArray(subchecks, sc_json);
sc = sc->next;
}
cJSON_AddItemToObject(resultObject, "checks", subchecks);
}
result = cJSON_PrintUnformatted(resultObject);
break;
}
default:
die(STATE_UNKNOWN, "Invalid format");
}
return result;
}
/*
* Helper function to properly indent the output lines when using multiline
* formats
*/
static char *generate_indentation_string(unsigned int indentation) {
char *result = calloc(indentation + 1, sizeof(char));
for (unsigned int i = 0; i < indentation; i++) {
result[i] = '\t';
}
return result;
}
/*
* Helper function to generate the output string of mp_subcheck
*/
static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) {
char *result = NULL;
mp_subcheck_list *subchecks = NULL;
switch (output_format) {
case MP_FORMAT_MULTI_LINE:
asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)),
check.output);
subchecks = check.subchecks;
while (subchecks != NULL) {
asprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
subchecks = subchecks->next;
}
return result;
default:
die(STATE_UNKNOWN, "Invalid format");
}
}
static inline cJSON *json_serialise_pd_value(mp_perfdata_value value) {
cJSON *result = cJSON_CreateObject();
switch (value.type) {
case PD_TYPE_DOUBLE:
cJSON_AddStringToObject(result, "type", "double");
break;
case PD_TYPE_INT:
cJSON_AddStringToObject(result, "type", "int");
break;
case PD_TYPE_UINT:
cJSON_AddStringToObject(result, "type", "uint");
break;
case PD_TYPE_NONE:
die(STATE_UNKNOWN, "Perfdata type was None in json_serialise_pd_value");
}
cJSON_AddStringToObject(result, "value", pd_value_to_string(value));
return result;
}
static inline cJSON *json_serialise_range(mp_range range) {
cJSON *result = cJSON_CreateObject();
if (range.alert_on_inside_range) {
cJSON_AddBoolToObject(result, "alert_on_inside", true);
} else {
cJSON_AddBoolToObject(result, "alert_on_inside", false);
}
if (range.end_infinity) {
cJSON_AddStringToObject(result, "end", "inf");
} else {
cJSON_AddItemToObject(result, "end", json_serialise_pd_value(range.end));
}
if (range.start_infinity) {
cJSON_AddStringToObject(result, "start", "inf");
} else {
cJSON_AddItemToObject(result, "start", json_serialise_pd_value(range.end));
}
return result;
}
static inline cJSON *json_serialise_pd(mp_perfdata pd_val) {
cJSON *result = cJSON_CreateObject();
// Label
cJSON_AddStringToObject(result, "label", pd_val.label);
// Value
cJSON_AddItemToObject(result, "value", json_serialise_pd_value(pd_val.value));
// Uom
cJSON_AddStringToObject(result, "uom", pd_val.uom);
// Warn/Crit
if (pd_val.warn_present) {
cJSON *warn = json_serialise_range(pd_val.warn);
cJSON_AddItemToObject(result, "warn", warn);
}
if (pd_val.crit_present) {
cJSON *crit = json_serialise_range(pd_val.crit);
cJSON_AddItemToObject(result, "crit", crit);
}
if (pd_val.min_present) {
cJSON_AddItemToObject(result, "min", json_serialise_pd_value(pd_val.min));
}
if (pd_val.max_present) {
cJSON_AddItemToObject(result, "max", json_serialise_pd_value(pd_val.max));
}
return result;
}
static inline cJSON *json_serialise_pd_list(pd_list *list) {
cJSON *result = cJSON_CreateArray();
do {
cJSON *pd_value = json_serialise_pd(list->data);
cJSON_AddItemToArray(result, pd_value);
list = list->next;
} while (list != NULL);
return result;
}
static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) {
cJSON *result = cJSON_CreateObject();
// Human readable output
cJSON *output = cJSON_CreateString(subcheck.output);
cJSON_AddItemToObject(result, "output", output);
// Test state (aka Exit Code)
cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck)));
cJSON_AddItemToObject(result, "state", state);
// Perfdata
if (subcheck.perfdata != NULL) {
cJSON *perfdata = json_serialise_pd_list(subcheck.perfdata);
cJSON_AddItemToObject(result, "perfdata", perfdata);
}
if (subcheck.subchecks != NULL) {
cJSON *subchecks = cJSON_CreateArray();
mp_subcheck_list *sc = subcheck.subchecks;
while (sc != NULL) {
cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
cJSON_AddItemToArray(subchecks, sc_json);
sc = sc->next;
}
cJSON_AddItemToObject(result, "checks", subchecks);
}
return result;
}
/*
* Wrapper function to print the output string of a mp_check object
* Use this in concrete plugins.
*/
void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); }
/*
* Convenience function to print the output string of a mp_check object and exit
* the program with the resulting state.
* Intended to be used to exit a monitoring plugin.
*/
void mp_exit(mp_check check) {
mp_print_output(check);
if (check.format == MP_FORMAT_TEST_JSON) {
exit(0);
}
exit(mp_compute_check_state(check));
}
/*
* Function to set the result state of a mp_subcheck object explicitly.
* This will overwrite the default state AND states derived from it's subchecks
*/
mp_subcheck mp_set_subcheck_state(mp_subcheck check, mp_state_enum state) {
check.state = state;
check.state_set_explicitly = true;
return check;
}
/*
* Function to set the default result state of a mp_subcheck object. This state
* will be used if neither an explicit state is set (see *mp_set_subcheck_state*)
* nor does it include other subchecks
*/
mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state) {
check.default_state = state;
return check;
}
char *mp_output_format_map[] = {
[MP_FORMAT_MULTI_LINE] = "multi-line",
[MP_FORMAT_TEST_JSON] = "mp-test-json",
};
/*
* Function to parse the output from a string
*/
parsed_output_format mp_parse_output_format(char *format_string) {
parsed_output_format result = {
.parsing_success = false,
.output_format = MP_FORMAT_DEFAULT,
};
for (mp_output_format i = 0; i < (sizeof(mp_output_format_map) / sizeof(char *)); i++) {
if (strcasecmp(mp_output_format_map[i], format_string) == 0) {
result.parsing_success = true;
result.output_format = i;
break;
}
}
return result;
}

85
lib/output.h Normal file
View file

@ -0,0 +1,85 @@
#pragma once
#include "../config.h"
#include "./perfdata.h"
#include "./states.h"
/*
* A partial check result
*/
typedef struct {
mp_state_enum state; // OK, Warning, Critical ... set explicitly
mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly
bool state_set_explicitly; // was the state set explicitly (or should it be derived from subchecks)
char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP connection to..")
pd_list *perfdata; // Performance data for this check
struct subcheck_list *subchecks; // subchecks deeper in the hierarchy
} mp_subcheck;
/*
* A list of subchecks, used in subchecks and the main check
*/
typedef struct subcheck_list {
mp_subcheck subcheck;
struct subcheck_list *next;
} mp_subcheck_list;
/*
* Possible output formats
*/
typedef enum output_format {
MP_FORMAT_MULTI_LINE,
MP_FORMAT_TEST_JSON,
} mp_output_format;
#define MP_FORMAT_DEFAULT MP_FORMAT_MULTI_LINE
/*
* The main state object of a plugin. Exists only ONCE per plugin.
* This is the "root" of a tree of singular checks.
* The final result is always derived from the children and the "worst" state
* in the first layer of subchecks
*/
typedef struct {
mp_output_format format; // The output format
char *summary; // Overall summary, if not set a summary will be automatically generated
mp_subcheck_list *subchecks;
} mp_check;
mp_check mp_check_init(void);
mp_subcheck mp_subcheck_init(void);
mp_subcheck mp_set_subcheck_state(mp_subcheck, mp_state_enum);
mp_subcheck mp_set_subcheck_default_state(mp_subcheck, mp_state_enum);
int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck);
int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck);
void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], mp_perfdata);
void mp_add_summary(mp_check check[static 1], char *summary);
mp_state_enum mp_compute_check_state(mp_check);
mp_state_enum mp_compute_subcheck_state(mp_subcheck);
typedef struct {
bool parsing_success;
mp_output_format output_format;
} parsed_output_format;
parsed_output_format mp_parse_output_format(char *format_string);
// TODO free and stuff
// void mp_cleanup_check(mp_check check[static 1]);
char *mp_fmt_output(mp_check);
void mp_print_output(mp_check);
/*
* ==================
* Exit functionality
* ==================
*/
void mp_exit(mp_check) __attribute__((noreturn));

516
lib/perfdata.c Normal file
View file

@ -0,0 +1,516 @@
#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;
}

203
lib/perfdata.h Normal file
View file

@ -0,0 +1,203 @@
#pragma once
#include "../config.h"
#include <inttypes.h>
#include <stdbool.h>
// Enum for the specific type of a perfdata_value
typedef enum pd_value_type {
PD_TYPE_NONE = 0,
PD_TYPE_INT,
PD_TYPE_UINT,
PD_TYPE_DOUBLE
} pd_value_type;
typedef struct {
enum pd_value_type type;
union {
long long pd_int;
unsigned long long pd_uint;
double pd_double;
};
} mp_perfdata_value;
#define MP_OUTSIDE false
#define MP_INSIDE true
/*
* New range type with generic numerical values
*/
typedef struct mp_range_struct {
mp_perfdata_value start;
bool start_infinity; /* false (default) or true */
mp_perfdata_value end;
bool end_infinity;
bool alert_on_inside_range; /* OUTSIDE (default) or INSIDE */
} mp_range;
/*
* Old range type with floating point values
*/
typedef struct range_struct {
double start;
bool start_infinity;
double end;
int end_infinity;
int alert_on; /* OUTSIDE (default) or INSIDE */
char *text; /* original unparsed text input */
} range;
/*
* Perfdata type for storing perfdata output
*/
typedef struct perfdata_struct {
char *label;
char *uom;
mp_perfdata_value value;
bool warn_present;
mp_range warn;
bool crit_present;
mp_range crit;
bool min_present;
mp_perfdata_value min;
bool max_present;
mp_perfdata_value max;
} mp_perfdata;
/*
* List of mp_perfdata values
*/
typedef struct pd_list_struct {
mp_perfdata data;
struct pd_list_struct *next;
} pd_list;
/*
* ============
* Initializers
* ============
*/
/*
* Initialize mp_perfdata value. Always use this to generate a new one
*/
mp_perfdata perfdata_init(void);
/*
* Initialize pd_list value. Always use this to generate a new one
*/
pd_list *pd_list_init(void);
/*
* Initialize a new mp_range value, with unset values (start and end are infinite
*/
mp_range mp_range_init(void);
/*
* Worker functions
*/
mp_range mp_range_set_start(mp_range, mp_perfdata_value);
mp_range mp_range_set_end(mp_range, mp_perfdata_value);
/*
* Parsing a range from a string
*/
typedef enum {
MP_PARSING_SUCCES = 0,
MP_PARSING_FAILURE,
MP_RANGE_PARSING_FAILURE,
MP_RANGE_PARSING_UNDERFLOW,
MP_RANGE_PARSING_OVERFLOW,
MP_RANGE_PARSING_INVALID_CHAR,
} mp_range_parser_error;
typedef struct mp_range_parsed {
mp_range_parser_error error;
mp_range range;
} mp_range_parsed;
mp_range_parsed mp_parse_range_string(const char * /*input*/);
/*
* Appends a mp_perfdata value to a pd_list
*/
void pd_list_append(pd_list[1], mp_perfdata);
#define mp_set_pd_value(P, V) \
_Generic((V), \
float: mp_set_pd_value_float, \
double: mp_set_pd_value_double, \
int: mp_set_pd_value_int, \
unsigned int: mp_set_pd_value_u_int, \
long: mp_set_pd_value_long, \
unsigned long: mp_set_pd_value_u_long, \
long long: mp_set_pd_value_long_long, \
unsigned long long: mp_set_pd_value_u_long_long)(P, V)
mp_perfdata mp_set_pd_value_float(mp_perfdata, float);
mp_perfdata mp_set_pd_value_double(mp_perfdata, double);
mp_perfdata mp_set_pd_value_int(mp_perfdata, int);
mp_perfdata mp_set_pd_value_u_int(mp_perfdata, unsigned int);
mp_perfdata mp_set_pd_value_long(mp_perfdata, long);
mp_perfdata mp_set_pd_value_u_long(mp_perfdata, unsigned long);
mp_perfdata mp_set_pd_value_long_long(mp_perfdata, long long);
mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
#define mp_create_pd_value(V) \
_Generic((V), \
float: mp_create_pd_value_float, \
double: mp_create_pd_value_double, \
int: mp_create_pd_value_int, \
unsigned int: mp_create_pd_value_u_int, \
long: mp_create_pd_value_long, \
unsigned long: mp_create_pd_value_u_long, \
long long: mp_create_pd_value_long_long, \
unsigned long long: mp_create_pd_value_u_long_long)(V)
mp_perfdata_value mp_create_pd_value_float(float);
mp_perfdata_value mp_create_pd_value_double(double);
mp_perfdata_value mp_create_pd_value_int(int);
mp_perfdata_value mp_create_pd_value_u_int(unsigned int);
mp_perfdata_value mp_create_pd_value_long(long);
mp_perfdata_value mp_create_pd_value_u_long(unsigned long);
mp_perfdata_value mp_create_pd_value_long_long(long long);
mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long);
/*
* Free the memory used by a pd_list
*/
void pd_list_free(pd_list[1]);
int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value);
// =================
// String formatters
// =================
/*
* Generate string from mp_perfdata value
*/
char *pd_to_string(mp_perfdata);
/*
* Generate string from perfdata_value value
*/
char *pd_value_to_string(mp_perfdata_value);
/*
* Generate string from pd_list value for the final output
*/
char *pd_list_to_string(pd_list);
/*
* Generate string from a mp_range value
*/
char *mp_range_to_string(mp_range);
char *fmt_range(range);

71
lib/states.h Normal file
View file

@ -0,0 +1,71 @@
#ifndef _MP_STATES_
#define _MP_STATES_
#include "../config.h"
#include <sys/param.h>
typedef enum state_enum {
STATE_OK,
STATE_WARNING,
STATE_CRITICAL,
STATE_UNKNOWN,
STATE_DEPENDENT
} mp_state_enum;
/* **************************************************************************
* max_state(STATE_x, STATE_y)
* compares STATE_x to STATE_y and returns result based on the following
* STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
*
* Note that numerically the above does not hold
****************************************************************************/
static inline mp_state_enum max_state(mp_state_enum a, mp_state_enum b) {
if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
return STATE_CRITICAL;
}
if (a == STATE_WARNING || b == STATE_WARNING) {
return STATE_WARNING;
}
if (a == STATE_OK || b == STATE_OK) {
return STATE_OK;
}
if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
return STATE_UNKNOWN;
}
if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
return STATE_DEPENDENT;
}
return MAX(a, b);
}
/* **************************************************************************
* max_state_alt(STATE_x, STATE_y)
* compares STATE_x to STATE_y and returns result based on the following
* STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
*
* The main difference between max_state_alt and max_state it that it doesn't
* allow setting a default to UNKNOWN. It will instead prioritixe any valid
* non-OK state.
****************************************************************************/
static inline mp_state_enum max_state_alt(mp_state_enum a, mp_state_enum b) {
if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
return STATE_CRITICAL;
}
if (a == STATE_WARNING || b == STATE_WARNING) {
return STATE_WARNING;
}
if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
return STATE_UNKNOWN;
}
if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
return STATE_DEPENDENT;
}
if (a == STATE_OK || b == STATE_OK) {
return STATE_OK;
}
return MAX(a, b);
}
#endif

View file

@ -8,9 +8,9 @@ check_PROGRAMS = @EXTRA_TEST@
AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
-I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3
EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output
np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t
np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t
np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini
EXTRA_DIST = $(np_test_scripts) $(np_test_files) var
@ -29,7 +29,7 @@ AM_CFLAGS = -g -I$(top_srcdir)/lib -I$(top_srcdir)/gl $(tap_cflags)
AM_LDFLAGS = $(tap_ldflags) -ltap
LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO)
SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c
SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c
test: ${noinst_PROGRAMS}
perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS)

View file

@ -0,0 +1,315 @@
#include "../lib/output.h"
#include "../../tap/tap.h"
#include "./states.h"
#include <string.h>
void test_one_subcheck(void);
void test_two_subchecks(void);
void test_perfdata_formatting(void);
void test_perfdata_formatting2(void);
void test_deep_check_hierarchy(void);
void test_deep_check_hierarchy2(void);
void test_default_states1(void);
void test_default_states2(void);
int main(void) {
plan_tests(19);
diag("Simple test with one subcheck");
test_one_subcheck();
diag("Test with two subchecks");
test_two_subchecks();
diag("Test for performance data formatting");
test_perfdata_formatting();
diag("Another test for performance data formatting");
test_perfdata_formatting2();
diag("Test for deeper hierarchies");
test_deep_check_hierarchy();
diag("Another test for deeper hierarchies");
test_deep_check_hierarchy2();
diag("Testing the default state logic");
test_default_states1();
diag("Testing the default state logic #2");
test_default_states2();
return exit_status();
}
void test_one_subcheck(void) {
mp_subcheck sc1 = mp_subcheck_init();
sc1.output = "foobar";
sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
mp_check check = mp_check_init();
mp_add_subcheck_to_check(&check, sc1);
ok(mp_compute_check_state(check) == STATE_WARNING, "Main state should be warning");
char *output = mp_fmt_output(check);
// diag("Formatted output");
// diag(output);
char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
"\t\\_[WARNING] - foobar\n";
// diag("Expected output");
// diag(expected);
ok(strcmp(output, expected) == 0, "Simple output test");
}
void test_perfdata_formatting2(void) {
mp_perfdata pd1 = perfdata_init();
mp_perfdata pd2 = perfdata_init();
pd1.label = "foo";
pd2.label = "bar";
pd1 = mp_set_pd_value(pd1, 23);
pd2 = mp_set_pd_value(pd2, 1LL);
pd_list *tmp = pd_list_init();
pd_list_append(tmp, pd1);
pd_list_append(tmp, pd2);
char *result = pd_list_to_string(*tmp);
ok(strcmp(result, "foo=23;;; bar=1;;;") == 0, "Perfdata string formatting");
}
void test_perfdata_formatting(void) {
mp_perfdata pd1 = perfdata_init();
pd1.uom = "s";
pd1.label = "foo";
pd1 = mp_set_pd_value(pd1, 23);
char *pd_string = pd_to_string(pd1);
ok(strcmp(pd_string, "foo=23s;;;") == 0, "Perfdata string formatting");
}
void test_two_subchecks(void) {
mp_subcheck sc1 = mp_subcheck_init();
sc1.output = "foobar";
sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state directly after setting it");
mp_perfdata pd1 = perfdata_init();
pd1 = mp_set_pd_value(pd1, 23);
pd1.uom = "s";
pd1.label = "foo";
mp_add_perfdata_to_subcheck(&sc1, pd1);
mp_subcheck sc2 = mp_subcheck_init();
sc2.output = "baz";
sc2 = mp_set_subcheck_state(sc2, STATE_OK);
ok(mp_compute_subcheck_state(sc2) == STATE_OK, "Test subcheck 2 state after setting it");
mp_add_subcheck_to_subcheck(&sc1, sc2);
ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state after adding a subcheck");
mp_check check = mp_check_init();
mp_add_subcheck_to_check(&check, sc1);
ok(mp_compute_check_state(check) == STATE_WARNING, "Test main check result");
char *output = mp_fmt_output(check);
// diag("Formatted output. Length: %u", strlen(output));
// diag(output);
ok(output != NULL, "Output should not be NULL");
char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
"\t\\_[WARNING] - foobar\n"
"\t\t\\_[OK] - baz\n"
"|foo=23s;;; \n";
// diag("Expected output. Length: %u", strlen(expected));
// diag(expected);
ok(strcmp(output, expected) == 0, "Output is as expected");
}
void test_deep_check_hierarchy(void) {
// level 4
mp_subcheck sc4 = mp_subcheck_init();
sc4.output = "level4";
sc4 = mp_set_subcheck_state(sc4, STATE_OK);
// level 3
mp_subcheck sc3 = mp_subcheck_init();
sc3.output = "level3";
sc3 = mp_set_subcheck_state(sc3, STATE_OK);
// level 2
mp_subcheck sc2 = mp_subcheck_init();
sc2.output = "baz";
sc2 = mp_set_subcheck_state(sc2, STATE_OK);
// level 1
mp_subcheck sc1 = mp_subcheck_init();
sc1.output = "foobar";
sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
mp_perfdata pd1 = perfdata_init();
pd1.uom = "s";
pd1.label = "foo";
pd1 = mp_set_pd_value(pd1, 23);
mp_add_perfdata_to_subcheck(&sc1, pd1);
// main check
mp_check check = mp_check_init();
mp_add_subcheck_to_subcheck(&sc3, sc4);
mp_add_subcheck_to_subcheck(&sc2, sc3);
mp_add_subcheck_to_subcheck(&sc1, sc2);
mp_add_subcheck_to_check(&check, sc1);
char *output = mp_fmt_output(check);
size_t output_length = strlen(output);
// diag("Formatted output of length %i", output_length);
// diag(output);
ok(output != NULL, "Output should not be NULL");
char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
"\t\\_[WARNING] - foobar\n"
"\t\t\\_[OK] - baz\n"
"\t\t\t\\_[OK] - level3\n"
"\t\t\t\t\\_[OK] - level4\n"
"|foo=23s;;; \n";
size_t expected_length = strlen(expected);
// diag("Expected output of length: %i", expected_length);
// diag(expected);
ok(output_length == expected_length, "Outputs are of equal length");
ok(strcmp(output, expected) == 0, "Output is as expected");
}
void test_deep_check_hierarchy2(void) {
// level 1
mp_subcheck sc1 = mp_subcheck_init();
sc1.output = "foobar";
sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
mp_perfdata pd1 = perfdata_init();
pd1.uom = "s";
pd1.label = "foo";
pd1 = mp_set_pd_value(pd1, 23);
mp_add_perfdata_to_subcheck(&sc1, pd1);
// level 2
mp_subcheck sc2 = mp_subcheck_init();
sc2.output = "baz";
sc2 = mp_set_subcheck_state(sc2, STATE_OK);
mp_perfdata pd2 = perfdata_init();
pd2.uom = "B";
pd2.label = "baz";
pd2 = mp_set_pd_value(pd2, 1024);
mp_add_perfdata_to_subcheck(&sc2, pd2);
// level 3
mp_subcheck sc3 = mp_subcheck_init();
sc3.output = "level3";
sc3 = mp_set_subcheck_state(sc3, STATE_OK);
mp_perfdata pd3 = perfdata_init();
pd3.label = "floatMe";
pd3 = mp_set_pd_value(pd3, 1024.1024);
mp_add_perfdata_to_subcheck(&sc3, pd3);
// level 4
mp_subcheck sc4 = mp_subcheck_init();
sc4.output = "level4";
sc4 = mp_set_subcheck_state(sc4, STATE_OK);
mp_check check = mp_check_init();
mp_add_subcheck_to_subcheck(&sc3, sc4);
mp_add_subcheck_to_subcheck(&sc2, sc3);
mp_add_subcheck_to_subcheck(&sc1, sc2);
mp_add_subcheck_to_check(&check, sc1);
char *output = mp_fmt_output(check);
// diag("Formatted output of length: %i", strlen(output));
// diag(output);
ok(output != NULL, "Output should not be NULL");
char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
"\t\\_[WARNING] - foobar\n"
"\t\t\\_[OK] - baz\n"
"\t\t\t\\_[OK] - level3\n"
"\t\t\t\t\\_[OK] - level4\n"
"|foo=23s;;; baz=1024B;;; floatMe=1024.102400;;; \n";
// diag("Expected output of length: %i", strlen(expected));
// diag(expected);
ok(strcmp(output, expected) == 0, "Output is as expected");
}
void test_default_states1(void) {
mp_subcheck sc = mp_subcheck_init();
mp_state_enum state1 = mp_compute_subcheck_state(sc);
ok(state1 == STATE_UNKNOWN, "Default default state is Unknown");
sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
mp_state_enum state2 = mp_compute_subcheck_state(sc);
ok(state2 == STATE_CRITICAL, "Default state is Critical");
sc = mp_set_subcheck_state(sc, STATE_OK);
mp_state_enum state3 = mp_compute_subcheck_state(sc);
ok(state3 == STATE_OK, "Default state is Critical");
}
void test_default_states2(void) {
mp_check check = mp_check_init();
mp_subcheck sc = mp_subcheck_init();
sc.output = "placeholder";
sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
mp_add_subcheck_to_check(&check, sc);
mp_state_enum result_state = mp_compute_check_state(check);
ok(result_state == STATE_CRITICAL, "Derived state is the proper default state");
}

View file

@ -0,0 +1,6 @@
#!/usr/bin/perl
use Test::More;
if (! -e "./test_generic_output") {
plan skip_all => "./test_generic_output not compiled - please enable libtap library to test";
}
exec "./test_generic_output";

59
lib/thresholds.c Normal file
View file

@ -0,0 +1,59 @@
#include "./thresholds.h"
#include "./utils_base.h"
#include "perfdata.h"
#include <stddef.h>
mp_thresholds mp_thresholds_init() {
mp_thresholds tmp = {
.critical = {},
.critical_is_set = false,
.warning = {},
.warning_is_set = false,
};
return tmp;
}
char *fmt_threshold_warning(const thresholds th) {
if (th.warning == NULL) {
return "";
}
return fmt_range(*th.warning);
}
char *fmt_threshold_critical(const thresholds th) {
if (th.critical == NULL) {
return "";
}
return fmt_range(*th.critical);
}
mp_perfdata mp_pd_set_thresholds(mp_perfdata perfdata, mp_thresholds threshold) {
if (threshold.critical_is_set) {
perfdata.crit = threshold.critical;
perfdata.crit_present = true;
}
if (threshold.warning_is_set) {
perfdata.warn = threshold.warning;
perfdata.warn_present = true;
}
return perfdata;
}
mp_state_enum mp_get_pd_status(mp_perfdata perfdata) {
if (perfdata.crit_present) {
if (mp_check_range(perfdata.value, perfdata.crit)) {
return STATE_CRITICAL;
}
}
if (perfdata.warn_present) {
if (mp_check_range(perfdata.value, perfdata.warn)) {
return STATE_CRITICAL;
}
}
return STATE_OK;
}

28
lib/thresholds.h Normal file
View file

@ -0,0 +1,28 @@
#pragma once
#include "./perfdata.h"
#include "states.h"
/*
* Old threshold type using the old range type
*/
typedef struct thresholds_struct {
range *warning;
range *critical;
} thresholds;
typedef struct mp_thresholds_struct {
bool warning_is_set;
mp_range warning;
bool critical_is_set;
mp_range critical;
} mp_thresholds;
mp_thresholds mp_thresholds_init(void);
mp_perfdata mp_pd_set_thresholds(mp_perfdata /* pd */, mp_thresholds /* th */);
mp_state_enum mp_get_pd_status(mp_perfdata /* pd */);
char *fmt_threshold_warning(thresholds th);
char *fmt_threshold_critical(thresholds th);

View file

@ -55,22 +55,24 @@ void np_init(char *plugin_name, int argc, char **argv) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
this_monitoring_plugin->plugin_name = strdup(plugin_name);
if (this_monitoring_plugin->plugin_name == NULL)
if (this_monitoring_plugin->plugin_name == NULL) {
die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
}
this_monitoring_plugin->argc = argc;
this_monitoring_plugin->argv = argv;
}
}
void np_set_args(int argc, char **argv) {
if (this_monitoring_plugin == NULL)
if (this_monitoring_plugin == NULL) {
die(STATE_UNKNOWN, _("This requires np_init to be called"));
}
this_monitoring_plugin->argc = argc;
this_monitoring_plugin->argv = argv;
}
void np_cleanup() {
void np_cleanup(void) {
if (this_monitoring_plugin != NULL) {
if (this_monitoring_plugin->state != NULL) {
if (this_monitoring_plugin->state->state_data) {
@ -162,8 +164,9 @@ range *parse_range_string(char *str) {
int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) {
thresholds *temp_thresholds = NULL;
if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL)
if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
temp_thresholds->warning = NULL;
temp_thresholds->critical = NULL;
@ -215,6 +218,46 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
printf("\n");
}
/* Returns true if alert should be raised based on the range, false otherwise */
bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
bool is_inside = false;
if (my_range.end_infinity == false && my_range.start_infinity == false) {
// range: .........|---inside---|...........
// value
if ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)) {
is_inside = true;
} else {
is_inside = false;
}
} else if (my_range.start_infinity == false && my_range.end_infinity == true) {
// range: .........|---inside---------
// value
if (cmp_perfdata_value(my_range.start, value) < 0) {
is_inside = true;
} else {
is_inside = false;
}
} else if (my_range.start_infinity == true && my_range.end_infinity == false) {
// range: -inside--------|....................
// value
if (cmp_perfdata_value(value, my_range.end) == -1) {
is_inside = true;
} else {
is_inside = false;
}
} else {
// range from -inf to inf, so always inside
is_inside = true;
}
if ((is_inside && my_range.alert_on_inside_range == INSIDE) || (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) {
return true;
}
return false;
}
/* Returns true if alert should be raised based on the range */
bool check_range(double value, range *my_range) {
bool no = false;
@ -228,24 +271,24 @@ bool check_range(double value, range *my_range) {
if (my_range->end_infinity == false && my_range->start_infinity == false) {
if ((my_range->start <= value) && (value <= my_range->end)) {
return no;
} else {
return yes;
}
} else if (my_range->start_infinity == false && my_range->end_infinity == true) {
return yes;
}
if (my_range->start_infinity == false && my_range->end_infinity == true) {
if (my_range->start <= value) {
return no;
} else {
return yes;
}
} else if (my_range->start_infinity == true && my_range->end_infinity == false) {
return yes;
}
if (my_range->start_infinity == true && my_range->end_infinity == false) {
if (value <= my_range->end) {
return no;
} else {
return yes;
}
} else {
return no;
return yes;
}
return no;
}
/* Returns status */
@ -265,7 +308,8 @@ int get_status(double value, thresholds *my_thresholds) {
char *np_escaped_string(const char *string) {
char *data;
int i, j = 0;
int i;
int j = 0;
data = strdup(string);
for (i = 0; data[i]; i++) {
if (data[i] == '\\') {
@ -302,7 +346,8 @@ int np_check_if_root(void) { return (geteuid() == 0); }
* data strings.
*/
char *np_extract_value(const char *varlist, const char *name, char sep) {
char *tmp = NULL, *value = NULL;
char *tmp = NULL;
char *value = NULL;
int i;
while (1) {
@ -325,15 +370,17 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
if ((tmp = index(varlist, sep))) {
/* Value is delimited by a comma */
if (tmp - varlist == 0)
if (tmp - varlist == 0) {
continue;
}
value = (char *)calloc(1, tmp - varlist + 1);
strncpy(value, varlist, tmp - varlist);
value[tmp - varlist] = '\0';
} else {
/* Value is delimited by a \0 */
if (strlen(varlist) == 0)
if (strlen(varlist) == 0) {
continue;
}
value = (char *)calloc(1, strlen(varlist) + 1);
strncpy(value, varlist, strlen(varlist));
value[strlen(varlist)] = '\0';
@ -351,9 +398,11 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
}
/* Clean-up trailing spaces/newlines */
if (value)
for (i = strlen(value) - 1; isspace(value[i]); i--)
if (value) {
for (i = strlen(value) - 1; isspace(value[i]); i--) {
value[i] = '\0';
}
}
return value;
}
@ -378,14 +427,18 @@ const char *state_text(int result) {
* return the corresponding STATE_ value or ERROR)
*/
int mp_translate_state(char *state_text) {
if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0"))
if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) {
return STATE_OK;
if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1"))
}
if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) {
return STATE_WARNING;
if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2"))
}
if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) {
return STATE_CRITICAL;
if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3"))
}
if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) {
return STATE_UNKNOWN;
}
return ERROR;
}
@ -394,7 +447,7 @@ int mp_translate_state(char *state_text) {
* hopefully a unique key per service/plugin invocation. Use the extra-opts
* parse of argv, so that uniqueness in parameters are reflected there.
*/
char *_np_state_generate_key() {
char *_np_state_generate_key(void) {
int i;
char **argv = this_monitoring_plugin->argv;
char keyname[41];
@ -441,7 +494,7 @@ char *_np_state_generate_key() {
return p;
}
void _cleanup_state_data() {
void _cleanup_state_data(void) {
if (this_monitoring_plugin->state->state_data != NULL) {
np_free(this_monitoring_plugin->state->state_data->data);
np_free(this_monitoring_plugin->state->state_data);
@ -453,19 +506,21 @@ void _cleanup_state_data() {
* envvar NAGIOS_PLUGIN_STATE_DIRECTORY
* statically compiled shared state directory
*/
char *_np_state_calculate_location_prefix() {
char *_np_state_calculate_location_prefix(void) {
char *env_dir;
/* Do not allow passing MP_STATE_PATH in setuid plugins
* for security reasons */
if (!mp_suid()) {
env_dir = getenv("MP_STATE_PATH");
if (env_dir && env_dir[0] != '\0')
if (env_dir && env_dir[0] != '\0') {
return env_dir;
}
/* This is the former ENV, for backward-compatibility */
env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
if (env_dir && env_dir[0] != '\0')
if (env_dir && env_dir[0] != '\0') {
return env_dir;
}
}
return NP_STATE_DIR_PREFIX;
@ -483,19 +538,22 @@ void np_enable_state(char *keyname, int expected_data_version) {
char *p = NULL;
int ret;
if (this_monitoring_plugin == NULL)
if (this_monitoring_plugin == NULL) {
die(STATE_UNKNOWN, _("This requires np_init to be called"));
}
this_state = (state_key *)calloc(1, sizeof(state_key));
if (this_state == NULL)
if (this_state == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
if (keyname == NULL) {
temp_keyname = _np_state_generate_key();
} else {
temp_keyname = strdup(keyname);
if (temp_keyname == NULL)
if (temp_keyname == NULL) {
die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
}
}
/* Die if invalid characters used for keyname */
p = temp_keyname;
@ -513,8 +571,9 @@ void np_enable_state(char *keyname, int expected_data_version) {
/* Calculate filename */
ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
this_monitoring_plugin->plugin_name, this_state->name);
if (ret < 0)
if (ret < 0) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
this_state->_filename = temp_filename;
@ -528,21 +587,23 @@ void np_enable_state(char *keyname, int expected_data_version) {
* If numerically lower, then return as no previous state. die with UNKNOWN
* if exceptional error.
*/
state_data *np_state_read() {
state_data *np_state_read(void) {
state_data *this_state_data = NULL;
FILE *statefile;
bool rc = false;
if (this_monitoring_plugin == NULL)
if (this_monitoring_plugin == NULL) {
die(STATE_UNKNOWN, _("This requires np_init to be called"));
}
/* Open file. If this fails, no previous state found */
statefile = fopen(this_monitoring_plugin->state->_filename, "r");
if (statefile != NULL) {
this_state_data = (state_data *)calloc(1, sizeof(state_data));
if (this_state_data == NULL)
if (this_state_data == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
this_state_data->data = NULL;
this_monitoring_plugin->state->state_data = this_state_data;
@ -581,8 +642,9 @@ bool _np_state_read_file(FILE *f) {
/* Note: This introduces a limit of 1024 bytes in the string data */
line = (char *)calloc(1, 1024);
if (line == NULL)
if (line == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
while (!failure && (fgets(line, 1024, f)) != NULL) {
pos = strlen(line);
@ -590,38 +652,42 @@ bool _np_state_read_file(FILE *f) {
line[pos - 1] = '\0';
}
if (line[0] == '#')
if (line[0] == '#') {
continue;
}
switch (expected) {
case STATE_FILE_VERSION:
i = atoi(line);
if (i != NP_STATE_FORMAT_VERSION)
if (i != NP_STATE_FORMAT_VERSION) {
failure++;
else
} else {
expected = STATE_DATA_VERSION;
}
break;
case STATE_DATA_VERSION:
i = atoi(line);
if (i != this_monitoring_plugin->state->data_version)
if (i != this_monitoring_plugin->state->data_version) {
failure++;
else
} else {
expected = STATE_DATA_TIME;
}
break;
case STATE_DATA_TIME:
/* If time > now, error */
data_time = strtoul(line, NULL, 10);
if (data_time > current_time)
if (data_time > current_time) {
failure++;
else {
} else {
this_monitoring_plugin->state->state_data->time = data_time;
expected = STATE_DATA_TEXT;
}
break;
case STATE_DATA_TEXT:
this_monitoring_plugin->state->state_data->data = strdup(line);
if (this_monitoring_plugin->state->state_data->data == NULL)
if (this_monitoring_plugin->state->state_data->data == NULL) {
die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
}
expected = STATE_DATA_END;
status = true;
break;
@ -648,16 +714,18 @@ void np_state_write_string(time_t data_time, char *data_string) {
char *directories = NULL;
char *p = NULL;
if (data_time == 0)
if (data_time == 0) {
time(&current_time);
else
} else {
current_time = data_time;
}
/* If file doesn't currently exist, create directories */
if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) {
result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
if (result < 0)
if (result < 0) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
for (p = directories + 1; *p; p++) {
if (*p == '/') {
@ -674,8 +742,9 @@ void np_state_write_string(time_t data_time, char *data_string) {
}
result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename);
if (result < 0)
if (result < 0) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
if ((fd = mkstemp(temp_file)) == -1) {
np_free(temp_file);

View file

@ -2,6 +2,13 @@
#define _UTILS_BASE_
/* Header file for Monitoring Plugins utils_base.c */
#include "../config.h"
#include <time.h>
#include "./perfdata.h"
#include "./thresholds.h"
#ifndef USE_OPENSSL
# include "sha256.h"
#endif
@ -19,20 +26,6 @@
#define OUTSIDE 0
#define INSIDE 1
typedef struct range_struct {
double start;
bool start_infinity;
double end;
int end_infinity;
int alert_on; /* OUTSIDE (default) or INSIDE */
char *text; /* original unparsed text input */
} range;
typedef struct thresholds_struct {
range *warning;
range *critical;
} thresholds;
#define NP_STATE_FORMAT_VERSION 1
typedef struct state_data_struct {
@ -61,6 +54,7 @@ int _set_thresholds(thresholds **, char *, char *);
void set_thresholds(thresholds **, char *, char *);
void print_thresholds(const char *, thresholds *);
bool check_range(double, range *);
bool mp_check_range(mp_perfdata_value, mp_range);
int get_status(double, thresholds *);
/* Handle timeouts */
@ -107,12 +101,12 @@ char *np_extract_value(const char *, const char *, char);
int mp_translate_state(char *);
void np_enable_state(char *, int);
state_data *np_state_read();
state_data *np_state_read(void);
void np_state_write_string(time_t, char *);
void np_init(char *, int argc, char **argv);
void np_set_args(int argc, char **argv);
void np_cleanup();
void np_cleanup(void);
const char *state_text(int);
#endif /* _UTILS_BASE_ */

3165
lib/vendor/cJSON/cJSON.c vendored Normal file

File diff suppressed because it is too large Load diff

306
lib/vendor/cJSON/cJSON.h vendored Normal file
View file

@ -0,0 +1,306 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 18
#include <stddef.h>
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* Limits the length of circular references can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT
#define CJSON_CIRCULAR_LIMIT 10000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* Create a string where valuestring references a string so
* it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
* they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
cJSON_Invalid\
)
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -28,6 +28,9 @@
*****************************************************************************/
#include "common.h"
#include "output.h"
#include "states.h"
#include <limits.h>
#ifdef HAVE_DECL_SWAPCTL
# ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
@ -69,8 +72,6 @@ int main(int argc, char **argv) {
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
char *status = strdup("");
/* Parse extra opts if any */
argv = np_extra_opts(&argc, argv, progname);
@ -90,59 +91,101 @@ int main(int argc, char **argv) {
}
double percent_used;
mp_check overall = mp_check_init();
if (config.output_format_is_set) {
overall.format = config.output_format;
}
mp_subcheck sc1 = mp_subcheck_init();
sc1 = mp_set_subcheck_default_state(sc1, STATE_OK);
/* if total_swap_mb == 0, let's not divide by 0 */
if (data.metrics.total != 0) {
percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total);
} else {
printf(_("SWAP %s - Swap is either disabled, not present, or of zero "
"size."),
state_text(data.statusCode));
exit(config.no_swap_state);
sc1 = mp_set_subcheck_state(sc1, config.no_swap_state);
sc1.output = (char *)_("Swap is either disabled, not present, or of zero size.");
mp_add_subcheck_to_check(&overall, sc1);
mp_exit(overall);
}
if (verbose) {
printf("Computed usage percentage: %g\n", percent_used);
}
uint64_t warn_print = config.warn.value;
if (config.warn.is_percentage) {
warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT);
}
uint64_t crit_print = config.crit.value;
if (config.crit.is_percentage) {
crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT);
}
char *perfdata = perfdata_uint64("swap", data.metrics.free, "B", config.warn_is_set, warn_print, config.crit_is_set, crit_print, true,
0, true, data.metrics.total);
mp_perfdata pd = perfdata_init();
pd.label = "swap";
pd = mp_set_pd_value(pd, data.metrics.free);
pd.uom = "B";
if (config.warn_is_set) {
if (verbose > 1) {
printf("Warn threshold value: %" PRIu64 "\n", config.warn.value);
uint64_t warn_print = config.warn.value;
if (config.warn.is_percentage) {
warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT);
}
if ((config.warn.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.warn.value))) ||
config.warn.value >= data.metrics.free) {
data.statusCode = max_state(data.statusCode, STATE_WARNING);
}
mp_perfdata_value warn_pd = mp_create_pd_value(warn_print);
mp_range warn_range = mp_range_init();
warn_range.end_infinity = false;
warn_range.end = warn_pd;
pd.warn = warn_range;
pd.warn_present = true;
}
if (config.crit_is_set) {
if (verbose > 1) {
printf("Crit threshold value: %" PRIu64 "\n", config.crit.value);
uint64_t crit_print = config.crit.value;
if (config.crit.is_percentage) {
crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT);
}
if ((config.crit.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.crit.value))) ||
config.crit.value >= data.metrics.free) {
data.statusCode = max_state(data.statusCode, STATE_CRITICAL);
mp_perfdata_value crit_pd = mp_create_pd_value(crit_print);
mp_range crit_range = mp_range_init();
crit_range.end_infinity = false;
crit_range.end = crit_pd;
pd.crit = crit_range;
pd.crit_present = true;
}
mp_perfdata_value max = mp_create_pd_value(data.metrics.total);
pd.max = max;
pd.max_present = true;
mp_perfdata_value min = mp_create_pd_value(0);
pd.min = min;
pd.min_present = true;
mp_add_perfdata_to_subcheck(&sc1, pd);
if (verbose > 1) {
printf("Warn threshold value: %" PRIu64 "\n", config.warn.value);
}
if (config.warn_is_set) {
if ((config.warn.is_percentage && (percent_used >= (100 - (double)config.warn.value))) || config.warn.value >= data.metrics.free) {
sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
}
}
printf(_("SWAP %s - %g%% free (%lluMiB out of %lluMiB) %s|%s\n"), state_text(data.statusCode), (HUNDRED_PERCENT - percent_used),
BYTES_TO_MiB(data.metrics.free), BYTES_TO_MiB(data.metrics.total), status, perfdata);
if (verbose > 1) {
printf("Crit threshold value: %" PRIu64 "\n", config.crit.value);
}
exit(data.statusCode);
if (config.crit_is_set) {
if ((config.crit.is_percentage && (percent_used >= (100 - (double)config.crit.value))) || config.crit.value >= data.metrics.free) {
sc1 = mp_set_subcheck_state(sc1, STATE_CRITICAL);
}
}
xasprintf(&sc1.output, _("%g%% free (%lluMiB out of %lluMiB)"), (100 - percent_used), data.metrics.free >> 20,
data.metrics.total >> 20);
overall.summary = "Swap";
mp_add_subcheck_to_check(&overall, sc1);
mp_exit(overall);
}
int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
@ -172,15 +215,22 @@ int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
return STATE_OK;
}
#define output_format_index CHAR_MAX + 1
/* process command-line arguments */
swap_config_wrapper process_arguments(int argc, char **argv) {
swap_config_wrapper conf_wrapper = {.errorcode = OK};
conf_wrapper.config = swap_config_init();
static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'},
{"allswaps", no_argument, 0, 'a'}, {"no-swap", required_argument, 0, 'n'},
{"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
{"critical", required_argument, 0, 'c'},
{"allswaps", no_argument, 0, 'a'},
{"no-swap", required_argument, 0, 'n'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
{"output-format", required_argument, 0, output_format_index},
{0, 0, 0, 0}};
while (true) {
int option = 0;
@ -263,6 +313,18 @@ swap_config_wrapper process_arguments(int argc, char **argv) {
case 'v': /* verbose */
verbose++;
break;
case output_format_index: {
parsed_output_format parser = mp_parse_output_format(optarg);
if (!parser.parsing_success) {
// TODO List all available formats here, maybe add anothoer usage function
printf("Invalid output format: %s\n", optarg);
exit(STATE_UNKNOWN);
}
conf_wrapper.config.output_format_is_set = true;
conf_wrapper.config.output_format = parser.output_format;
break;
}
case 'V': /* version */
print_revision(progname, NP_VERSION);
exit(STATE_UNKNOWN);
@ -319,6 +381,7 @@ void print_help(swap_config config) {
_("Resulting state when there is no swap regardless of thresholds. "
"Default:"),
state_text(config.no_swap_state));
printf(UT_OUTPUT_FORMAT);
printf(UT_VERBOSE);
printf("\n");

View file

@ -1,6 +1,7 @@
#pragma once
#include "../common.h"
#include "output.h"
#ifndef SWAP_CONVERSION
# define SWAP_CONVERSION 1
@ -32,6 +33,9 @@ typedef struct {
check_swap_threshold crit;
bool on_aix;
int conversion_factor;
bool output_format_is_set;
mp_output_format output_format;
} swap_config;
swap_config swap_config_init(void);

View file

@ -14,6 +14,8 @@ swap_config swap_config_init(void) {
tmp.warn_is_set = false;
tmp.crit_is_set = false;
tmp.output_format_is_set = false;
#ifdef _AIX
tmp.on_aix = true;
#else

View file

@ -32,6 +32,7 @@
#define _COMMON_H_
#include "config.h"
#include "../lib/monitoringplug.h"
#ifdef HAVE_FEATURES_H
#include <features.h>
@ -178,14 +179,6 @@ enum {
ERROR = -1
};
enum {
STATE_OK,
STATE_WARNING,
STATE_CRITICAL,
STATE_UNKNOWN,
STATE_DEPENDENT
};
enum {
DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */

View file

@ -40,7 +40,6 @@
#include "./common.h"
#include "./utils.h"
#include "../lib/maxfd.h"
/* extern so plugin has pid to kill exec'd process on timeouts */
extern pid_t *childpid;

View file

@ -40,6 +40,7 @@
/** includes **/
#include "runcmd.h"
#include "../lib/monitoringplug.h"
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif

View file

@ -29,6 +29,7 @@
#define MAX_CN_LENGTH 256
#include "common.h"
#include "netutils.h"
#include "../lib/monitoringplug.h"
#ifdef HAVE_SSL
static SSL_CTX *ctx = NULL;

View file

@ -5,39 +5,54 @@
#
use strict;
use Test::More tests => 14;
use warnings;
use Test::More tests => 21;
use NPTest;
my $successOutput = '/^SWAP OK - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
my $failureOutput = '/^SWAP CRITICAL - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
my $warnOutput = '/^SWAP WARNING - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
use JSON;
my $result;
my $outputFormat = '--output-format mp-test-json';
my $output;
my $message = '/^[0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
$result = NPTest->testCmd( "./check_swap" ); # Always OK
$result = NPTest->testCmd( "./check_swap $outputFormat" ); # Always OK
cmp_ok( $result->return_code, "==", 0, "Always OK" );
like( $result->output, $successOutput, "Right output" );
$output = decode_json($result->output);
is($output->{'state'}, "OK", "State was correct");
like($output->{'checks'}->[0]->{'output'}, $message, "Output was correct");
$result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576" ); # 1 MB free
cmp_ok( $result->return_code, "==", 0, "At least 1MB free" );
like( $result->output, $successOutput, "Right output" );
$result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576 $outputFormat" ); # 1 MB free
cmp_ok( $result->return_code, "==", 0, "Always OK" );
$output = decode_json($result->output);
is($output->{'state'}, "OK", "State was correct");
like($output->{'checks'}->[0]->{'output'}, $message, "Output was correct");
$result = NPTest->testCmd( "./check_swap -w 1% -c 1%" ); # 1% free
cmp_ok( $result->return_code, "==", 0, 'At least 1% free' );
like( $result->output, $successOutput, "Right output" );
$result = NPTest->testCmd( "./check_swap -w 1% -c 1% $outputFormat" ); # 1% free
cmp_ok( $result->return_code, "==", 0, "Always OK" );
$output = decode_json($result->output);
is($output->{'state'}, "OK", "State was correct");
like($output->{'checks'}->[0]->{'output'}, $message, "Output was correct");
$result = NPTest->testCmd( "./check_swap -w 100% -c 100%" ); # 100% (always critical)
cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' );
like( $result->output, $failureOutput, "Right output" );
$result = NPTest->testCmd( "./check_swap -w 100% -c 100% $outputFormat" ); # 100% (always critical)
cmp_ok( $result->return_code, "==", 0, "Always OK" );
$output = decode_json($result->output);
is($output->{'state'}, "CRITICAL", "State was correct");
like($output->{'checks'}->[0]->{'output'}, $message, "Output was correct");
$result = NPTest->testCmd( "./check_swap -w 100% -c 1%" ); # 100% (always warn)
cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' );
like( $result->output, $warnOutput, "Right output" );
$result = NPTest->testCmd( "./check_swap -w 100% -c 1% $outputFormat" ); # 100% (always warn)
cmp_ok( $result->return_code, "==", 0, "Always OK" );
$output = decode_json($result->output);
is($output->{'state'}, "WARNING", "State was correct");
like($output->{'checks'}->[0]->{'output'}, $message, "Output was correct");
$result = NPTest->testCmd( "./check_swap -w 100%" ); # 100% (single threshold, always warn)
cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' );
like( $result->output, $warnOutput, "Right output" );
$result = NPTest->testCmd( "./check_swap -w 100% $outputFormat" ); # 100% (single threshold, always warn)
cmp_ok( $result->return_code, "==", 0, "Always OK" );
$output = decode_json($result->output);
is($output->{'state'}, "WARNING", "State was correct");
like($output->{'checks'}->[0]->{'output'}, $message, "Output was correct");
$result = NPTest->testCmd( "./check_swap -c 100%" ); # 100% (single threshold, always critical)
cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' );
like( $result->output, $failureOutput, "Right output" );
$result = NPTest->testCmd( "./check_swap -c 100% $outputFormat" ); # 100% (single threshold, always critical)
cmp_ok( $result->return_code, "==", 0, "Always OK" );
$output = decode_json($result->output);
is($output->{'state'}, "CRITICAL", "State was correct");
like($output->{'checks'}->[0]->{'output'}, $message, "Output was correct");

View file

@ -42,54 +42,6 @@ extern const char *progname;
time_t start_time, end_time;
/* **************************************************************************
* max_state(STATE_x, STATE_y)
* compares STATE_x to STATE_y and returns result based on the following
* STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
*
* Note that numerically the above does not hold
****************************************************************************/
int max_state(int a, int b) {
if (a == STATE_CRITICAL || b == STATE_CRITICAL)
return STATE_CRITICAL;
else if (a == STATE_WARNING || b == STATE_WARNING)
return STATE_WARNING;
else if (a == STATE_OK || b == STATE_OK)
return STATE_OK;
else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
return STATE_UNKNOWN;
else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
return STATE_DEPENDENT;
else
return max(a, b);
}
/* **************************************************************************
* max_state_alt(STATE_x, STATE_y)
* compares STATE_x to STATE_y and returns result based on the following
* STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
*
* The main difference between max_state_alt and max_state it that it doesn't
* allow setting a default to UNKNOWN. It will instead prioritixe any valid
* non-OK state.
****************************************************************************/
int max_state_alt(int a, int b) {
if (a == STATE_CRITICAL || b == STATE_CRITICAL)
return STATE_CRITICAL;
else if (a == STATE_WARNING || b == STATE_WARNING)
return STATE_WARNING;
else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
return STATE_UNKNOWN;
else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
return STATE_DEPENDENT;
else if (a == STATE_OK || b == STATE_OK)
return STATE_OK;
else
return max(a, b);
}
void usage(const char *msg) {
printf("%s\n", msg);
print_usage();

View file

@ -13,11 +13,11 @@ in order to resist overflow attacks. In addition, a few functions are
provided to standardize version and error reporting across the entire
suite of plugins. */
/* now some functions etc are being defined in ../lib/utils_base.c */
#include "utils_base.h"
#include "../config.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#ifdef NP_EXTRA_OPTS
/* Include extra-opts functions if compiled in */
@ -78,9 +78,6 @@ char *strpcat (char *, const char *, const char *);
int xvasprintf (char **strp, const char *fmt, va_list ap);
int xasprintf (char **strp, const char *fmt, ...);
int max_state (int a, int b);
int max_state_alt (int a, int b);
void usage (const char *) __attribute__((noreturn));
void usage2(const char *, const char *) __attribute__((noreturn));
void usage3(const char *, int) __attribute__((noreturn));
@ -198,4 +195,8 @@ The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\
copies of the plugins under the terms of the GNU General Public License.\n\
For more information about these matters, see the file named COPYING.\n")
#define UT_OUTPUT_FORMAT _("\
--output-format=OUTPUT_FORMAT\n\
Select output format. Valid values: \"multi-line\", \"mp-test-json\"\n")
#endif /* NP_UTILS_H */