summaryrefslogtreecommitdiffstats
path: root/lib/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/output.c')
-rw-r--r--lib/output.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/lib/output.c b/lib/output.c
new file mode 100644
index 00000000..62a00fed
--- /dev/null
+++ b/lib/output.c
@@ -0,0 +1,477 @@
1#include "./output.h"
2#include "./utils_base.h"
3#include "../plugins/utils.h"
4
5#include <assert.h>
6#include <stdlib.h>
7#include <string.h>
8#include <strings.h>
9// #include <cjson/cJSON.h>
10#include "./vendor/cJSON/cJSON.h"
11
12// == Prototypes ==
13static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation);
14static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
15
16// == Implementation ==
17
18/*
19 * Generate output string for a mp_subcheck object
20 */
21static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
22 char *result = strdup("");
23 int added = 0;
24
25 if (check.perfdata != NULL) {
26 added = xasprintf(&result, "%s", pd_list_to_string(*check.perfdata));
27 }
28
29 if (check.subchecks == NULL) {
30 // No subchecks, return here
31 return result;
32 }
33
34 mp_subcheck_list *subchecks = check.subchecks;
35
36 while (subchecks != NULL) {
37 if (added > 0) {
38 added = xasprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck));
39 } else {
40 // TODO free previous result here?
41 added = xasprintf(&result, "%s", result, fmt_subcheck_perfdata(subchecks->subcheck));
42 }
43
44 subchecks = subchecks->next;
45 }
46
47 return result;
48}
49
50/*
51 * Initialiser for a mp_check object. Always use this to get a new one!
52 * It sets useful defaults
53 */
54mp_check mp_check_init(void) {
55 mp_check check = {0};
56 check.format = MP_FORMAT_DEFAULT;
57 return check;
58}
59
60/*
61 * Initialiser for a mp_subcheck object. Always use this to get a new one!
62 * It sets useful defaults
63 */
64mp_subcheck mp_subcheck_init(void) {
65 mp_subcheck tmp = {0};
66 tmp.default_state = STATE_UNKNOWN; // Default state is unknown
67 tmp.state_set_explicitly = false;
68 return tmp;
69}
70
71/*
72 * Add a subcheck to a (the one and only) check object
73 */
74int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck subcheck) {
75 assert(subcheck.output != NULL); // There must be output in a subcheck
76
77 mp_subcheck_list *tmp = NULL;
78
79 if (check->subchecks == NULL) {
80 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
81 if (check->subchecks == NULL) {
82 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
83 }
84
85 check->subchecks->subcheck = subcheck;
86 check->subchecks->next = NULL;
87 } else {
88 // Search for the end
89 tmp = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
90 if (tmp == NULL) {
91 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
92 }
93
94 tmp->subcheck = subcheck;
95 tmp->next = check->subchecks;
96
97 check->subchecks = tmp;
98 }
99
100 return 0;
101}
102
103/*
104 * Add a mp_perfdata data point to a mp_subcheck object
105 */
106void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata perfData) {
107 if (check->perfdata == NULL) {
108 check->perfdata = pd_list_init();
109 }
110 pd_list_append(check->perfdata, perfData);
111}
112
113/*
114 * Add a mp_subcheck object to another one. The seconde mp_subcheck (argument) is the lower in the
115 * hierarchy
116 */
117int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
118 if (subcheck.output == NULL) {
119 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL");
120 }
121
122 mp_subcheck_list *tmp = NULL;
123
124 if (check->subchecks == NULL) {
125 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
126 if (check->subchecks == NULL) {
127 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
128 }
129
130 tmp = check->subchecks;
131 } else {
132 // Search for the end
133 tmp = check->subchecks;
134
135 while (tmp->next != NULL) {
136 tmp = tmp->next;
137 }
138
139 tmp->next = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
140 if (tmp->next == NULL) {
141 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
142 }
143
144 tmp = tmp->next;
145 }
146
147 tmp->subcheck = subcheck;
148
149 return 0;
150}
151
152/*
153 * Add a manual summary to a mp_check object, effectively replacing
154 * the autogenerated one
155 */
156void mp_add_summary(mp_check check[static 1], char *summary) { check->summary = summary; }
157
158/*
159 * Generate the summary string of a mp_check object based on it's subchecks
160 */
161char *get_subcheck_summary(mp_check check) {
162 mp_subcheck_list *subchecks = check.subchecks;
163
164 unsigned int ok = 0;
165 unsigned int warning = 0;
166 unsigned int critical = 0;
167 unsigned int unknown = 0;
168 while (subchecks != NULL) {
169 switch (subchecks->subcheck.state) {
170 case STATE_OK:
171 ok++;
172 break;
173 case STATE_WARNING:
174 warning++;
175 break;
176 case STATE_CRITICAL:
177 critical++;
178 break;
179 case STATE_UNKNOWN:
180 unknown++;
181 break;
182 default:
183 die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary");
184 }
185 subchecks = subchecks->next;
186 }
187 char *result = NULL;
188 xasprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown);
189 return result;
190}
191
192/*
193 * Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states
194 */
195mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) {
196 if (check.state_set_explicitly) {
197 return check.state;
198 }
199
200 mp_subcheck_list *scl = check.subchecks;
201 mp_state_enum result = check.default_state;
202
203 while (scl != NULL) {
204 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
205 scl = scl->next;
206 }
207
208 return result;
209}
210
211/*
212 * Generate the result state of a mp_check object based on it's own state and it's subchecks states
213 */
214mp_state_enum mp_compute_check_state(const mp_check check) {
215 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
216
217 mp_subcheck_list *scl = check.subchecks;
218 mp_state_enum result = STATE_OK;
219
220 while (scl != NULL) {
221 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
222 scl = scl->next;
223 }
224
225 return result;
226}
227
228/*
229 * Generate output string for a mp_check object
230 * Non static to be available for testing functions
231 */
232char *mp_fmt_output(mp_check check) {
233 char *result = NULL;
234
235 switch (check.format) {
236 case MP_FORMAT_SUMMARY_ONLY:
237 if (check.summary == NULL) {
238 check.summary = get_subcheck_summary(check);
239 }
240
241 xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary);
242 return result;
243
244 case MP_FORMAT_ONE_LINE: {
245 /* SERVICE STATUS: First line of output | First part of performance data
246 * Any number of subsequent lines of output, but note that buffers
247 * may have a limited size | Second part of performance data, which
248 * may have continuation lines, too
249 */
250 if (check.summary == NULL) {
251 check.summary = get_subcheck_summary(check);
252 }
253
254 xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary);
255
256 mp_subcheck_list *subchecks = check.subchecks;
257
258 while (subchecks != NULL) {
259 xasprintf(&result, "%s - %s", result, fmt_subcheck_output(MP_FORMAT_ONE_LINE, subchecks->subcheck, 1));
260 subchecks = subchecks->next;
261 }
262
263 return result;
264 }
265 case MP_FORMAT_ICINGA_WEB_2: {
266 if (check.summary == NULL) {
267 check.summary = get_subcheck_summary(check);
268 }
269
270 xasprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary);
271
272 mp_subcheck_list *subchecks = check.subchecks;
273
274 while (subchecks != NULL) {
275 xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_ICINGA_WEB_2, subchecks->subcheck, 1));
276 subchecks = subchecks->next;
277 }
278
279 char *pd_string = NULL;
280 subchecks = check.subchecks;
281
282 while (subchecks != NULL) {
283 if (pd_string == NULL) {
284 xasprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
285 } else {
286 xasprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck));
287 }
288
289 subchecks = subchecks->next;
290 }
291
292 if (pd_string != NULL && strlen(pd_string) > 0) {
293 xasprintf(&result, "%s|%s", result, pd_string);
294 }
295
296 break;
297 }
298 case MP_FORMAT_TEST_JSON: {
299 cJSON *resultObject = cJSON_CreateObject();
300 if (resultObject == NULL) {
301 die(STATE_UNKNOWN, "cJSON_CreateObject failed");
302 }
303
304 cJSON *resultState = cJSON_CreateString(state_text(mp_compute_check_state(check)));
305 cJSON_AddItemToObject(resultObject, "state", resultState);
306
307 if (check.summary == NULL) {
308 check.summary = get_subcheck_summary(check);
309 }
310
311 cJSON *summary = cJSON_CreateString(check.summary);
312 cJSON_AddItemToObject(resultObject, "summary", summary);
313
314 if (check.subchecks != NULL) {
315 cJSON *subchecks = cJSON_CreateArray();
316
317 mp_subcheck_list *sc = check.subchecks;
318
319 while (sc != NULL) {
320 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
321 cJSON_AddItemToArray(subchecks, sc_json);
322 sc = sc->next;
323 }
324
325 cJSON_AddItemToObject(resultObject, "checks", subchecks);
326 }
327
328 result = cJSON_PrintUnformatted(resultObject);
329 break;
330 }
331 default:
332 die(STATE_UNKNOWN, "Invalid format");
333 }
334
335 return result;
336}
337
338/*
339 * Helper function to properly indent the output lines when using multiline
340 * formats
341 */
342static char *generate_indentation_string(unsigned int indentation) {
343 char *result = calloc(indentation + 1, sizeof(char));
344
345 for (unsigned int i = 0; i < indentation; i++) {
346 result[i] = '\t';
347 }
348
349 return result;
350}
351
352/*
353 * Helper function to generate the output string of mp_subcheck
354 */
355static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) {
356 char *result = NULL;
357 mp_subcheck_list *subchecks = NULL;
358
359 switch (output_format) {
360 case MP_FORMAT_ICINGA_WEB_2:
361 xasprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)),
362 check.output);
363
364 subchecks = check.subchecks;
365
366 while (subchecks != NULL) {
367 xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
368 subchecks = subchecks->next;
369 }
370 return result;
371 case MP_FORMAT_ONE_LINE:
372 xasprintf(&result, "[%s] - %s", state_text(mp_compute_subcheck_state(check)), check.output);
373
374 subchecks = check.subchecks;
375
376 while (subchecks != NULL) {
377 xasprintf(&result, " - %s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
378 subchecks = subchecks->next;
379 }
380 return result;
381 case MP_FORMAT_SUMMARY_ONLY:
382 return result;
383 default:
384 die(STATE_UNKNOWN, "Invalid format");
385 }
386}
387
388static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) {
389 cJSON *result = cJSON_CreateObject();
390 cJSON *output = cJSON_CreateString(subcheck.output);
391 cJSON_AddItemToObject(result, "output", output);
392 cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck)));
393 cJSON_AddItemToObject(result, "state", state);
394
395 if (subcheck.subchecks != NULL) {
396 cJSON *subchecks = cJSON_CreateArray();
397
398 mp_subcheck_list *sc = subcheck.subchecks;
399
400 while (sc != NULL) {
401 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
402 cJSON_AddItemToArray(subchecks, sc_json);
403 sc = sc->next;
404 }
405
406 cJSON_AddItemToObject(result, "checks", subchecks);
407 }
408
409 return result;
410}
411
412/*
413 * Wrapper function to print the output string of a mp_check object
414 * Use this in concrete plugins.
415 */
416void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); }
417
418/*
419 * Convenience function to print the output string of a mp_check object and exit
420 * the program with the resulting state.
421 * Intended to be used to exit a monitoring plugin.
422 */
423void mp_exit(mp_check check) {
424 mp_print_output(check);
425 if (check.format == MP_FORMAT_TEST_JSON) {
426 exit(0);
427 }
428
429 exit(mp_compute_check_state(check));
430}
431
432/*
433 * Function to set the result state of a mp_subcheck object explicitly.
434 * This will overwrite the default state AND states derived from it's subchecks
435 */
436mp_subcheck mp_set_subcheck_state(mp_subcheck check, mp_state_enum state) {
437 check.state = state;
438 check.state_set_explicitly = true;
439 return check;
440}
441
442/*
443 * Function to set the default result state of a mp_subcheck object. This state
444 * will be used if neither an explicit state is set (see *mp_set_subcheck_state*)
445 * nor does it include other subchecks
446 */
447mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state) {
448 check.default_state = state;
449 return check;
450}
451
452char *mp_output_format_map[] = {
453 [MP_FORMAT_ONE_LINE] = "one-line",
454 [MP_FORMAT_ICINGA_WEB_2] = "icingaweb2",
455 [MP_FORMAT_SUMMARY_ONLY] = "summary-only",
456 [MP_FORMAT_TEST_JSON] = "mp-test-json",
457};
458
459/*
460 * Function to parse the output from a string
461 */
462parsed_output_format mp_parse_output_format(char *format_string) {
463 parsed_output_format result = {
464 .parsing_success = false,
465 .output_format = MP_FORMAT_DEFAULT,
466 };
467
468 for (mp_output_format i = 0; i < (sizeof(mp_output_format_map) / sizeof(char *)); i++) {
469 if (strcasecmp(mp_output_format_map[i], format_string) == 0) {
470 result.parsing_success = true;
471 result.output_format = i;
472 break;
473 }
474 }
475
476 return result;
477}