summaryrefslogtreecommitdiffstats
path: root/lib/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/output.c')
-rw-r--r--lib/output.c778
1 files changed, 778 insertions, 0 deletions
diff --git a/lib/output.c b/lib/output.c
new file mode 100644
index 00000000..b1e91231
--- /dev/null
+++ b/lib/output.c
@@ -0,0 +1,778 @@
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#include "perfdata.h"
12#include "states.h"
13
14// == Global variables
15static mp_output_format output_format = MP_FORMAT_DEFAULT;
16static mp_output_detail_level level_of_detail = MP_DETAIL_ALL;
17
18// == Prototypes ==
19static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
20 unsigned int indentation);
21static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
22
23// mp_compare_state compares two state arguments
24// if *first* is WORSE than *second*, the result is < 0
25// if *first* is equal to *second*, the result is 0
26// if *first* is BETTER (less bad) the result is > 0
27static int mp_compare_state(mp_state_enum first, mp_state_enum second);
28
29// == Implementation ==
30
31// get_subcheck_failed_output retrieves the output of the
32// worst and first leave node in a subcheck tree
33// or NULL if no such message exists
34// the return string is a copy of the original
35static char *get_subcheck_failed_output(const mp_subcheck tree) {
36 if (tree.subchecks == NULL) {
37 // this is a leave node
38 if (mp_compute_subcheck_state(tree) == STATE_OK) {
39 // ALL OK, nothing to return
40 return NULL;
41 }
42
43 char *result = strdup(tree.output);
44 return result;
45 }
46
47 // not a leave node, go through tree
48 mp_subcheck_list *subcheck = tree.subchecks;
49 mp_subcheck *worst_first_node = NULL;
50 mp_state_enum worst_state = STATE_OK;
51 while (subcheck != NULL) {
52 mp_state_enum current = mp_compute_subcheck_state(subcheck->subcheck);
53 if (mp_compare_state(current, worst_state) < 0) {
54 worst_first_node = &subcheck->subcheck;
55 }
56
57 subcheck = subcheck->next;
58 }
59
60 if (worst_first_node == NULL) {
61 // we did not find a failed subcheck, return the output
62 // of the current node
63 char *result = strdup(tree.output);
64 return result;
65 }
66
67 return get_subcheck_failed_output(*worst_first_node);
68}
69
70/*
71 * Generate output string for a mp_subcheck object
72 */
73static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
74 char *result = strdup("");
75 int added = 0;
76
77 if (check.perfdata != NULL) {
78 added = asprintf(&result, "%s", pd_list_to_string(*check.perfdata));
79 }
80
81 if (check.subchecks == NULL) {
82 // No subchecks, return here
83 return result;
84 }
85
86 mp_subcheck_list *subchecks = check.subchecks;
87
88 while (subchecks != NULL) {
89 if (added > 0) {
90 added = asprintf(&result, "%s %s", result, fmt_subcheck_perfdata(subchecks->subcheck));
91 } else {
92 // TODO free previous result here?
93 added = asprintf(&result, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
94 }
95
96 subchecks = subchecks->next;
97 }
98
99 return result;
100}
101
102/*
103 * Initialiser for a mp_check object. Always use this to get a new one!
104 * It sets useful defaults
105 */
106mp_check mp_check_init(void) {
107 mp_check check = {
108 .ok_summary = NULL,
109 .evaluation_function = &mp_eval_check_default,
110 .default_output_override = NULL,
111 .default_output_override_content = NULL,
112 };
113 return check;
114}
115
116/*
117 * Initialiser for a mp_subcheck object. Always use this to get a new one!
118 * It sets useful defaults
119 */
120mp_subcheck mp_subcheck_init(void) {
121 mp_subcheck tmp = {0};
122 tmp.default_state = STATE_UNKNOWN; // Default state is unknown
123 tmp.state_set_explicitly = false;
124 return tmp;
125}
126
127/*
128 * Add a subcheck to a (the one and only) check object
129 */
130int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck subcheck) {
131 assert(subcheck.output != NULL); // There must be output in a subcheck
132
133 mp_subcheck_list *tmp = NULL;
134
135 if (check->subchecks == NULL) {
136 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
137 if (check->subchecks == NULL) {
138 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
139 }
140
141 check->subchecks->subcheck = subcheck;
142 check->subchecks->next = NULL;
143 } else {
144 // Search for the end
145 tmp = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
146 if (tmp == NULL) {
147 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
148 }
149
150 tmp->subcheck = subcheck;
151 tmp->next = check->subchecks;
152
153 check->subchecks = tmp;
154 }
155
156 return 0;
157}
158
159/*
160 * Add a mp_perfdata data point to a mp_subcheck object
161 */
162void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata perfData) {
163 if (check->perfdata == NULL) {
164 check->perfdata = pd_list_init();
165 }
166 pd_list_append(check->perfdata, perfData);
167}
168
169/*
170 * Add a mp_subcheck object to another one. The seconde mp_subcheck (argument) is the lower in the
171 * hierarchy
172 */
173int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
174 if (subcheck.output == NULL) {
175 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__,
176 "Sub check output is NULL");
177 }
178
179 mp_subcheck_list *tmp = NULL;
180
181 if (check->subchecks == NULL) {
182 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
183 if (check->subchecks == NULL) {
184 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
185 }
186
187 tmp = check->subchecks;
188 } else {
189 // Search for the end
190 tmp = check->subchecks;
191
192 while (tmp->next != NULL) {
193 tmp = tmp->next;
194 }
195
196 tmp->next = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
197 if (tmp->next == NULL) {
198 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
199 }
200
201 tmp = tmp->next;
202 }
203
204 tmp->subcheck = subcheck;
205
206 return 0;
207}
208
209/*
210 * Add a manual summary to a mp_check object, effectively replacing
211 * the autogenerated one
212 */
213void mp_set_summary(mp_check check[static 1], char *summary) { check->summary = strdup(summary); }
214
215/*
216 * set the summary for the OK state
217 * this allows to set the content in the first line of the plugin
218 * if the overall state is OK
219 */
220void mp_set_ok_summary(mp_check check[static 1], char *ok_summary) {
221 check->ok_summary = strdup(ok_summary);
222}
223/*
224 * Generate the summary string of a mp_check object based on its subchecks
225 */
226char *get_subcheck_summary(mp_check check) {
227 mp_subcheck_list *subchecks = check.subchecks;
228
229 unsigned int ok_count = 0;
230 unsigned int warning_count = 0;
231 unsigned int critical_count = 0;
232 unsigned int unknown_count = 0;
233 char *result = NULL;
234 while (subchecks != NULL) {
235 switch (mp_compute_subcheck_state(subchecks->subcheck)) {
236 case STATE_OK:
237 ok_count++;
238 break;
239 case STATE_WARNING:
240 if (critical_count == 0 && unknown_count == 0 && warning_count == 0) {
241 // set summary to first warning subcheck output
242 asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck));
243 }
244 warning_count++;
245 break;
246 case STATE_CRITICAL:
247 if (critical_count == 0) {
248 // set summary to first critical subcheck output
249 asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck));
250 }
251 critical_count++;
252 break;
253 case STATE_UNKNOWN:
254 if (critical_count == 0 && unknown_count == 0) {
255 // set summary to first unknown subcheck output
256 asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck));
257 }
258 unknown_count++;
259 break;
260 default:
261 die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary");
262 }
263 subchecks = subchecks->next;
264 }
265
266 if (result == NULL) {
267 // Nothing in result yet, we must be in an OK state
268 if (check.ok_summary != NULL) {
269 asprintf(&result, "%s", check.ok_summary);
270 } else if (ok_count > 0) {
271 asprintf(&result, "ok=%d", ok_count);
272 }
273 }
274
275 return result;
276}
277
278mp_state_enum mp_compute_subcheck_state(const mp_subcheck subcheck) {
279 if (subcheck.evaluation_function == NULL) {
280 return mp_eval_subcheck_default(subcheck);
281 }
282 return subcheck.evaluation_function(subcheck);
283}
284
285/*
286 * Generate the result state of a mp_subcheck object based on its own state and its subchecks
287 * states
288 */
289mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck) {
290 if (subcheck.evaluation_function != NULL) {
291 return subcheck.evaluation_function(subcheck);
292 }
293
294 if (subcheck.state_set_explicitly) {
295 return subcheck.state;
296 }
297
298 mp_subcheck_list *scl = subcheck.subchecks;
299
300 if (scl == NULL) {
301 return subcheck.default_state;
302 }
303
304 mp_state_enum result = STATE_OK;
305
306 while (scl != NULL) {
307 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
308 scl = scl->next;
309 }
310
311 return result;
312}
313
314mp_state_enum mp_compute_check_state(const mp_check check) {
315 // just a safety check
316 if (check.evaluation_function == NULL) {
317 return mp_eval_check_default(check);
318 }
319 return check.evaluation_function(check);
320}
321
322/*
323 * Generate the result state of a mp_check object based on its own state and its subchecks states
324 */
325mp_state_enum mp_eval_check_default(const mp_check check) {
326 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
327
328 mp_subcheck_list *scl = check.subchecks;
329 mp_state_enum result = STATE_OK;
330
331 while (scl != NULL) {
332 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
333 scl = scl->next;
334 }
335
336 return result;
337}
338
339// Remove troublesome symbols from plugin output
340char *sanitize_output_insitu(char *input) {
341 if (input == NULL) {
342 return input;
343 }
344
345 for (char *walker = input; *walker != '\0'; walker++) {
346 if (*walker == '|') {
347 *walker = ' ';
348 }
349 }
350
351 return input;
352}
353
354/*
355 * Generate output string for a mp_check object
356 * Non static to be available for testing functions
357 */
358char *mp_fmt_output(mp_check check) {
359 char *result = NULL;
360
361 switch (output_format) {
362 case MP_FORMAT_MULTI_LINE: {
363 if (check.default_output_override != NULL) {
364 result = check.default_output_override(check.default_output_override_content);
365 break;
366 }
367
368 if (check.summary == NULL) {
369 check.summary = get_subcheck_summary(check);
370 }
371
372 asprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary);
373
374 mp_subcheck_list *subchecks = check.subchecks;
375
376 while (subchecks != NULL) {
377 if (level_of_detail == MP_DETAIL_ALL ||
378 mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) {
379 asprintf(&result, "%s\n%s", result,
380 fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1));
381 }
382 subchecks = subchecks->next;
383 }
384
385 char *pd_string = NULL;
386 subchecks = check.subchecks;
387
388 while (subchecks != NULL) {
389 if (pd_string == NULL) {
390 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
391 } else {
392 asprintf(&pd_string, "%s %s", pd_string,
393 fmt_subcheck_perfdata(subchecks->subcheck));
394 }
395
396 subchecks = subchecks->next;
397 }
398
399 result = sanitize_output_insitu(result);
400
401 if (pd_string != NULL && strlen(pd_string) > 0) {
402 asprintf(&result, "%s|%s", result, pd_string);
403 }
404
405 break;
406 }
407 case MP_FORMAT_TEST_JSON: {
408 cJSON *resultObject = cJSON_CreateObject();
409 if (resultObject == NULL) {
410 die(STATE_UNKNOWN, "cJSON_CreateObject failed");
411 }
412
413 cJSON *resultState = cJSON_CreateString(state_text(mp_compute_check_state(check)));
414 cJSON_AddItemToObject(resultObject, "state", resultState);
415
416 if (check.summary == NULL) {
417 check.summary = get_subcheck_summary(check);
418 }
419
420 cJSON *summary = cJSON_CreateString(check.summary);
421 cJSON_AddItemToObject(resultObject, "summary", summary);
422
423 if (check.subchecks != NULL) {
424 cJSON *subchecks = cJSON_CreateArray();
425
426 mp_subcheck_list *sc = check.subchecks;
427
428 while (sc != NULL) {
429 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
430 cJSON_AddItemToArray(subchecks, sc_json);
431 sc = sc->next;
432 }
433
434 cJSON_AddItemToObject(resultObject, "checks", subchecks);
435 }
436
437 result = cJSON_PrintUnformatted(resultObject);
438 break;
439 }
440 default:
441 die(STATE_UNKNOWN, "Invalid format");
442 }
443
444 return result;
445}
446
447/*
448 * Helper function to properly indent the output lines when using multiline
449 * formats
450 */
451static char *generate_indentation_string(unsigned int indentation) {
452 char *result = calloc(indentation + 1, sizeof(char));
453
454 for (unsigned int i = 0; i < indentation; i++) {
455 result[i] = '\t';
456 }
457
458 return result;
459}
460
461/*
462 * Helper function to generate the output string of mp_subcheck
463 */
464static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
465 unsigned int indentation) {
466 char *result = NULL;
467 mp_subcheck_list *subchecks = NULL;
468
469 switch (output_format) {
470 case MP_FORMAT_MULTI_LINE: {
471 char *tmp_string = NULL;
472 if ((tmp_string = strchr(check.output, '\n')) != NULL) {
473 // This is a multiline string, put the correct indentation in before proceeding
474 char *intermediate_string = "";
475 bool have_residual_chars = false;
476
477 while (tmp_string != NULL) {
478 *tmp_string = '\0';
479 asprintf(&intermediate_string, "%s%s\n%s", intermediate_string, check.output,
480 generate_indentation_string(
481 indentation + 1)); // one more indentation to make it look better
482
483 if (*(tmp_string + 1) != '\0') {
484 check.output = tmp_string + 1;
485 have_residual_chars = true;
486 } else {
487 // Null after the \n, so this is the end
488 have_residual_chars = false;
489 break;
490 }
491
492 tmp_string = strchr(check.output, '\n');
493 }
494
495 // add the rest (if any)
496 if (have_residual_chars) {
497 char *tmp = check.output;
498 xasprintf(&check.output, "%s%s%s", intermediate_string,
499 generate_indentation_string(indentation + 1), tmp);
500 } else {
501 check.output = intermediate_string;
502 }
503 }
504 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation),
505 state_text(mp_compute_subcheck_state(check)), check.output);
506
507 subchecks = check.subchecks;
508
509 while (subchecks != NULL) {
510 asprintf(&result, "%s\n%s", result,
511 fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
512 subchecks = subchecks->next;
513 }
514 return result;
515 }
516 default:
517 die(STATE_UNKNOWN, "Invalid format");
518 }
519}
520
521static inline cJSON *json_serialise_pd_value(mp_perfdata_value value) {
522 cJSON *result = cJSON_CreateObject();
523
524 switch (value.type) {
525 case PD_TYPE_DOUBLE:
526 cJSON_AddStringToObject(result, "type", "double");
527 break;
528 case PD_TYPE_INT:
529 cJSON_AddStringToObject(result, "type", "int");
530 break;
531 case PD_TYPE_UINT:
532 cJSON_AddStringToObject(result, "type", "uint");
533 break;
534 case PD_TYPE_NONE:
535 die(STATE_UNKNOWN, "Perfdata type was None in json_serialise_pd_value");
536 }
537 cJSON_AddStringToObject(result, "value", pd_value_to_string(value));
538
539 return result;
540}
541
542static inline cJSON *json_serialise_range(mp_range range) {
543 cJSON *result = cJSON_CreateObject();
544
545 if (range.alert_on_inside_range) {
546 cJSON_AddBoolToObject(result, "alert_on_inside", true);
547 } else {
548 cJSON_AddBoolToObject(result, "alert_on_inside", false);
549 }
550
551 if (range.end_infinity) {
552 cJSON_AddStringToObject(result, "end", "inf");
553 } else {
554 cJSON_AddItemToObject(result, "end", json_serialise_pd_value(range.end));
555 }
556
557 if (range.start_infinity) {
558 cJSON_AddStringToObject(result, "start", "inf");
559 } else {
560 cJSON_AddItemToObject(result, "start", json_serialise_pd_value(range.end));
561 }
562
563 return result;
564}
565
566static inline cJSON *json_serialise_pd(mp_perfdata pd_val) {
567 cJSON *result = cJSON_CreateObject();
568
569 // Label
570 cJSON_AddStringToObject(result, "label", pd_val.label);
571
572 // Value
573 cJSON_AddItemToObject(result, "value", json_serialise_pd_value(pd_val.value));
574
575 // Uom
576 cJSON_AddStringToObject(result, "uom", pd_val.uom);
577
578 // Warn/Crit
579 if (pd_val.warn_present) {
580 cJSON *warn = json_serialise_range(pd_val.warn);
581 cJSON_AddItemToObject(result, "warn", warn);
582 }
583 if (pd_val.crit_present) {
584 cJSON *crit = json_serialise_range(pd_val.crit);
585 cJSON_AddItemToObject(result, "crit", crit);
586 }
587
588 if (pd_val.min_present) {
589 cJSON_AddItemToObject(result, "min", json_serialise_pd_value(pd_val.min));
590 }
591 if (pd_val.max_present) {
592 cJSON_AddItemToObject(result, "max", json_serialise_pd_value(pd_val.max));
593 }
594
595 return result;
596}
597
598static inline cJSON *json_serialise_pd_list(pd_list *list) {
599 cJSON *result = cJSON_CreateArray();
600
601 do {
602 cJSON *pd_value = json_serialise_pd(list->data);
603 cJSON_AddItemToArray(result, pd_value);
604 list = list->next;
605 } while (list != NULL);
606
607 return result;
608}
609
610static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) {
611 cJSON *result = cJSON_CreateObject();
612
613 // Human readable output
614 cJSON *output = cJSON_CreateString(subcheck.output);
615 cJSON_AddItemToObject(result, "output", output);
616
617 // Test state (aka Exit Code)
618 cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck)));
619 cJSON_AddItemToObject(result, "state", state);
620
621 // Perfdata
622 if (subcheck.perfdata != NULL) {
623 cJSON *perfdata = json_serialise_pd_list(subcheck.perfdata);
624 cJSON_AddItemToObject(result, "perfdata", perfdata);
625 }
626
627 if (subcheck.subchecks != NULL) {
628 cJSON *subchecks = cJSON_CreateArray();
629
630 mp_subcheck_list *sc = subcheck.subchecks;
631
632 while (sc != NULL) {
633 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
634 cJSON_AddItemToArray(subchecks, sc_json);
635 sc = sc->next;
636 }
637
638 cJSON_AddItemToObject(result, "checks", subchecks);
639 }
640
641 return result;
642}
643
644/*
645 * Wrapper function to print the output string of a mp_check object
646 * Use this in concrete plugins.
647 */
648void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); }
649
650/*
651 * Convenience function to print the output string of a mp_check object and exit
652 * the program with the resulting state.
653 * Intended to be used to exit a monitoring plugin.
654 */
655void mp_exit(mp_check check) {
656 mp_print_output(check);
657 if (output_format == MP_FORMAT_TEST_JSON) {
658 exit(0);
659 }
660
661 exit(mp_compute_check_state(check));
662}
663
664/*
665 * Function to set the result state of a mp_subcheck object explicitly.
666 * This will overwrite the default state AND states derived from it's subchecks
667 */
668mp_subcheck mp_set_subcheck_state(mp_subcheck check, mp_state_enum state) {
669 check.state = state;
670 check.state_set_explicitly = true;
671 return check;
672}
673
674/*
675 * Function to set the default result state of a mp_subcheck object. This state
676 * will be used if neither an explicit state is set (see *mp_set_subcheck_state*)
677 * nor does it include other subchecks
678 */
679mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state) {
680 check.default_state = state;
681 return check;
682}
683
684char *mp_output_format_map[] = {
685 [MP_FORMAT_MULTI_LINE] = "multi-line",
686 [MP_FORMAT_TEST_JSON] = "mp-test-json",
687};
688
689/*
690 * Function to parse the output from a string
691 */
692parsed_output_format mp_parse_output_format(char *format_string) {
693 parsed_output_format result = {
694 .parsing_success = false,
695 .output_format = MP_FORMAT_DEFAULT,
696 };
697
698 for (mp_output_format i = 0; i < (sizeof(mp_output_format_map) / sizeof(char *)); i++) {
699 if (strcasecmp(mp_output_format_map[i], format_string) == 0) {
700 result.parsing_success = true;
701 result.output_format = i;
702 break;
703 }
704 }
705
706 return result;
707}
708
709void mp_set_format(mp_output_format format) { output_format = format; }
710
711mp_output_format mp_get_format(void) { return output_format; }
712
713void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; }
714
715mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; }
716
717mp_state_enum mp_eval_ok(mp_check overall) {
718 (void)overall;
719 return STATE_OK;
720}
721
722mp_state_enum mp_eval_warning(mp_check overall) {
723 (void)overall;
724 return STATE_WARNING;
725}
726
727mp_state_enum mp_eval_critical(mp_check overall) {
728 (void)overall;
729 return STATE_CRITICAL;
730}
731
732mp_state_enum mp_eval_unknown(mp_check overall) {
733 (void)overall;
734 return STATE_UNKNOWN;
735}
736
737static int mp_compare_state(mp_state_enum first, mp_state_enum second) {
738 switch (first) {
739 case STATE_OK:
740 switch (second) {
741 case STATE_OK:
742 return 0;
743 case STATE_WARNING:
744 case STATE_UNKNOWN:
745 case STATE_CRITICAL:
746 return 1;
747 }
748 case STATE_WARNING:
749 switch (second) {
750 case STATE_OK:
751 return -1;
752 case STATE_WARNING:
753 return 0;
754 case STATE_UNKNOWN:
755 case STATE_CRITICAL:
756 return 1;
757 }
758 case STATE_UNKNOWN:
759 switch (second) {
760 case STATE_OK:
761 case STATE_WARNING:
762 return -1;
763 case STATE_UNKNOWN:
764 return 0;
765 case STATE_CRITICAL:
766 return 1;
767 }
768 case STATE_CRITICAL:
769 switch (second) {
770 case STATE_OK:
771 case STATE_WARNING:
772 case STATE_UNKNOWN:
773 return -1;
774 case STATE_CRITICAL:
775 return 0;
776 }
777 }
778}