#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; }