diff options
Diffstat (limited to 'lib/perfdata.c')
-rw-r--r-- | lib/perfdata.c | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/lib/perfdata.c b/lib/perfdata.c new file mode 100644 index 00000000..661756c5 --- /dev/null +++ b/lib/perfdata.c | |||
@@ -0,0 +1,516 @@ | |||
1 | #include "./perfdata.h" | ||
2 | #include "../plugins/common.h" | ||
3 | #include "../plugins/utils.h" | ||
4 | #include "utils_base.h" | ||
5 | |||
6 | #include <assert.h> | ||
7 | #include <limits.h> | ||
8 | #include <stdlib.h> | ||
9 | |||
10 | char *pd_value_to_string(const mp_perfdata_value pd) { | ||
11 | char *result = NULL; | ||
12 | |||
13 | assert(pd.type != PD_TYPE_NONE); | ||
14 | |||
15 | switch (pd.type) { | ||
16 | case PD_TYPE_INT: | ||
17 | asprintf(&result, "%lli", pd.pd_int); | ||
18 | break; | ||
19 | case PD_TYPE_UINT: | ||
20 | asprintf(&result, "%llu", pd.pd_int); | ||
21 | break; | ||
22 | case PD_TYPE_DOUBLE: | ||
23 | asprintf(&result, "%f", pd.pd_double); | ||
24 | break; | ||
25 | default: | ||
26 | // die here | ||
27 | die(STATE_UNKNOWN, "Invalid mp_perfdata mode\n"); | ||
28 | } | ||
29 | |||
30 | return result; | ||
31 | } | ||
32 | |||
33 | char *pd_to_string(mp_perfdata pd) { | ||
34 | assert(pd.label != NULL); | ||
35 | char *result = NULL; | ||
36 | asprintf(&result, "%s=", pd.label); | ||
37 | |||
38 | asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); | ||
39 | |||
40 | if (pd.uom != NULL) { | ||
41 | asprintf(&result, "%s%s", result, pd.uom); | ||
42 | } | ||
43 | |||
44 | if (pd.warn_present) { | ||
45 | asprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn)); | ||
46 | } else { | ||
47 | asprintf(&result, "%s;", result); | ||
48 | } | ||
49 | |||
50 | if (pd.crit_present) { | ||
51 | asprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit)); | ||
52 | } else { | ||
53 | asprintf(&result, "%s;", result); | ||
54 | } | ||
55 | if (pd.min_present) { | ||
56 | asprintf(&result, "%s;%s", result, pd_value_to_string(pd.min)); | ||
57 | } else { | ||
58 | asprintf(&result, "%s;", result); | ||
59 | } | ||
60 | |||
61 | if (pd.max_present) { | ||
62 | asprintf(&result, "%s;%s", result, pd_value_to_string(pd.max)); | ||
63 | } | ||
64 | |||
65 | /*printf("pd_to_string: %s\n", result); */ | ||
66 | |||
67 | return result; | ||
68 | } | ||
69 | |||
70 | char *pd_list_to_string(const pd_list pd) { | ||
71 | char *result = pd_to_string(pd.data); | ||
72 | |||
73 | for (pd_list *elem = pd.next; elem != NULL; elem = elem->next) { | ||
74 | asprintf(&result, "%s %s", result, pd_to_string(elem->data)); | ||
75 | } | ||
76 | |||
77 | return result; | ||
78 | } | ||
79 | |||
80 | mp_perfdata perfdata_init() { | ||
81 | mp_perfdata pd = {}; | ||
82 | return pd; | ||
83 | } | ||
84 | |||
85 | pd_list *pd_list_init() { | ||
86 | pd_list *tmp = (pd_list *)calloc(1, sizeof(pd_list)); | ||
87 | if (tmp == NULL) { | ||
88 | die(STATE_UNKNOWN, "calloc failed\n"); | ||
89 | } | ||
90 | tmp->next = NULL; | ||
91 | return tmp; | ||
92 | } | ||
93 | |||
94 | mp_range mp_range_init() { | ||
95 | mp_range result = { | ||
96 | .alert_on_inside_range = OUTSIDE, | ||
97 | .start = {}, | ||
98 | .start_infinity = true, | ||
99 | .end = {}, | ||
100 | .end_infinity = true, | ||
101 | }; | ||
102 | |||
103 | return result; | ||
104 | } | ||
105 | |||
106 | mp_range mp_range_set_start(mp_range input, mp_perfdata_value perf_val) { | ||
107 | input.start = perf_val; | ||
108 | input.start_infinity = false; | ||
109 | return input; | ||
110 | } | ||
111 | |||
112 | mp_range mp_range_set_end(mp_range input, mp_perfdata_value perf_val) { | ||
113 | input.end = perf_val; | ||
114 | input.end_infinity = false; | ||
115 | return input; | ||
116 | } | ||
117 | |||
118 | void pd_list_append(pd_list pdl[1], const mp_perfdata pd) { | ||
119 | assert(pdl != NULL); | ||
120 | |||
121 | if (pdl->data.value.type == PD_TYPE_NONE) { | ||
122 | // first entry is still empty | ||
123 | pdl->data = pd; | ||
124 | } else { | ||
125 | // find last element in the list | ||
126 | pd_list *curr = pdl; | ||
127 | pd_list *next = pdl->next; | ||
128 | |||
129 | while (next != NULL) { | ||
130 | curr = next; | ||
131 | next = next->next; | ||
132 | } | ||
133 | |||
134 | if (curr->data.value.type == PD_TYPE_NONE) { | ||
135 | // still empty | ||
136 | curr->data = pd; | ||
137 | } else { | ||
138 | // new a new one | ||
139 | curr->next = pd_list_init(); | ||
140 | curr->next->data = pd; | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void pd_list_free(pd_list pdl[1]) { | ||
146 | while (pdl != NULL) { | ||
147 | pd_list *old = pdl; | ||
148 | pdl = pdl->next; | ||
149 | free(old); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * returns -1 if a < b, 0 if a == b, 1 if a > b | ||
155 | */ | ||
156 | int cmp_perfdata_value(const mp_perfdata_value a, const mp_perfdata_value b) { | ||
157 | // Test if types are different | ||
158 | if (a.type == b.type) { | ||
159 | |||
160 | switch (a.type) { | ||
161 | case PD_TYPE_UINT: | ||
162 | if (a.pd_uint < b.pd_uint) { | ||
163 | return -1; | ||
164 | } else if (a.pd_uint == b.pd_uint) { | ||
165 | return 0; | ||
166 | } else { | ||
167 | return 1; | ||
168 | } | ||
169 | break; | ||
170 | case PD_TYPE_INT: | ||
171 | if (a.pd_int < b.pd_int) { | ||
172 | return -1; | ||
173 | } else if (a.pd_int == b.pd_int) { | ||
174 | return 0; | ||
175 | } else { | ||
176 | return 1; | ||
177 | } | ||
178 | break; | ||
179 | case PD_TYPE_DOUBLE: | ||
180 | if (a.pd_int < b.pd_int) { | ||
181 | return -1; | ||
182 | } else if (a.pd_int == b.pd_int) { | ||
183 | return 0; | ||
184 | } else { | ||
185 | return 1; | ||
186 | } | ||
187 | break; | ||
188 | default: | ||
189 | die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | // Get dirty here | ||
194 | long double floating_a = 0; | ||
195 | |||
196 | switch (a.type) { | ||
197 | case PD_TYPE_UINT: | ||
198 | floating_a = a.pd_uint; | ||
199 | break; | ||
200 | case PD_TYPE_INT: | ||
201 | floating_a = a.pd_int; | ||
202 | break; | ||
203 | case PD_TYPE_DOUBLE: | ||
204 | floating_a = a.pd_double; | ||
205 | break; | ||
206 | default: | ||
207 | die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__); | ||
208 | } | ||
209 | |||
210 | long double floating_b = 0; | ||
211 | switch (b.type) { | ||
212 | case PD_TYPE_UINT: | ||
213 | floating_b = b.pd_uint; | ||
214 | break; | ||
215 | case PD_TYPE_INT: | ||
216 | floating_b = b.pd_int; | ||
217 | break; | ||
218 | case PD_TYPE_DOUBLE: | ||
219 | floating_b = b.pd_double; | ||
220 | break; | ||
221 | default: | ||
222 | die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__); | ||
223 | } | ||
224 | |||
225 | if (floating_a < floating_b) { | ||
226 | return -1; | ||
227 | } | ||
228 | if (floating_a == floating_b) { | ||
229 | return 0; | ||
230 | } | ||
231 | return 1; | ||
232 | } | ||
233 | |||
234 | char *mp_range_to_string(const mp_range input) { | ||
235 | char *result = ""; | ||
236 | if (input.alert_on_inside_range == INSIDE) { | ||
237 | asprintf(&result, "@"); | ||
238 | } | ||
239 | |||
240 | if (input.start_infinity) { | ||
241 | asprintf(&result, "%s~:", result); | ||
242 | } else { | ||
243 | asprintf(&result, "%s%s:", result, pd_value_to_string(input.start)); | ||
244 | } | ||
245 | |||
246 | if (!input.end_infinity) { | ||
247 | asprintf(&result, "%s%s", result, pd_value_to_string(input.end)); | ||
248 | } | ||
249 | return result; | ||
250 | } | ||
251 | |||
252 | mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { return mp_set_pd_value_double(pd, value); } | ||
253 | |||
254 | mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { | ||
255 | pd.value.pd_double = value; | ||
256 | pd.value.type = PD_TYPE_DOUBLE; | ||
257 | return pd; | ||
258 | } | ||
259 | |||
260 | mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); } | ||
261 | |||
262 | mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } | ||
263 | |||
264 | mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { return mp_set_pd_value_long_long(pd, (long long)value); } | ||
265 | |||
266 | mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) { | ||
267 | return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); | ||
268 | } | ||
269 | |||
270 | mp_perfdata mp_set_pd_value_long_long(mp_perfdata pd, long long value) { | ||
271 | pd.value.pd_int = value; | ||
272 | pd.value.type = PD_TYPE_INT; | ||
273 | return pd; | ||
274 | } | ||
275 | |||
276 | mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata pd, unsigned long long value) { | ||
277 | pd.value.pd_uint = value; | ||
278 | pd.value.type = PD_TYPE_UINT; | ||
279 | return pd; | ||
280 | } | ||
281 | |||
282 | mp_perfdata_value mp_create_pd_value_double(double value) { | ||
283 | mp_perfdata_value res = {0}; | ||
284 | res.type = PD_TYPE_DOUBLE; | ||
285 | res.pd_double = value; | ||
286 | return res; | ||
287 | } | ||
288 | |||
289 | mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); } | ||
290 | |||
291 | mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); } | ||
292 | |||
293 | mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } | ||
294 | |||
295 | mp_perfdata_value mp_create_pd_value_long(long value) { return mp_create_pd_value_long_long((long long)value); } | ||
296 | |||
297 | mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } | ||
298 | |||
299 | mp_perfdata_value mp_create_pd_value_long_long(long long value) { | ||
300 | mp_perfdata_value res = {0}; | ||
301 | res.type = PD_TYPE_INT; | ||
302 | res.pd_int = value; | ||
303 | return res; | ||
304 | } | ||
305 | |||
306 | mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long value) { | ||
307 | mp_perfdata_value res = {0}; | ||
308 | res.type = PD_TYPE_UINT; | ||
309 | res.pd_uint = value; | ||
310 | return res; | ||
311 | } | ||
312 | |||
313 | char *fmt_range(range foo) { return foo.text; } | ||
314 | |||
315 | typedef struct integer_parser_wrapper { | ||
316 | int error; | ||
317 | mp_perfdata_value value; | ||
318 | } integer_parser_wrapper; | ||
319 | |||
320 | typedef struct double_parser_wrapper { | ||
321 | int error; | ||
322 | mp_perfdata_value value; | ||
323 | } double_parser_wrapper; | ||
324 | |||
325 | typedef struct perfdata_value_parser_wrapper { | ||
326 | int error; | ||
327 | mp_perfdata_value value; | ||
328 | } perfdata_value_parser_wrapper; | ||
329 | |||
330 | double_parser_wrapper parse_double(const char *input); | ||
331 | integer_parser_wrapper parse_integer(const char *input); | ||
332 | perfdata_value_parser_wrapper parse_pd_value(const char *input); | ||
333 | |||
334 | mp_range_parsed mp_parse_range_string(const char *input) { | ||
335 | if (input == NULL) { | ||
336 | mp_range_parsed result = { | ||
337 | .error = MP_RANGE_PARSING_FAILURE, | ||
338 | }; | ||
339 | return result; | ||
340 | } | ||
341 | |||
342 | if (strlen(input) == 0) { | ||
343 | mp_range_parsed result = { | ||
344 | .error = MP_RANGE_PARSING_FAILURE, | ||
345 | }; | ||
346 | return result; | ||
347 | } | ||
348 | |||
349 | mp_range_parsed result = { | ||
350 | .range = mp_range_init(), | ||
351 | .error = MP_PARSING_SUCCES, | ||
352 | }; | ||
353 | |||
354 | if (input[0] == '@') { | ||
355 | // found an '@' at beginning, so invert the range logic | ||
356 | result.range.alert_on_inside_range = INSIDE; | ||
357 | |||
358 | // advance the pointer one symbol | ||
359 | input++; | ||
360 | } | ||
361 | |||
362 | char *working_copy = strdup(input); | ||
363 | input = working_copy; | ||
364 | |||
365 | char *separator = index(working_copy, ':'); | ||
366 | if (separator != NULL) { | ||
367 | // Found a separator | ||
368 | // set the separator to 0, so we have two different strings | ||
369 | *separator = '\0'; | ||
370 | |||
371 | if (input[0] == '~') { | ||
372 | // the beginning starts with '~', so it might be infinity | ||
373 | if (&input[1] != separator) { | ||
374 | // the next symbol after '~' is not the separator! | ||
375 | // so input is probably wrong | ||
376 | result.error = MP_RANGE_PARSING_FAILURE; | ||
377 | free(working_copy); | ||
378 | return result; | ||
379 | } | ||
380 | |||
381 | result.range.start_infinity = true; | ||
382 | } else { | ||
383 | // No '~' at the beginning, so this should be a number | ||
384 | result.range.start_infinity = false; | ||
385 | perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input); | ||
386 | |||
387 | if (parsed_pd.error != MP_PARSING_SUCCES) { | ||
388 | result.error = parsed_pd.error; | ||
389 | free(working_copy); | ||
390 | return result; | ||
391 | } | ||
392 | |||
393 | result.range.start = parsed_pd.value; | ||
394 | result.range.start_infinity = false; | ||
395 | } | ||
396 | // got the first part now | ||
397 | // advance the pointer | ||
398 | input = separator + 1; | ||
399 | } | ||
400 | |||
401 | // End part or no separator | ||
402 | if (input[0] == '\0') { | ||
403 | // the end is infinite | ||
404 | result.range.end_infinity = true; | ||
405 | } else { | ||
406 | perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input); | ||
407 | |||
408 | if (parsed_pd.error != MP_PARSING_SUCCES) { | ||
409 | result.error = parsed_pd.error; | ||
410 | return result; | ||
411 | } | ||
412 | result.range.end = parsed_pd.value; | ||
413 | result.range.end_infinity = false; | ||
414 | } | ||
415 | free(working_copy); | ||
416 | return result; | ||
417 | } | ||
418 | |||
419 | double_parser_wrapper parse_double(const char *input) { | ||
420 | double_parser_wrapper result = { | ||
421 | .error = MP_PARSING_SUCCES, | ||
422 | }; | ||
423 | |||
424 | if (input == NULL) { | ||
425 | result.error = MP_PARSING_FAILURE; | ||
426 | return result; | ||
427 | } | ||
428 | |||
429 | char *endptr = NULL; | ||
430 | errno = 0; | ||
431 | double tmp = strtod(input, &endptr); | ||
432 | |||
433 | if (input == endptr) { | ||
434 | // man 3 strtod says, no conversion performed | ||
435 | result.error = MP_PARSING_FAILURE; | ||
436 | return result; | ||
437 | } | ||
438 | |||
439 | if (errno) { | ||
440 | // some other error | ||
441 | // TODO maybe differentiate a little bit | ||
442 | result.error = MP_PARSING_FAILURE; | ||
443 | return result; | ||
444 | } | ||
445 | |||
446 | result.value = mp_create_pd_value(tmp); | ||
447 | return result; | ||
448 | } | ||
449 | |||
450 | integer_parser_wrapper parse_integer(const char *input) { | ||
451 | integer_parser_wrapper result = { | ||
452 | .error = MP_PARSING_SUCCES, | ||
453 | }; | ||
454 | |||
455 | if (input == NULL) { | ||
456 | result.error = MP_PARSING_FAILURE; | ||
457 | return result; | ||
458 | } | ||
459 | |||
460 | char *endptr = NULL; | ||
461 | errno = 0; | ||
462 | long long tmp = strtoll(input, &endptr, 0); | ||
463 | |||
464 | // validating *sigh* | ||
465 | if (*endptr != '\0') { | ||
466 | // something went wrong in strtoll | ||
467 | if (tmp == LLONG_MIN) { | ||
468 | // underflow | ||
469 | result.error = MP_RANGE_PARSING_UNDERFLOW; | ||
470 | return result; | ||
471 | } | ||
472 | |||
473 | if (tmp == LLONG_MAX) { | ||
474 | // overflow | ||
475 | result.error = MP_RANGE_PARSING_OVERFLOW; | ||
476 | return result; | ||
477 | } | ||
478 | |||
479 | // still wrong, but not sure why, probably invalid characters | ||
480 | if (errno == EINVAL) { | ||
481 | result.error = MP_RANGE_PARSING_INVALID_CHAR; | ||
482 | return result; | ||
483 | } | ||
484 | |||
485 | // some other error, do catch all here | ||
486 | result.error = MP_RANGE_PARSING_FAILURE; | ||
487 | return result; | ||
488 | } | ||
489 | |||
490 | // no error, should be fine | ||
491 | result.value = mp_create_pd_value(tmp); | ||
492 | return result; | ||
493 | } | ||
494 | |||
495 | perfdata_value_parser_wrapper parse_pd_value(const char *input) { | ||
496 | // try integer first | ||
497 | integer_parser_wrapper tmp_int = parse_integer(input); | ||
498 | |||
499 | if (tmp_int.error == MP_PARSING_SUCCES) { | ||
500 | perfdata_value_parser_wrapper result = { | ||
501 | .error = tmp_int.error, | ||
502 | .value = tmp_int.value, | ||
503 | }; | ||
504 | return result; | ||
505 | } | ||
506 | |||
507 | double_parser_wrapper tmp_double = parse_double(input); | ||
508 | perfdata_value_parser_wrapper result = {}; | ||
509 | if (tmp_double.error == MP_PARSING_SUCCES) { | ||
510 | result.error = tmp_double.error; | ||
511 | result.value = tmp_double.value; | ||
512 | } else { | ||
513 | result.error = tmp_double.error; | ||
514 | } | ||
515 | return result; | ||
516 | } | ||