1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "zbxalgo.h"
22
23 #include "log.h"
24
25 /******************************************************************************
26 * *
27 * Module for evaluating expressions *
28 * --------------------------------------- *
29 * *
30 * Global variables are used for efficiency reasons so that arguments do not *
31 * have to be passed to each of evaluate_termX() functions. For this reason, *
32 * too, this module is isolated into a separate file. *
33 * *
34 * The priority of supported operators is as follows: *
35 * *
36 * - (unary) evaluate_term8() *
37 * not evaluate_term7() *
38 * * / evaluate_term6() *
39 * + - evaluate_term5() *
40 * < <= >= > evaluate_term4() *
41 * = <> evaluate_term3() *
42 * and evaluate_term2() *
43 * or evaluate_term1() *
44 * *
45 * Function evaluate_term9() is used for parsing tokens on the lowest level: *
46 * those can be suffixed numbers like "12.345K" or parenthesized expressions. *
47 * *
48 ******************************************************************************/
49
50 static const char *ptr; /* character being looked at */
51 static int level; /* expression nesting level */
52
53 static char *buffer; /* error message buffer */
54 static size_t max_buffer_len; /* error message buffer size */
55
56 /******************************************************************************
57 * *
58 * Purpose: check whether the character delimits a numeric token *
59 * *
60 ******************************************************************************/
is_number_delimiter(char c)61 static int is_number_delimiter(char c)
62 {
63 return 0 == isdigit(c) && '.' != c && 0 == isalpha(c) ? SUCCEED : FAIL;
64 }
65
66 /******************************************************************************
67 * *
68 * Purpose: check whether the character delimits a symbolic operator token *
69 * *
70 ******************************************************************************/
is_operator_delimiter(char c)71 static int is_operator_delimiter(char c)
72 {
73 return ' ' == c || '(' == c || '\r' == c || '\n' == c || '\t' == c || ')' == c || '\0' == c ? SUCCEED : FAIL;
74 }
75
76 /******************************************************************************
77 * *
78 * Purpose: evaluate a quoted string like "/etc/passwd" *
79 * Characters '\' and '"' are expected to be escaped or parsing fails *
80 * *
81 ******************************************************************************/
evaluate_string(zbx_variant_t * res)82 static void evaluate_string(zbx_variant_t *res)
83 {
84 const char *start;
85 char *res_temp = NULL, *dst;
86 int str_len = 0;
87
88 for (start = ptr; '"' != *ptr; ptr++)
89 {
90 if ('\\' == *ptr)
91 {
92 ptr++;
93
94 if ('\\' != *ptr && '\"' != *ptr && '\0' != *ptr)
95 {
96 zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
97 " invalid escape sequence at \"%s\".", ptr - 1);
98 zbx_variant_set_dbl(res, ZBX_INFINITY);
99 return;
100 }
101
102 }
103
104 if ('\0' == *ptr)
105 {
106 zbx_variant_set_dbl(res, ZBX_INFINITY);
107 zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
108 " unterminated string at \"%s\".", start);
109 return;
110 }
111 }
112
113 str_len = ptr - start;
114 res_temp = zbx_malloc(NULL, str_len + 1);
115
116 for (dst = res_temp; start != ptr; start++)
117 {
118 switch (*start)
119 {
120 case '\\':
121 start++;
122 break;
123 case '\r':
124 continue;
125 }
126 *dst++ = *start;
127 }
128 *dst = '\0';
129 zbx_variant_set_str(res, res_temp);
130 }
131
132 /******************************************************************************
133 * *
134 * Purpose: evaluate a suffixed number like 12.345K *
135 * *
136 ******************************************************************************/
evaluate_number(int * unknown_idx)137 static double evaluate_number(int *unknown_idx)
138 {
139 double result;
140 int len;
141
142 /* Is it a special token of unknown value (e.g. ZBX_UNKNOWN0, ZBX_UNKNOWN1) ? */
143 if (0 == strncmp(ZBX_UNKNOWN_STR, ptr, ZBX_UNKNOWN_STR_LEN))
144 {
145 const char *p0, *p1;
146
147 p0 = ptr + ZBX_UNKNOWN_STR_LEN;
148 p1 = p0;
149
150 /* extract the message number which follows after 'ZBX_UNKNOWN' */
151 while (0 != isdigit((unsigned char)*p1))
152 p1++;
153
154 if (p0 < p1 && SUCCEED == is_number_delimiter(*p1))
155 {
156 ptr = p1;
157
158 /* return 'unknown' and corresponding message number about its origin */
159 *unknown_idx = atoi(p0);
160 return ZBX_UNKNOWN;
161 }
162
163 ptr = p0;
164
165 return ZBX_INFINITY;
166 }
167
168 if (SUCCEED == zbx_suffixed_number_parse(ptr, &len) && SUCCEED == is_number_delimiter(*(ptr + len)))
169 {
170 result = atof(ptr) * suffix2factor(*(ptr + len - 1));
171 ptr += len;
172 }
173 else
174 result = ZBX_INFINITY;
175
176 return result;
177 }
178
179 /******************************************************************************
180 * *
181 * Function: variant_convert_to_double *
182 * *
183 * Purpose: cast string variant to a double variant *
184 * *
185 * Parameters: var - [IN/OUT] the variant to cast *
186 * *
187 ******************************************************************************/
variant_convert_to_double(zbx_variant_t * var)188 static void variant_convert_to_double(zbx_variant_t *var)
189 {
190 if (ZBX_VARIANT_STR == var->type)
191 {
192 double var_double_value = evaluate_string_to_double(var->data.str);
193 if (ZBX_INFINITY == var_double_value)
194 {
195 zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
196 " value \"%s\" is not a numeric operand.", var->data.str);
197 }
198 zbx_variant_clear(var);
199 zbx_variant_set_dbl(var, var_double_value);
200 }
201 }
202
203 /******************************************************************************
204 * *
205 * Function: variant_get_double *
206 * *
207 * Purpose: get variant value in double (float64) format *
208 * *
209 * Parameters: var - [IN] the input variant *
210 * *
211 * Return value: Depending on variant type: *
212 * DBL - the variant value *
213 * STR - if variant value contains valid float64 string (with supported *
214 * Zabbix suffixes) the converted value is returned. Otherwise *
215 * ZBX_INFINITY is returned. *
216 * other types - ZBX_INFINITY
217 * *
218 ******************************************************************************/
variant_get_double(const zbx_variant_t * var)219 static double variant_get_double(const zbx_variant_t *var)
220 {
221 switch (var->type)
222 {
223 case ZBX_VARIANT_DBL:
224 return var->data.dbl;
225 case ZBX_VARIANT_STR:
226 return evaluate_string_to_double(var->data.str);
227 default:
228 THIS_SHOULD_NEVER_HAPPEN;
229 return ZBX_INFINITY;
230 }
231 }
232
233 static zbx_variant_t evaluate_term1(int *unknown_idx);
234
235 /******************************************************************************
236 * *
237 * Purpose: evaluate a suffixed number or a parenthesized expression *
238 * *
239 ******************************************************************************/
evaluate_term9(int * unknown_idx)240 static zbx_variant_t evaluate_term9(int *unknown_idx)
241 {
242 zbx_variant_t res;
243
244 while (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr)
245 ptr++;
246
247 if ('\0' == *ptr)
248 {
249 zbx_strlcpy(buffer, "Cannot evaluate expression: unexpected end of expression.", max_buffer_len);
250 zbx_variant_set_dbl(&res, ZBX_INFINITY);
251 return res;
252 }
253
254 if ('(' == *ptr)
255 {
256 ptr++;
257
258 res = evaluate_term1(unknown_idx);
259
260 if (ZBX_VARIANT_DBL == res.type && ZBX_INFINITY == res.data.dbl)
261 return res;
262
263 /* if evaluate_term1() returns ZBX_UNKNOWN then continue as with regular number */
264
265 if (')' != *ptr)
266 {
267 zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
268 " expected closing parenthesis at \"%s\".", ptr);
269
270 zbx_variant_clear(&res);
271 zbx_variant_set_dbl(&res, ZBX_INFINITY);
272
273 return res;
274 }
275
276 ptr++;
277 }
278 else
279 {
280 if ('"' == *ptr)
281 {
282 ptr++;
283 evaluate_string(&res);
284
285 if (ZBX_VARIANT_DBL == res.type && ZBX_INFINITY == res.data.dbl)
286 return res;
287
288 ptr++;
289
290 /* We do not really need to do this check. */
291 /* The only reason we do it is to keep it consistent with */
292 /* numeric tokens, where operators are not allowed after them: 123and. */
293 /* Check below ensures that '"123"and' expression will fail as well. */
294 if (FAIL == is_operator_delimiter(*ptr) && FAIL == is_number_delimiter(*ptr))
295 {
296 zbx_variant_clear(&res);
297 zbx_variant_set_dbl(&res, ZBX_INFINITY);
298 zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
299 " unexpected token at \"%s\".", ptr);
300 }
301
302 }
303 else
304 {
305 zbx_variant_set_dbl(&res, evaluate_number(unknown_idx));
306
307 if (ZBX_INFINITY == res.data.dbl)
308 {
309 zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
310 " expected numeric token at \"%s\".", ptr);
311 return res;
312 }
313 }
314 }
315
316 while ('\0' != *ptr && (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr))
317 ptr++;
318
319 return res;
320 }
321
322 /******************************************************************************
323 * *
324 * Purpose: evaluate "-" (unary) *
325 * *
326 * -0.0 -> -0.0 *
327 * -1.2 -> -1.2 *
328 * -Unknown -> Unknown *
329 * *
330 ******************************************************************************/
evaluate_term8(int * unknown_idx)331 static zbx_variant_t evaluate_term8(int *unknown_idx)
332 {
333 while (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr)
334 ptr++;
335
336 if ('-' == *ptr)
337 {
338 zbx_variant_t res;
339 ptr++;
340 res = evaluate_term9(unknown_idx);
341 variant_convert_to_double(&res);
342
343 if (ZBX_UNKNOWN == res.data.dbl || ZBX_INFINITY == res.data.dbl)
344 return res;
345
346 res.data.dbl = -res.data.dbl;
347 return res;
348 }
349 else
350 return evaluate_term9(unknown_idx);
351 }
352
353 /******************************************************************************
354 * *
355 * Purpose: evaluate "not" *
356 * *
357 * not 0.0 -> 1.0 *
358 * not 1.2 -> 0.0 *
359 * not Unknown -> Unknown *
360 * *
361 ******************************************************************************/
evaluate_term7(int * unknown_idx)362 static zbx_variant_t evaluate_term7(int *unknown_idx)
363 {
364 while (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr)
365 ptr++;
366
367 if ('n' == ptr[0] && 'o' == ptr[1] && 't' == ptr[2] && SUCCEED == is_operator_delimiter(ptr[3]))
368 {
369 zbx_variant_t res;
370 ptr += 3;
371 res = evaluate_term8(unknown_idx);
372 variant_convert_to_double(&res);
373
374 if (ZBX_UNKNOWN == res.data.dbl || ZBX_INFINITY == res.data.dbl)
375 return res;
376
377 res.data.dbl = (SUCCEED == zbx_double_compare(res.data.dbl, 0.0) ? 1.0 : 0.0);
378 return res;
379 }
380 else
381 return evaluate_term8(unknown_idx);
382 }
383
384 /******************************************************************************
385 * *
386 * Purpose: evaluate "*" and "/" *
387 * *
388 * 0.0 * Unknown -> Unknown (yes, not 0 as we don't want to lose *
389 * Unknown in arithmetic operations) *
390 * 1.2 * Unknown -> Unknown *
391 * 0.0 / 1.2 -> 0.0 *
392 * 1.2 / 0.0 -> error (ZBX_INFINITY) *
393 * Unknown / 0.0 -> error (ZBX_INFINITY) *
394 * Unknown / 1.2 -> Unknown *
395 * Unknown / Unknown -> Unknown *
396 * 0.0 / Unknown -> Unknown *
397 * 1.2 / Unknown -> Unknown *
398 * *
399 ******************************************************************************/
evaluate_term6(int * unknown_idx)400 static zbx_variant_t evaluate_term6(int *unknown_idx)
401 {
402 char op;
403 int res_idx = -1, oper_idx = -2; /* set invalid values to catch errors */
404 zbx_variant_t res;
405
406 res = evaluate_term7(&res_idx);
407
408 if (ZBX_VARIANT_DBL == res.type)
409 {
410 if (ZBX_INFINITY == res.data.dbl)
411 return res;
412
413 if (ZBX_UNKNOWN == res.data.dbl)
414 *unknown_idx = res_idx;
415 }
416
417 /* if evaluate_term7() returns ZBX_UNKNOWN then continue as with regular number */
418
419 while ('*' == *ptr || '/' == *ptr)
420 {
421 zbx_variant_t operand;
422
423 variant_convert_to_double(&res);
424
425 if (ZBX_INFINITY == res.data.dbl)
426 return res;
427
428 op = *ptr++;
429
430 /* 'ZBX_UNKNOWN' in multiplication and division produces 'ZBX_UNKNOWN'. */
431 /* Even if 1st operand is Unknown we evaluate 2nd operand too to catch fatal errors in it. */
432
433 operand = evaluate_term7(&oper_idx);
434 variant_convert_to_double(&operand);
435
436 if (ZBX_INFINITY == operand.data.dbl)
437 {
438 zbx_variant_clear(&res);
439 zbx_variant_set_dbl(&res, ZBX_INFINITY);
440 zbx_variant_clear(&operand);
441 return res;
442 }
443
444 if ('*' == op)
445 {
446 if (ZBX_UNKNOWN == operand.data.dbl) /* (anything) * Unknown */
447 {
448 *unknown_idx = oper_idx;
449 res_idx = oper_idx;
450 res.data.dbl = ZBX_UNKNOWN;
451 }
452 else if (ZBX_UNKNOWN == res.data.dbl) /* Unknown * known */
453 *unknown_idx = res_idx;
454 else
455 res.data.dbl *= operand.data.dbl;
456 }
457 else
458 {
459 /* catch division by 0 even if 1st operand is Unknown */
460
461 if (ZBX_UNKNOWN != operand.data.dbl && SUCCEED == zbx_double_compare(operand.data.dbl, 0.0))
462 {
463 zbx_strlcpy(buffer, "Cannot evaluate expression: division by zero.", max_buffer_len);
464 zbx_variant_clear(&res);
465 zbx_variant_set_dbl(&res, ZBX_INFINITY);
466 zbx_variant_clear(&operand);
467 return res;
468 }
469
470 if (ZBX_UNKNOWN == operand.data.dbl) /* (anything) / Unknown */
471 {
472 *unknown_idx = oper_idx;
473 res_idx = oper_idx;
474 res.data.dbl = ZBX_UNKNOWN;
475 }
476 else if (ZBX_UNKNOWN == res.data.dbl) /* Unknown / known */
477 {
478 *unknown_idx = res_idx;
479 }
480 else
481 res.data.dbl /= operand.data.dbl;
482 }
483
484 zbx_variant_clear(&operand);
485 }
486 return res;
487 }
488
489 /******************************************************************************
490 * *
491 * Purpose: evaluate "+" and "-" *
492 * *
493 * 0.0 +/- Unknown -> Unknown *
494 * 1.2 +/- Unknown -> Unknown *
495 * Unknown +/- Unknown -> Unknown *
496 * *
497 ******************************************************************************/
evaluate_term5(int * unknown_idx)498 static zbx_variant_t evaluate_term5(int *unknown_idx)
499 {
500 char op;
501 zbx_variant_t res, operand;
502 int res_idx = -3, oper_idx = -4; /* set invalid values to catch errors */
503
504 res = evaluate_term6(&res_idx);
505
506 if (ZBX_VARIANT_DBL == res.type)
507 {
508 if (ZBX_INFINITY == res.data.dbl)
509 return res;
510
511 if (ZBX_UNKNOWN == res.data.dbl)
512 *unknown_idx = res_idx;
513 }
514
515 /* if evaluate_term6() returns ZBX_UNKNOWN then continue as with regular number */
516
517 while ('+' == *ptr || '-' == *ptr)
518 {
519 variant_convert_to_double(&res);
520
521 if (ZBX_INFINITY == res.data.dbl)
522 return res;
523
524 op = *ptr++;
525
526 /* even if 1st operand is Unknown we evaluate 2nd operand to catch fatal error if any occurs */
527
528 operand = evaluate_term6(&oper_idx);
529 variant_convert_to_double(&operand);
530
531 if (ZBX_INFINITY == operand.data.dbl)
532 {
533 zbx_variant_clear(&res);
534 zbx_variant_set_dbl(&res, ZBX_INFINITY);
535 zbx_variant_clear(&operand);
536 return res;
537 }
538
539 if (ZBX_UNKNOWN == operand.data.dbl) /* (anything) +/- Unknown */
540 {
541 *unknown_idx = oper_idx;
542 res_idx = oper_idx;
543 res.data.dbl = ZBX_UNKNOWN;
544 }
545 else if (ZBX_UNKNOWN == res.data.dbl) /* Unknown +/- known */
546 {
547 *unknown_idx = res_idx;
548 }
549 else
550 {
551 if ('+' == op)
552 res.data.dbl += operand.data.dbl;
553 else
554 res.data.dbl -= operand.data.dbl;
555 }
556
557 zbx_variant_clear(&operand);
558 }
559
560 return res;
561 }
562
563 /******************************************************************************
564 * *
565 * Purpose: evaluate "<", "<=", ">=", ">" *
566 * *
567 * 0.0 < Unknown -> Unknown *
568 * 1.2 < Unknown -> Unknown *
569 * Unknown < Unknown -> Unknown *
570 * *
571 ******************************************************************************/
evaluate_term4(int * unknown_idx)572 static zbx_variant_t evaluate_term4(int *unknown_idx)
573 {
574 char op;
575 zbx_variant_t res, operand;
576 int res_idx = -5, oper_idx = -6; /* set invalid values to catch errors */
577
578 res = evaluate_term5(&res_idx);
579
580 if (ZBX_VARIANT_DBL == res.type)
581 {
582 if (ZBX_INFINITY == res.data.dbl)
583 return res;
584
585 if (ZBX_UNKNOWN == res.data.dbl)
586 *unknown_idx = res_idx;
587 }
588
589 /* if evaluate_term5() returns ZBX_UNKNOWN then continue as with regular number */
590
591 while (1)
592 {
593 if ('<' == ptr[0] && '=' == ptr[1])
594 {
595 op = 'l';
596 ptr += 2;
597 }
598 else if ('>' == ptr[0] && '=' == ptr[1])
599 {
600 op = 'g';
601 ptr += 2;
602 }
603 else if (('<' == ptr[0] && '>' != ptr[1]) || '>' == ptr[0])
604 {
605 op = *ptr++;
606 }
607 else
608 break;
609
610 variant_convert_to_double(&res);
611
612 if (ZBX_INFINITY == res.data.dbl)
613 return res;
614
615 /* even if 1st operand is Unknown we evaluate 2nd operand to catch fatal error if any occurs */
616
617 operand = evaluate_term5(&oper_idx);
618
619 variant_convert_to_double(&operand);
620
621 if (ZBX_INFINITY == operand.data.dbl)
622 {
623 zbx_variant_clear(&res);
624 zbx_variant_set_dbl(&res, ZBX_INFINITY);
625 zbx_variant_clear(&operand);
626 return res;
627 }
628
629 if (ZBX_UNKNOWN == operand.data.dbl) /* (anything) < Unknown */
630 {
631 *unknown_idx = oper_idx;
632 res_idx = oper_idx;
633 res.data.dbl = ZBX_UNKNOWN;
634 }
635 else if (ZBX_UNKNOWN == res.data.dbl) /* Unknown < known */
636 {
637 *unknown_idx = res_idx;
638 }
639 else
640 {
641 if ('<' == op)
642 res.data.dbl = (res.data.dbl < operand.data.dbl - ZBX_DOUBLE_EPSILON);
643 else if ('l' == op)
644 res.data.dbl = (res.data.dbl <= operand.data.dbl + ZBX_DOUBLE_EPSILON);
645 else if ('g' == op)
646 res.data.dbl = (res.data.dbl >= operand.data.dbl - ZBX_DOUBLE_EPSILON);
647 else
648 res.data.dbl = (res.data.dbl > operand.data.dbl + ZBX_DOUBLE_EPSILON);
649 }
650
651 zbx_variant_clear(&operand);
652 }
653
654 return res;
655 }
656
657 /******************************************************************************
658 * *
659 * Purpose: evaluate "=" and "<>" *
660 * *
661 * 0.0 = Unknown -> Unknown *
662 * 1.2 = Unknown -> Unknown *
663 * Unknown = Unknown -> Unknown *
664 * 0.0 <> Unknown -> Unknown *
665 * 1.2 <> Unknown -> Unknown *
666 * Unknown <> Unknown -> Unknown *
667 * *
668 ******************************************************************************/
evaluate_term3(int * unknown_idx)669 static zbx_variant_t evaluate_term3(int *unknown_idx)
670 {
671 char op;
672 int res_idx = -7, oper_idx = -8; /* set invalid values to catch errors */
673 zbx_variant_t res, operand;
674 double left, right, value;
675
676 res = evaluate_term4(&res_idx);
677
678 if (ZBX_VARIANT_DBL == res.type)
679 {
680 if (ZBX_INFINITY == res.data.dbl)
681 return res;
682
683 if (ZBX_UNKNOWN == res.data.dbl)
684 *unknown_idx = res_idx;
685 }
686
687 /* if evaluate_term4() returns ZBX_UNKNOWN then continue as with regular number */
688
689 while (1)
690 {
691 if ('=' == *ptr)
692 {
693 op = *ptr++;
694 }
695 else if ('<' == ptr[0] && '>' == ptr[1])
696 {
697 op = '#';
698 ptr += 2;
699 }
700 else
701 break;
702
703 /* even if 1st operand is Unknown we evaluate 2nd operand to catch fatal error if any occurs */
704
705 operand = evaluate_term4(&oper_idx);
706
707 if (ZBX_VARIANT_DBL == operand.type && ZBX_INFINITY == operand.data.dbl)
708 {
709 zbx_variant_clear(&res);
710 return operand;
711 }
712
713 if (ZBX_VARIANT_DBL == res.type && ZBX_UNKNOWN == res.data.dbl)
714 {
715 zbx_variant_clear(&operand);
716 continue;
717 }
718
719 if (ZBX_VARIANT_DBL == operand.type && ZBX_UNKNOWN == operand.data.dbl)
720 {
721 zbx_variant_clear(&res);
722 res = operand;
723 *unknown_idx = oper_idx;
724 continue;
725 }
726
727 left = variant_get_double(&res);
728 right = variant_get_double(&operand);
729
730 if (ZBX_INFINITY != left && ZBX_INFINITY != right)
731 {
732 /* both operands either are of double type or could be cast to it - */
733 /* compare them as double values */
734 value = (SUCCEED == zbx_double_compare(left, right) ? 1 : 0);
735 }
736 else if (ZBX_VARIANT_DBL == res.type || ZBX_VARIANT_DBL == operand.type)
737 {
738 /* if one of operands has double type and the other */
739 /* cannot be cast to double - they cannot be equal */
740 value = 0;
741 }
742 else
743 {
744 /* at this point both operands should be strings and should be */
745 /* compared as such but check for their types just in case */
746 if (ZBX_VARIANT_STR != res.type || ZBX_VARIANT_STR != operand.type)
747 {
748 zbx_strlcpy(buffer, "Cannot evaluate expression: unsupported value type found.",
749 max_buffer_len);
750 value = ZBX_INFINITY;
751 THIS_SHOULD_NEVER_HAPPEN;
752 }
753 else
754 value = !strcmp(res.data.str, operand.data.str);
755 }
756
757 if ('#' == op)
758 value = (SUCCEED == zbx_double_compare(value, 0.0) ? 1.0 : 0.0);
759
760 zbx_variant_clear(&res);
761 zbx_variant_clear(&operand);
762 zbx_variant_set_dbl(&res, value);
763 }
764
765 return res;
766 }
767
768 /******************************************************************************
769 * *
770 * Purpose: evaluate "and" *
771 * *
772 * 0.0 and Unknown -> 0.0 *
773 * Unknown and 0.0 -> 0.0 *
774 * 1.0 and Unknown -> Unknown *
775 * Unknown and 1.0 -> Unknown *
776 * Unknown and Unknown -> Unknown *
777 * *
778 ******************************************************************************/
evaluate_term2(int * unknown_idx)779 static zbx_variant_t evaluate_term2(int *unknown_idx)
780 {
781 zbx_variant_t res, operand;
782 int res_idx = -9, oper_idx = -10; /* set invalid values to catch errors */
783
784 res = evaluate_term3(&res_idx);
785
786 if (ZBX_VARIANT_DBL == res.type)
787 {
788 if (ZBX_INFINITY == res.data.dbl)
789 return res;
790
791 if (ZBX_UNKNOWN == res.data.dbl)
792 *unknown_idx = res_idx;
793 }
794
795 /* if evaluate_term3() returns ZBX_UNKNOWN then continue as with regular number */
796
797 while ('a' == ptr[0] && 'n' == ptr[1] && 'd' == ptr[2] && SUCCEED == is_operator_delimiter(ptr[3]))
798 {
799 ptr += 3;
800 variant_convert_to_double(&res);
801
802 if (ZBX_INFINITY == res.data.dbl)
803 return res;
804
805 operand = evaluate_term3(&oper_idx);
806 variant_convert_to_double(&operand);
807
808 if (ZBX_INFINITY == operand.data.dbl)
809 {
810 zbx_variant_clear(&res);
811 zbx_variant_set_dbl(&res, ZBX_INFINITY);
812 zbx_variant_clear(&operand);
813 return res;
814 }
815
816 if (ZBX_UNKNOWN == res.data.dbl)
817 {
818 if (ZBX_UNKNOWN == operand.data.dbl) /* Unknown and Unknown */
819 {
820 *unknown_idx = oper_idx;
821 res_idx = oper_idx;
822 res.data.dbl = ZBX_UNKNOWN;
823 }
824 else if (SUCCEED == zbx_double_compare(operand.data.dbl, 0.0)) /* Unknown and 0 */
825 {
826 res.data.dbl = 0.0;
827 }
828 else /* Unknown and 1 */
829 *unknown_idx = res_idx;
830 }
831 else if (ZBX_UNKNOWN == operand.data.dbl)
832 {
833 if (SUCCEED == zbx_double_compare(res.data.dbl, 0.0)) /* 0 and Unknown */
834 {
835 res.data.dbl = 0.0;
836 }
837 else /* 1 and Unknown */
838 {
839 *unknown_idx = oper_idx;
840 res_idx = oper_idx;
841 res.data.dbl = ZBX_UNKNOWN;
842 }
843 }
844 else
845 {
846 res.data.dbl = (SUCCEED != zbx_double_compare(res.data.dbl, 0.0) &&
847 SUCCEED != zbx_double_compare(operand.data.dbl, 0.0));
848 }
849
850 zbx_variant_clear(&operand);
851 }
852
853 return res;
854 }
855
856 /******************************************************************************
857 * *
858 * Purpose: evaluate "or" *
859 * *
860 * 1.0 or Unknown -> 1.0 *
861 * Unknown or 1.0 -> 1.0 *
862 * 0.0 or Unknown -> Unknown *
863 * Unknown or 0.0 -> Unknown *
864 * Unknown or Unknown -> Unknown *
865 * *
866 ******************************************************************************/
evaluate_term1(int * unknown_idx)867 static zbx_variant_t evaluate_term1(int *unknown_idx)
868 {
869 int res_idx = -11, oper_idx = -12; /* set invalid values to catch errors */
870 zbx_variant_t res, operand;
871
872 level++;
873
874 if (32 < level)
875 {
876 zbx_strlcpy(buffer, "Cannot evaluate expression: nesting level is too deep.", max_buffer_len);
877 zbx_variant_set_dbl(&res, ZBX_INFINITY);
878 return res;
879 }
880
881 res = evaluate_term2(&res_idx);
882
883 if (ZBX_VARIANT_DBL == res.type)
884 {
885 if (ZBX_INFINITY == res.data.dbl)
886 return res;
887
888 if (ZBX_UNKNOWN == res.data.dbl)
889 *unknown_idx = res_idx;
890 }
891
892 /* if evaluate_term2() returns ZBX_UNKNOWN then continue as with regular number */
893
894 while ('o' == ptr[0] && 'r' == ptr[1] && SUCCEED == is_operator_delimiter(ptr[2]))
895 {
896 ptr += 2;
897 variant_convert_to_double(&res);
898
899 if (ZBX_INFINITY == res.data.dbl)
900 return res;
901
902 operand = evaluate_term2(&oper_idx);
903
904 variant_convert_to_double(&operand);
905
906 if (ZBX_INFINITY == operand.data.dbl)
907 {
908 zbx_variant_clear(&res);
909 zbx_variant_set_dbl(&res, ZBX_INFINITY);
910 zbx_variant_clear(&operand);
911 return res;
912 }
913
914 if (ZBX_UNKNOWN == res.data.dbl)
915 {
916 if (ZBX_UNKNOWN == operand.data.dbl) /* Unknown or Unknown */
917 {
918 *unknown_idx = oper_idx;
919 res_idx = oper_idx;
920 res.data.dbl = ZBX_UNKNOWN;
921 }
922 else if (SUCCEED != zbx_double_compare(operand.data.dbl, 0.0)) /* Unknown or 1 */
923 {
924 res.data.dbl = 1;
925 }
926 else /* Unknown or 0 */
927 *unknown_idx = res_idx;
928 }
929 else if (ZBX_UNKNOWN == operand.data.dbl)
930 {
931 if (SUCCEED != zbx_double_compare(res.data.dbl, 0.0)) /* 1 or Unknown */
932 {
933 res.data.dbl = 1;
934 }
935 else /* 0 or Unknown */
936 {
937 *unknown_idx = oper_idx;
938 res_idx = oper_idx;
939 res.data.dbl = ZBX_UNKNOWN;
940 }
941 }
942 else
943 {
944 res.data.dbl = (SUCCEED != zbx_double_compare(res.data.dbl, 0.0) ||
945 SUCCEED != zbx_double_compare(operand.data.dbl, 0.0));
946 }
947 zbx_variant_clear(&operand);
948 }
949
950 level--;
951
952 return res;
953 }
954
955 /******************************************************************************
956 * *
957 * Purpose: evaluate an expression like "(26.416>10) or (0=1)" *
958 * *
959 ******************************************************************************/
evaluate(double * value,const char * expression,char * error,size_t max_error_len,zbx_vector_ptr_t * unknown_msgs)960 int evaluate(double *value, const char *expression, char *error, size_t max_error_len,
961 zbx_vector_ptr_t *unknown_msgs)
962 {
963 int unknown_idx = -13; /* index of message in 'unknown_msgs' vector, set to invalid value */
964 /* to catch errors */
965 zbx_variant_t res;
966
967 zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, expression);
968
969 ptr = expression;
970 level = 0;
971 buffer = error;
972 max_buffer_len = max_error_len;
973 res = evaluate_term1(&unknown_idx);
974
975 if (ZBX_VARIANT_STR == res.type)
976 {
977 if (0 == strlen(res.data.str))
978 {
979 zbx_strlcpy(buffer, "Cannot evaluate expression: unexpected end of expression.",
980 max_buffer_len);
981 zbx_variant_clear(&res);
982 zbx_variant_set_dbl(&res, ZBX_INFINITY);
983 }
984 else
985 {
986 variant_convert_to_double(&res);
987 }
988 }
989
990 *value = res.data.dbl;
991 zbx_variant_clear(&res);
992
993 if ('\0' != *ptr && ZBX_INFINITY != *value)
994 {
995 zbx_snprintf(error, max_error_len, "Cannot evaluate expression: unexpected token at \"%s\".", ptr);
996 *value = ZBX_INFINITY;
997 }
998
999 if (ZBX_UNKNOWN == *value)
1000 {
1001 /* Map Unknown result to error. Callers currently do not operate with ZBX_UNKNOWN. */
1002 if (NULL != unknown_msgs)
1003 {
1004 if (0 > unknown_idx)
1005 {
1006 THIS_SHOULD_NEVER_HAPPEN;
1007 zabbix_log(LOG_LEVEL_WARNING, "%s() internal error: " ZBX_UNKNOWN_STR " index:%d"
1008 " expression:'%s'", __func__, unknown_idx, expression);
1009 zbx_snprintf(error, max_error_len, "Internal error: " ZBX_UNKNOWN_STR " index %d."
1010 " Please report this to Zabbix developers.", unknown_idx);
1011 }
1012 else if (unknown_msgs->values_num > unknown_idx)
1013 {
1014 zbx_snprintf(error, max_error_len, "Cannot evaluate expression: \"%s\".",
1015 (char *)(unknown_msgs->values[unknown_idx]));
1016 }
1017 else
1018 {
1019 zbx_snprintf(error, max_error_len, "Cannot evaluate expression: unsupported "
1020 ZBX_UNKNOWN_STR "%d value.", unknown_idx);
1021 }
1022 }
1023 else
1024 {
1025 THIS_SHOULD_NEVER_HAPPEN;
1026 /* do not leave garbage in error buffer, write something helpful */
1027 zbx_snprintf(error, max_error_len, "%s(): internal error: no message for unknown result",
1028 __func__);
1029 }
1030
1031 *value = ZBX_INFINITY;
1032 }
1033
1034 if (ZBX_INFINITY == *value)
1035 {
1036 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() error:'%s'", __func__, error);
1037 return FAIL;
1038 }
1039
1040 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() value:" ZBX_FS_DBL, __func__, *value);
1041
1042 return SUCCEED;
1043 }
1044
1045 /******************************************************************************
1046 * *
1047 * Function: evaluate_unknown *
1048 * *
1049 * Purpose: evaluate an expression like "(26.416>10) and not(0=ZBX_UNKNOWN0)" *
1050 * *
1051 * Parameters: expression - [IN] expression to evaluate *
1052 * value - [OUT] expression evaluation result *
1053 * error - [OUT] error message buffer *
1054 * max_error_len - [IN] error buffer size *
1055 * *
1056 * Return value: SUCCEED - expression evaluated successfully, *
1057 * or evaluation result is undefined (ZBX_UNKNOWN) *
1058 * FAIL - expression evaluation failed *
1059 * *
1060 ******************************************************************************/
evaluate_unknown(const char * expression,double * value,char * error,size_t max_error_len)1061 int evaluate_unknown(const char *expression, double *value, char *error, size_t max_error_len)
1062 {
1063 const char *__function_name = "evaluate_with_unknown";
1064 zbx_variant_t res;
1065 int unknown_idx = -13; /* index of message in 'unknown_msgs' vector, set to invalid value */
1066 /* to catch errors */
1067
1068 zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __function_name, expression);
1069
1070 ptr = expression;
1071 level = 0;
1072
1073 buffer = error;
1074 max_buffer_len = max_error_len;
1075 res = evaluate_term1(&unknown_idx);
1076 variant_convert_to_double(&res);
1077 *value = res.data.dbl;
1078 zbx_variant_clear(&res);
1079
1080 if ('\0' != *ptr && ZBX_INFINITY != *value)
1081 {
1082 zbx_snprintf(error, max_error_len, "Cannot evaluate expression: unexpected token at \"%s\".", ptr);
1083 *value = ZBX_INFINITY;
1084 }
1085
1086 if (ZBX_INFINITY == *value)
1087 {
1088 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() error:'%s'", __function_name, error);
1089 return FAIL;
1090 }
1091
1092 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() value:" ZBX_FS_DBL, __function_name, *value);
1093
1094 return SUCCEED;
1095 }
1096
1097 /******************************************************************************
1098 * *
1099 * Function: evaluate_string_to_double *
1100 * *
1101 * Purpose: cast string to a double, expand suffixes and parse negative sign *
1102 * *
1103 * Parameters: in - [IN] the input string *
1104 * Return value: - the resulting double *
1105 * *
1106 ******************************************************************************/
evaluate_string_to_double(const char * in)1107 double evaluate_string_to_double(const char *in)
1108 {
1109 int len;
1110 double result_double_value;
1111 const char *tmp_ptr = in;
1112
1113 if (1 < strlen(in) && '-' == in[0])
1114 tmp_ptr++;
1115
1116 if (SUCCEED == zbx_suffixed_number_parse(tmp_ptr, &len) && '\0' == *(tmp_ptr + len))
1117 {
1118 result_double_value = atof(tmp_ptr) * suffix2factor(*(tmp_ptr + len - 1));
1119
1120 /* negative sign detected */
1121 if (tmp_ptr != in)
1122 result_double_value = -(result_double_value);
1123 }
1124 else
1125 {
1126 result_double_value = ZBX_INFINITY;
1127 }
1128
1129 return result_double_value;
1130 }
1131