From 522dfca8ca87de0c0e04edae6590600393fa9d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20K=C3=A4stle?= <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:16:51 +0200 Subject: [PATCH 1/3] lib: properly name function to set summary --- lib/output.c | 2 +- lib/output.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/output.c b/lib/output.c index 54d505d9..e9e58ac8 100644 --- a/lib/output.c +++ b/lib/output.c @@ -164,7 +164,7 @@ int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subchec * 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; } +void mp_set_summary(mp_check check[static 1], char *summary) { check->summary = strdup(summary); } /* * Generate the summary string of a mp_check object based on its subchecks diff --git a/lib/output.h b/lib/output.h index f5011268..6ca63cfe 100644 --- a/lib/output.h +++ b/lib/output.h @@ -87,7 +87,7 @@ 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); +void mp_set_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); From eb0f272cf7f6cf4bc8c2a804e87444cb908c60ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20K=C3=A4stle?= <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:31:56 +0200 Subject: [PATCH 2/3] lib: set first non-ok subcheck as the summary for the overall check --- lib/output.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/output.c b/lib/output.c index e9e58ac8..0e7f2f10 100644 --- a/lib/output.c +++ b/lib/output.c @@ -176,18 +176,31 @@ char *get_subcheck_summary(mp_check check) { unsigned int warning = 0; unsigned int critical = 0; unsigned int unknown = 0; + char *result = NULL; while (subchecks != NULL) { switch (mp_compute_subcheck_state(subchecks->subcheck)) { case STATE_OK: ok++; break; case STATE_WARNING: + if (critical == 0 && unknown == 0 && warning == 0) { + // set summary to first warning subcheck output + asprintf(&result, "%s", subchecks->subcheck.output); + } warning++; break; case STATE_CRITICAL: + if (critical == 0) { + // set summary to first critical subcheck output + asprintf(&result, "%s", subchecks->subcheck.output); + } critical++; break; case STATE_UNKNOWN: + if (critical == 0 && unknown == 0) { + // set summary to first unknown subcheck output + asprintf(&result, "%s", subchecks->subcheck.output); + } unknown++; break; default: @@ -195,8 +208,25 @@ char *get_subcheck_summary(mp_check check) { } subchecks = subchecks->next; } - char *result = NULL; - asprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown); + + if (result == NULL) { + if (ok > 0) { + asprintf(&result, "ok=%d", ok); + } + + if (warning > 0) { + asprintf(&result, "%swarning=%d", (result == NULL ? "" : ", "), warning); + } + + if (critical > 0) { + asprintf(&result, "%scritical=%d", (result == NULL ? "" : ", "), critical); + } + + if (unknown > 0) { + asprintf(&result, "%sunknown=%d", (result == NULL ? "" : ", "), unknown); + } + } + return result; } From 9c60c29700d4badea6d9ebfad0cb97e2ea8827fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20K=C3=A4stle?= Date: Wed, 22 Apr 2026 13:47:00 +0200 Subject: [PATCH 3/3] Fetch summarily recursively from failed subchecks --- lib/output.c | 132 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 22 deletions(-) diff --git a/lib/output.c b/lib/output.c index 0e7f2f10..cbb8da97 100644 --- a/lib/output.c +++ b/lib/output.c @@ -20,8 +20,53 @@ static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck che unsigned int indentation); static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck); +// mp_compare_state compares two state arguments +// if *first* is WORSE than *second*, the result is < 0 +// if *first* is equal to *second*, the result is 0 +// if *first* is BETTER (less bad) the result is > 0 +static int mp_compare_state(mp_state_enum first, mp_state_enum second); + // == Implementation == +// get_subcheck_failed_output retrieves the output of the +// worst and first leave node in a subcheck tree +// or NULL if no such message exists +// the return string is a copy of the original +static char *get_subcheck_failed_output(const mp_subcheck tree) { + if (tree.subchecks == NULL) { + // this is a leave node + if (mp_compute_subcheck_state(tree) == STATE_OK) { + // ALL OK, nothing to return + return NULL; + } + + char *result = strdup(tree.output); + return result; + } + + // not a leave node, go through tree + mp_subcheck_list *subcheck = tree.subchecks; + mp_subcheck *worst_first_node = NULL; + mp_state_enum worst_state = STATE_OK; + while (subcheck != NULL) { + mp_state_enum current = mp_compute_subcheck_state(subcheck->subcheck); + if (mp_compare_state(current, worst_state) < 0) { + worst_first_node = &subcheck->subcheck; + } + + subcheck = subcheck->next; + } + + if (worst_first_node == NULL) { + // we did not find a failed subcheck, return the output + // of the current node + char *result = strdup(tree.output); + return result; + } + + return get_subcheck_failed_output(*worst_first_node); +} + /* * Generate output string for a mp_subcheck object */ @@ -172,36 +217,36 @@ void mp_set_summary(mp_check check[static 1], char *summary) { check->summary = 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; + unsigned int ok_count = 0; + unsigned int warning_count = 0; + unsigned int critical_count = 0; + unsigned int unknown_count = 0; char *result = NULL; while (subchecks != NULL) { switch (mp_compute_subcheck_state(subchecks->subcheck)) { case STATE_OK: - ok++; + ok_count++; break; case STATE_WARNING: - if (critical == 0 && unknown == 0 && warning == 0) { + if (critical_count == 0 && unknown_count == 0 && warning_count == 0) { // set summary to first warning subcheck output - asprintf(&result, "%s", subchecks->subcheck.output); + asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck)); } - warning++; + warning_count++; break; case STATE_CRITICAL: - if (critical == 0) { + if (critical_count == 0) { // set summary to first critical subcheck output - asprintf(&result, "%s", subchecks->subcheck.output); + asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck)); } - critical++; + critical_count++; break; case STATE_UNKNOWN: - if (critical == 0 && unknown == 0) { + if (critical_count == 0 && unknown_count == 0) { // set summary to first unknown subcheck output - asprintf(&result, "%s", subchecks->subcheck.output); + asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck)); } - unknown++; + unknown_count++; break; default: die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary"); @@ -210,20 +255,20 @@ char *get_subcheck_summary(mp_check check) { } if (result == NULL) { - if (ok > 0) { - asprintf(&result, "ok=%d", ok); + if (ok_count > 0) { + asprintf(&result, "ok=%d", ok_count); } - if (warning > 0) { - asprintf(&result, "%swarning=%d", (result == NULL ? "" : ", "), warning); + if (warning_count > 0) { + asprintf(&result, "%swarning=%d", (result == NULL ? "" : ", "), warning_count); } - if (critical > 0) { - asprintf(&result, "%scritical=%d", (result == NULL ? "" : ", "), critical); + if (critical_count > 0) { + asprintf(&result, "%scritical=%d", (result == NULL ? "" : ", "), critical_count); } - if (unknown > 0) { - asprintf(&result, "%sunknown=%d", (result == NULL ? "" : ", "), unknown); + if (unknown_count > 0) { + asprintf(&result, "%sunknown=%d", (result == NULL ? "" : ", "), unknown_count); } } @@ -688,3 +733,46 @@ mp_state_enum mp_eval_unknown(mp_check overall) { (void)overall; return STATE_UNKNOWN; } + +static int mp_compare_state(mp_state_enum first, mp_state_enum second) { + switch (first) { + case STATE_OK: + switch (second) { + case STATE_OK: + return 0; + case STATE_WARNING: + case STATE_UNKNOWN: + case STATE_CRITICAL: + return 1; + } + case STATE_WARNING: + switch (second) { + case STATE_OK: + return -1; + case STATE_WARNING: + return 0; + case STATE_UNKNOWN: + case STATE_CRITICAL: + return 1; + } + case STATE_UNKNOWN: + switch (second) { + case STATE_OK: + case STATE_WARNING: + return -1; + case STATE_UNKNOWN: + return 0; + case STATE_CRITICAL: + return 1; + } + case STATE_CRITICAL: + switch (second) { + case STATE_OK: + case STATE_WARNING: + case STATE_UNKNOWN: + return -1; + case STATE_CRITICAL: + return 0; + } + } +}