Module: monitoring-plugins
    Branch: master
    Commit: 44e1913da4d227aaabb7b5ccfae65d879082a5e4
    Author: Lorenz Kästle <[email protected]>
 Committer: GitHub <[email protected]>
      Date: Sat May 30 12:30:39 2026 +0200
       URL: 
https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=44e1913d

Error summary (#2259)

* lib: properly name function to set summary

* lib: set first non-ok subcheck as the summary for the overall check

* Fetch summarily recursively from failed subchecks

---------

Co-authored-by: Lorenz Kästle <[email protected]>

---

 lib/output.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 lib/output.h |   2 +-
 2 files changed, 130 insertions(+), 12 deletions(-)

diff --git a/lib/output.c b/lib/output.c
index 3c04d63d..9bcd02d9 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
  */
@@ -164,7 +209,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
@@ -172,31 +217,61 @@ void mp_add_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:
-                       warning++;
+                       if (critical_count == 0 && unknown_count == 0 && 
warning_count == 0) {
+                               // set summary to first warning subcheck output
+                               asprintf(&result, "%s", 
get_subcheck_failed_output(subchecks->subcheck));
+                       }
+                       warning_count++;
                        break;
                case STATE_CRITICAL:
-                       critical++;
+                       if (critical_count == 0) {
+                               // set summary to first critical subcheck output
+                               asprintf(&result, "%s", 
get_subcheck_failed_output(subchecks->subcheck));
+                       }
+                       critical_count++;
                        break;
                case STATE_UNKNOWN:
-                       unknown++;
+                       if (critical_count == 0 && unknown_count == 0) {
+                               // set summary to first unknown subcheck output
+                               asprintf(&result, "%s", 
get_subcheck_failed_output(subchecks->subcheck));
+                       }
+                       unknown_count++;
                        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);
+
+       if (result == NULL) {
+               if (ok_count > 0) {
+                       asprintf(&result, "ok=%d", ok_count);
+               }
+
+               if (warning_count > 0) {
+                       asprintf(&result, "%swarning=%d", (result == NULL ? "" 
: ", "), warning_count);
+               }
+
+               if (critical_count > 0) {
+                       asprintf(&result, "%scritical=%d", (result == NULL ? "" 
: ", "), critical_count);
+               }
+
+               if (unknown_count > 0) {
+                       asprintf(&result, "%sunknown=%d", (result == NULL ? "" 
: ", "), unknown_count);
+               }
+       }
+
        return result;
 }
 
@@ -658,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;
+               }
+       }
+}
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);

Reply via email to