/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "zbxeval.h" #include "eval.h" #include "zbxalgo.h" #include "zbxvariant.h" #include "zbxnum.h" #include "zbxexpr.h" #include "zbxstr.h" /* exit code in addition to SUCCEED/FAIL */ #define UNKNOWN 1 /* bit function types */ typedef enum { FUNCTION_OPTYPE_BIT_AND = 0, FUNCTION_OPTYPE_BIT_OR, FUNCTION_OPTYPE_BIT_XOR, FUNCTION_OPTYPE_BIT_LSHIFT, FUNCTION_OPTYPE_BIT_RSHIFT } zbx_function_bit_optype_t; /* trim function types */ typedef enum { FUNCTION_OPTYPE_TRIM_ALL = 0, FUNCTION_OPTYPE_TRIM_LEFT, FUNCTION_OPTYPE_TRIM_RIGHT } zbx_function_trim_optype_t; /****************************************************************************** * * * Purpose: convert variant string value containing suffixed number to * * floating point variant value * * * * Parameters: value - [OUT] the output value * * value_num - [IN] the value to convert * * * * Return value: SUCCEED - the value was converted successfully * * FAIL - otherwise * * * ******************************************************************************/ static int variant_convert_suffixed_num(zbx_variant_t *value, const zbx_variant_t *value_num) { char suffix; double result; if (ZBX_VARIANT_STR != value_num->type) return FAIL; if (SUCCEED != eval_suffixed_number_parse(value_num->data.str, &suffix)) return FAIL; result = atof(value_num->data.str) * suffix2factor(suffix); if (FP_ZERO != fpclassify(result) && FP_NORMAL != fpclassify(result)) return FAIL; zbx_variant_set_dbl(value, result); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate unary operator * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the operator token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the operator was evaluated successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_op_unary(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t *right; double value; if (1 > output->values_num) { *error = zbx_dsprintf(*error, "unary operator requires one operand at \"%s\"", ctx->expression + token->loc.l); return FAIL; } right = &output->values[output->values_num - 1]; if (ZBX_VARIANT_ERR == right->type) return SUCCEED; if (SUCCEED != zbx_variant_convert(right, ZBX_VARIANT_DBL)) { *error = zbx_dsprintf(*error, "unary operator operand \"%s\" is not a numeric value at \"%s\"", zbx_variant_value_desc(right), ctx->expression + token->loc.l); return FAIL; } switch (token->type) { case ZBX_EVAL_TOKEN_OP_MINUS: value = -right->data.dbl; break; case ZBX_EVAL_TOKEN_OP_NOT: value = (SUCCEED == zbx_double_compare(right->data.dbl, 0) ? 1 : 0); break; default: THIS_SHOULD_NEVER_HAPPEN; *error = zbx_dsprintf(*error, "unknown unary operator at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (FP_ZERO != fpclassify(value) && FP_NORMAL != fpclassify(value)) { *error = zbx_dsprintf(*error, "calculation resulted in NaN or Infinity at \"%s\"", ctx->expression + token->loc.l); return FAIL; } zbx_variant_clear(right); zbx_variant_set_dbl(right, value); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate logical or/and operator with one operand being error * * * * Parameters: token - [IN] the operator token * * value - [IN] the other operand * * result - [OUT] the resulting value * * * * Return value: SUCCEED - the operator was evaluated successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_op_logic_err(const zbx_eval_token_t *token, const zbx_variant_t *value, double *result) { zbx_variant_t value_dbl; if (ZBX_VARIANT_ERR == value->type) return FAIL; zbx_variant_copy(&value_dbl, value); if (SUCCEED != zbx_variant_convert(&value_dbl, ZBX_VARIANT_DBL)) { zbx_variant_clear(&value_dbl); return FAIL; } switch (token->type) { case ZBX_EVAL_TOKEN_OP_AND: if (SUCCEED == zbx_double_compare(value_dbl.data.dbl, 0)) { *result = 0; return SUCCEED; } break; case ZBX_EVAL_TOKEN_OP_OR: if (SUCCEED != zbx_double_compare(value_dbl.data.dbl, 0)) { *result = 1; return SUCCEED; } break; } return FAIL; } /****************************************************************************** * * * Purpose: compare two variant values supporting suffixed numbers * * * * Return value: <0 - the first value is less than the second * * >0 - the first value is greater than the second * * 0 - the values are equal * * * ******************************************************************************/ static int eval_variant_compare(const zbx_variant_t *left, const zbx_variant_t *right) { zbx_variant_t val_l, val_r; int ret; zbx_variant_set_none(&val_l); zbx_variant_set_none(&val_r); if (SUCCEED == variant_convert_suffixed_num(&val_l, left)) left = &val_l; if (SUCCEED == variant_convert_suffixed_num(&val_r, right)) right = &val_r; ret = zbx_variant_compare(left, right); zbx_variant_clear(&val_l); zbx_variant_clear(&val_r); return ret; } /****************************************************************************** * * * Purpose: evaluate binary operator * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the operator token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the operator was evaluated successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_op_binary(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t *left, *right; double value; if (2 > output->values_num) { *error = zbx_dsprintf(*error, "binary operator requires two operands at \"%s\"", ctx->expression + token->loc.l); return FAIL; } left = &output->values[output->values_num - 2]; right = &output->values[output->values_num - 1]; /* process error operands */ if (ZBX_VARIANT_ERR == left->type) { if (ZBX_EVAL_TOKEN_OP_AND == token->type || ZBX_EVAL_TOKEN_OP_OR == token->type) { if (SUCCEED == eval_execute_op_logic_err(token, right, &value)) goto finish; } zbx_variant_clear(right); output->values_num--; return SUCCEED; } else if (ZBX_VARIANT_ERR == right->type) { if (ZBX_EVAL_TOKEN_OP_AND == token->type || ZBX_EVAL_TOKEN_OP_OR == token->type) { if (SUCCEED == eval_execute_op_logic_err(token, left, &value)) goto finish; } zbx_variant_clear(left); *left = *right; output->values_num--; return SUCCEED; } /* check logical equal, not equal operators */ if (ZBX_EVAL_TOKEN_OP_EQ == token->type || ZBX_EVAL_TOKEN_OP_NE == token->type) { if (ZBX_VARIANT_DBL_VECTOR == left->type || ZBX_VARIANT_DBL_VECTOR == right->type) { *error = zbx_dsprintf(*error, "vector cannot be used with comparison operator at \"%s\"", ctx->expression + token->loc.l); return FAIL; } switch (token->type) { case ZBX_EVAL_TOKEN_OP_EQ: value = (0 == eval_variant_compare(left, right) ? 1 : 0); goto finish; case ZBX_EVAL_TOKEN_OP_NE: value = (0 == eval_variant_compare(left, right) ? 0 : 1); goto finish; } } /* check arithmetic operators */ if (SUCCEED != zbx_variant_convert(left, ZBX_VARIANT_DBL)) { *error = zbx_dsprintf(*error, "left operand \"%s\" is not a numeric value for operator at \"%s\"", zbx_variant_value_desc(left), ctx->expression + token->loc.l); return FAIL; } if (SUCCEED != zbx_variant_convert(right, ZBX_VARIANT_DBL)) { *error = zbx_dsprintf(*error, "right operand \"%s\" is not a numeric value for operator at \"%s\"", zbx_variant_value_desc(right), ctx->expression + token->loc.l); return FAIL; } /* check logical operators */ switch (token->type) { case ZBX_EVAL_TOKEN_OP_AND: if (SUCCEED == zbx_double_compare(left->data.dbl, 0) || SUCCEED == zbx_double_compare(right->data.dbl, 0)) { value = 0; } else value = 1; goto finish; case ZBX_EVAL_TOKEN_OP_OR: if (SUCCEED != zbx_double_compare(left->data.dbl, 0) || SUCCEED != zbx_double_compare(right->data.dbl, 0)) { value = 1; } else value = 0; goto finish; } /* check arithmetic operators */ switch (token->type) { case ZBX_EVAL_TOKEN_OP_LT: value = (0 > zbx_variant_compare(left, right) ? 1 : 0); break; case ZBX_EVAL_TOKEN_OP_LE: value = (0 >= zbx_variant_compare(left, right) ? 1 : 0); break; case ZBX_EVAL_TOKEN_OP_GT: value = (0 < zbx_variant_compare(left, right) ? 1 : 0); break; case ZBX_EVAL_TOKEN_OP_GE: value = (0 <= zbx_variant_compare(left, right) ? 1 : 0); break; case ZBX_EVAL_TOKEN_OP_ADD: value = left->data.dbl + right->data.dbl; break; case ZBX_EVAL_TOKEN_OP_SUB: value = left->data.dbl - right->data.dbl; break; case ZBX_EVAL_TOKEN_OP_MUL: value = left->data.dbl * right->data.dbl; break; case ZBX_EVAL_TOKEN_OP_DIV: if (SUCCEED == zbx_double_compare(right->data.dbl, 0)) { *error = zbx_dsprintf(*error, "division by zero at \"%s\"", ctx->expression + token->loc.l); return FAIL; } value = left->data.dbl / right->data.dbl; break; } if (FP_ZERO != fpclassify(value) && FP_NORMAL != fpclassify(value)) { *error = zbx_dsprintf(*error, "calculation resulted in NaN or Infinity at \"%s\"", ctx->expression + token->loc.l); return FAIL; } finish: zbx_variant_clear(left); zbx_variant_clear(right); zbx_variant_set_dbl(left, value); output->values_num--; return SUCCEED; } /****************************************************************************** * * * Purpose: check if the value is suffixed number and return the suffix if * * exists * * * * Parameters: value - [IN] the value to check * * suffix - [OUT] the suffix or 0 if number does not have suffix * * (optional) * * * * Return value: SUCCEED - the value is suffixed number * * FAIL - otherwise * * * ******************************************************************************/ int eval_suffixed_number_parse(const char *value, char *suffix) { int len, num_len; if ('-' == *value) value++; len = strlen(value); if (SUCCEED != zbx_suffixed_number_parse(value, &num_len) || num_len != len) return FAIL; if (NULL != suffix) *suffix = value[len - 1]; return SUCCEED; } /****************************************************************************** * * * Purpose: push value in output stack * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the value token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the value was pushed successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_push_value(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t value; char *dst; const char *src; if (ZBX_VARIANT_NONE == token->value.type) { if (ZBX_EVAL_TOKEN_VAR_NUM == token->type) { zbx_uint64_t ui64; if (SUCCEED == zbx_is_uint64_n(ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1, &ui64)) { zbx_variant_set_ui64(&value, ui64); } else { zbx_variant_set_dbl(&value, atof(ctx->expression + token->loc.l) * suffix2factor(ctx->expression[token->loc.r])); } } else { dst = zbx_malloc(NULL, token->loc.r - token->loc.l + 2); zbx_variant_set_str(&value, dst); if (ZBX_EVAL_TOKEN_VAR_STR == token->type) { for (src = ctx->expression + token->loc.l + 1; src < ctx->expression + token->loc.r; src++) { if ('\\' == *src) src++; *dst++ = *src; } } else { memcpy(dst, ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1); dst += token->loc.r - token->loc.l + 1; } *dst = '\0'; } } else { if (ZBX_VARIANT_ERR == token->value.type && 0 == (ctx->rules & ZBX_EVAL_PROCESS_ERROR)) { *error = zbx_strdup(*error, token->value.data.err); return FAIL; } /* Expanded user macro token variables can contain suffixed numbers. */ /* Try to convert them and just copy the expanded value if failed. */ if (ZBX_EVAL_TOKEN_VAR_USERMACRO != token->type || SUCCEED != variant_convert_suffixed_num(&value, &token->value)) { zbx_variant_copy(&value, &token->value); } } zbx_vector_var_append_ptr(output, &value); return SUCCEED; } /****************************************************************************** * * * Purpose: push null value in output stack * * * * Parameters: output - [IN/OUT] the output value stack * * * ******************************************************************************/ static void eval_execute_push_null(zbx_vector_var_t *output) { zbx_variant_t value; zbx_variant_set_none(&value); zbx_vector_var_append_ptr(output, &value); } /****************************************************************************** * * * Purpose: check if expression fragment matches the specified text * * * * Parameters: ctx - [IN] the evaluation context * * loc - [IN] the expression fragment location * * text - [IN] the text to compare with * * len - [IN] the text length * * * * Return value: SUCCEED - the expression fragment matches the text * * FAIL - otherwise * * * ******************************************************************************/ int eval_compare_token(const zbx_eval_context_t *ctx, const zbx_strloc_t *loc, const char *text, size_t len) { if (loc->r - loc->l + 1 != len) return FAIL; if (0 != memcmp(ctx->expression + loc->l, text, len)) return FAIL; return SUCCEED; } /****************************************************************************** * * * Purpose: handle function return * * * * Parameters: args_num - [IN] the number of function arguments * * value - [IN] the return value * * output - [IN/OUT] the output value stack * * * * Comments: The function arguments on output stack are replaced with the * * return value. * * * ******************************************************************************/ static void eval_function_return(zbx_uint32_t args_num, zbx_variant_t *value, zbx_vector_var_t *output) { int i; for (i = output->values_num - (int)args_num; i < output->values_num; i++) zbx_variant_clear(&output->values[i]); output->values_num -= (int)args_num; zbx_vector_var_append_ptr(output, value); } /****************************************************************************** * * * Purpose: validate function arguments * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function arguments contain error values - the * * first error is returned as function value without * * evaluating the function * * FAIL - argument validation failed * * UNKNOWN - argument validation succeeded, function result is * * unknown at the moment, function must be evaluated * * with the prepared arguments * * * ******************************************************************************/ static int eval_validate_function_args(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i; if (output->values_num < (int)token->opt) { *error = zbx_dsprintf(*error, "not enough arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } for (i = output->values_num - token->opt; i < output->values_num; i++) { if (ZBX_VARIANT_ERR == output->values[i].type) { zbx_variant_t value = output->values[i]; /* first error argument is used as function return value */ zbx_variant_set_none(&output->values[i]); eval_function_return(token->opt, &value, output); return SUCCEED; } } return UNKNOWN; } static const char *eval_type_desc(unsigned char type) { switch (type) { case ZBX_VARIANT_DBL: return "a numeric"; case ZBX_VARIANT_UI64: return "an unsigned integer"; case ZBX_VARIANT_STR: return "a string"; default: return zbx_get_variant_type_desc(type); } } /****************************************************************************** * * * Purpose: convert function argument to the specified type * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * type - [IN] the required type * * arg - [IN/OUT] the argument to convert * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - argument was converted successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_convert_function_arg(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, unsigned char type, zbx_variant_t *arg, char **error) { zbx_variant_t value; if (ZBX_VARIANT_DBL == type && SUCCEED == variant_convert_suffixed_num(&value, arg)) { zbx_variant_clear(arg); *arg = value; return SUCCEED; } if (SUCCEED == zbx_variant_convert(arg, type)) return SUCCEED; *error = zbx_dsprintf(*error, "function argument \"%s\" is not %s value at \"%s\"", zbx_variant_value_desc(arg), eval_type_desc(type), ctx->expression + token->loc.l); return FAIL; } /****************************************************************************** * * * Purpose: validate and prepare (convert to floating values) math function * * arguments * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function arguments contain error values - the * * first error is returned as function value without * * evaluating the function * * FAIL - argument validation/conversion failed * * UNKNOWN - argument conversion succeeded, function result is * * unknown at the moment, function must be evaluated * * with the prepared arguments * * * * Comments: Math function accepts either 1+ arguments that can be converted * * to floating values or a single argument of non-zero length * * floating value vector. * * * ******************************************************************************/ static int eval_prepare_math_function_args(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; if (0 == token->opt) { *error = zbx_dsprintf(*error, "no arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; i = output->values_num - token->opt; if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type) { for (; i < output->values_num; i++) { if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_DBL, &output->values[i], error)) return FAIL; } } else { if (1 != token->opt) { *error = zbx_dsprintf(*error, "too many arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (0 == output->values[i].data.dbl_vector->values_num) { *error = zbx_dsprintf(*error, "no input data for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } } return UNKNOWN; } /****************************************************************************** * * * Purpose: evaluate min() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_min(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; double min; zbx_variant_t value; if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; i = output->values_num - token->opt; if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type) { min = output->values[i++].data.dbl; for (; i < output->values_num; i++) { if (min > output->values[i].data.dbl) min = output->values[i].data.dbl; } } else { zbx_vector_dbl_t *dbl_vector = output->values[i].data.dbl_vector; min = dbl_vector->values[0]; for (i = 1; i < dbl_vector->values_num; i++) { if (min > dbl_vector->values[i]) min = dbl_vector->values[i]; } } zbx_variant_set_dbl(&value, min); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate max() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_max(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; double max; zbx_variant_t value; if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; i = output->values_num - token->opt; if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type) { max = output->values[i++].data.dbl; for (; i < output->values_num; i++) { if (max < output->values[i].data.dbl) max = output->values[i].data.dbl; } } else { zbx_vector_dbl_t *dbl_vector = output->values[i].data.dbl_vector; max = dbl_vector->values[0]; for (i = 1; i < dbl_vector->values_num; i++) { if (max < dbl_vector->values[i]) max = dbl_vector->values[i]; } } zbx_variant_set_dbl(&value, max); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate sum() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_sum(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; double sum = 0; zbx_variant_t value; if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; i = output->values_num - token->opt; if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type) { for (; i < output->values_num; i++) sum += output->values[i].data.dbl; } else { zbx_vector_dbl_t *dbl_vector = output->values[i].data.dbl_vector; for (i = 0; i < dbl_vector->values_num; i++) sum += dbl_vector->values[i]; } zbx_variant_set_dbl(&value, sum); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate avg() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_avg(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; double avg = 0; zbx_variant_t value; if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; i = output->values_num - token->opt; if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type) { for (; i < output->values_num; i++) avg += output->values[i].data.dbl; avg /= token->opt; } else { zbx_vector_dbl_t *dbl_vector = output->values[i].data.dbl_vector; for (i = 0; i < dbl_vector->values_num; i++) avg += dbl_vector->values[i]; avg /= dbl_vector->values_num; } zbx_variant_set_dbl(&value, avg); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate abs() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_abs(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; zbx_variant_set_dbl(&value, fabs(arg->data.dbl)); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate length() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_length(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error)) return FAIL; zbx_variant_set_dbl(&value, zbx_strlen_utf8(arg->data.str)); eval_function_return(1, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate date() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_date(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t value; struct tm *tm; time_t now; if (0 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } now = ctx->ts.sec; if (NULL == (tm = localtime(&now))) { *error = zbx_dsprintf(*error, "cannot convert time for function at \"%s\": %s", ctx->expression + token->loc.l, zbx_strerror(errno)); return FAIL; } zbx_variant_set_str(&value, zbx_dsprintf(NULL, "%.4d%.2d%.2d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday)); eval_function_return(0, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate time() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_time(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t value; struct tm *tm; time_t now; if (0 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } now = ctx->ts.sec; if (NULL == (tm = localtime(&now))) { *error = zbx_dsprintf(*error, "cannot convert time for function at \"%s\": %s", ctx->expression + token->loc.l, zbx_strerror(errno)); return FAIL; } zbx_variant_set_str(&value, zbx_dsprintf(NULL, "%.2d%.2d%.2d", tm->tm_hour, tm->tm_min, tm->tm_sec)); eval_function_return(0, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate now() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_now(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t value; if (0 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } zbx_variant_set_str(&value, zbx_dsprintf(NULL, "%d", ctx->ts.sec)); eval_function_return(0, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate dayofweek() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_dayofweek(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t value; struct tm *tm; time_t now; if (0 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } now = ctx->ts.sec; if (NULL == (tm = localtime(&now))) { *error = zbx_dsprintf(*error, "cannot convert time for function at \"%s\": %s", ctx->expression + token->loc.l, zbx_strerror(errno)); return FAIL; } zbx_variant_set_str(&value, zbx_dsprintf(NULL, "%d", 0 == tm->tm_wday ? 7 : tm->tm_wday)); eval_function_return(0, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate dayofmonth() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_dayofmonth(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t value; struct tm *tm; time_t now; if (0 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } now = ctx->ts.sec; if (NULL == (tm = localtime(&now))) { *error = zbx_dsprintf(*error, "cannot convert time for function at \"%s\": %s", ctx->expression + token->loc.l, zbx_strerror(errno)); return FAIL; } zbx_variant_set_str(&value, zbx_dsprintf(NULL, "%d", tm->tm_mday)); eval_function_return(0, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate bitand(), bitor(), bitxor(), bitlshift(), * * bitrshift() functions * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * type - [IN] the function type * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_bitwise(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_function_bit_optype_t type, zbx_vector_var_t *output, char **error) { zbx_variant_t value, *left, *right; int ret; if (2 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; left = &output->values[output->values_num - 2]; right = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, left, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, right, error)) { return FAIL; } switch (type) { case FUNCTION_OPTYPE_BIT_AND: zbx_variant_set_ui64(&value, left->data.ui64 & right->data.ui64); break; case FUNCTION_OPTYPE_BIT_OR: zbx_variant_set_ui64(&value, left->data.ui64 | right->data.ui64); break; case FUNCTION_OPTYPE_BIT_XOR: zbx_variant_set_ui64(&value, left->data.ui64 ^ right->data.ui64); break; case FUNCTION_OPTYPE_BIT_LSHIFT: zbx_variant_set_ui64(&value, left->data.ui64 << right->data.ui64); break; case FUNCTION_OPTYPE_BIT_RSHIFT: zbx_variant_set_ui64(&value, left->data.ui64 >> right->data.ui64); } eval_function_return(2, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate bitnot() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_bitnot(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t value, *arg; int ret; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, arg, error)) return FAIL; zbx_variant_set_ui64(&value, ~arg->data.ui64); eval_function_return(1, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate left() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_left(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, *len, value; size_t sz; char *strval; if (2 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 2]; len = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, len, error)) { return FAIL; } sz = zbx_strlen_utf8_nchars(arg->data.str, (size_t)len->data.ui64) + 1; strval = zbx_malloc(NULL, sz); zbx_strlcpy_utf8(strval, arg->data.str, sz); zbx_variant_set_str(&value, strval); eval_function_return(2, &value, output); return SUCCEED; } static int eval_validate_statistical_function_args(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } i = output->values_num - 1; if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type) { *error = zbx_dsprintf(*error, "invalid argument type \"%s\" for function at \"%s\"", zbx_variant_type_desc(&output->values[i]), ctx->expression + token->loc.l); return FAIL; } if (0 == output->values[i].data.dbl_vector->values_num) { *error = zbx_dsprintf(*error, "empty vector argument for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } return UNKNOWN; } /****************************************************************************** * * * Purpose: common operations for aggregate function calculation * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * stat_func - [IN] pointer to aggregate function to be called * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_statistical_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_statistical_func_t stat_func, zbx_vector_var_t *output, char **error) { int ret; double result; zbx_variant_t value; zbx_vector_dbl_t *dbl_vector; if (UNKNOWN != (ret = eval_validate_statistical_function_args(ctx, token, output, error))) return ret; dbl_vector = output->values[output->values_num - (int)token->opt].data.dbl_vector; if (FAIL == stat_func(dbl_vector, &result, error)) return FAIL; zbx_variant_set_dbl(&value, result); eval_function_return((int)token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate right() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_right(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, *len, value; size_t sz, srclen; char *strval, *p; if (2 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 2]; len = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, len, error)) { return FAIL; } srclen = zbx_strlen_utf8(arg->data.str); if (len->data.ui64 < srclen) { p = zbx_strshift_utf8(arg->data.str, srclen - len->data.ui64); sz = zbx_strlen_utf8_nchars(p, (size_t)len->data.ui64) + 1; strval = zbx_malloc(NULL, sz); zbx_strlcpy_utf8(strval, p, sz); } else strval = zbx_strdup(NULL, arg->data.str); zbx_variant_set_str(&value, strval); eval_function_return(2, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate mid() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_mid(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, *start, *len, value; size_t sz, srclen; char *strval, *p; if (3 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 3]; start = &output->values[output->values_num - 2]; len = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, start, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, len, error)) { return FAIL; } srclen = zbx_strlen_utf8(arg->data.str); if (0 == start->data.ui64 || start->data.ui64 > srclen) { *error = zbx_dsprintf(*error, "invalid function second argument at \"%s\"", ctx->expression + token->loc.l); return FAIL; } p = zbx_strshift_utf8(arg->data.str, start->data.ui64 - 1); if (srclen >= start->data.ui64 + len->data.ui64) { sz = zbx_strlen_utf8_nchars(p, len->data.ui64) + 1; strval = zbx_malloc(NULL, sz); zbx_strlcpy_utf8(strval, p, sz); } else strval = zbx_strdup(NULL, p); zbx_variant_set_str(&value, strval); eval_function_return(3, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate trim(), rtrim(), ltrim() functions * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * type - [IN] the function type * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_trim(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_function_trim_optype_t type, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *sym, *arg, value, sym_val; if (1 > token->opt || 2 < token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; if (2 == token->opt) { arg = &output->values[output->values_num - 2]; sym = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, sym, error)) return FAIL; } else { arg = &output->values[output->values_num - 1]; zbx_variant_set_str(&sym_val, zbx_strdup(NULL, ZBX_WHITESPACE)); sym = &sym_val; } if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error)) return FAIL; switch (type) { case FUNCTION_OPTYPE_TRIM_ALL: zbx_ltrim_utf8(arg->data.str, sym->data.str); zbx_rtrim_utf8(arg->data.str, sym->data.str); break; case FUNCTION_OPTYPE_TRIM_RIGHT: zbx_rtrim_utf8(arg->data.str, sym->data.str); break; case FUNCTION_OPTYPE_TRIM_LEFT: zbx_ltrim_utf8(arg->data.str, sym->data.str); break; } if (2 != token->opt) zbx_variant_clear(&sym_val); zbx_variant_set_str(&value, zbx_strdup(NULL, arg->data.str)); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate concat() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_concat(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; zbx_variant_t value; char *result = NULL; if (2 > token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; for (i = output->values_num - (int)token->opt; i < output->values_num; i++) { zbx_variant_t *arg; arg = &output->values[i]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error)) { zbx_free(result); return FAIL; } result = zbx_strdcat(result, zbx_variant_value_desc(arg)); } zbx_variant_set_str(&value, result); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate insert() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_insert(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, *start, *len, *replacement, value; char *strval, *p; size_t str_alloc, str_len, sz, src_len; if (4 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 4]; start = &output->values[output->values_num - 3]; len = &output->values[output->values_num - 2]; replacement = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, start, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, len, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, replacement, error)) { return FAIL; } src_len = zbx_strlen_utf8(arg->data.str); if (0 == start->data.ui64 || start->data.ui64 > src_len) { *error = zbx_dsprintf(*error, "invalid function second argument at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (src_len < start->data.ui64 - 1 + len->data.ui64) { *error = zbx_dsprintf(*error, "invalid function third argument at \"%s\"", ctx->expression + token->loc.l); return FAIL; } strval = zbx_strdup(NULL, arg->data.str); p = zbx_strshift_utf8(strval, start->data.ui64 - 1); sz = zbx_strlen_utf8_nchars(p, len->data.ui64); str_alloc = str_len = strlen(strval) + 1; zbx_replace_mem_dyn(&strval, &str_alloc, &str_len, (size_t)(p - strval), sz, replacement->data.str, strlen(replacement->data.str)); zbx_variant_set_str(&value, strval); eval_function_return(4, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate replace() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_replace(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, *pattern, *replacement, value; if (3 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 3]; pattern = &output->values[output->values_num - 2]; replacement = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, pattern, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, replacement, error)) { return FAIL; } if ('\0' != *pattern->data.str) zbx_variant_set_str(&value, zbx_string_replace(arg->data.str, pattern->data.str, replacement->data.str)); else zbx_variant_copy(&value, arg); eval_function_return(3, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate repeat() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_repeat(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *str, *num, value; char *strval = NULL; zbx_uint64_t i; size_t len_utf8; if (2 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; str = &output->values[output->values_num - 2]; num = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, str, error) || SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, num, error)) { return FAIL; } if (0 != (len_utf8 = zbx_strlen_utf8(str->data.str))) { if (num->data.ui64 * len_utf8 >= MAX_STRING_LEN) { *error = zbx_dsprintf(*error, "maximum allowed string length (%d) exceeded: " ZBX_FS_UI64, MAX_STRING_LEN, num->data.ui64 * len_utf8); return FAIL; } for (i = num->data.ui64; i > 0; i--) strval = zbx_strdcat(strval, str->data.str); } if (NULL == strval) strval = zbx_strdup(NULL, ""); zbx_variant_set_str(&value, strval); eval_function_return(2, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate bytelength() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_bytelength(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; if (SUCCEED == zbx_variant_convert(arg, ZBX_VARIANT_UI64)) { zbx_uint64_t byte = __UINT64_C(0xFF00000000000000); int i; for (i = 8; i > 0; i--) { if (byte & arg->data.ui64) break; byte = byte >> 8; } zbx_variant_set_dbl(&value, i); } else if (SUCCEED != zbx_variant_convert(arg, ZBX_VARIANT_STR)) { *error = zbx_dsprintf(*error, "invalid function argument at \"%s\"", ctx->expression + token->loc.l); return FAIL; } else zbx_variant_set_dbl(&value, strlen(arg->data.str)); eval_function_return(1, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate bitlength() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_bitlength(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; if (SUCCEED == zbx_variant_convert(arg, ZBX_VARIANT_UI64)) { int i, bits; bits = sizeof(uint64_t) * 8; for (i = bits - 1; i >= 0; i--) { if (__UINT64_C(1) << i & arg->data.ui64) break; } zbx_variant_set_dbl(&value, ++i); } else if (SUCCEED != zbx_variant_convert(arg, ZBX_VARIANT_STR)) { *error = zbx_dsprintf(*error, "invalid function argument at \"%s\"", ctx->expression + token->loc.l); return FAIL; } else zbx_variant_set_dbl(&value, strlen(arg->data.str) * 8); eval_function_return(1, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate char() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_char(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_UI64, arg, error)) return FAIL; if (127 < arg->data.ui64) { *error = zbx_dsprintf(*error, "function argument \"%s\" is out of allowed range at \"%s\"", zbx_variant_value_desc(arg), ctx->expression + token->loc.l); return FAIL; } zbx_variant_set_str(&value, zbx_dsprintf(NULL, "%c", (char)arg->data.ui64)); eval_function_return(1, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate ascii() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_ascii(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int ret; zbx_variant_t *arg, value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; if (SUCCEED != zbx_variant_convert(arg, ZBX_VARIANT_STR) || 1 != zbx_utf8_char_len(arg->data.str)) { *error = zbx_dsprintf(*error, "invalid function argument at \"%s\"", ctx->expression + token->loc.l); return FAIL; } zbx_variant_set_ui64(&value, (zbx_uint64_t)*arg->data.str); eval_function_return(1, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate between() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_between(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; double between; zbx_variant_t value; if (3 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; i = output->values_num - token->opt; between = output->values[i++].data.dbl; if (output->values[i++].data.dbl <= between && between <= output->values[i].data.dbl) zbx_variant_set_dbl(&value, 1); else zbx_variant_set_dbl(&value, 0); eval_function_return(3, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate in() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_in(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, arg_idx, found = 0, ret; zbx_variant_t value, *arg, *ref_str = NULL; double ref = 0; if (2 > token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; zbx_variant_set_dbl(&value, 0); for (i = arg_idx = output->values_num - token->opt; i < output->values_num; i++) { zbx_variant_t val_copy; if (SUCCEED != variant_convert_suffixed_num(&val_copy, &output->values[i])) { zbx_variant_copy(&val_copy, &output->values[i]); if (SUCCEED != zbx_variant_convert(&val_copy, ZBX_VARIANT_DBL)) { zbx_variant_clear(&val_copy); break; } } if (i == arg_idx) { ref = val_copy.data.dbl; continue; } if (0 == found && SUCCEED == zbx_double_compare(ref, val_copy.data.dbl)) found = 1; } if (i == output->values_num) { if (1 == found) zbx_variant_set_dbl(&value, 1); goto out; } for (i = arg_idx; i < output->values_num; i++) { arg = &output->values[i]; if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_STR, arg, error)) return FAIL; if (i == arg_idx) { ref_str = arg; continue; } if (0 == strcmp(ref_str->data.str, arg->data.str)) { zbx_variant_set_dbl(&value, 1); break; } } out: eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate histogram_quantile() function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_function_histogram_quantile(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { int i, ret; zbx_variant_t value; zbx_vector_dbl_t values, *v = NULL; double q, result; const char *err_fn = ctx->expression + token->loc.l; if (2 > token->opt || (2 < token->opt && (token->opt % 2 == 0))) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", err_fn); return FAIL; } if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error))) return ret; i = output->values_num - (int)token->opt + 1; if (2 == token->opt) { if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type) { *error = zbx_dsprintf(*error, "invalid type of second argument for function at \"%s\"", err_fn); return FAIL; } if (output->values[i].data.dbl_vector->values_num % 2 != 0) { *error = zbx_dsprintf(*error, "invalid values number of second argument for function at \"%s\"", err_fn); return FAIL; } } else { if ((output->values_num - i) % 2 != 0) { *error = zbx_dsprintf(*error, "invalid number of histogram arguments for function at \"%s\"", err_fn); return FAIL; } for (; i < output->values_num; i++) { if (ZBX_VARIANT_STR == output->values[i].type ) { zbx_strupper(output->values[i].data.str); if (0 == strcmp(output->values[i].data.str, "+INF") || 0 == strcmp(output->values[i].data.str, "INF")) { zbx_variant_clear(&output->values[i]); zbx_variant_set_dbl(&output->values[i], ZBX_INFINITY); } else { *error = zbx_dsprintf(*error, "invalid string values of backet" " for function at \"%s\"", err_fn); return FAIL; } } else if (SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_DBL, &output->values[i], error)) { return FAIL; } } } i = output->values_num - (int)token->opt; if (ZBX_VARIANT_DBL != output->values[i].type && SUCCEED != eval_convert_function_arg(ctx, token, ZBX_VARIANT_DBL, &output->values[i], error)) { return FAIL; } q = output->values[i].data.dbl; if (0 > q || 1 < q) { *error = zbx_dsprintf(*error, "invalid value of quantile for function at \"%s\"", err_fn); return FAIL; } i = output->values_num - (int)token->opt + 1; if (2 == token->opt) { v = output->values[i].data.dbl_vector; } else { zbx_vector_dbl_create(&values); while (i < output->values_num) { zbx_vector_dbl_append(&values, output->values[i++].data.dbl); } } if (SUCCEED == (ret = zbx_eval_calc_histogram_quantile(q, NULL != v ? v : &values, err_fn, &result, error))) { zbx_variant_set_dbl(&value, result); eval_function_return(token->opt, &value, output); } if (NULL == v) zbx_vector_dbl_destroy(&values); return ret; } /****************************************************************************** * * * Purpose: evaluate function by calling custom callback (if configured) * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * function_cb - [IN] the callback function * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the function was executed successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_cb_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_eval_function_cb_t function_cb, zbx_vector_var_t *output, char **error) { zbx_variant_t value, *args; char *errmsg = NULL; args = (0 == token->opt ? NULL : &output->values[output->values_num - token->opt]); if (SUCCEED != function_cb(ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1, token->opt, args, ctx->data_cb, &ctx->ts, &value, &errmsg)) { *error = zbx_dsprintf(*error, "%s at \"%s\".", errmsg, ctx->expression + token->loc.l); zbx_free(errmsg); if (0 == (ctx->rules & ZBX_EVAL_PROCESS_ERROR)) return FAIL; zbx_variant_set_error(&value, *error); *error = NULL; } eval_function_return(token->opt, &value, output); return SUCCEED; } #define ZBX_MATH_CONST_PI 3.141592653589793238463 #define ZBX_MATH_CONST_E 2.7182818284590452354 #define ZBX_MATH_RANDOM 0 static double eval_math_func_degrees(double radians) { return radians * (180.0 / ZBX_MATH_CONST_PI); } static double eval_math_func_radians(double degrees) { return degrees * (ZBX_MATH_CONST_PI / 180); } static double eval_math_func_cot(double x) { return cos(x) / sin(x); } static double eval_math_func_signum(double x) { if (0 > x) return -1; if (0 == x) return 0; return 1; } /****************************************************************************** * * * Purpose: evaluate mathematical function by calling passed function * * with 1 double argument * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * func - [IN] the pointer to math function * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_math_function_single_param(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error, double (*func)(double)) { int ret; double result; zbx_variant_t *arg, value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; arg = &output->values[output->values_num - 1]; if (((log == func || log10 == func) && 0 >= arg->data.dbl) || (sqrt == func && 0 > arg->data.dbl) || (eval_math_func_cot == func && 0 == arg->data.dbl)) { *error = zbx_dsprintf(*error, "invalid argument for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } result = func(arg->data.dbl); if (FP_ZERO != fpclassify(result) && FP_NORMAL != fpclassify(result)) { *error = zbx_dsprintf(*error, "calculation resulted in NaN or Infinity at \"%s\"", ctx->expression + token->loc.l); return FAIL; } zbx_variant_set_dbl(&value, result); eval_function_return(token->opt, &value, output); return SUCCEED; } static double eval_math_func_round(double n, double decimal_points) { double multiplier; multiplier = pow(10.0, decimal_points); return round(n * multiplier ) / multiplier; } static double eval_math_func_truncate(double n, double decimal_points) { double multiplier = 1; if (0 < decimal_points) multiplier = pow(10, decimal_points); if (0 > n) multiplier = -multiplier; return floor(multiplier * n) / multiplier; } /****************************************************************************** * * * Purpose: evaluate mathematical function by calling passed function * * with 2 double arguments * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * func - [IN] the pointer to math function * * * * Return value: SUCCEED - function evaluation succeeded * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_math_function_double_param(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error, double (*func)(double, double)) { int ret; double result; zbx_variant_t *arg1, *arg2, value; if (2 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (UNKNOWN != (ret = eval_prepare_math_function_args(ctx, token, output, error))) return ret; arg1 = &output->values[output->values_num - 2]; arg2 = &output->values[output->values_num - 1]; if (((eval_math_func_round == func || eval_math_func_truncate == func) && (0 > arg2->data.dbl || 0.0 != fmod(arg2->data.dbl, 1))) || (fmod == func && 0.0 == arg2->data.dbl)) { *error = zbx_dsprintf(*error, "invalid second argument for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (atan2 == func && 0.0 == arg1->data.dbl && 0.0 == arg2->data.dbl) { *error = zbx_dsprintf(*error, "undefined result for arguments (0,0) for function 'atan2' at \"%s\"", ctx->expression + token->loc.l); return FAIL; } result = func(arg1->data.dbl, arg2->data.dbl); if (FP_ZERO != fpclassify(result) && FP_NORMAL != fpclassify(result)) { *error = zbx_dsprintf(*error, "calculation resulted in NaN or Infinity at \"%s\"", ctx->expression + token->loc.l); return FAIL; } zbx_variant_set_dbl(&value, result); eval_function_return(token->opt, &value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate mathematical function that returns constant value * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * value - [IN] the value to be returned * * * * Return value: SUCCEED - function evaluation succeeded * * * ******************************************************************************/ static int eval_execute_math_return_value(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error, double value) { zbx_variant_t ret_value; if (0 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (ZBX_MATH_RANDOM == value) { struct timespec ts; if (SUCCEED != clock_gettime(CLOCK_MONOTONIC, &ts)) { *error = zbx_strdup(*error, "failed to generate seed for random number generator"); return FAIL; } else { srandom((unsigned int)(ts.tv_nsec ^ ts.tv_sec)); zbx_variant_set_dbl(&ret_value, random()); } } else zbx_variant_set_dbl(&ret_value, value); eval_function_return(0, &ret_value, output); return SUCCEED; } static int eval_execute_function_count(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { zbx_variant_t *arg, ret_value; if (1 != token->opt) { *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } arg = &output->values[output->values_num - 1]; if (ZBX_VARIANT_DBL_VECTOR != arg->type) { *error = zbx_dsprintf(*error, "invalid type of argument for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } zbx_variant_set_ui64(&ret_value, (zbx_uint64_t)arg->data.dbl_vector->values_num); eval_function_return(token->opt, &ret_value, output); return SUCCEED; } /****************************************************************************** * * * Purpose: evaluate common function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the function was executed successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_common_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { if ((zbx_uint32_t)output->values_num < token->opt) { *error = zbx_dsprintf(*error, "not enough arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (SUCCEED == eval_compare_token(ctx, &token->loc, "min", ZBX_CONST_STRLEN("min"))) return eval_execute_function_min(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "max", ZBX_CONST_STRLEN("max"))) return eval_execute_function_max(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "sum", ZBX_CONST_STRLEN("sum"))) return eval_execute_function_sum(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "avg", ZBX_CONST_STRLEN("avg"))) return eval_execute_function_avg(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "abs", ZBX_CONST_STRLEN("abs"))) return eval_execute_function_abs(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "length", ZBX_CONST_STRLEN("length"))) return eval_execute_function_length(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "date", ZBX_CONST_STRLEN("date"))) return eval_execute_function_date(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "time", ZBX_CONST_STRLEN("time"))) return eval_execute_function_time(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "now", ZBX_CONST_STRLEN("now"))) return eval_execute_function_now(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "dayofweek", ZBX_CONST_STRLEN("dayofweek"))) return eval_execute_function_dayofweek(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "dayofmonth", ZBX_CONST_STRLEN("dayofmonth"))) return eval_execute_function_dayofmonth(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitand", ZBX_CONST_STRLEN("bitand"))) return eval_execute_function_bitwise(ctx, token, FUNCTION_OPTYPE_BIT_AND, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitor", ZBX_CONST_STRLEN("bitor"))) return eval_execute_function_bitwise(ctx, token, FUNCTION_OPTYPE_BIT_OR, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitxor", ZBX_CONST_STRLEN("bitxor"))) return eval_execute_function_bitwise(ctx, token, FUNCTION_OPTYPE_BIT_XOR, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitlshift", ZBX_CONST_STRLEN("bitlshift"))) return eval_execute_function_bitwise(ctx, token, FUNCTION_OPTYPE_BIT_LSHIFT, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitrshift", ZBX_CONST_STRLEN("bitrshift"))) return eval_execute_function_bitwise(ctx, token, FUNCTION_OPTYPE_BIT_RSHIFT, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitnot", ZBX_CONST_STRLEN("bitnot"))) return eval_execute_function_bitnot(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "between", ZBX_CONST_STRLEN("between"))) return eval_execute_function_between(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "in", ZBX_CONST_STRLEN("in"))) return eval_execute_function_in(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "ascii", ZBX_CONST_STRLEN("ascii"))) return eval_execute_function_ascii(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "char", ZBX_CONST_STRLEN("char"))) return eval_execute_function_char(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "left", ZBX_CONST_STRLEN("left"))) return eval_execute_function_left(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "right", ZBX_CONST_STRLEN("right"))) return eval_execute_function_right(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "mid", ZBX_CONST_STRLEN("mid"))) return eval_execute_function_mid(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitlength", ZBX_CONST_STRLEN("bitlength"))) return eval_execute_function_bitlength(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "bytelength", ZBX_CONST_STRLEN("bytelength"))) return eval_execute_function_bytelength(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "concat", ZBX_CONST_STRLEN("concat"))) return eval_execute_function_concat(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "insert", ZBX_CONST_STRLEN("insert"))) return eval_execute_function_insert(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "replace", ZBX_CONST_STRLEN("replace"))) return eval_execute_function_replace(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "repeat", ZBX_CONST_STRLEN("repeat"))) return eval_execute_function_repeat(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "ltrim", ZBX_CONST_STRLEN("ltrim"))) return eval_execute_function_trim(ctx, token, FUNCTION_OPTYPE_TRIM_LEFT, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "rtrim", ZBX_CONST_STRLEN("rtrim"))) return eval_execute_function_trim(ctx, token, FUNCTION_OPTYPE_TRIM_RIGHT, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "trim", ZBX_CONST_STRLEN("trim"))) return eval_execute_function_trim(ctx, token, FUNCTION_OPTYPE_TRIM_ALL, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "cbrt", ZBX_CONST_STRLEN("cbrt"))) return eval_execute_math_function_single_param(ctx, token, output, error, cbrt); if (SUCCEED == eval_compare_token(ctx, &token->loc, "ceil", ZBX_CONST_STRLEN("ceil"))) return eval_execute_math_function_single_param(ctx, token, output, error, ceil); if (SUCCEED == eval_compare_token(ctx, &token->loc, "exp", ZBX_CONST_STRLEN("exp"))) return eval_execute_math_function_single_param(ctx, token, output, error, exp); if (SUCCEED == eval_compare_token(ctx, &token->loc, "expm1", ZBX_CONST_STRLEN("expm1"))) return eval_execute_math_function_single_param(ctx, token, output, error, expm1); if (SUCCEED == eval_compare_token(ctx, &token->loc, "floor", ZBX_CONST_STRLEN("floor"))) return eval_execute_math_function_single_param(ctx, token, output, error, floor); if (SUCCEED == eval_compare_token(ctx, &token->loc, "signum", ZBX_CONST_STRLEN("signum"))) return eval_execute_math_function_single_param(ctx, token, output, error, eval_math_func_signum); if (SUCCEED == eval_compare_token(ctx, &token->loc, "degrees", ZBX_CONST_STRLEN("degrees"))) return eval_execute_math_function_single_param(ctx, token, output, error, eval_math_func_degrees); if (SUCCEED == eval_compare_token(ctx, &token->loc, "radians", ZBX_CONST_STRLEN("radians"))) return eval_execute_math_function_single_param(ctx, token, output, error, eval_math_func_radians); if (SUCCEED == eval_compare_token(ctx, &token->loc, "acos", ZBX_CONST_STRLEN("acos"))) return eval_execute_math_function_single_param(ctx, token, output, error, acos); if (SUCCEED == eval_compare_token(ctx, &token->loc, "asin", ZBX_CONST_STRLEN("asin"))) return eval_execute_math_function_single_param(ctx, token, output, error, asin); if (SUCCEED == eval_compare_token(ctx, &token->loc, "atan", ZBX_CONST_STRLEN("atan"))) return eval_execute_math_function_single_param(ctx, token, output, error, atan); if (SUCCEED == eval_compare_token(ctx, &token->loc, "cos", ZBX_CONST_STRLEN("cos"))) return eval_execute_math_function_single_param(ctx, token, output, error, cos); if (SUCCEED == eval_compare_token(ctx, &token->loc, "cosh", ZBX_CONST_STRLEN("cosh"))) return eval_execute_math_function_single_param(ctx, token, output, error, cosh); if (SUCCEED == eval_compare_token(ctx, &token->loc, "cot", ZBX_CONST_STRLEN("cot"))) return eval_execute_math_function_single_param(ctx, token, output, error, eval_math_func_cot); if (SUCCEED == eval_compare_token(ctx, &token->loc, "sin", ZBX_CONST_STRLEN("sin"))) return eval_execute_math_function_single_param(ctx, token, output, error, sin); if (SUCCEED == eval_compare_token(ctx, &token->loc, "sinh", ZBX_CONST_STRLEN("sinh"))) return eval_execute_math_function_single_param(ctx, token, output, error, sinh); if (SUCCEED == eval_compare_token(ctx, &token->loc, "tan", ZBX_CONST_STRLEN("tan"))) return eval_execute_math_function_single_param(ctx, token, output, error, tan); if (SUCCEED == eval_compare_token(ctx, &token->loc, "log", ZBX_CONST_STRLEN("log"))) return eval_execute_math_function_single_param(ctx, token, output, error, log); if (SUCCEED == eval_compare_token(ctx, &token->loc, "log10", ZBX_CONST_STRLEN("log10"))) return eval_execute_math_function_single_param(ctx, token, output, error, log10); if (SUCCEED == eval_compare_token(ctx, &token->loc, "sqrt", ZBX_CONST_STRLEN("sqrt"))) return eval_execute_math_function_single_param(ctx, token, output, error, sqrt); if (SUCCEED == eval_compare_token(ctx, &token->loc, "power", ZBX_CONST_STRLEN("power"))) return eval_execute_math_function_double_param(ctx, token, output, error, pow); if (SUCCEED == eval_compare_token(ctx, &token->loc, "round", ZBX_CONST_STRLEN("round"))) return eval_execute_math_function_double_param(ctx, token, output, error, eval_math_func_round); if (SUCCEED == eval_compare_token(ctx, &token->loc, "mod", ZBX_CONST_STRLEN("mod"))) return eval_execute_math_function_double_param(ctx, token, output, error, fmod); if (SUCCEED == eval_compare_token(ctx, &token->loc, "truncate", ZBX_CONST_STRLEN("truncate"))) return eval_execute_math_function_double_param(ctx, token, output, error, eval_math_func_truncate); if (SUCCEED == eval_compare_token(ctx, &token->loc, "atan2", ZBX_CONST_STRLEN("atan2"))) return eval_execute_math_function_double_param(ctx, token, output, error, atan2); if (SUCCEED == eval_compare_token(ctx, &token->loc, "pi", ZBX_CONST_STRLEN("pi"))) return eval_execute_math_return_value(ctx, token, output, error, ZBX_MATH_CONST_PI); if (SUCCEED == eval_compare_token(ctx, &token->loc, "e", ZBX_CONST_STRLEN("e"))) return eval_execute_math_return_value(ctx, token, output, error, ZBX_MATH_CONST_E); if (SUCCEED == eval_compare_token(ctx, &token->loc, "rand", ZBX_CONST_STRLEN("rand"))) return eval_execute_math_return_value(ctx, token, output, error, ZBX_MATH_RANDOM); if (SUCCEED == eval_compare_token(ctx, &token->loc, "kurtosis", ZBX_CONST_STRLEN("kurtosis"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_kurtosis, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "mad", ZBX_CONST_STRLEN("mad"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_mad, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "skewness", ZBX_CONST_STRLEN("skewness"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_skewness, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "stddevpop", ZBX_CONST_STRLEN("stddevpop"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_stddevpop, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "stddevsamp", ZBX_CONST_STRLEN("stddevsamp"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_stddevsamp, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "sumofsquares", ZBX_CONST_STRLEN("sumofsquares"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_sumofsquares, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "varpop", ZBX_CONST_STRLEN("varpop"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_varpop, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "varsamp", ZBX_CONST_STRLEN("varsamp"))) return eval_execute_statistical_function(ctx, token, zbx_eval_calc_varsamp, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "count", ZBX_CONST_STRLEN("count"))) return eval_execute_function_count(ctx, token, output, error); if (SUCCEED == eval_compare_token(ctx, &token->loc, "histogram_quantile", ZBX_CONST_STRLEN("histogram_quantile"))) return eval_execute_function_histogram_quantile(ctx, token, output, error); if (NULL != ctx->common_func_cb) return eval_execute_cb_function(ctx, token, ctx->common_func_cb, output, error); *error = zbx_dsprintf(*error, "Unknown function at \"%s\".", ctx->expression + token->loc.l); return FAIL; } /****************************************************************************** * * * Purpose: evaluate history function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * * output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the function was executed successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_history_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { if ((zbx_uint32_t)output->values_num < token->opt) { *error = zbx_dsprintf(*error, "not enough arguments for function at \"%s\"", ctx->expression + token->loc.l); return FAIL; } if (NULL != ctx->history_func_cb) return eval_execute_cb_function(ctx, token, ctx->history_func_cb, output, error); *error = zbx_dsprintf(*error, "Unknown function at \"%s\".", ctx->expression + token->loc.l); return FAIL; } /****************************************************************************** * * * Purpose: throw exception by returning the specified error * * * * Parameters: output - [IN/OUT] the output value stack * * error - [OUT] the error message in the case of failure * * * ******************************************************************************/ static void eval_throw_exception(zbx_vector_var_t *output, char **error) { zbx_variant_t *arg; if (0 == output->values_num) { *error = zbx_strdup(*error, "exception must have one argument"); return; } arg = &output->values[output->values_num - 1]; zbx_variant_convert(arg, ZBX_VARIANT_STR); *error = arg->data.str; zbx_variant_set_none(arg); } /****************************************************************************** * * * Purpose: evaluate pre-parsed expression * * * * Parameters: ctx - [IN] the evaluation context * * value - [OUT] the resulting value * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the expression was evaluated successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute(const zbx_eval_context_t *ctx, zbx_variant_t *value, char **error) { zbx_vector_var_t output; int i, ret = FAIL; char *errmsg = NULL; zbx_vector_var_create(&output); for (i = 0; i < ctx->stack.values_num; i++) { zbx_eval_token_t *token = &ctx->stack.values[i]; if (0 != (token->type & ZBX_EVAL_CLASS_OPERATOR1)) { if (SUCCEED != eval_execute_op_unary(ctx, token, &output, &errmsg)) goto out; } else if (0 != (token->type & ZBX_EVAL_CLASS_OPERATOR2)) { if (SUCCEED != eval_execute_op_binary(ctx, token, &output, &errmsg)) goto out; } else { switch (token->type) { case ZBX_EVAL_TOKEN_NOP: break; case ZBX_EVAL_TOKEN_VAR_NUM: case ZBX_EVAL_TOKEN_VAR_STR: case ZBX_EVAL_TOKEN_VAR_MACRO: case ZBX_EVAL_TOKEN_VAR_USERMACRO: if (SUCCEED != eval_execute_push_value(ctx, token, &output, &errmsg)) goto out; break; case ZBX_EVAL_TOKEN_ARG_QUERY: case ZBX_EVAL_TOKEN_ARG_PERIOD: if (SUCCEED != eval_execute_push_value(ctx, token, &output, &errmsg)) goto out; break; case ZBX_EVAL_TOKEN_ARG_NULL: eval_execute_push_null(&output); break; case ZBX_EVAL_TOKEN_FUNCTION: if (SUCCEED != eval_execute_common_function(ctx, token, &output, &errmsg)) goto out; break; case ZBX_EVAL_TOKEN_HIST_FUNCTION: if (SUCCEED != eval_execute_history_function(ctx, token, &output, &errmsg)) goto out; break; case ZBX_EVAL_TOKEN_FUNCTIONID: if (ZBX_VARIANT_NONE == token->value.type) { errmsg = zbx_strdup(errmsg, "trigger history functions must be" " pre-calculated"); goto out; } if (SUCCEED != eval_execute_push_value(ctx, token, &output, &errmsg)) goto out; break; case ZBX_EVAL_TOKEN_EXCEPTION: eval_throw_exception(&output, &errmsg); goto out; default: errmsg = zbx_dsprintf(errmsg, "unknown token at \"%s\"", ctx->expression + token->loc.l); goto out; } } } if (1 != output.values_num) { errmsg = zbx_strdup(errmsg, "output stack after expression execution must contain one value"); goto out; } if (ZBX_VARIANT_ERR == output.values[0].type) { errmsg = zbx_strdup(errmsg, output.values[0].data.err); goto out; } *value = output.values[0]; output.values_num = 0; ret = SUCCEED; out: if (SUCCEED != ret) { if (0 != islower(*errmsg)) { *error = zbx_dsprintf(NULL, "Cannot evaluate expression: %s", errmsg); zbx_free(errmsg); } else *error = errmsg; } for (i = 0; i < output.values_num; i++) zbx_variant_clear(&output.values[i]); zbx_vector_var_destroy(&output); return ret; } /****************************************************************************** * * * Purpose: initialize execution context * * * * Parameters: ctx - [IN] the evaluation context * * ts - [IN] the timestamp of the execution time * * common_func_cb - [IN] the common function callback (optional) * * history_func_cb - [IN] the history function callback (optional)* * data_cb - [IN] the caller data to be passed to callback* * functions * * * ******************************************************************************/ static void eval_init_execute_context(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_eval_function_cb_t common_func_cb, zbx_eval_function_cb_t history_func_cb, void *data_cb) { ctx->common_func_cb = common_func_cb; ctx->history_func_cb = history_func_cb; ctx->data_cb = data_cb; if (NULL == ts) ctx->ts.sec = ctx->ts.ns = 0; else ctx->ts = *ts; } /****************************************************************************** * * * Purpose: evaluate parsed expression * * * * Parameters: ctx - [IN] the evaluation context * * ts - [IN] the timestamp of the execution time * * value - [OUT] the resulting value * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the expression was evaluated successfully * * FAIL - otherwise * * * ******************************************************************************/ int zbx_eval_execute(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) { eval_init_execute_context(ctx, ts, NULL, NULL, NULL); return eval_execute(ctx, value, error); } /****************************************************************************** * * * Purpose: evaluate parsed expression with callback for custom function * * processing * * * * Parameters: ctx - [IN] the evaluation context * * ts - [IN] the timestamp of the execution time * * common_func_cb - [IN] the common function callback (optional) * * history_func_cb - [IN] the history function callback (optional)* * value - [OUT] the resulting value * * error - [OUT] the error message * * * * Return value: SUCCEED - the expression was evaluated successfully * * FAIL - otherwise * * * * Comments: The callback will be called for unsupported math and all history * * functions. * * * ******************************************************************************/ int zbx_eval_execute_ext(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_eval_function_cb_t common_func_cb, zbx_eval_function_cb_t history_func_cb, void *data, zbx_variant_t *value, char **error) { eval_init_execute_context(ctx, ts, common_func_cb, history_func_cb, data); return eval_execute(ctx, value, error); }