From 0645c9fc2c7f801ba3c7d68a17c137a63ada299f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Feb 2025 21:58:34 +0100 Subject: Implement new output functionality --- lib/output.c | 464 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 464 insertions(+) create mode 100644 lib/output.c (limited to 'lib/output.c') diff --git a/lib/output.c b/lib/output.c new file mode 100644 index 00000000..9ba049e2 --- /dev/null +++ b/lib/output.c @@ -0,0 +1,464 @@ +#include "./output.h" +#include "./utils_base.h" +#include "../plugins/utils.h" + +#include +#include +#include +#include +// #include +#include "./vendor/cJSON/cJSON.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 = xasprintf(&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 = xasprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck)); + } else { + // TODO free previous result here? + added = xasprintf(&result, "%s", result, 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; + xasprintf(&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_SUMMARY_ONLY: + if (check.summary == NULL) { + check.summary = get_subcheck_summary(check); + } + + xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary); + return result; + + case MP_FORMAT_ONE_LINE: { + /* SERVICE STATUS: First line of output | First part of performance data + * Any number of subsequent lines of output, but note that buffers + * may have a limited size | Second part of performance data, which + * may have continuation lines, too + */ + if (check.summary == NULL) { + check.summary = get_subcheck_summary(check); + } + + xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary); + + mp_subcheck_list *subchecks = check.subchecks; + + while (subchecks != NULL) { + xasprintf(&result, "%s - %s", result, fmt_subcheck_output(MP_FORMAT_ONE_LINE, subchecks->subcheck, 1)); + subchecks = subchecks->next; + } + + break; + } + case MP_FORMAT_ICINGA_WEB_2: { + if (check.summary == NULL) { + check.summary = get_subcheck_summary(check); + } + + xasprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary); + + mp_subcheck_list *subchecks = check.subchecks; + + while (subchecks != NULL) { + xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_ICINGA_WEB_2, subchecks->subcheck, 1)); + subchecks = subchecks->next; + } + + char *pd_string = NULL; + subchecks = check.subchecks; + + while (subchecks != NULL) { + if (pd_string == NULL) { + xasprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); + } else { + xasprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck)); + } + + subchecks = subchecks->next; + } + + if (pd_string != NULL && strlen(pd_string) > 0) { + xasprintf(&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; + + switch (output_format) { + case MP_FORMAT_ICINGA_WEB_2: + xasprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), + check.output); + + mp_subcheck_list *subchecks = check.subchecks; + + while (subchecks != NULL) { + xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); + subchecks = subchecks->next; + } + return result; + case MP_FORMAT_ONE_LINE: + return result; + case MP_FORMAT_SUMMARY_ONLY: + return result; + default: + die(STATE_UNKNOWN, "Invalid format"); + } +} + +static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) { + cJSON *result = cJSON_CreateObject(); + cJSON *output = cJSON_CreateString(subcheck.output); + cJSON_AddItemToObject(result, "output", output); + cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck))); + cJSON_AddItemToObject(result, "state", state); + + 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); + 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_ONE_LINE] = "one-line", + [MP_FORMAT_ICINGA_WEB_2] = "icingaweb2", + [MP_FORMAT_SUMMARY_ONLY] = "summary-only", + [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; +} -- cgit v1.2.3-74-g34f1 From 24172ca0e014454a7a6f3f1a12104175f9ff7c71 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 19 Feb 2025 11:49:43 +0100 Subject: Exit with 0 in JSON-format if everything else works --- lib/output.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/output.c') diff --git a/lib/output.c b/lib/output.c index 9ba049e2..2c40bc7f 100644 --- a/lib/output.c +++ b/lib/output.c @@ -413,6 +413,10 @@ void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); } */ 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)); } -- cgit v1.2.3-74-g34f1 From 6320a4e9534595d9c5f0d6d73e6858f548092a3e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 19 Feb 2025 11:50:08 +0100 Subject: Fix one-line formatting --- lib/output.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'lib/output.c') diff --git a/lib/output.c b/lib/output.c index 2c40bc7f..62a00fed 100644 --- a/lib/output.c +++ b/lib/output.c @@ -260,7 +260,7 @@ char *mp_fmt_output(mp_check check) { subchecks = subchecks->next; } - break; + return result; } case MP_FORMAT_ICINGA_WEB_2: { if (check.summary == NULL) { @@ -354,13 +354,14 @@ static char *generate_indentation_string(unsigned int indentation) { */ 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_ICINGA_WEB_2: xasprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), check.output); - mp_subcheck_list *subchecks = check.subchecks; + subchecks = check.subchecks; while (subchecks != NULL) { xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); @@ -368,6 +369,14 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch } return result; case MP_FORMAT_ONE_LINE: + xasprintf(&result, "[%s] - %s", state_text(mp_compute_subcheck_state(check)), check.output); + + subchecks = check.subchecks; + + while (subchecks != NULL) { + xasprintf(&result, " - %s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); + subchecks = subchecks->next; + } return result; case MP_FORMAT_SUMMARY_ONLY: return result; -- cgit v1.2.3-74-g34f1 From 24f21473fb493edf7fced3b4f680c99123145014 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:59:32 +0100 Subject: Use asprintf in lib directly instead of xasprintf --- lib/output.c | 32 ++++++++++++++++---------------- lib/perfdata.c | 36 ++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 34 deletions(-) (limited to 'lib/output.c') diff --git a/lib/output.c b/lib/output.c index 62a00fed..4c5041c8 100644 --- a/lib/output.c +++ b/lib/output.c @@ -23,7 +23,7 @@ static inline char *fmt_subcheck_perfdata(mp_subcheck check) { int added = 0; if (check.perfdata != NULL) { - added = xasprintf(&result, "%s", pd_list_to_string(*check.perfdata)); + added = asprintf(&result, "%s", pd_list_to_string(*check.perfdata)); } if (check.subchecks == NULL) { @@ -35,10 +35,10 @@ static inline char *fmt_subcheck_perfdata(mp_subcheck check) { while (subchecks != NULL) { if (added > 0) { - added = xasprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck)); + added = asprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck)); } else { // TODO free previous result here? - added = xasprintf(&result, "%s", result, fmt_subcheck_perfdata(subchecks->subcheck)); + added = asprintf(&result, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); } subchecks = subchecks->next; @@ -185,7 +185,7 @@ char *get_subcheck_summary(mp_check check) { subchecks = subchecks->next; } char *result = NULL; - xasprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown); + asprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown); return result; } @@ -238,7 +238,7 @@ char *mp_fmt_output(mp_check check) { check.summary = get_subcheck_summary(check); } - xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary); + asprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary); return result; case MP_FORMAT_ONE_LINE: { @@ -251,12 +251,12 @@ char *mp_fmt_output(mp_check check) { check.summary = get_subcheck_summary(check); } - xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary); + asprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary); mp_subcheck_list *subchecks = check.subchecks; while (subchecks != NULL) { - xasprintf(&result, "%s - %s", result, fmt_subcheck_output(MP_FORMAT_ONE_LINE, subchecks->subcheck, 1)); + asprintf(&result, "%s - %s", result, fmt_subcheck_output(MP_FORMAT_ONE_LINE, subchecks->subcheck, 1)); subchecks = subchecks->next; } @@ -267,12 +267,12 @@ char *mp_fmt_output(mp_check check) { check.summary = get_subcheck_summary(check); } - xasprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary); + asprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary); mp_subcheck_list *subchecks = check.subchecks; while (subchecks != NULL) { - xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_ICINGA_WEB_2, subchecks->subcheck, 1)); + asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_ICINGA_WEB_2, subchecks->subcheck, 1)); subchecks = subchecks->next; } @@ -281,16 +281,16 @@ char *mp_fmt_output(mp_check check) { while (subchecks != NULL) { if (pd_string == NULL) { - xasprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); + asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); } else { - xasprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck)); + asprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck)); } subchecks = subchecks->next; } if (pd_string != NULL && strlen(pd_string) > 0) { - xasprintf(&result, "%s|%s", result, pd_string); + asprintf(&result, "%s|%s", result, pd_string); } break; @@ -358,23 +358,23 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch switch (output_format) { case MP_FORMAT_ICINGA_WEB_2: - xasprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), + asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), check.output); subchecks = check.subchecks; while (subchecks != NULL) { - xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); + asprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); subchecks = subchecks->next; } return result; case MP_FORMAT_ONE_LINE: - xasprintf(&result, "[%s] - %s", state_text(mp_compute_subcheck_state(check)), check.output); + asprintf(&result, "[%s] - %s", state_text(mp_compute_subcheck_state(check)), check.output); subchecks = check.subchecks; while (subchecks != NULL) { - xasprintf(&result, " - %s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); + asprintf(&result, " - %s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); subchecks = subchecks->next; } return result; diff --git a/lib/perfdata.c b/lib/perfdata.c index f894df39..661756c5 100644 --- a/lib/perfdata.c +++ b/lib/perfdata.c @@ -14,13 +14,13 @@ char *pd_value_to_string(const mp_perfdata_value pd) { switch (pd.type) { case PD_TYPE_INT: - xasprintf(&result, "%lli", pd.pd_int); + asprintf(&result, "%lli", pd.pd_int); break; case PD_TYPE_UINT: - xasprintf(&result, "%llu", pd.pd_int); + asprintf(&result, "%llu", pd.pd_int); break; case PD_TYPE_DOUBLE: - xasprintf(&result, "%f", pd.pd_double); + asprintf(&result, "%f", pd.pd_double); break; default: // die here @@ -33,33 +33,33 @@ char *pd_value_to_string(const mp_perfdata_value pd) { char *pd_to_string(mp_perfdata pd) { assert(pd.label != NULL); char *result = NULL; - xasprintf(&result, "%s=", pd.label); + asprintf(&result, "%s=", pd.label); - xasprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); + asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); if (pd.uom != NULL) { - xasprintf(&result, "%s%s", result, pd.uom); + asprintf(&result, "%s%s", result, pd.uom); } if (pd.warn_present) { - xasprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn)); + asprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn)); } else { - xasprintf(&result, "%s;", result); + asprintf(&result, "%s;", result); } if (pd.crit_present) { - xasprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit)); + asprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit)); } else { - xasprintf(&result, "%s;", result); + asprintf(&result, "%s;", result); } if (pd.min_present) { - xasprintf(&result, "%s;%s", result, pd_value_to_string(pd.min)); + asprintf(&result, "%s;%s", result, pd_value_to_string(pd.min)); } else { - xasprintf(&result, "%s;", result); + asprintf(&result, "%s;", result); } if (pd.max_present) { - xasprintf(&result, "%s;%s", result, pd_value_to_string(pd.max)); + asprintf(&result, "%s;%s", result, pd_value_to_string(pd.max)); } /*printf("pd_to_string: %s\n", result); */ @@ -71,7 +71,7 @@ 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) { - xasprintf(&result, "%s %s", result, pd_to_string(elem->data)); + asprintf(&result, "%s %s", result, pd_to_string(elem->data)); } return result; @@ -234,17 +234,17 @@ int cmp_perfdata_value(const mp_perfdata_value a, const mp_perfdata_value b) { char *mp_range_to_string(const mp_range input) { char *result = ""; if (input.alert_on_inside_range == INSIDE) { - xasprintf(&result, "@"); + asprintf(&result, "@"); } if (input.start_infinity) { - xasprintf(&result, "%s~:", result); + asprintf(&result, "%s~:", result); } else { - xasprintf(&result, "%s%s:", result, pd_value_to_string(input.start)); + asprintf(&result, "%s%s:", result, pd_value_to_string(input.start)); } if (!input.end_infinity) { - xasprintf(&result, "%s%s", result, pd_value_to_string(input.end)); + asprintf(&result, "%s%s", result, pd_value_to_string(input.end)); } return result; } -- cgit v1.2.3-74-g34f1 From 90b7df9980cd307afb31f2d0489c0a0702e2b81f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:28:50 +0100 Subject: Implement JSON serialising of perfdata --- lib/output.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) (limited to 'lib/output.c') diff --git a/lib/output.c b/lib/output.c index 4c5041c8..2c537a01 100644 --- a/lib/output.c +++ b/lib/output.c @@ -8,6 +8,8 @@ #include // #include #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); @@ -359,7 +361,7 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch switch (output_format) { case MP_FORMAT_ICINGA_WEB_2: asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), - check.output); + check.output); subchecks = check.subchecks; @@ -385,13 +387,112 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch } } +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(); -- cgit v1.2.3-74-g34f1 From 3cd29d86cc51b763a0cf706e64884602cb3c9314 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 20 Feb 2025 23:45:13 +0100 Subject: Remove output formats one-line and summary-only --- lib/output.c | 43 ------------------------------------------- lib/output.h | 2 -- plugins/check_swap.c | 2 +- 3 files changed, 1 insertion(+), 46 deletions(-) (limited to 'lib/output.c') diff --git a/lib/output.c b/lib/output.c index 2c537a01..07a77165 100644 --- a/lib/output.c +++ b/lib/output.c @@ -235,35 +235,6 @@ char *mp_fmt_output(mp_check check) { char *result = NULL; switch (check.format) { - case MP_FORMAT_SUMMARY_ONLY: - if (check.summary == NULL) { - check.summary = get_subcheck_summary(check); - } - - asprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary); - return result; - - case MP_FORMAT_ONE_LINE: { - /* SERVICE STATUS: First line of output | First part of performance data - * Any number of subsequent lines of output, but note that buffers - * may have a limited size | Second part of performance data, which - * may have continuation lines, too - */ - 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 - %s", result, fmt_subcheck_output(MP_FORMAT_ONE_LINE, subchecks->subcheck, 1)); - subchecks = subchecks->next; - } - - return result; - } case MP_FORMAT_ICINGA_WEB_2: { if (check.summary == NULL) { check.summary = get_subcheck_summary(check); @@ -370,18 +341,6 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch subchecks = subchecks->next; } return result; - case MP_FORMAT_ONE_LINE: - asprintf(&result, "[%s] - %s", 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; - case MP_FORMAT_SUMMARY_ONLY: - return result; default: die(STATE_UNKNOWN, "Invalid format"); } @@ -551,9 +510,7 @@ mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state } char *mp_output_format_map[] = { - [MP_FORMAT_ONE_LINE] = "one-line", [MP_FORMAT_ICINGA_WEB_2] = "icingaweb2", - [MP_FORMAT_SUMMARY_ONLY] = "summary-only", [MP_FORMAT_TEST_JSON] = "mp-test-json", }; diff --git a/lib/output.h b/lib/output.h index c7455d29..14c4bcf4 100644 --- a/lib/output.h +++ b/lib/output.h @@ -29,9 +29,7 @@ typedef struct subcheck_list { * Possible output formats */ typedef enum output_format { - MP_FORMAT_ONE_LINE, MP_FORMAT_ICINGA_WEB_2, - MP_FORMAT_SUMMARY_ONLY, MP_FORMAT_TEST_JSON, } mp_output_format; diff --git a/plugins/check_swap.c b/plugins/check_swap.c index 1f2d0273..262d8d51 100644 --- a/plugins/check_swap.c +++ b/plugins/check_swap.c @@ -382,7 +382,7 @@ void print_help(swap_config config) { "Default:"), state_text(config.no_swap_state)); printf(" %s\n", "--output-format"); - printf(" %s\n", _("Select output format. Valid values: \"one-line\", \"icingaweb2\", \"summary-only\", \"mp-test-json\"")); + printf(" %s\n", _("Select output format. Valid values: \"icingaweb2\", \"mp-test-json\"")); printf(UT_VERBOSE); printf("\n"); -- cgit v1.2.3-74-g34f1 From 1d590a0efe4193ac9298d3eba448ab8bc3bb665b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 20 Feb 2025 23:52:32 +0100 Subject: Rename icingaweb2 format to multi-line --- lib/output.c | 8 ++++---- lib/output.h | 4 ++-- plugins/utils.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/output.c') diff --git a/lib/output.c b/lib/output.c index 07a77165..17919afc 100644 --- a/lib/output.c +++ b/lib/output.c @@ -235,7 +235,7 @@ char *mp_fmt_output(mp_check check) { char *result = NULL; switch (check.format) { - case MP_FORMAT_ICINGA_WEB_2: { + case MP_FORMAT_MULTI_LINE: { if (check.summary == NULL) { check.summary = get_subcheck_summary(check); } @@ -245,7 +245,7 @@ char *mp_fmt_output(mp_check check) { mp_subcheck_list *subchecks = check.subchecks; while (subchecks != NULL) { - asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_ICINGA_WEB_2, subchecks->subcheck, 1)); + asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); subchecks = subchecks->next; } @@ -330,7 +330,7 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch mp_subcheck_list *subchecks = NULL; switch (output_format) { - case MP_FORMAT_ICINGA_WEB_2: + case MP_FORMAT_MULTI_LINE: asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), check.output); @@ -510,7 +510,7 @@ mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state } char *mp_output_format_map[] = { - [MP_FORMAT_ICINGA_WEB_2] = "icingaweb2", + [MP_FORMAT_MULTI_LINE] = "multi-line", [MP_FORMAT_TEST_JSON] = "mp-test-json", }; diff --git a/lib/output.h b/lib/output.h index 14c4bcf4..ffc36f53 100644 --- a/lib/output.h +++ b/lib/output.h @@ -29,11 +29,11 @@ typedef struct subcheck_list { * Possible output formats */ typedef enum output_format { - MP_FORMAT_ICINGA_WEB_2, + MP_FORMAT_MULTI_LINE, MP_FORMAT_TEST_JSON, } mp_output_format; -#define MP_FORMAT_DEFAULT MP_FORMAT_ICINGA_WEB_2 +#define MP_FORMAT_DEFAULT MP_FORMAT_MULTI_LINE /* * The main state object of a plugin. Exists only ONCE per plugin. diff --git a/plugins/utils.h b/plugins/utils.h index bc26f704..029ae5a6 100644 --- a/plugins/utils.h +++ b/plugins/utils.h @@ -197,6 +197,6 @@ 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: \"icingaweb2\", \"mp-test-json\"\n") + Select output format. Valid values: \"multi-line\", \"mp-test-json\"\n") #endif /* NP_UTILS_H */ -- cgit v1.2.3-74-g34f1