diff options
author | Alexander Shubin <aleksandrs.subins@zabbix.com> | 2021-05-10 16:36:00 +0300 |
---|---|---|
committer | Alexander Shubin <aleksandrs.subins@zabbix.com> | 2021-05-10 16:36:00 +0300 |
commit | e222c02f21443c63e44745fe481342c7df5cf010 (patch) | |
tree | 600938ae7f9e38ffda1b88822c05caf1f42b386a /src | |
parent | b559fd0b02b03665b538232b7a9c7cfa6408b369 (diff) | |
parent | 17c7dcad60169b733b6852fd34ff06e4b4947f61 (diff) |
A.F.I...S. [ZBXNEXT-6411] updated to latest master; resolved conflicts in :
# create/src/schema.tmpl
# create/src/templates.tmpl - ours for now
# include/zbxserver.h
# src/libs/zbxdbcache/dbconfig.c
# src/libs/zbxdbupgrade/dbupgrade_5030.c
# src/libs/zbxserver/expression.c
# ui/include/classes/import/CImportDataAdapter.php
# ui/include/classes/import/CConfigurationImport.php
# ui/include/classes/import/CConfigurationImportcompare.php
# ui/include/classes/import/importers/CTemplateImporter.php
# ui/include/classes/import/converters/C52ImportConverter.php
# ui/include/defines.inc.php
# ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
# ui/tests/unit/include/classes/import/converters/C52ImportConverterTest.php
Diffstat (limited to 'src')
64 files changed, 14965 insertions, 2760 deletions
diff --git a/src/go/cmd/zabbix_agent2/service_windows.go b/src/go/cmd/zabbix_agent2/service_windows.go index 441de2b96c7..e7f21ce59a3 100644 --- a/src/go/cmd/zabbix_agent2/service_windows.go +++ b/src/go/cmd/zabbix_agent2/service_windows.go @@ -463,7 +463,7 @@ loop: closeChan <- true break loop default: - log.Warningf("unsupported windows service command received") + log.Debugf("unsupported windows service command '%s' received", getCmdName(c.Cmd)) } case <-stopChan: changes <- svc.Status{State: svc.StopPending} @@ -478,3 +478,38 @@ loop: return } + +func getCmdName(cmd svc.Cmd) string { + switch cmd { + case svc.Stop: + return "Stop" + case svc.Pause: + return "Pause" + case svc.Continue: + return "Continue" + case svc.Interrogate: + return "Interrogate" + case svc.Shutdown: + return "Shutdown" + case svc.ParamChange: + return "ParamChange" + case svc.NetBindAdd: + return "NetBindAdd" + case svc.NetBindRemove: + return "NetBindRemove" + case svc.NetBindEnable: + return "NetBindEnable" + case svc.NetBindDisable: + return "NetBindDisable" + case svc.DeviceEvent: + return "DeviceEvent" + case svc.HardwareProfileChange: + return "HardwareProfileChange" + case svc.PowerEvent: + return "PowerEvent" + case svc.SessionChange: + return "SessionChange" + default: + return "unknown" + } +} diff --git a/src/go/plugins/windows/services/services_windows.go b/src/go/plugins/windows/services/services_windows.go index 0e14aa9fb20..e179ff08180 100644 --- a/src/go/plugins/windows/services/services_windows.go +++ b/src/go/plugins/windows/services/services_windows.go @@ -420,7 +420,7 @@ func (p *Plugin) exportServices(params []string) (result interface{}, err error) } stateFilter := stateFlagAll - if len(params) > 1 && params[1] != "" { + if len(params) > 1 && params[1] != "all" && params[1] != "" { switch params[1] { case "stopped": stateFilter = stateFlagStopped diff --git a/src/libs/Makefile.am b/src/libs/Makefile.am index da4bf1cf79a..58b13e190b3 100644 --- a/src/libs/Makefile.am +++ b/src/libs/Makefile.am @@ -34,7 +34,8 @@ DIST_SUBDIRS = \ zbxvault \ zbxdiag \ zbxtrends \ - zbxavailability + zbxavailability \ + zbxeval if SERVER SERVER_SUBDIRS = \ @@ -57,7 +58,8 @@ SERVER_SUBDIRS = \ zbxvault \ zbxdiag \ zbxtrends \ - zbxavailability + zbxavailability \ + zbxeval else if PROXY PROXY_SUBDIRS = \ @@ -79,7 +81,8 @@ PROXY_SUBDIRS = \ zbxvault \ zbxdiag \ zbxtrends \ - zbxavailability + zbxavailability \ + zbxeval endif endif diff --git a/src/libs/zbxalgo/Makefile.am b/src/libs/zbxalgo/Makefile.am index ebadf2c82db..a57a7d9a16b 100644 --- a/src/libs/zbxalgo/Makefile.am +++ b/src/libs/zbxalgo/Makefile.am @@ -21,4 +21,5 @@ libzbxalgo_a_SOURCES = \ prediction.c \ queue.c \ vector.c \ - vectorimpl.h + vectorimpl.h \ + serialize.c diff --git a/src/libs/zbxalgo/evaluate.c b/src/libs/zbxalgo/evaluate.c index 08f8da71051..227b7c5383a 100644 --- a/src/libs/zbxalgo/evaluate.c +++ b/src/libs/zbxalgo/evaluate.c @@ -19,7 +19,7 @@ #include "common.h" #include "zbxalgo.h" - +#include "zbxvariant.h" #include "log.h" /****************************************************************************** diff --git a/src/libs/zbxalgo/serialize.c b/src/libs/zbxalgo/serialize.c new file mode 100644 index 00000000000..9cb0dc09a0c --- /dev/null +++ b/src/libs/zbxalgo/serialize.c @@ -0,0 +1,100 @@ +/* +** Zabbix +** Copyright (C) 2001-2021 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 "common.h" +#include "zbxserialize.h" +/****************************************************************************** + * * + * Function: zbx_serialize_uint31_compact * + * * + * Purpose: serialize 31 bit unsigned integer into utf-8 like byte stream * + * * + * Parameters: ptr - [OUT] the output buffer * + * value - [IN] the value to serialize * + * * + * Return value: The number of bytes written to the buffer. * + * * + * Comments: This serialization method should be used with variables usually * + * having small value while still supporting larger values. * + * * + ******************************************************************************/ +zbx_uint32_t zbx_serialize_uint31_compact(unsigned char *ptr, zbx_uint32_t value) +{ + if (0x7f >= value) + { + ptr[0] = (unsigned char)value; + return 1; + } + else + { + unsigned char buf[6]; + zbx_uint32_t len, pos = (zbx_uint32_t)(sizeof(buf) - 1); + + while (value > (zbx_uint32_t)(0x7f >> (sizeof(buf) - pos))) + { + buf[pos] = (unsigned char)(0x80 | (value & 0x3f)); + value >>= 6; + pos--; + } + + buf[pos] = (unsigned char)(value | (0xfe << (pos + 1))); + + len = (zbx_uint32_t)(sizeof(buf) - pos); + memcpy(ptr, buf + pos, len); + return len; + } +} + +/****************************************************************************** + * * + * Function: zbx_deserialize_uint31_compact * + * * + * Purpose: deserialize 31 bit unsigned integer from utf-8 like byte stream * + * * + * Parameters: ptr - [IN] the byte stream * + * value - [OUT] the deserialized value * + * * + * Return value: The number of bytes read from byte stream. * + * * + ******************************************************************************/ +zbx_uint32_t zbx_deserialize_uint31_compact(const unsigned char *ptr, zbx_uint32_t *value) +{ + if (0 == (*ptr & 0x80)) + { + *value = *ptr; + return 1; + } + else + { + zbx_uint32_t pos = 2, i; + + while (0 != (*ptr & (0x80 >> pos))) + pos++; + + *value = *ptr & (0xff >> (pos + 1)); + + for (i = 1; i < pos; i++) + { + *value <<= 6; + *value |= (*(++ptr)) & 0x3f; + } + + return pos; + } +} diff --git a/src/libs/zbxalgo/vector.c b/src/libs/zbxalgo/vector.c index 2afbddba3f8..037f4fe03dc 100644 --- a/src/libs/zbxalgo/vector.c +++ b/src/libs/zbxalgo/vector.c @@ -28,6 +28,7 @@ ZBX_PTR_VECTOR_IMPL(str, char *) ZBX_PTR_VECTOR_IMPL(ptr, void *) ZBX_VECTOR_IMPL(ptr_pair, zbx_ptr_pair_t) ZBX_VECTOR_IMPL(uint64_pair, zbx_uint64_pair_t) +ZBX_VECTOR_IMPL(dbl, double) void zbx_ptr_free(void *data) { diff --git a/src/libs/zbxcommon/misc.c b/src/libs/zbxcommon/misc.c index c1838a19c75..16ca58c87ba 100644 --- a/src/libs/zbxcommon/misc.c +++ b/src/libs/zbxcommon/misc.c @@ -3569,7 +3569,7 @@ zbx_function_type_t zbx_get_function_type(const char *func) if (0 == strncmp(func, "trend", 5)) return ZBX_FUNCTION_TYPE_TRENDS; - if (SUCCEED == str_in_list("nodata,date,dayofmonth,dayofweek,time,now", func, ',')) + if (0 == strcmp(func, "nodata")) return ZBX_FUNCTION_TYPE_TIMER; return ZBX_FUNCTION_TYPE_HISTORY; diff --git a/src/libs/zbxcommon/str.c b/src/libs/zbxcommon/str.c index e82be3e1b4c..3cb5be162ce 100644 --- a/src/libs/zbxcommon/str.c +++ b/src/libs/zbxcommon/str.c @@ -358,6 +358,58 @@ void zbx_chrcpy_alloc(char **str, size_t *alloc_len, size_t *offset, char c) zbx_strncpy_alloc(str, alloc_len, offset, &c, 1); } +void zbx_strquote_alloc(char **str, size_t *str_alloc, size_t *str_offset, const char *value_str) +{ + size_t size; + const char *src; + char *dst; + + for (size = 2, src = value_str; '\0' != *src; src++) + { + switch (*src) + { + case '\\': + case '"': + size++; + } + size++; + } + + if (*str_alloc <= *str_offset + size) + { + if (0 == *str_alloc) + *str_alloc = size; + + do + { + *str_alloc *= 2; + } + while (*str_alloc - *str_offset <= size); + + *str = zbx_realloc(*str, *str_alloc); + } + + dst = *str + *str_offset; + *dst++ = '"'; + + for (src = value_str; '\0' != *src; src++, dst++) + { + switch (*src) + { + case '\\': + case '"': + *dst++ = '\\'; + break; + } + + *dst = *src; + } + + *dst++ = '"'; + *dst = '\0'; + *str_offset += size; +} + /* Has to be rewritten to avoid malloc */ char *string_replace(const char *str, const char *sub_str1, const char *sub_str2) { @@ -3522,7 +3574,7 @@ int zbx_strcmp_natural(const char *s1, const char *s2) /****************************************************************************** * * - * Function: zbx_token_parse_user_macro * + * Function: token_parse_user_macro * * * * Purpose: parses user macro token * * * @@ -3538,7 +3590,7 @@ int zbx_strcmp_natural(const char *s1, const char *s2) * structure is filled with user macro specific data. * * * ******************************************************************************/ -static int zbx_token_parse_user_macro(const char *expression, const char *macro, zbx_token_t *token) +static int token_parse_user_macro(const char *expression, const char *macro, zbx_token_t *token) { size_t offset; int macro_r, context_l, context_r; @@ -3583,7 +3635,7 @@ static int zbx_token_parse_user_macro(const char *expression, const char *macro, /****************************************************************************** * * - * Function: zbx_token_parse_lld_macro * + * Function: token_parse_lld_macro * * * * Purpose: parses lld macro token * * * @@ -3599,7 +3651,7 @@ static int zbx_token_parse_user_macro(const char *expression, const char *macro, * structure is filled with lld macro specific data. * * * ******************************************************************************/ -static int zbx_token_parse_lld_macro(const char *expression, const char *macro, zbx_token_t *token) +static int token_parse_lld_macro(const char *expression, const char *macro, zbx_token_t *token) { const char *ptr; size_t offset; @@ -3636,7 +3688,7 @@ static int zbx_token_parse_lld_macro(const char *expression, const char *macro, /****************************************************************************** * * - * Function: zbx_token_parse_expression_macro * + * Function: token_parse_expression_macro * * * * Purpose: parses expression macro token * * * @@ -3655,7 +3707,7 @@ static int zbx_token_parse_lld_macro(const char *expression, const char *macro, * contain user macro contexts and item keys with string arguments. * * * ******************************************************************************/ -static int zbx_token_parse_expression_macro(const char *expression, const char *macro, zbx_token_t *token) +static int token_parse_expression_macro(const char *expression, const char *macro, zbx_token_t *token) { const char *ptr; size_t offset; @@ -3691,6 +3743,7 @@ static int zbx_token_parse_expression_macro(const char *expression, const char * { switch (tmp.type) { + case ZBX_TOKEN_MACRO: case ZBX_TOKEN_LLD_MACRO: case ZBX_TOKEN_LLD_FUNC_MACRO: case ZBX_TOKEN_USER_MACRO: @@ -3729,7 +3782,7 @@ static int zbx_token_parse_expression_macro(const char *expression, const char * /****************************************************************************** * * - * Function: zbx_token_parse_objectid * + * Function: token_parse_objectid * * * * Purpose: parses object id token * * * @@ -3745,7 +3798,7 @@ static int zbx_token_parse_expression_macro(const char *expression, const char * * structure is filled with object id specific data. * * * ******************************************************************************/ -static int zbx_token_parse_objectid(const char *expression, const char *macro, zbx_token_t *token) +static int token_parse_objectid(const char *expression, const char *macro, zbx_token_t *token) { const char *ptr; size_t offset; @@ -3783,7 +3836,7 @@ static int zbx_token_parse_objectid(const char *expression, const char *macro, z /****************************************************************************** * * - * Function: zbx_token_parse_macro_segment * + * Function: token_parse_macro_segment * * * * Purpose: parses macro name segment * * * @@ -3801,7 +3854,7 @@ static int zbx_token_parse_objectid(const char *expression, const char *macro, z * FAIL - otherwise * * * ******************************************************************************/ -static int zbx_token_parse_macro_segment(const char *expression, const char *segment, int *strict, int *next) +static int token_parse_macro_segment(const char *expression, const char *segment, int *strict, int *next) { const char *ptr = segment; @@ -3858,7 +3911,7 @@ static int zbx_token_parse_macro_segment(const char *expression, const char *seg /****************************************************************************** * * - * Function: zbx_token_parse_macro_name * + * Function: token_parse_macro_name * * * * Purpose: parses macro name * * * @@ -3874,13 +3927,13 @@ static int zbx_token_parse_macro_segment(const char *expression, const char *seg * be '}' or it's not a valid macro. * * * ******************************************************************************/ -static int zbx_token_parse_macro_name(const char *expression, const char *ptr, zbx_strloc_t *loc) +static int token_parse_macro_name(const char *expression, const char *ptr, zbx_strloc_t *loc) { int strict, offset, ret; loc->l = ptr - expression; - while (SUCCEED == (ret = zbx_token_parse_macro_segment(expression, ptr, &strict, &offset))) + while (SUCCEED == (ret = token_parse_macro_segment(expression, ptr, &strict, &offset))) { if (0 == strict && expression + loc->l == ptr) return FAIL; @@ -3899,7 +3952,7 @@ static int zbx_token_parse_macro_name(const char *expression, const char *ptr, z /****************************************************************************** * * - * Function: zbx_token_parse_macro * + * Function: token_parse_macro * * * * Purpose: parses normal macro token * * * @@ -3915,12 +3968,12 @@ static int zbx_token_parse_macro_name(const char *expression, const char *ptr, z * structure is filled with simple macro specific data. * * * ******************************************************************************/ -static int zbx_token_parse_macro(const char *expression, const char *macro, zbx_token_t *token) +static int token_parse_macro(const char *expression, const char *macro, zbx_token_t *token) { zbx_strloc_t loc; zbx_token_macro_t *data; - if (SUCCEED != zbx_token_parse_macro_name(expression, macro + 1, &loc)) + if (SUCCEED != token_parse_macro_name(expression, macro + 1, &loc)) return FAIL; if ('}' != expression[loc.r + 1]) @@ -3940,7 +3993,7 @@ static int zbx_token_parse_macro(const char *expression, const char *macro, zbx_ /****************************************************************************** * * - * Function: zbx_token_parse_function * + * Function: token_parse_function * * * * Purpose: parses function inside token * * * @@ -3953,7 +4006,7 @@ static int zbx_token_parse_macro(const char *expression, const char *macro, zbx_ * FAIL - func does not point at valid function * * * ******************************************************************************/ -static int zbx_token_parse_function(const char *expression, const char *func, +static int token_parse_function(const char *expression, const char *func, zbx_strloc_t *func_loc, zbx_strloc_t *func_param) { size_t par_l, par_r; @@ -3972,7 +4025,7 @@ static int zbx_token_parse_function(const char *expression, const char *func, /****************************************************************************** * * - * Function: zbx_token_parse_func_macro * + * Function: token_parse_func_macro * * * * Purpose: parses function macro token * * * @@ -3994,7 +4047,7 @@ static int zbx_token_parse_function(const char *expression, const char *func, * specific data. * * * ******************************************************************************/ -static int zbx_token_parse_func_macro(const char *expression, const char *macro, const char *func, +static int token_parse_func_macro(const char *expression, const char *macro, const char *func, zbx_token_t *token, int token_type) { zbx_strloc_t func_loc, func_param; @@ -4005,7 +4058,7 @@ static int zbx_token_parse_func_macro(const char *expression, const char *macro, if ('\0' == *func) return FAIL; - if (SUCCEED != zbx_token_parse_function(expression, func, &func_loc, &func_param)) + if (SUCCEED != token_parse_function(expression, func, &func_loc, &func_param)) return FAIL; ptr = expression + func_loc.r + 1; @@ -4038,7 +4091,7 @@ static int zbx_token_parse_func_macro(const char *expression, const char *macro, /****************************************************************************** * * - * Function: zbx_token_parse_simple_macro_key * + * Function: token_parse_simple_macro_key * * * * Purpose: parses simple macro token with given key * * * @@ -4060,7 +4113,7 @@ static int zbx_token_parse_func_macro(const char *expression, const char *macro, * specific data. * * * ******************************************************************************/ -static int zbx_token_parse_simple_macro_key(const char *expression, const char *macro, const char *key, +static int token_parse_simple_macro_key(const char *expression, const char *macro, const char *key, zbx_token_t *token) { size_t offset; @@ -4072,7 +4125,7 @@ static int zbx_token_parse_simple_macro_key(const char *expression, const char * { zbx_token_t key_token; - if (SUCCEED != zbx_token_parse_macro(expression, key, &key_token)) + if (SUCCEED != token_parse_macro(expression, key, &key_token)) return FAIL; ptr = expression + key_token.loc.r + 1; @@ -4090,7 +4143,7 @@ static int zbx_token_parse_simple_macro_key(const char *expression, const char * if (0 == ptr - key) return FAIL; - if (SUCCEED != zbx_token_parse_function(expression, ptr + 1, &func_loc, &func_param)) + if (SUCCEED != token_parse_function(expression, ptr + 1, &func_loc, &func_param)) return FAIL; key_loc.l = key - expression; @@ -4127,7 +4180,7 @@ static int zbx_token_parse_simple_macro_key(const char *expression, const char * /****************************************************************************** * * - * Function: zbx_token_parse_simple_macro * + * Function: token_parse_simple_macro * * * * Purpose: parses simple macro token * * * @@ -4148,7 +4201,7 @@ static int zbx_token_parse_simple_macro_key(const char *expression, const char * * specific data. * * * ******************************************************************************/ -static int zbx_token_parse_simple_macro(const char *expression, const char *macro, zbx_token_t *token) +static int token_parse_simple_macro(const char *expression, const char *macro, zbx_token_t *token) { const char *ptr; @@ -4167,12 +4220,12 @@ static int zbx_token_parse_simple_macro(const char *expression, const char *macr if (1 == ptr - macro) return FAIL; - return zbx_token_parse_simple_macro_key(expression, macro, ptr + 1, token); + return token_parse_simple_macro_key(expression, macro, ptr + 1, token); } /****************************************************************************** * * - * Function: zbx_token_parse_nested_macro * + * Function: token_parse_nested_macro * * * * Purpose: parses token with nested macros * * * @@ -4196,7 +4249,7 @@ static int zbx_token_parse_simple_macro(const char *expression, const char *macr * filled with macro specific data. * * * ******************************************************************************/ -static int zbx_token_parse_nested_macro(const char *expression, const char *macro, zbx_token_t *token) +static int token_parse_nested_macro(const char *expression, const char *macro, zbx_token_t *token) { const char *ptr; @@ -4233,7 +4286,7 @@ static int zbx_token_parse_nested_macro(const char *expression, const char *macr { zbx_strloc_t loc; - if (SUCCEED != zbx_token_parse_macro_name(expression, macro + 2, &loc)) + if (SUCCEED != token_parse_macro_name(expression, macro + 2, &loc)) return FAIL; if ('}' != expression[loc.r + 1]) @@ -4249,11 +4302,11 @@ static int zbx_token_parse_nested_macro(const char *expression, const char *macr /* simple macros {{MACRO}:key.function()} */ if ('.' == ptr[1]) { - return zbx_token_parse_func_macro(expression, macro, ptr + 2, token, '#' == macro[2] ? + return token_parse_func_macro(expression, macro, ptr + 2, token, '#' == macro[2] ? ZBX_TOKEN_LLD_FUNC_MACRO : ZBX_TOKEN_FUNC_MACRO); } else if ('#' != macro[2] && ':' == ptr[1]) - return zbx_token_parse_simple_macro_key(expression, macro, ptr + 2, token); + return token_parse_simple_macro_key(expression, macro, ptr + 2, token); return FAIL; } @@ -4294,7 +4347,31 @@ int zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_toke while (SUCCEED != ret) { - ptr = strchr(ptr, '{'); + int quoted = 0; + + /* skip macros in string constants when looking for functionid */ + for (; '{' != *ptr || 0 != quoted; ptr++) + { + if ('\0' == *ptr) + break; + + if (0 != (token_search & ZBX_TOKEN_SEARCH_FUNCTIONID)) + { + switch (*ptr) + { + case '\\': + if (0 != quoted) + { + if ('\0' == *(++ptr)) + return FAIL; + } + break; + case '"': + quoted = !quoted; + break; + } + } + } if (0 != (token_search & ZBX_TOKEN_SEARCH_REFERENCES)) { @@ -4317,7 +4394,7 @@ int zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_toke token_search &= ~ZBX_TOKEN_SEARCH_REFERENCES; } - if (NULL == ptr) + if (NULL == ptr || '\0' == *ptr) return FAIL; if ('\0' == ptr[1]) @@ -4326,17 +4403,17 @@ int zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_toke switch (ptr[1]) { case '$': - ret = zbx_token_parse_user_macro(expression, ptr, token); + ret = token_parse_user_macro(expression, ptr, token); break; case '#': - ret = zbx_token_parse_lld_macro(expression, ptr, token); + ret = token_parse_lld_macro(expression, ptr, token); break; case '?': if (0 != (token_search & ZBX_TOKEN_SEARCH_EXPRESSION_MACRO)) - ret = zbx_token_parse_expression_macro(expression, ptr, token); + ret = token_parse_expression_macro(expression, ptr, token); break; case '{': - ret = zbx_token_parse_nested_macro(expression, ptr, token); + ret = token_parse_nested_macro(expression, ptr, token); break; case '0': case '1': @@ -4348,12 +4425,12 @@ int zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_toke case '7': case '8': case '9': - if (SUCCEED == (ret = zbx_token_parse_objectid(expression, ptr, token))) + if (SUCCEED == (ret = token_parse_objectid(expression, ptr, token))) break; ZBX_FALLTHROUGH; default: - if (SUCCEED != (ret = zbx_token_parse_macro(expression, ptr, token))) - ret = zbx_token_parse_simple_macro(expression, ptr, token); + if (SUCCEED != (ret = token_parse_macro(expression, ptr, token))) + ret = token_parse_simple_macro(expression, ptr, token); } ptr++; @@ -4364,6 +4441,66 @@ int zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_toke /****************************************************************************** * * + * Function: zbx_token_parse_user_macro * + * * + * Purpose: public wrapper for token_parse_user_macro() function * + * * + ******************************************************************************/ +int zbx_token_parse_user_macro(const char *expression, const char *macro, zbx_token_t *token) +{ + return token_parse_user_macro(expression, macro, token); +} + +/****************************************************************************** + * * + * Function: zbx_token_parse_macro * + * * + * Purpose: public wrapper for token_parse_macro() function * + * * + ******************************************************************************/ +int zbx_token_parse_macro(const char *expression, const char *macro, zbx_token_t *token) +{ + return token_parse_macro(expression, macro, token); +} + +/****************************************************************************** + * * + * Function: zbx_token_parse_objectid * + * * + * Purpose: public wrapper for token_parse_objectid() function * + * * + ******************************************************************************/ +int zbx_token_parse_objectid(const char *expression, const char *macro, zbx_token_t *token) +{ + return token_parse_objectid(expression, macro, token); +} + +/****************************************************************************** + * * + * Function: zbx_token_parse_lld_macro * + * * + * Purpose: public wrapper for token_parse_lld_macro() function * + * * + ******************************************************************************/ +int zbx_token_parse_lld_macro(const char *expression, const char *macro, zbx_token_t *token) +{ + return token_parse_lld_macro(expression, macro, token); +} + +/****************************************************************************** + * * + * Function: zbx_token_parse_nested_macro * + * * + * Purpose: public wrapper for token_parse_nested_macro() function * + * * + ******************************************************************************/ +int zbx_token_parse_nested_macro(const char *expression, const char *macro, zbx_token_t *token) +{ + return token_parse_nested_macro(expression, macro, token); +} + +/****************************************************************************** + * * * Function: zbx_no_function * * * * Purpose: count calculated item (prototype) formula characters that can be * @@ -4403,7 +4540,7 @@ static size_t zbx_no_function(const char *expr) ptr += len + 1; /* skip to the position after user macro */ } else if ('{' == *ptr && '{' == *(ptr + 1) && '#' == *(ptr + 2) && - SUCCEED == zbx_token_parse_nested_macro(ptr, ptr, &token)) + SUCCEED == token_parse_nested_macro(ptr, ptr, &token)) { ptr += token.loc.r - token.loc.l + 1; } @@ -4611,144 +4748,6 @@ int zbx_suffixed_number_parse(const char *number, int *len) /****************************************************************************** * * - * Function: zbx_expression_next_constant * - * * - * Purpose: gets next constant (numeric/string value or unresolved user * - * macro) from trigger expression. * - * * - * Parameters: src - [IN] the expression * - * pos - [IN] the starting position * - * loc - [IN] the substring location * - * * - * Return value: SUCCEED - the next constant was located. * - * FAIL - otherwise. * - * * - ******************************************************************************/ -int zbx_expression_next_constant(const char *str, size_t pos, zbx_strloc_t *loc) -{ - const char *s; - zbx_token_t token; - int offset = 0, len; - - for (s = str + pos; '\0' != *s; s++) - { - switch (*s) - { - case '"': - loc->l = s - str; - - for (++s;'\0' != *s; s++) - { - if ('"' == *s) - { - loc->r = s - str; - return SUCCEED; - } - if ('\\' == *s) - { - if ('\\' != s[1] && '"' != s[1]) - return FAIL; - s++; - } - } - return FAIL; - case '{': - if (SUCCEED == zbx_token_find(str, s - str, &token, ZBX_TOKEN_SEARCH_BASIC)) - { - if (ZBX_TOKEN_USER_MACRO == token.type) - { - *loc = token.loc; - return SUCCEED; - } - /* Skip all other tokens. Currently it can be only {TRIGGER.VALUE} macro. */ - s = str + token.loc.r; - } - continue; - case '-': - offset = 1; - continue; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '.': - if (SUCCEED != zbx_suffixed_number_parse(s, &len)) - return FAIL; - - loc->l = s - str - offset; - loc->r = s - str + len - 1; - return SUCCEED; - default: - offset = 0; - } - } - return FAIL; -} - -/****************************************************************************** - * * - * Function: zbx_expression_extract_constant * - * * - * Purpose: extracts constant from trigger expression unquoting/escaping if * - * necessary * - * * - * Parameters: src - [IN] the source string * - * loc - [IN] the substring location * - * * - * Return value: The constant. * - * * - ******************************************************************************/ -char *zbx_expression_extract_constant(const char *src, const zbx_strloc_t *loc) -{ - char *str, *pout; - const char *pin; - size_t len; - - len = loc->r - loc->l + 1; - str = zbx_malloc(NULL, len + 1); - - if ('"' == src[loc->l]) - { - for (pout = str, pin = src + loc->l + 1; pin <= src + loc->r - 1; pin++) - { - if ('\\' == *pin) - { - pin++; - switch (*pin) - { - case '\\': - *pout++ = '\\'; - break; - case '"': - *pout++ = '"'; - break; - default: - THIS_SHOULD_NEVER_HAPPEN; - *pout++ = '?'; - } - } - else - *pout++ = *pin; - } - *pout++ ='\0'; - } - else - { - memcpy(str, src + loc->l, len); - str[len] = '\0'; - } - - return str; -} - -/****************************************************************************** - * * * Function: num_param * * * * Purpose: find number of parameters in parameter list * @@ -5278,7 +5277,7 @@ int replace_key_params_dyn(char **data, int key_type, replace_key_param_f cb, vo i += len + 1; /* skip to the position after user macro */ } else if ('{' == (*data)[i] && '{' == (*data)[i + 1] && '#' == (*data)[i + 2] && - SUCCEED == zbx_token_parse_nested_macro(&(*data)[i], &(*data)[i], &token)) + SUCCEED == token_parse_nested_macro(&(*data)[i], &(*data)[i], &token)) { i += token.loc.r - token.loc.l + 1; } @@ -6026,3 +6025,84 @@ const char *zbx_print_double(char *buffer, size_t size, double val) return buffer; } + +/****************************************************************************** + * * + * Function: zbx_substr_unquote * + * * + * Purpose: unquotes valid substring at the specified location * + * * + * Parameters: src - [IN] the source string * + * left - [IN] the left substring position 9start) * + * right - [IN] the right substirng position (end) * + * * + * Return value: The unquoted and copied substring. * + * * + ******************************************************************************/ +char *zbx_substr_unquote(const char *src, size_t left, size_t right) +{ + char *str, *ptr; + + if ('"' == src[left]) + { + src += left + 1; + str = zbx_malloc(NULL, right - left); + ptr = str; + + while ('"' != *src) + { + if ('\\' == *src) + { + switch (*(++src)) + { + case '\\': + *ptr++ = '\\'; + break; + case '"': + *ptr++ = '"'; + break; + case '\0': + THIS_SHOULD_NEVER_HAPPEN; + *ptr = '\0'; + return str; + } + } + else + *ptr++ = *src; + src++; + } + *ptr = '\0'; + } + else + { + str = zbx_malloc(NULL, right - left + 2); + memcpy(str, src + left, right - left + 1); + str[right - left + 1] = '\0'; + } + + return str; +} + +/****************************************************************************** + * * + * Function: zbx_substr * + * * + * Purpose: extracts substring at the specified location * + * * + * Parameters: src - [IN] the source string * + * left - [IN] the left substring position 9start) * + * right - [IN] the right substirng position (end) * + * * + * Return value: The unquoted and copied substring. * + * * + ******************************************************************************/ +char *zbx_substr(const char *src, size_t left, size_t right) +{ + char *str; + + str = zbx_malloc(NULL, right - left + 2); + memcpy(str, src + left, right - left + 1); + str[right - left + 1] = '\0'; + + return str; +} diff --git a/src/libs/zbxcommon/time.c b/src/libs/zbxcommon/time.c index b5864e2837b..170d7c26f90 100644 --- a/src/libs/zbxcommon/time.c +++ b/src/libs/zbxcommon/time.c @@ -22,12 +22,17 @@ static void tm_add(struct tm *tm, int multiplier, zbx_time_unit_t base); static void tm_sub(struct tm *tm, int multiplier, zbx_time_unit_t base); -static int time_unit_seconds[ZBX_TIME_UNIT_COUNT] = {0, SEC_PER_HOUR, SEC_PER_DAY, SEC_PER_WEEK, 0, 0}; +static int time_unit_seconds[ZBX_TIME_UNIT_COUNT] = {0, 1, SEC_PER_MIN, SEC_PER_HOUR, SEC_PER_DAY, SEC_PER_WEEK, 0, + 0}; zbx_time_unit_t zbx_tm_str_to_unit(const char *text) { switch (*text) { + case 's': + return ZBX_TIME_UNIT_SECOND; + case 'm': + return ZBX_TIME_UNIT_MINUTE; case 'h': return ZBX_TIME_UNIT_HOUR; case 'd': @@ -315,9 +320,12 @@ void zbx_tm_round_up(struct tm *tm, zbx_time_unit_t base) if (0 != tm->tm_sec) { tm->tm_sec = 0; - tm->tm_min++; + zbx_tm_add(tm, 1, ZBX_TIME_UNIT_MINUTE); } + if (ZBX_TIME_UNIT_MINUTE == base) + return; + if (0 != tm->tm_min) { tm->tm_min = 0; @@ -400,6 +408,8 @@ void zbx_tm_round_down(struct tm *tm, zbx_time_unit_t base) ZBX_FALLTHROUGH; case ZBX_TIME_UNIT_HOUR: tm->tm_min = 0; + ZBX_FALLTHROUGH; + case ZBX_TIME_UNIT_MINUTE: tm->tm_sec = 0; break; default: diff --git a/src/libs/zbxcommon/variant.c b/src/libs/zbxcommon/variant.c index 2beedc54600..4cf1089c710 100644 --- a/src/libs/zbxcommon/variant.c +++ b/src/libs/zbxcommon/variant.c @@ -18,7 +18,12 @@ **/ #include "common.h" -#include "zbxalgo.h" +#include "log.h" +#include "zbxvariant.h" + +#include "../zbxalgo/vectorimpl.h" + +ZBX_VECTOR_IMPL(var, zbx_variant_t) void *zbx_variant_data_bin_copy(const void *bin) { @@ -63,6 +68,13 @@ void zbx_variant_clear(zbx_variant_t *value) case ZBX_VARIANT_BIN: zbx_free(value->data.bin); break; + case ZBX_VARIANT_ERR: + zbx_free(value->data.err); + break; + case ZBX_VARIANT_DBL_VECTOR: + zbx_vector_dbl_destroy(value->data.dbl_vector); + zbx_free(value->data.dbl_vector); + break; } value->type = ZBX_VARIANT_NONE; @@ -109,6 +121,18 @@ void zbx_variant_set_bin(zbx_variant_t *value, void *value_bin) value->type = ZBX_VARIANT_BIN; } +void zbx_variant_set_error(zbx_variant_t *value, char *error) +{ + value->data.err = error; + value->type = ZBX_VARIANT_ERR; +} + +void zbx_variant_set_dbl_vector(zbx_variant_t *value, zbx_vector_dbl_t *dbl_vector) +{ + value->data.dbl_vector = dbl_vector; + value->type = ZBX_VARIANT_DBL_VECTOR; +} + /****************************************************************************** * * * Function: zbx_variant_copy * @@ -124,6 +148,8 @@ void zbx_variant_set_bin(zbx_variant_t *value, void *value_bin) ******************************************************************************/ void zbx_variant_copy(zbx_variant_t *value, const zbx_variant_t *source) { + zbx_vector_dbl_t *dbl_vector; + switch (source->type) { case ZBX_VARIANT_STR: @@ -141,6 +167,16 @@ void zbx_variant_copy(zbx_variant_t *value, const zbx_variant_t *source) case ZBX_VARIANT_NONE: value->type = ZBX_VARIANT_NONE; break; + case ZBX_VARIANT_ERR: + zbx_variant_set_error(value, zbx_strdup(NULL, source->data.err)); + break; + case ZBX_VARIANT_DBL_VECTOR: + dbl_vector = (zbx_vector_dbl_t *)zbx_malloc(NULL, sizeof(zbx_vector_dbl_t)); + zbx_vector_dbl_create(dbl_vector); + zbx_vector_dbl_append_array(dbl_vector, source->data.dbl_vector->values, + source->data.dbl_vector->values_num); + zbx_variant_set_dbl_vector(value, dbl_vector); + break; } } @@ -188,6 +224,11 @@ static int variant_to_ui64(zbx_variant_t *value) if (0 > value->data.dbl) return FAIL; + /* uint64_t(double(UINT64_MAX)) conversion results in 0, to avoid */ + /* conversion issues require floating value to be less than UINT64_MAX */ + if (ZBX_MAX_UINT64 <= value->data.dbl) + return FAIL; + zbx_variant_set_ui64(value, value->data.dbl); return SUCCEED; case ZBX_VARIANT_STR: @@ -288,7 +329,7 @@ int zbx_variant_set_numeric(zbx_variant_t *value, const char *text) const char *zbx_variant_value_desc(const zbx_variant_t *value) { - static ZBX_THREAD_LOCAL char buffer[ZBX_MAX_DOUBLE_LEN + 1]; + static ZBX_THREAD_LOCAL char buffer[64]; zbx_uint32_t size, i, len; switch (value->type) @@ -318,6 +359,11 @@ const char *zbx_variant_value_desc(const zbx_variant_t *value) else buffer[0] = '\0'; return buffer; + case ZBX_VARIANT_ERR: + return value->data.err; + case ZBX_VARIANT_DBL_VECTOR: + zbx_snprintf(buffer, sizeof(buffer), "double vector[0:%d]", value->data.dbl_vector->values_num); + return buffer; default: THIS_SHOULD_NEVER_HAPPEN; return ZBX_UNKNOWN_STR; @@ -338,6 +384,10 @@ const char *zbx_get_variant_type_desc(unsigned char type) return "none"; case ZBX_VARIANT_BIN: return "binary"; + case ZBX_VARIANT_ERR: + return "error"; + case ZBX_VARIANT_DBL_VECTOR: + return "double vector"; default: THIS_SHOULD_NEVER_HAPPEN; return ZBX_UNKNOWN_STR; @@ -408,6 +458,54 @@ static int variant_compare_bin(const zbx_variant_t *value1, const zbx_variant_t /****************************************************************************** * * + * Function: variant_compare_error * + * * + * Purpose: compare two variant values when at least one contains error * + * * + ******************************************************************************/ +static int variant_compare_error(const zbx_variant_t *value1, const zbx_variant_t *value2) +{ + if (ZBX_VARIANT_ERR == value1->type) + { + if (ZBX_VARIANT_ERR != value2->type) + return 1; + + return strcmp(value1->data.err, value2->data.err); + } + + return -1; +} + +/****************************************************************************** + * * + * Function: variant_compare_dbl_vector * + * * + * Purpose: compare two variant values when at least one contains error * + * * + ******************************************************************************/ +static int variant_compare_dbl_vector(const zbx_variant_t *value1, const zbx_variant_t *value2) +{ + if (ZBX_VARIANT_DBL_VECTOR == value1->type) + { + int i; + + if (ZBX_VARIANT_DBL_VECTOR != value2->type) + return 1; + + ZBX_RETURN_IF_NOT_EQUAL(value1->data.dbl_vector->values_num, value2->data.dbl_vector->values_num); + + for (i = 0; i < value1->data.dbl_vector->values_num; i++) + { + ZBX_RETURN_IF_NOT_EQUAL(value1->data.dbl_vector->values[i], value2->data.dbl_vector->values[i]); + } + + return 0; + } + + return -1; +} +/****************************************************************************** + * * * Function: variant_compare_str * * * * Purpose: compare two variant values when at least one is string * @@ -504,12 +602,18 @@ static int variant_compare_ui64(const zbx_variant_t *value1, const zbx_variant_t * Comments: The following comparison logic is applied: * * 1) value of 'none' type is always less than other types, two * * 'none' types are equal * - * 2) value of binary type is always greater than other types, two * - * binary types are compared by length and then by contents * - * 3) if both values have uint64 types, they are compared as is * - * 4) if both values can be converted to floating point values the * + * 2) value of error type is always greater than other types, two * + * error types are compared by error messages as strings * + * 3) value of binary type is always greater than other types * + * except error, two binary types are compared by length and * + * then by contents * + * 4) value of double vector type is always greater than other * + * types except error and binary, two double vectors are compared* + * by their size and contents * + * 5) if both values have uint64 types, they are compared as is * + * 6) if both values can be converted to floating point values the * * conversion is done and the result is compared * - * 5) if any of value is of string type, the other is converted to * + * 7) if any of value is of string type, the other is converted to * * string and both are compared * * * ******************************************************************************/ @@ -518,9 +622,15 @@ int zbx_variant_compare(const zbx_variant_t *value1, const zbx_variant_t *value2 if (ZBX_VARIANT_NONE == value1->type || ZBX_VARIANT_NONE == value2->type) return variant_compare_empty(value1, value2); + if (ZBX_VARIANT_ERR == value1->type || ZBX_VARIANT_ERR == value2->type) + return variant_compare_error(value1, value2); + if (ZBX_VARIANT_BIN == value1->type || ZBX_VARIANT_BIN == value2->type) return variant_compare_bin(value1, value2); + if (ZBX_VARIANT_DBL_VECTOR == value1->type || ZBX_VARIANT_DBL_VECTOR == value2->type) + return variant_compare_dbl_vector(value1, value2); + if (ZBX_VARIANT_UI64 == value1->type && ZBX_VARIANT_UI64 == value2->type) return variant_compare_ui64(value1, value2); diff --git a/src/libs/zbxcommon/variant_misc.c b/src/libs/zbxcommon/variant_misc.c index a88ebfeb7d2..87272b3f27f 100644 --- a/src/libs/zbxcommon/variant_misc.c +++ b/src/libs/zbxcommon/variant_misc.c @@ -18,6 +18,7 @@ **/ #include "common.h" +#include "zbxvariant.h" /****************************************************************************** * * diff --git a/src/libs/zbxcommon/xml.c b/src/libs/zbxcommon/xml.c index 235bee69e59..52938a7585f 100644 --- a/src/libs/zbxcommon/xml.c +++ b/src/libs/zbxcommon/xml.c @@ -20,6 +20,7 @@ #include "zbxalgo.h" #include "log.h" #include "zbxjson.h" +#include "zbxvariant.h" #ifdef HAVE_LIBXML2 # include <libxml/xpath.h> diff --git a/src/libs/zbxdb/db.c b/src/libs/zbxdb/db.c index 8a385625755..672f5226b67 100644 --- a/src/libs/zbxdb/db.c +++ b/src/libs/zbxdb/db.c @@ -45,7 +45,7 @@ struct zbx_db_result MYSQL_RES *result; #elif defined(HAVE_ORACLE) OCIStmt *stmthp; /* the statement handle for select operations */ - int ncolumn; + int ncolumn; DB_ROW values; ub4 *values_alloc; OCILobLocator **clobs; @@ -76,6 +76,8 @@ static int db_auto_increment; #if defined(HAVE_MYSQL) static MYSQL *conn = NULL; +static zbx_uint32_t ZBX_MYSQL_SVERSION = ZBX_DBVERSION_UNDEFINED; +static int ZBX_MARIADB_SFORK = OFF; #elif defined(HAVE_ORACLE) #include "zbxalgo.h" @@ -90,6 +92,8 @@ typedef struct } zbx_oracle_db_handle_t; +static zbx_uint32_t ZBX_ORACLE_SVERSION = ZBX_DBVERSION_UNDEFINED; + static zbx_oracle_db_handle_t oracle; static ub4 OCI_DBserver_status(void); @@ -97,7 +101,8 @@ static ub4 OCI_DBserver_status(void); #elif defined(HAVE_POSTGRESQL) static PGconn *conn = NULL; static unsigned int ZBX_PG_BYTEAOID = 0; -static int ZBX_PG_SVERSION = 0, ZBX_TSDB_VERSION = -1; +static int ZBX_TSDB_VERSION = -1; +static zbx_uint32_t ZBX_PG_SVERSION = ZBX_DBVERSION_UNDEFINED; char ZBX_PG_ESCAPE_BACKSLASH = 1; #elif defined(HAVE_SQLITE3) static sqlite3 *conn = NULL; @@ -786,9 +791,6 @@ int zbx_db_connect(char *host, char *user, char *password, char *dbname, char *d ZBX_PG_BYTEAOID = atoi(row[0]); DBfree_result(result); - ZBX_PG_SVERSION = PQserverVersion(conn); - zabbix_log(LOG_LEVEL_DEBUG, "PostgreSQL Server version: %d", ZBX_PG_SVERSION); - /* disable "nonstandard use of \' in a string literal" warning */ if (0 < (ret = zbx_db_execute("set escape_string_warning to off"))) ret = ZBX_DB_OK; @@ -2438,31 +2440,304 @@ char *zbx_db_dyn_escape_like_pattern(const char *src) * Return value: the string length in bytes * * * ******************************************************************************/ -int zbx_db_strlen_n(const char *text, size_t maxlen) +int zbx_db_strlen_n(const char *text_loc, size_t maxlen) { - return zbx_strlen_utf8_nchars(text, maxlen); + return zbx_strlen_utf8_nchars(text_loc, maxlen); } -#if defined(HAVE_POSTGRESQL) /****************************************************************************** * * - * Function: zbx_dbms_get_version * + * Function: zbx_db_version_check * + * * + * Purpose: determine if a vendor database(MySQL, MariaDB, PostgreSQL, * + * Oracle, ElasticDB) version satisfies Zabbix requirements * + * * + * Parameters: database - [IN] database name * + * current_version - [IN] detected numeric version * + * min_version - [IN] minimum required numeric version * + * max_version - [IN] maximum required numeric version * + * * + * Return value: resulting status flag * + * * + ******************************************************************************/ +int zbx_db_version_check(const char *database, zbx_uint32_t current_version, zbx_uint32_t min_version, + zbx_uint32_t max_version) +{ + int flag; + + if (ZBX_DBVERSION_UNDEFINED == current_version) + { + flag = DB_VERSION_FAILED_TO_RETRIEVE; + zabbix_log(LOG_LEVEL_WARNING, "Failed to retrieve %s version", database); + } + else if (min_version > current_version && ZBX_DBVERSION_UNDEFINED != min_version) + { + flag = DB_VERSION_LOWER_THAN_MINIMUM; + zabbix_log(LOG_LEVEL_WARNING, "Unsupported DB! %s version is %lu which is smaller than minimum of %lu", + database, (unsigned long)current_version, (unsigned long)min_version); + } + else if (max_version < current_version && ZBX_DBVERSION_UNDEFINED != max_version) + { + flag = DB_VERSION_HIGHER_THAN_MAXIMUM; + zabbix_log(LOG_LEVEL_WARNING, "Unsupported DB! %s version is %lu which is higher than maximum of %lu", + database, (unsigned long)current_version, (unsigned long)max_version); + } + else + flag = DB_VERSION_SUPPORTED; + + return flag; +} + +/****************************************************************************** + * * + * Function: zbx_db_version_json_create * + * * + * Purpose: prepare json for front-end with the DB current, minimum and * + * maximum versions and a flag that indicates if the version * + * satisfies the requirements * * * - * Purpose: returns DBMS version as integer: MMmmuu * + * Parameters: json - [IN/OUT] json data * + * database - [IN] name of DB (MySQL/ElasticDB) * + * friendly_current_version - [IN] string current version * + * friendly_min_version - [IN] string min version * + * friendly_max_version - [IN] string max version * + * flag - [IN] status if DB satisfies the * + * requirements * + * * + ******************************************************************************/ +void zbx_db_version_json_create(struct zbx_json *json, const char *database, const char *friendly_current_version, + const char *friendly_min_version, const char *friendly_max_version, int flag) +{ + zbx_json_addobject(json, NULL); + zbx_json_addstring(json, "database", database, ZBX_JSON_TYPE_STRING); + + if (DB_VERSION_FAILED_TO_RETRIEVE != flag) + zbx_json_addstring(json, "current_version", friendly_current_version, ZBX_JSON_TYPE_STRING); + + zbx_json_addstring(json, "min_version", friendly_min_version, ZBX_JSON_TYPE_STRING); + zbx_json_addstring(json, "max_version", friendly_max_version, ZBX_JSON_TYPE_STRING); + zbx_json_addint64(json, "flag", flag); + zbx_json_close(json); +} + +/****************************************************************************** + * * + * Function: zbx_dbms_version_get * + * * + * Purpose: For PostgreSQL, MySQL and MariaDB: * + * returns DBMS version as integer: MMmmuu * * M = major version part * * m = minor version part * * u = patch version part * * * - * Example: 1.2.34 version will be returned as 10234 * + * Example: if the original DB version was 1.2.34 then 10234 gets returned * + * * + * Purpose: For OracleDB: * + * returns DBMS version as integer: MRruRRivUU * + * MR = major release version part * + * ru = release update version part * + * RR = release update version revision part * + * iv = increment version part * + * UU = unused, reserved for future use * + * * + * Example: if the OracleDB version was 18.1.0.0.7 then 1801000007 gets * + * returned * * * - * Return value: DBMS version or 0 if unknown * + * Return value: DBMS version or DBVERSION_UNDEFINED if unknown * * * ******************************************************************************/ -int zbx_dbms_get_version(void) +zbx_uint32_t zbx_dbms_version_get(void) { +#if defined(HAVE_MYSQL) + return ZBX_MYSQL_SVERSION; +#elif defined(HAVE_POSTGRESQL) return ZBX_PG_SVERSION; +#elif defined(HAVE_ORACLE) + return ZBX_ORACLE_SVERSION; +#else + return ZBX_DBVERSION_UNDEFINED; +#endif } +#ifdef HAVE_MYSQL +/****************************************************************************** + * * + * Function: zbx_dbms_mariadb_used * + * * + * Purpose: returns flag if the mariadb was detected * + * * + * Return value: ON - mariadb detected * + * OFF - otherwise (it is unforked mysql) * + ******************************************************************************/ +int zbx_dbms_mariadb_used(void) +{ + return ZBX_MARIADB_SFORK; +} +#endif + +/*************************************************************************************************************** + * * + * Function: zbx_dbms_version_extract * + * * + * Purpose: retrieves the DB version and makes sure it is stored in the numeric format, also fills the json * + * to report to front-end * + * * + * For PostgreSQL: * + * numeric version is available from the API * + * * + * For MySQL and MariaDB: * + * numeric version is available from the API, but also the additional processing is required * + * to determine if it is a MySQL or MariaDB and save this result as well * + * * + * For Oracle: * + * numeric version needs to be manually parsed from the string result * + * Oracle DB format is like 18.1.2.3.0 where * + * 18 - major release version * + * 1 - release update version * + * 2 - release update version revision * + * 3 - increment version * + * 0 - unused, reserved for future use * + * * + * Oracle Examples: * + * For "Oracle Database 18c Express Edition Release 1.0.0.0.0 - Production" => 100000000 * + * For "Oracle Database 18c Express Edition Release 18.2.0.0.7 - Production" => 1802000007 * + * For "Oracle Database 18c Express Edition Release 0.0.34.123.7 - Production" => DBVERSION_UNDEFINED * + * For "Oracle Database 18c Express Edition Release 1.0.3.x.7 - Production" => DBVERISON_UNDEFINED * + * For "<anything else>" => DBVERSION_UNDEFINED * + * * + **************************************************************************************************************/ +zbx_uint32_t zbx_dbms_version_extract(struct zbx_json *json) +{ +#define RIGHT2(x) ((int)((zbx_uint32_t)(x) - ((zbx_uint32_t)((x)/100))*100)) +#if defined(HAVE_MYSQL) + int flag; + const char *info; + char *version_friendly; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + ZBX_MYSQL_SVERSION = (zbx_uint32_t)mysql_get_server_version(conn); + + if (NULL != (info = mysql_get_server_info(conn)) && NULL != strstr(info, "MariaDB")) + { + zabbix_log(LOG_LEVEL_DEBUG, "MariaDB fork detected"); + ZBX_MARIADB_SFORK = ON; + } + + version_friendly = zbx_dsprintf(NULL, "%d.%.2d.%.2d", RIGHT2(ZBX_MYSQL_SVERSION/10000), + RIGHT2(ZBX_MYSQL_SVERSION/100), RIGHT2(ZBX_MYSQL_SVERSION)); + + if (ON == ZBX_MARIADB_SFORK) + { + flag = zbx_db_version_check("MariaDB", ZBX_MYSQL_SVERSION, ZBX_MARIA_MIN_VERSION, ZBX_DBVERSION_UNDEFINED); + zbx_db_version_json_create(json, "MariaDB", version_friendly, + ZBX_MARIA_MIN_VERSION_FRIENDLY, ZBX_MARIA_MAX_VERSION_FRIENDLY, flag); + } + else + { + flag = zbx_db_version_check("MySQL", ZBX_MYSQL_SVERSION, ZBX_MYSQL_MIN_VERSION, ZBX_MYSQL_MAX_VERSION); + zbx_db_version_json_create(json, "MySQL", version_friendly, + ZBX_MYSQL_MIN_VERSION_FRIENDLY, ZBX_MYSQL_MAX_VERSION_FRIENDLY, flag); + } + + zbx_free(version_friendly); +#elif defined(HAVE_POSTGRESQL) + int flag; + char *version_friendly; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + ZBX_PG_SVERSION = PQserverVersion(conn); + version_friendly = zbx_dsprintf(NULL, "%d.%d.%d", RIGHT2(ZBX_PG_SVERSION/10000), + RIGHT2(ZBX_PG_SVERSION/100), RIGHT2(ZBX_PG_SVERSION)); + flag = zbx_db_version_check("PostgreSQL", ZBX_PG_SVERSION, ZBX_POSTGRESQL_MIN_VERSION, + ZBX_POSTGRESQL_MAX_VERSION); + zbx_db_version_json_create(json, "PostgreSQL", version_friendly, + ZBX_POSTGRESQL_MIN_VERSION_FRIENDLY, ZBX_POSTGRESQL_MAX_VERSION_FRIENDLY, flag); + zbx_free(version_friendly); +#elif defined(HAVE_ORACLE) +# ifdef HAVE_OCI_SERVER_RELEASE2 + char *version_str = "Version "; + ub4 oci_ver = 0; +# endif + char *start, *release_str = "Release "; + char version_friendly[MAX_STRING_LEN / 8]; + int flag, major_release_version, release_update_version, release_update_version_revision, + increment_version, reserved_for_future_use, overall_status = SUCCEED; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); +# ifdef HAVE_OCI_SERVER_RELEASE2 + if (OCI_SUCCESS != OCIServerRelease2(oracle.svchp, oracle.errhp, (OraText *) version_friendly, + (ub4)sizeof(version_friendly), OCI_HTYPE_SVCCTX, &oci_ver, OCI_DEFAULT)) +# else + if (OCI_SUCCESS != OCIServerVersion(oracle.svchp, oracle.errhp, (OraText *) version_friendly, + (ub4)sizeof(version_friendly), OCI_HTYPE_SVCCTX)) +# endif + { + overall_status = FAIL; + goto out; + } + + zabbix_log(LOG_LEVEL_DEBUG, "OracleDB version retrieved unparsed: %s", version_friendly); + + if ( +# ifdef HAVE_OCI_SERVER_RELEASE2 + NULL != (start = strstr(version_friendly, version_str)) || +# endif + NULL != (start = strstr(version_friendly, release_str))) + { + size_t next_start_index; + + next_start_index = start - version_friendly + strlen(release_str); /* same length for version_str */ + + if (5 != sscanf(version_friendly + next_start_index, "%d.%d.%d.%d.%d", &major_release_version, + &release_update_version, &release_update_version_revision, &increment_version, + &reserved_for_future_use) || major_release_version >= 100 || + major_release_version <= 0 || release_update_version >= 100 || + release_update_version < 0 || release_update_version_revision >= 100 || + release_update_version_revision < 0 || increment_version >= 100 || + increment_version < 0) + { + zabbix_log(LOG_LEVEL_WARNING, "Unexpected Oracle DB version format: %s", version_friendly); + overall_status = FAIL; + } + } + else + { + zabbix_log(LOG_LEVEL_WARNING, "Cannot find Release keyword in Oracle DB version."); + overall_status = FAIL; + } +out: + if (FAIL == overall_status) + { + zabbix_log(LOG_LEVEL_WARNING, "Failed to detect OracleDB version"); + ZBX_ORACLE_SVERSION = ZBX_DBVERSION_UNDEFINED; + } + else + { + ZBX_ORACLE_SVERSION = major_release_version * 100000000 + release_update_version * 1000000 + + release_update_version_revision * 10000 + increment_version * 100 + + reserved_for_future_use; +# ifndef HAVE_OCI_SERVER_RELEASE2 + if (18 <= major_release_version) + { + zabbix_log(LOG_LEVEL_WARNING, "Unable to determine the accurate Oracle DB version " + "(possibly there is a DB driver - DB version mismatch, " + "only the major Oracle DB version can be established): %s", version_friendly); + } +# endif + } + + flag = zbx_db_version_check("Oracle", ZBX_ORACLE_SVERSION, ZBX_ORACLE_MIN_VERSION, ZBX_ORACLE_MAX_VERSION); + zbx_db_version_json_create(json, "Oracle", version_friendly, ZBX_ORACLE_MIN_VERSION_FRIENDLY, + ZBX_ORACLE_MAX_VERSION_FRIENDLY, flag); +#else + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); +#endif + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() version:%lu", __func__, (unsigned long)zbx_dbms_version_get()); + + return zbx_dbms_version_get(); +} + +#if defined(HAVE_POSTGRESQL) /****************************************************************************** * * * Function: zbx_tsdb_get_version * diff --git a/src/libs/zbxdbcache/dbcache.c b/src/libs/zbxdbcache/dbcache.c index 273f2127d2f..64fc243487c 100644 --- a/src/libs/zbxdbcache/dbcache.c +++ b/src/libs/zbxdbcache/dbcache.c @@ -1649,12 +1649,23 @@ static void recalculate_triggers(const ZBX_DC_HISTORY *history, int history_num, if (0 != item_num) { DCconfig_get_triggers_by_itemids(&trigger_info, &trigger_order, itemids, timespecs, item_num); + prepare_triggers((DC_TRIGGER **)trigger_order.values, trigger_order.values_num); zbx_determine_items_in_expressions(&trigger_order, itemids, item_num); } if (0 != timers_num) + { + int offset = trigger_order.values_num; + zbx_dc_get_triggers_by_timers(&trigger_info, &trigger_order, timers); + if (offset != trigger_order.values_num) + { + prepare_triggers((DC_TRIGGER **)trigger_order.values + offset, + trigger_order.values_num - offset); + } + } + zbx_vector_ptr_sort(&trigger_order, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC); evaluate_expressions(&trigger_order); zbx_process_triggers(&trigger_order, trigger_diff); diff --git a/src/libs/zbxdbcache/dbconfig.c b/src/libs/zbxdbcache/dbconfig.c index 0a7ea268563..020776a8b74 100644 --- a/src/libs/zbxdbcache/dbconfig.c +++ b/src/libs/zbxdbcache/dbconfig.c @@ -33,6 +33,8 @@ #include "zbxtasks.h" #include "../zbxcrypto/tls_tcp_active.h" #include "../zbxalgo/vectorimpl.h" +#include "base64.h" +#include "zbxeval.h" #define ZBX_DBCONFIG_IMPL #include "dbconfig.h" @@ -41,6 +43,7 @@ #include "actions.h" #include "zbxtrends.h" #include "zbxvault.h" +#include "zbxserialize.h" int sync_in_progress = 0; @@ -153,7 +156,6 @@ int is_item_processed_by_server(unsigned char type, const char *key) switch (type) { - case ITEM_TYPE_AGGREGATE: case ITEM_TYPE_CALCULATED: ret = SUCCEED; break; @@ -226,7 +228,6 @@ static unsigned char poller_by_item(unsigned char type, const char *key) return ZBX_POLLER_TYPE_NORMAL; case ITEM_TYPE_CALCULATED: - case ITEM_TYPE_AGGREGATE: case ITEM_TYPE_INTERNAL: if (0 == CONFIG_HISTORYPOLLER_FORKS) break; @@ -2709,6 +2710,39 @@ static void dc_interface_update_agent_stats(ZBX_DC_INTERFACE *interface, unsigne interface->items_num += num; } +static unsigned char *dup_serialized_expression(const unsigned char *src) +{ + zbx_uint32_t offset, len; + unsigned char *dst; + + if (NULL == src || '\0' == *src) + return NULL; + + offset = zbx_deserialize_uint31_compact(src, &len); + if (0 == len) + return NULL; + + dst = (unsigned char *)zbx_malloc(NULL, offset + len); + memcpy(dst, src, offset + len); + + return dst; +} + +static unsigned char *config_decode_serialized_expression(const char *src) +{ + unsigned char *dst; + int data_len, src_len; + + if (NULL == src || '\0' == *src) + return NULL; + + src_len = strlen(src) * 3 / 4; + dst = __config_mem_malloc_func(NULL, src_len); + str_base64_decode(src, (char *)dst, src_len, &data_len); + + return dst; +} + static void DCsync_items(zbx_dbsync_t *sync, int flags) { char **row; @@ -3147,14 +3181,22 @@ static void DCsync_items(zbx_dbsync_t *sync, int flags) if (ITEM_TYPE_CALCULATED == item->type) { - calcitem = (ZBX_DC_CALCITEM *)DCfind_id(&config->calcitems, itemid, sizeof(ZBX_DC_CALCITEM), &found); + calcitem = (ZBX_DC_CALCITEM *)DCfind_id(&config->calcitems, itemid, sizeof(ZBX_DC_CALCITEM), + &found); DCstrpool_replace(found, &calcitem->params, row[11]); + + if (1 == found && NULL != calcitem->formula_bin) + __config_mem_free_func((void *)calcitem->formula_bin); + + calcitem->formula_bin = config_decode_serialized_expression(row[50]); } else if (NULL != (calcitem = (ZBX_DC_CALCITEM *)zbx_hashset_search(&config->calcitems, &itemid))) { /* remove calculated item parameters */ + if (NULL != calcitem->formula_bin) + __config_mem_free_func((void *)calcitem->formula_bin); zbx_strpool_release(calcitem->params); zbx_hashset_remove_direct(&config->calcitems, calcitem); } @@ -3448,6 +3490,10 @@ static void DCsync_items(zbx_dbsync_t *sync, int flags) { calcitem = (ZBX_DC_CALCITEM *)zbx_hashset_search(&config->calcitems, &itemid); zbx_strpool_release(calcitem->params); + + if (NULL != calcitem->formula_bin) + __config_mem_free_func((void *)calcitem->formula_bin); + zbx_hashset_remove_direct(&config->calcitems, calcitem); } @@ -3649,6 +3695,18 @@ static void DCsync_triggers(zbx_dbsync_t *sync) __config_mem_free_func); trigger->topoindex = 1; } + else + { + if (NULL != trigger->expression_bin) + __config_mem_free_func((void *)trigger->expression_bin); + if (NULL != trigger->recovery_expression_bin) + __config_mem_free_func((void *)trigger->recovery_expression_bin); + } + + trigger->expression_bin = config_decode_serialized_expression(row[16]); + trigger->recovery_expression_bin = config_decode_serialized_expression(row[17]); + trigger->timer = atoi(row[18]); + trigger->revision = config->sync_start_ts; } /* remove deleted triggers from buffer */ @@ -3668,10 +3726,14 @@ static void DCsync_triggers(zbx_dbsync_t *sync) /* force trigger list update for items used in removed trigger */ - get_functionids(&functionids, trigger->expression); + zbx_get_serialized_expression_functionids(trigger->expression, trigger->expression_bin, + &functionids); if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == trigger->recovery_mode) - get_functionids(&functionids, trigger->recovery_expression); + { + zbx_get_serialized_expression_functionids(trigger->recovery_expression, + trigger->recovery_expression_bin, &functionids); + } for (i = 0; i < functionids.values_num; i++) { @@ -3700,6 +3762,11 @@ static void DCsync_triggers(zbx_dbsync_t *sync) zbx_vector_ptr_destroy(&trigger->tags); + if (NULL != trigger->expression_bin) + __config_mem_free_func((void *)trigger->expression_bin); + if (NULL != trigger->recovery_expression_bin) + __config_mem_free_func((void *)trigger->recovery_expression_bin); + zbx_hashset_remove_direct(&config->triggers, trigger); } zbx_vector_uint64_destroy(&functionids); @@ -3867,7 +3934,7 @@ static int dc_function_calculate_trends_nextcheck(time_t from, const char *perio static int dc_function_calculate_nextcheck(const zbx_trigger_timer_t *timer, time_t from, zbx_uint64_t seed) { - if (ZBX_FUNCTION_TYPE_TIMER == timer->type) + if (ZBX_TRIGGER_TIMER_FUNCTION_TIME == timer->type || ZBX_TRIGGER_TIMER_TRIGGER == timer->type) { int nextcheck; @@ -3879,15 +3946,15 @@ static int dc_function_calculate_nextcheck(const zbx_trigger_timer_t *timer, tim return nextcheck; } - else if (ZBX_FUNCTION_TYPE_TRENDS == timer->type) + else if (ZBX_TRIGGER_TIMER_FUNCTION_TREND == timer->type) { struct tm tm; time_t nextcheck; - int offsets[ZBX_TIME_UNIT_COUNT] = {0, SEC_PER_MIN * 10, SEC_PER_HOUR + SEC_PER_MIN * 10, + int offsets[ZBX_TIME_UNIT_COUNT] = {0, 0, 0, SEC_PER_MIN * 10, SEC_PER_HOUR + SEC_PER_MIN * 10, SEC_PER_HOUR + SEC_PER_MIN * 10, - SEC_PER_HOUR + SEC_PER_MIN * 10}; - int periods[ZBX_TIME_UNIT_COUNT] = {0, SEC_PER_MIN * 10, SEC_PER_HOUR, SEC_PER_HOUR * 11, - SEC_PER_DAY - SEC_PER_HOUR, SEC_PER_DAY - SEC_PER_HOUR}; + SEC_PER_HOUR + SEC_PER_MIN * 10, SEC_PER_HOUR + SEC_PER_MIN * 10}; + int periods[ZBX_TIME_UNIT_COUNT] = {0, 0, 0, SEC_PER_MIN * 10, SEC_PER_HOUR, + SEC_PER_HOUR * 11, SEC_PER_DAY - SEC_PER_HOUR, SEC_PER_DAY - SEC_PER_HOUR}; if (ZBX_TIME_UNIT_HOUR == timer->trend_base) { @@ -3905,15 +3972,17 @@ static int dc_function_calculate_nextcheck(const zbx_trigger_timer_t *timer, tim } else { - int ret; + int ret = FAIL; char *error = NULL, *period_shift; - period_shift = zbx_function_get_param_dyn(timer->parameter, 2); - - ret = dc_function_calculate_trends_nextcheck(from, period_shift, timer->trend_base, &nextcheck, - &error); - - zbx_free(period_shift); + if (NULL != (period_shift = strchr(timer->parameter, ':'))) + { + period_shift++; + ret = dc_function_calculate_trends_nextcheck(from, period_shift, timer->trend_base, + &nextcheck, &error); + } + else + error = zbx_dsprintf(NULL, "invalid first parameter"); if (FAIL == ret) { @@ -3935,21 +4004,22 @@ static int dc_function_calculate_nextcheck(const zbx_trigger_timer_t *timer, tim /****************************************************************************** * * - * Function: dc_trigger_timer_create * + * Function: dc_trigger_function_timer_create * * * - * Purpose: create trigger timer based on the specified function * + * Purpose: create trigger timer based on the trend function * * * * Return value: Created timer or NULL in the case of error. * * * ******************************************************************************/ -static zbx_trigger_timer_t *dc_trigger_timer_create(ZBX_DC_FUNCTION *function) +static zbx_trigger_timer_t *dc_trigger_function_timer_create(ZBX_DC_FUNCTION *function) { zbx_trigger_timer_t *timer; zbx_time_unit_t trend_base; + zbx_uint32_t type; if (ZBX_FUNCTION_TYPE_TRENDS == function->type) { - char *error = NULL; + char *error = NULL; if (FAIL == zbx_trends_parse_base(function->parameter, &trend_base, &error)) { @@ -3958,17 +4028,22 @@ static zbx_trigger_timer_t *dc_trigger_timer_create(ZBX_DC_FUNCTION *function) zbx_free(error); return NULL; } + type = ZBX_TRIGGER_TIMER_FUNCTION_TREND; } else + { trend_base = ZBX_TIME_UNIT_UNKNOWN; + type = ZBX_TRIGGER_TIMER_FUNCTION_TIME; + } timer = (zbx_trigger_timer_t *)__config_mem_malloc_func(NULL, sizeof(zbx_trigger_timer_t)); - timer->type = function->type; + timer->objectid = function->functionid; timer->triggerid = function->triggerid; timer->revision = function->revision; timer->trend_base = trend_base; timer->lock = 0; + timer->type = type; function->timer_revision = function->revision; @@ -3980,6 +4055,32 @@ static zbx_trigger_timer_t *dc_trigger_timer_create(ZBX_DC_FUNCTION *function) return timer; } +/****************************************************************************** + * * + * Function: dc_trigger_timer_create * + * * + * Purpose: create trigger timer based on the specified trigger * + * * + * Return value: Created timer or NULL in the case of error. * + * * + ******************************************************************************/ +static zbx_trigger_timer_t *dc_trigger_timer_create(ZBX_DC_TRIGGER *trigger) +{ + zbx_trigger_timer_t *timer; + + timer = (zbx_trigger_timer_t *)__config_mem_malloc_func(NULL, sizeof(zbx_trigger_timer_t)); + timer->type = ZBX_TRIGGER_TIMER_TRIGGER; + timer->objectid = trigger->triggerid; + timer->triggerid = trigger->triggerid; + timer->revision = trigger->revision; + timer->trend_base = ZBX_TIME_UNIT_UNKNOWN; + timer->lock = 0; + timer->parameter = NULL; + + trigger->timer_revision = trigger->revision; + + return timer; +} /****************************************************************************** * * @@ -4058,7 +4159,7 @@ static void dc_schedule_trigger_timers(zbx_hashset_t *trend_queue, int now) if (TRIGGER_STATUS_ENABLED != trigger->status || TRIGGER_FUNCTIONAL_TRUE != trigger->functional) continue; - if (NULL == (timer = dc_trigger_timer_create(function))) + if (NULL == (timer = dc_trigger_function_timer_create(function))) continue; if (NULL != trend_queue && NULL != (old = (zbx_trigger_timer_t *)zbx_hashset_search(trend_queue, @@ -4084,6 +4185,25 @@ static void dc_schedule_trigger_timers(zbx_hashset_t *trend_queue, int now) dc_schedule_trigger_timer(timer, NULL, &ts); } } + + zbx_hashset_iter_reset(&config->triggers, &iter); + while (NULL != (trigger = (ZBX_DC_TRIGGER *)zbx_hashset_iter_next(&iter))) + { + if (ZBX_TRIGGER_TIMER_DEFAULT == trigger->timer) + continue; + + if (NULL == (timer = dc_trigger_timer_create(trigger))) + continue; + + if (0 == (ts.sec = dc_function_calculate_nextcheck(timer, now, timer->triggerid))) + { + dc_trigger_timer_free(timer); + trigger->timer_revision = 0; + } + else + dc_schedule_trigger_timer(timer, NULL, &ts); + + } } static void DCsync_functions(zbx_dbsync_t *sync) @@ -5766,7 +5886,7 @@ static void dc_load_trigger_queue(zbx_hashset_t *trend_functions) { zbx_trigger_timer_t timer_local, *timer; - if (ZBX_FUNCTION_TYPE_TRENDS != atoi(row[1])) + if (ZBX_TRIGGER_TIMER_FUNCTION_TREND != atoi(row[1])) { THIS_SHOULD_NEVER_HAPPEN; continue; @@ -7751,8 +7871,18 @@ static void DCget_item(DC_ITEM *dst_item, const ZBX_DC_ITEM *src_item) dst_item->jmx_endpoint = NULL; break; case ITEM_TYPE_CALCULATED: - calcitem = (ZBX_DC_CALCITEM *)zbx_hashset_search(&config->calcitems, &src_item->itemid); - dst_item->params = zbx_strdup(NULL, NULL != calcitem ? calcitem->params : ""); + if (NULL != (calcitem = (ZBX_DC_CALCITEM *)zbx_hashset_search(&config->calcitems, + &src_item->itemid))) + { + dst_item->params = zbx_strdup(NULL, calcitem->params); + dst_item->formula_bin = dup_serialized_expression(calcitem->formula_bin); + } + else + { + dst_item->params = zbx_strdup(NULL, ""); + dst_item->formula_bin = NULL; + } + break; default: /* nothing to do */; @@ -7789,8 +7919,11 @@ void DCconfig_clean_items(DC_ITEM *items, int *errcodes, size_t num) case ITEM_TYPE_DB_MONITOR: case ITEM_TYPE_SSH: case ITEM_TYPE_TELNET: + zbx_free(items[i].params); + break; case ITEM_TYPE_CALCULATED: zbx_free(items[i].params); + zbx_free(items[i].formula_bin); break; } @@ -7821,8 +7954,6 @@ static void DCget_trigger(DC_TRIGGER *dst_trigger, const ZBX_DC_TRIGGER *src_tri dst_trigger->triggerid = src_trigger->triggerid; dst_trigger->description = zbx_strdup(NULL, src_trigger->description); - dst_trigger->expression_orig = zbx_strdup(NULL, src_trigger->expression); - dst_trigger->recovery_expression_orig = zbx_strdup(NULL, src_trigger->recovery_expression); dst_trigger->error = zbx_strdup(NULL, src_trigger->error); dst_trigger->timespec.sec = 0; dst_trigger->timespec.ns = 0; @@ -7840,14 +7971,17 @@ static void DCget_trigger(DC_TRIGGER *dst_trigger, const ZBX_DC_TRIGGER *src_tri dst_trigger->opdata = zbx_strdup(NULL, src_trigger->opdata); dst_trigger->event_name = ('\0' != *src_trigger->event_name ? zbx_strdup(NULL, src_trigger->event_name) : NULL); dst_trigger->flags = 0; - - dst_trigger->expression = NULL; - dst_trigger->recovery_expression = NULL; dst_trigger->new_error = NULL; dst_trigger->expression = zbx_strdup(NULL, src_trigger->expression); dst_trigger->recovery_expression = zbx_strdup(NULL, src_trigger->recovery_expression); + dst_trigger->expression_bin = dup_serialized_expression(src_trigger->expression_bin); + dst_trigger->recovery_expression_bin = dup_serialized_expression(src_trigger->recovery_expression_bin); + + dst_trigger->eval_ctx = NULL; + dst_trigger->eval_ctx_r = NULL; + zbx_vector_ptr_create(&dst_trigger->tags); if (0 != src_trigger->tags.values_num) @@ -7886,17 +8020,30 @@ static void DCclean_trigger(DC_TRIGGER *trigger) { zbx_free(trigger->new_error); zbx_free(trigger->error); - zbx_free(trigger->expression_orig); - zbx_free(trigger->recovery_expression_orig); zbx_free(trigger->expression); zbx_free(trigger->recovery_expression); zbx_free(trigger->description); zbx_free(trigger->correlation_tag); zbx_free(trigger->opdata); zbx_free(trigger->event_name); + zbx_free(trigger->expression_bin); + zbx_free(trigger->recovery_expression_bin); zbx_vector_ptr_clear_ext(&trigger->tags, (zbx_clean_func_t)zbx_free_tag); zbx_vector_ptr_destroy(&trigger->tags); + + if (NULL != trigger->eval_ctx) + { + zbx_eval_clear(trigger->eval_ctx); + zbx_free(trigger->eval_ctx); + } + + if (NULL != trigger->eval_ctx_r) + { + zbx_eval_clear(trigger->eval_ctx_r); + zbx_free(trigger->eval_ctx_r); + } + } /****************************************************************************** @@ -8498,22 +8645,35 @@ void DCconfig_get_triggers_by_itemids(zbx_hashset_t *trigger_info, zbx_vector_pt * * * Function: DCconfig_find_active_time_function * * * - * Purpose: checks if the expression contains time based functions * + * Purpose: check if the expression contains time based functions * + * * + * Parameters: expression - [IN] the original expression * + * data - [IN] the parsed and serialized expression * + * trigger_timer - [IN] the trigger time function flags * * * ******************************************************************************/ -static int DCconfig_find_active_time_function(const char *expression) +static int DCconfig_find_active_time_function(const char *expression, const unsigned char *data, + unsigned char trigger_timer) { - zbx_uint64_t functionid; + int i, ret = SUCCEED; const ZBX_DC_FUNCTION *dc_function; const ZBX_DC_HOST *dc_host; const ZBX_DC_ITEM *dc_item; + zbx_vector_uint64_t functionids; + + zbx_vector_uint64_create(&functionids); + zbx_get_serialized_expression_functionids(expression, data, &functionids); - while (SUCCEED == get_N_functionid(expression, 1, &functionid, NULL, &expression)) + for (i = 0; i < functionids.values_num; i++) { - if (NULL == (dc_function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, &functionid))) + if (NULL == (dc_function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, + &functionids.values[i]))) + { continue; + } - if (ZBX_FUNCTION_TYPE_TIMER == dc_function->type || ZBX_FUNCTION_TYPE_TRENDS == dc_function->type) + if (ZBX_TRIGGER_TIMER_DEFAULT != trigger_timer || ZBX_FUNCTION_TYPE_TRENDS == dc_function->type || + ZBX_FUNCTION_TYPE_TIMER == dc_function->type) { if (NULL == (dc_item = zbx_hashset_search(&config->items, &dc_function->itemid))) continue; @@ -8522,11 +8682,15 @@ static int DCconfig_find_active_time_function(const char *expression) continue; if (SUCCEED != DCin_maintenance_without_data_collection(dc_host, dc_item)) - return SUCCEED; + goto out; } } - return FAIL; + ret = (ZBX_TRIGGER_TIMER_DEFAULT != trigger_timer ? SUCCEED : FAIL); +out: + zbx_vector_uint64_destroy(&functionids); + + return ret; } /****************************************************************************** @@ -8561,7 +8725,8 @@ void zbx_dc_get_triggers_by_timers(zbx_hashset_t *trigger_info, zbx_vector_ptr_t DC_TRIGGER *trigger, trigger_local; unsigned char flags; - if (SUCCEED == DCconfig_find_active_time_function(dc_trigger->expression)) + if (SUCCEED == DCconfig_find_active_time_function(dc_trigger->expression, + dc_trigger->expression_bin, dc_trigger->timer & ZBX_TRIGGER_TIMER_EXPRESSION)) { flags = ZBX_DC_TRIGGER_PROBLEM_EXPRESSION; } @@ -8573,8 +8738,12 @@ void zbx_dc_get_triggers_by_timers(zbx_hashset_t *trigger_info, zbx_vector_ptr_t if (TRIGGER_VALUE_PROBLEM != dc_trigger->value) continue; - if (SUCCEED != DCconfig_find_active_time_function(dc_trigger->recovery_expression)) + if (SUCCEED != DCconfig_find_active_time_function(dc_trigger->recovery_expression, + dc_trigger->recovery_expression_bin, + dc_trigger->timer & ZBX_TRIGGER_TIMER_RECOVERY_EXPRESSION)) + { continue; + } flags = 0; } @@ -8595,6 +8764,58 @@ void zbx_dc_get_triggers_by_timers(zbx_hashset_t *trigger_info, zbx_vector_ptr_t /****************************************************************************** * * + * Function: trigger_timer_validate * + * * + * Purpose: validate trigger timer * + * * + * Parameters: timer - [IN] trigger timer * + * dc_trigger - [OUT] the trigger data * + * * + * Return value: SUCCEED - the timer is valid * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int trigger_timer_validate(zbx_trigger_timer_t *timer, ZBX_DC_TRIGGER **dc_trigger) +{ + ZBX_DC_FUNCTION *dc_function; + + *dc_trigger = (ZBX_DC_TRIGGER *)zbx_hashset_search(&config->triggers, &timer->triggerid); + + if (0 != (timer->type & ZBX_TRIGGER_TIMER_FUNCTION)) + { + if (NULL == (dc_function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, &timer->objectid))) + return FAIL; + + if (dc_function->revision > timer->revision || + NULL == *dc_trigger || + TRIGGER_STATUS_ENABLED != (*dc_trigger)->status || + TRIGGER_FUNCTIONAL_TRUE != (*dc_trigger)->functional) + { + if (dc_function->timer_revision == timer->revision) + dc_function->timer_revision = 0; + return FAIL; + } + } + else + { + if (NULL == (*dc_trigger)) + return FAIL; + + if ((*dc_trigger)->revision > timer->revision || + TRIGGER_STATUS_ENABLED != (*dc_trigger)->status || + TRIGGER_FUNCTIONAL_TRUE != (*dc_trigger)->functional) + { + if ((*dc_trigger)->timer_revision == timer->revision) + (*dc_trigger)->timer_revision = 0; + return FAIL; + } + } + + return SUCCEED; +} + +/****************************************************************************** + * * * Function: zbx_dc_get_trigger_timers * * * * Purpose: gets timers from trigger queue * @@ -8624,7 +8845,6 @@ void zbx_dc_get_trigger_timers(zbx_vector_ptr_t *timers, int now, int soft_limit zbx_binary_heap_elem_t *elem; zbx_trigger_timer_t *timer; ZBX_DC_TRIGGER *dc_trigger; - ZBX_DC_FUNCTION *dc_function; elem = zbx_binary_heap_find_min(&config->trigger_queue); timer = (zbx_trigger_timer_t *)elem->data; @@ -8647,17 +8867,8 @@ void zbx_dc_get_trigger_timers(zbx_vector_ptr_t *timers, int now, int soft_limit zbx_binary_heap_remove_min(&config->trigger_queue); - /* check if function exists and trigger should be calculated */ - if (NULL == (dc_function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, &timer->objectid)) || - dc_function->revision > timer->revision || - NULL == (dc_trigger = (ZBX_DC_TRIGGER *)zbx_hashset_search(&config->triggers, - &timer->triggerid)) || - TRIGGER_STATUS_ENABLED != dc_trigger->status || - TRIGGER_FUNCTIONAL_TRUE != dc_trigger->functional) + if (SUCCEED != trigger_timer_validate(timer, &dc_trigger)) { - if (NULL != dc_function && dc_function->revision == timer->revision) - dc_function->timer_revision = 0; - dc_trigger_timer_free(timer); continue; } @@ -8671,7 +8882,7 @@ void zbx_dc_get_trigger_timers(zbx_vector_ptr_t *timers, int now, int soft_limit /* 1) time functions uses current time, so trigger evaluation time does not affect their results */ /* 2) trend function of the same trigger with the same evaluation timestamp is being */ /* evaluated by the same process */ - if (0 == dc_trigger->locked || ZBX_FUNCTION_TYPE_TRENDS != timer->type || + if (0 == dc_trigger->locked || ZBX_TRIGGER_TIMER_FUNCTION_TREND != timer->type || (NULL != first_timer && 1 == first_timer->lock)) { /* resetting execution timer will cause a new execution time to be set */ @@ -8712,12 +8923,25 @@ static void dc_reschedule_trigger_timers(zbx_vector_ptr_t *timers) /* schedule calculation error can result in 0 execution time */ if (0 == timer->exec_ts.sec) { - ZBX_DC_FUNCTION *function; + if (0 != (timer->type & ZBX_TRIGGER_TIMER_FUNCTION)) + { + ZBX_DC_FUNCTION *function; - if (NULL != (function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, - &timer->objectid)) && function->timer_revision == timer->revision) + if (NULL != (function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, + &timer->objectid)) && function->timer_revision == timer->revision) + { + function->timer_revision = 0; + } + } + else if (ZBX_TRIGGER_TIMER_TRIGGER == timer->type) { - function->timer_revision = 0; + ZBX_DC_TRIGGER *trigger; + + if (NULL != (trigger = (ZBX_DC_TRIGGER *)zbx_hashset_search(&config->triggers, + &timer->objectid)) && trigger->timer_revision == timer->revision) + { + trigger->timer_revision = 0; + } } dc_trigger_timer_free(timer); } @@ -8777,7 +9001,7 @@ void zbx_dc_clear_timer_queue(zbx_vector_ptr_t *timers) { zbx_trigger_timer_t *timer = (zbx_trigger_timer_t *)config->trigger_queue.elems[i].data; - if (ZBX_FUNCTION_TYPE_TRENDS == timer->type && + if (ZBX_TRIGGER_TIMER_FUNCTION_TREND == timer->type && NULL != (function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, &timer->objectid)) && function->timer_revision == timer->revision) @@ -10740,30 +10964,40 @@ out: * Function: dc_expand_user_macros * * * * Purpose: expand user macros in the specified text value * - * WARNING - DO NOT USE FOR TRIGGERS, for triggers use the dedicated function * * * - * Parameters: text - [IN] the text value to expand * - * hostids - [IN] an array of related hostids * - * hostids_num - [IN] the number of hostids * - * * - * Return value: The text value with expanded user macros. Unknown or invalid * - * macros will be left unresolved. * + * Parameters: text - [IN] the text value to expand * + * len - [IN] the text length * + * hostids - [IN] an array of related hostids * + * hostids_num - [IN] the number of hostids * + * value - [IN] the expanded macro with expanded user * + * macros. Unknown or invalid macros will be * + * left unresolved. * + * error - [IN] the error message, optional. If specified * + * the function will return failure on first * + * unknown user macro * + * * + * Return value: SUCCEED - the macros were expanded successfully * + * FAIL - error parameter was given and at least one of * + * macros was not expanded * * * * Comments: The returned value must be freed by the caller. * - * This function must be used only by configuration syncer * * * ******************************************************************************/ -char *dc_expand_user_macros(const char *text, zbx_uint64_t *hostids, int hostids_num) +int dc_expand_user_macros_len(const char *text, size_t text_len, zbx_uint64_t *hostids, int hostids_num, + char **value, char **error) { zbx_token_t token; - int pos = 0, len, last_pos = 0; - char *str = NULL, *name = NULL, *context = NULL, *value = NULL; - size_t str_alloc = 0, str_offset = 0; + int len; + char *str = NULL, *name = NULL, *context = NULL, *macro_value = NULL; + size_t str_alloc = 0, str_offset = 0, pos = 0, last_pos = 0; if ('\0' == *text) - return zbx_strdup(NULL, text); + { + *value = zbx_strdup(NULL, text); + return SUCCEED; + } - for (; SUCCEED == zbx_token_find(text, pos, &token, ZBX_TOKEN_SEARCH_BASIC); pos++) + for (; SUCCEED == zbx_token_find(text, pos, &token, ZBX_TOKEN_SEARCH_BASIC) && token.loc.r < text_len; pos++) { if (ZBX_TOKEN_USER_MACRO != token.type) continue; @@ -10771,66 +11005,86 @@ char *dc_expand_user_macros(const char *text, zbx_uint64_t *hostids, int hostids if (SUCCEED != zbx_user_macro_parse_dyn(text + token.loc.l, &name, &context, &len, NULL)) continue; - zbx_strncpy_alloc(&str, &str_alloc, &str_offset, text + last_pos, token.loc.l - last_pos); - dc_get_user_macro(hostids, hostids_num, name, context, &value); + if (last_pos < token.loc.l) + zbx_strncpy_alloc(&str, &str_alloc, &str_offset, text + last_pos, token.loc.l - last_pos); - if (NULL != value) - { - zbx_strcpy_alloc(&str, &str_alloc, &str_offset, value); - zbx_free(value); + dc_get_user_macro(hostids, hostids_num, name, context, ¯o_value); + + zbx_free(name); + zbx_free(context); + if (NULL != macro_value) + { + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, macro_value); + zbx_free(macro_value); } else { + if (NULL != error) + { + *error = zbx_dsprintf(NULL, "unknown user macro \"%.*s\"", + (int)(token.loc.r - token.loc.l + 1), text + token.loc.l); + zbx_free(str); + return FAIL; + } zbx_strncpy_alloc(&str, &str_alloc, &str_offset, text + token.loc.l, token.loc.r - token.loc.l + 1); } - zbx_free(name); - zbx_free(context); - pos = token.loc.r; last_pos = pos + 1; } - zbx_strcpy_alloc(&str, &str_alloc, &str_offset, text + last_pos); + if (last_pos < text_len) + zbx_strncpy_alloc(&str, &str_alloc, &str_offset, text + last_pos, text_len - last_pos); - return str; + *value = str; + + return SUCCEED; } /****************************************************************************** * * - * Function: zbx_dc_expand_user_macros * + * Function: zbx_dc_expand_user_macros_len * * * - * Purpose: expand user macros in the specified text value * + * Purpose: expand user macros in the specified text * * * - * Parameters: text - [IN] the text value to expand * - * hostid - [IN] related hostid * + * Parameters: text - [IN] the text value to expand * + * len - [IN] the text length * + * hostids - [IN] an array of related hostids * + * hostids_num - [IN] the number of hostids * + * value - [IN] the expanded macro with expanded user * + * macros. Unknown or invalid macros will be * + * left unresolved. * + * error - [IN] the error message, optional. If specified * + * the function will return failure on first * + * unknown user macro * * * - * Return value: The text value with expanded user macros. Unknown or invalid * - * macros will be left unresolved. * + * Return value: SUCCEED - the macros were expanded successfully * + * FAIL - error parameter was given and at least one of * + * macros was not expanded * * * * Comments: The returned value must be freed by the caller. * * * ******************************************************************************/ -char *zbx_dc_expand_user_macros(const char *text, zbx_uint64_t hostid) +int zbx_dc_expand_user_macros_len(const char *text, size_t text_len, zbx_uint64_t *hostids, int hostids_num, + char **value, char **error) { - char *resolved_text; + int ret; RDLOCK_CACHE; - resolved_text = dc_expand_user_macros(text, &hostid, 1); + ret = dc_expand_user_macros_len(text, text_len, hostids, hostids_num, value, error); UNLOCK_CACHE; - return resolved_text; + return ret; } /****************************************************************************** * * - * Function: dc_expand_user_macros_in_expression * + * Function: dc_expand_user_macros * * * - * Purpose: expand user macros for triggers and calculated items in the * - * specified text value and autoquote macros that are not already * - * quoted that cannot be casted to a double * + * Purpose: expand user macros in the specified text value * + * WARNING - DO NOT USE FOR TRIGGERS, for triggers use the dedicated function * * * * Parameters: text - [IN] the text value to expand * * hostids - [IN] an array of related hostids * @@ -10840,84 +11094,35 @@ char *zbx_dc_expand_user_macros(const char *text, zbx_uint64_t hostid) * macros will be left unresolved. * * * * Comments: The returned value must be freed by the caller. * + * This function must be used only by configuration syncer * * * ******************************************************************************/ -char *dc_expand_user_macros_in_expression(const char *text, zbx_uint64_t *hostids, int hostids_num) +char *dc_expand_user_macros(const char *text, zbx_uint64_t *hostids, int hostids_num) { zbx_token_t token; - int pos = 0, last_pos = 0, cur_token_inside_quote = 0, prev_token_loc_r = -1, len; + int pos = 0, len, last_pos = 0; char *str = NULL, *name = NULL, *context = NULL, *value = NULL; - size_t str_alloc = 0, str_offset = 0, i; + size_t str_alloc = 0, str_offset = 0; if ('\0' == *text) return zbx_strdup(NULL, text); for (; SUCCEED == zbx_token_find(text, pos, &token, ZBX_TOKEN_SEARCH_BASIC); pos++) { - for (i = prev_token_loc_r + 1; i < token.loc.l; i++) - { - switch (text[i]) - { - case '\\': - if (0 != cur_token_inside_quote) - i++; - break; - case '"': - cur_token_inside_quote = !cur_token_inside_quote; - break; - } - } - if (ZBX_TOKEN_USER_MACRO != token.type) - { - prev_token_loc_r = token.loc.r; continue; - } if (SUCCEED != zbx_user_macro_parse_dyn(text + token.loc.l, &name, &context, &len, NULL)) - { - prev_token_loc_r = token.loc.r; continue; - } zbx_strncpy_alloc(&str, &str_alloc, &str_offset, text + last_pos, token.loc.l - last_pos); dc_get_user_macro(hostids, hostids_num, name, context, &value); if (NULL != value) { - size_t sz; - char *tmp; - - sz = zbx_get_escape_string_len(value, "\"\\"); - - if (0 == cur_token_inside_quote && ZBX_INFINITY == evaluate_string_to_double(value)) - { - /* autoquote */ - tmp = zbx_malloc(NULL, sz + 3); - tmp[0] = '\"'; - zbx_escape_string(tmp + 1, sz + 1, value, "\"\\"); - tmp[sz + 1] = '\"'; - tmp[sz + 2] = '\0'; - zbx_free(value); - value = tmp; - } - else - { - if (sz != strlen(value)) - { - tmp = zbx_malloc(NULL, sz + 1); - zbx_escape_string(tmp, sz + 1, value, "\"\\"); - tmp[sz] = '\0'; - zbx_free(value); - value = tmp; - } - } - } - - if (NULL != value) - { zbx_strcpy_alloc(&str, &str_alloc, &str_offset, value); zbx_free(value); + } else { @@ -10930,7 +11135,6 @@ char *dc_expand_user_macros_in_expression(const char *text, zbx_uint64_t *hostid pos = token.loc.r; last_pos = pos + 1; - prev_token_loc_r = token.loc.r; } zbx_strcpy_alloc(&str, &str_alloc, &str_offset, text + last_pos); @@ -10940,71 +11144,28 @@ char *dc_expand_user_macros_in_expression(const char *text, zbx_uint64_t *hostid /****************************************************************************** * * - * Function: dc_expression_expand_user_macros * - * * - * Purpose: expand user macros in trigger expression * - * * - * Parameters: expression - [IN] the expression to expand * - * error - [OUT] the error message * - * * - * Return value: The expanded expression or NULL in the case of error. * - * If NULL is returned the error message is set. * - * * - * Comments: The returned expression must be freed by the caller. * - * * - ******************************************************************************/ -static char *dc_expression_expand_user_macros(const char *expression) -{ - zbx_vector_uint64_t functionids, hostids; - char *out; - - zbx_vector_uint64_create(&functionids); - zbx_vector_uint64_create(&hostids); - - get_functionids(&functionids, expression); - dc_get_hostids_by_functionids(functionids.values, functionids.values_num, &hostids); - - out = dc_expand_user_macros_in_expression(expression, hostids.values, hostids.values_num); - - if (NULL != strstr(out, "{$")) - { - zabbix_log(LOG_LEVEL_DEBUG, "cannot evaluate expression: invalid macro value"); - zbx_free(out); - } - - zbx_vector_uint64_destroy(&hostids); - zbx_vector_uint64_destroy(&functionids); - - return out; -} - -/****************************************************************************** - * * - * Function: DCexpression_expand_user_macros * + * Function: zbx_dc_expand_user_macros * * * - * Purpose: expand user macros in trigger expression * + * Purpose: expand user macros in the specified text value * * * - * Parameters: expression - [IN] the expression to expand * + * Parameters: text - [IN] the text value to expand * + * hostid - [IN] related hostid * * * - * Return value: The expanded expression or NULL in the case of error. * - * If NULL is returned the error message is set. * + * Return value: The text value with expanded user macros. Unknown or invalid * + * macros will be left unresolved. * * * - * Comments: The returned expression must be freed by the caller. * - * This function is a locking wrapper of * - * dc_expression_expand_user_macros() function for external usage. * + * Comments: The returned value must be freed by the caller. * * * ******************************************************************************/ -char *DCexpression_expand_user_macros(const char *expression) +char *zbx_dc_expand_user_macros(const char *text, zbx_uint64_t hostid) { - char *expression_ex; + char *resolved_text; RDLOCK_CACHE; - - expression_ex = dc_expression_expand_user_macros(expression); - + resolved_text = dc_expand_user_macros(text, &hostid, 1); UNLOCK_CACHE; - return expression_ex; + return resolved_text; } /****************************************************************************** @@ -11123,59 +11284,52 @@ int DCget_item_queue(zbx_vector_ptr_t *queue, int from, int to) * Function: dc_trigger_items_hosts_enabled * * * * Purpose: check that functionids in trigger (recovery) expression * - * correspond to enabled items and hosts * * * * Parameters: expression - [IN] trigger (recovery) expression * + * data - [IN] parsed and serialized expression * * * * Return value: SUCCEED - all functionids correspond to enabled items and * * enabled hosts * * FAIL - at least one item or host is disabled * * * ******************************************************************************/ -static int dc_trigger_items_hosts_enabled(const char *expression) +static int dc_trigger_items_hosts_enabled(const char *expression, const unsigned char *data) { zbx_uint64_t functionid; const ZBX_DC_ITEM *dc_item; const ZBX_DC_FUNCTION *dc_function; const ZBX_DC_HOST *dc_host; - const char *p, *q; - - for (p = expression; '\0' != *p; p++) - { - if ('{' != *p) - continue; + int i, ret = FAIL; + zbx_vector_uint64_t functionids; - if ('$' == p[1]) - { - int macro_r, context_l, context_r; + zbx_vector_uint64_create(&functionids); + zbx_get_serialized_expression_functionids(expression, data, &functionids); - if (SUCCEED == zbx_user_macro_parse(p, ¯o_r, &context_l, &context_r, NULL)) - p += macro_r; - else - p++; + for (i = 0; i < functionids.values_num; i++) + { + functionid = functionids.values[i]; - continue; - } + if (NULL == (dc_function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, &functionid))) + goto out; - if (NULL == (q = strchr(p + 1, '}'))) - return FAIL; + if (NULL == (dc_item = (ZBX_DC_ITEM *)zbx_hashset_search(&config->items, &dc_function->itemid))) + goto out; - if (SUCCEED != is_uint64_n(p + 1, q - p - 1, &functionid)) - continue; + if (ITEM_STATUS_ACTIVE != dc_item->status) + goto out; - if (NULL == (dc_function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions, &functionid)) || - NULL == (dc_item = (ZBX_DC_ITEM *)zbx_hashset_search(&config->items, &dc_function->itemid)) || - ITEM_STATUS_ACTIVE != dc_item->status || - NULL == (dc_host = (ZBX_DC_HOST *)zbx_hashset_search(&config->hosts, &dc_item->hostid)) || - HOST_STATUS_MONITORED != dc_host->status) - { - return FAIL; - } + if (NULL == (dc_host = (ZBX_DC_HOST *)zbx_hashset_search(&config->hosts, &dc_item->hostid))) + goto out; - p = q; + if (HOST_STATUS_MONITORED != dc_host->status) + goto out; } - return SUCCEED; + ret = SUCCEED; +out: + zbx_vector_uint64_destroy(&functionids); + + return ret; } /****************************************************************************** @@ -11353,9 +11507,11 @@ static void dc_status_update(void) switch (dc_trigger->status) { case TRIGGER_STATUS_ENABLED: - if (SUCCEED == dc_trigger_items_hosts_enabled(dc_trigger->expression) && + if (SUCCEED == dc_trigger_items_hosts_enabled(dc_trigger->expression, + dc_trigger->expression_bin) && (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION != dc_trigger->recovery_mode || - SUCCEED == dc_trigger_items_hosts_enabled(dc_trigger->recovery_expression))) + SUCCEED == dc_trigger_items_hosts_enabled(dc_trigger->recovery_expression, + dc_trigger->recovery_expression_bin))) { switch (dc_trigger->value) { @@ -12691,38 +12847,62 @@ void zbx_dc_get_nested_hostgroupids(zbx_uint64_t *groupids, int groupids_num, zb /****************************************************************************** * * - * Function: zbx_dc_get_nested_hostgroupids_by_names * + * Function: zbx_dc_get_hostids_by_group_name * * * - * Purpose: gets nested group ids for the specified host groups * + * Purpose: gets hostids belonging to the group and its nested groups * * * - * Parameter: groups - [IN] the parent group names * - * nested_groupids - [OUT] the nested + parent group ids * + * Parameter: name - [IN] the group name * + * hostids - [OUT] the hostids * * * ******************************************************************************/ -void zbx_dc_get_nested_hostgroupids_by_names(zbx_vector_str_t *groups, zbx_vector_uint64_t *nested_groupids) +void zbx_dc_get_hostids_by_group_name(const char *name, zbx_vector_uint64_t *hostids) { - int i, index; + int i; + zbx_vector_uint64_t groupids; + zbx_dc_hostgroup_t group_local, *group; + + zbx_vector_uint64_create(&groupids); + + group_local.name = name; WRLOCK_CACHE; - for (i = 0; i < groups->values_num; i++) + if (FAIL != (i = zbx_vector_ptr_bsearch(&config->hostgroups_name, &group_local, dc_compare_hgroups))) { - zbx_dc_hostgroup_t group_local, *group; + group = (zbx_dc_hostgroup_t *)config->hostgroups_name.values[i]; + dc_get_nested_hostgroupids(group->groupid, &groupids); + } + + UNLOCK_CACHE; + + zbx_vector_uint64_sort(&groupids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(&groupids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); - group_local.name = groups->values[i]; + RDLOCK_CACHE; - if (FAIL != (index = zbx_vector_ptr_bsearch(&config->hostgroups_name, &group_local, - dc_compare_hgroups))) + for (i = 0; i < groupids.values_num; i++) + { + zbx_hashset_iter_t iter; + zbx_uint64_t *phostid; + + if (NULL == (group = (zbx_dc_hostgroup_t *)zbx_hashset_search(&config->hostgroups, + &groupids.values[i]))) { - group = (zbx_dc_hostgroup_t *)config->hostgroups_name.values[index]; - dc_get_nested_hostgroupids(group->groupid, nested_groupids); + continue; } + + zbx_hashset_iter_reset(&group->hostids, &iter); + + while (NULL != (phostid = (zbx_uint64_t *)zbx_hashset_iter_next(&iter))) + zbx_vector_uint64_append(hostids, *phostid); } UNLOCK_CACHE; - zbx_vector_uint64_sort(nested_groupids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); - zbx_vector_uint64_uniq(nested_groupids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_destroy(&groupids); + + zbx_vector_uint64_sort(hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); } /****************************************************************************** @@ -13792,96 +13972,6 @@ char *zbx_dc_expand_user_macros_in_func_params(const char *params, zbx_uint64_t return resolved_params; } -char *dc_expand_user_macros_in_calcitem(const char *formula, zbx_uint64_t hostid) -{ - char *exp, *tmp,*expanded, error[128]; - const char *e; - size_t exp_alloc = 128, exp_offset = 0, tmp_alloc = 128, tmp_offset = 0, f_pos, par_l, par_r; - ZBX_DC_HOST *dc_host; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() formula:%s", __func__, formula); - - exp = (char *)zbx_malloc(NULL, exp_alloc); - tmp = (char *)zbx_malloc(NULL, tmp_alloc); - - for (e = formula; SUCCEED == zbx_function_find(e, &f_pos, &par_l, &par_r, error, sizeof(error)); e += par_r + 1) - { - size_t param_pos, param_len, sep_pos; - int quoted; - char *hostkey, *host = NULL, *key = NULL; - zbx_uint64_t func_hostid = 0; - - /* substitute user macros in the part of the string preceding function parameters */ - - zbx_strncpy_alloc(&tmp, &tmp_alloc, &tmp_offset, e, par_l + 1); - expanded = dc_expand_user_macros_in_expression(tmp, &hostid, 1); - zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, expanded); - zbx_free(expanded); - tmp_offset = 0; - - /* substitute user macros in function parameters */ - - zbx_function_param_parse(e + par_l + 1, ¶m_pos, ¶m_len, &sep_pos); - - /* convert relative offset to absolute */ - param_pos += par_l + 1; - sep_pos += par_l + 1; - - zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, e + par_l + 1, sep_pos - par_l); - - /* calculated function has only fist parameter - host:key */ - if (sep_pos == par_r) - continue; - - /* extract host:key parameter to find the function hostid */ - hostkey = zbx_function_param_unquote_dyn(e + param_pos, param_len, "ed); - if (SUCCEED == parse_host_key(hostkey, &host, &key)) - { - if (NULL != host) - { - if (NULL != (dc_host = DCfind_host(host))) - func_hostid = dc_host->hostid; - } - else - func_hostid = hostid; - } - zbx_free(host); - zbx_free(key); - zbx_free(hostkey); - - if (0 == func_hostid) - { - /* couldn't obtain target host, copy the rest of the function as it is */ - zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, e + sep_pos + 1, par_r - sep_pos); - continue; - } - - /* extract remaining parameters and expand user macros */ - zbx_strncpy_alloc(&tmp, &tmp_alloc, &tmp_offset, e + sep_pos + 1, par_r - sep_pos - 1); - expanded = dc_expand_user_macros_in_func_params(tmp, func_hostid); - zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, expanded); - zbx_free(expanded); - tmp_offset = 0; - - zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, ")"); - } - - if (par_l <= par_r) - { - /* substitute user macros in the remaining part */ - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, e); - expanded = dc_expand_user_macros_in_expression(tmp, &hostid, 1); - zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, expanded); - zbx_free(expanded); - } - - zbx_free(tmp); - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s() formula:%s", __func__, exp); - - return exp; -} - /********************************************************************************* * * * Function: zbx_get_host_interfaces_availability * @@ -13932,6 +14022,35 @@ void zbx_get_host_interfaces_availability(zbx_uint64_t hostid, zbx_agent_availab } +/********************************************************************************* + * * + * Function: zbx_dc_eval_expand_user_macros * + * * + * Purpose: resolve user macros in parsed expression * + * * + * Parameters: ctx - [IN] the expression evaluation context * + * * + ********************************************************************************/ +void zbx_dc_eval_expand_user_macros(zbx_eval_context_t *ctx) +{ + zbx_vector_uint64_t hostids, functionids; + + zbx_vector_uint64_create(&hostids); + zbx_vector_uint64_create(&functionids); + + zbx_eval_get_functionids(ctx, &functionids); + + RDLOCK_CACHE; + + dc_get_hostids_by_functionids(functionids.values, functionids.values_num, &hostids); + (void)zbx_eval_expand_user_macros(ctx, hostids.values, hostids.values_num, dc_expand_user_macros_len, NULL); + + UNLOCK_CACHE; + + zbx_vector_uint64_destroy(&functionids); + zbx_vector_uint64_destroy(&hostids); +} + #ifdef HAVE_TESTS # include "../../../tests/libs/zbxdbcache/dc_item_poller_type_update_test.c" # include "../../../tests/libs/zbxdbcache/dc_function_calculate_nextcheck_test.c" diff --git a/src/libs/zbxdbcache/dbconfig.h b/src/libs/zbxdbcache/dbconfig.h index 1ca6fdaffe7..54f0bc13bcf 100644 --- a/src/libs/zbxdbcache/dbconfig.h +++ b/src/libs/zbxdbcache/dbconfig.h @@ -34,7 +34,11 @@ typedef struct const char *correlation_tag; const char *opdata; const char *event_name; + const unsigned char *expression_bin; + const unsigned char *recovery_expression_bin; int lastchange; + int revision; + int timer_revision; unsigned char topoindex; unsigned char priority; unsigned char type; @@ -45,11 +49,18 @@ typedef struct unsigned char functional; /* see TRIGGER_FUNCTIONAL_* defines */ unsigned char recovery_mode; /* see TRIGGER_RECOVERY_MODE_* defines */ unsigned char correlation_mode; /* see ZBX_TRIGGER_CORRELATION_* defines */ + unsigned char timer; zbx_vector_ptr_t tags; } ZBX_DC_TRIGGER; +/* specifies if trigger expression/recovery expression has timer functions */ +/* (date, time, now, dayofweek or dayofmonth) */ +#define ZBX_TRIGGER_TIMER_DEFAULT 0x00 +#define ZBX_TRIGGER_TIMER_EXPRESSION 0x01 +#define ZBX_TRIGGER_TIMER_RECOVERY_EXPRESSION 0x02 + typedef struct zbx_dc_trigger_deplist { zbx_uint64_t triggerid; @@ -229,8 +240,9 @@ ZBX_DC_JMXITEM; typedef struct { - zbx_uint64_t itemid; - const char *params; + zbx_uint64_t itemid; + const char *params; + const unsigned char *formula_bin; } ZBX_DC_CALCITEM; @@ -912,13 +924,14 @@ char *dc_expand_user_macros_in_expression(const char *text, zbx_uint64_t *hostid char *dc_expand_user_macros_in_func_params(const char *params, zbx_uint64_t hostid); char *dc_expand_user_macros_in_calcitem(const char *formula, zbx_uint64_t hostid); -/****************************************************************************** - * * - * dc_expand_user_macros - has no autoquoting * - * for triggers and calculated items use * - * dc_expand_user_macros_in_expression - which autoquotes macros that are * - * not already quoted and cannot be casted to a double * - * * - ******************************************************************************/ char *dc_expand_user_macros(const char *text, zbx_uint64_t *hostids, int hostids_num); +int dc_expand_user_macros_len(const char *text, size_t text_len, zbx_uint64_t *hostids, int hostids_num, + char **value, char **error); + +#define ZBX_TRIGGER_TIMER_NONE 0x0000 +#define ZBX_TRIGGER_TIMER_TRIGGER 0x0001 +#define ZBX_TRIGGER_TIMER_FUNCTION_TIME 0x0002 +#define ZBX_TRIGGER_TIMER_FUNCTION_TREND 0x0004 +#define ZBX_TRIGGER_TIMER_FUNCTION (ZBX_TRIGGER_TIMER_FUNCTION_TIME | ZBX_TRIGGER_TIMER_FUNCTION_TREND) + #endif diff --git a/src/libs/zbxdbcache/dbconfig_maintenance.c b/src/libs/zbxdbcache/dbconfig_maintenance.c index fde177b3e2c..43dccd68331 100644 --- a/src/libs/zbxdbcache/dbconfig_maintenance.c +++ b/src/libs/zbxdbcache/dbconfig_maintenance.c @@ -1532,8 +1532,15 @@ int zbx_dc_get_event_maintenances(zbx_vector_ptr_t *event_queries, const zbx_vec { continue; } - get_functionids(&query->functionids, trigger->expression); - get_functionids(&query->functionids, trigger->recovery_expression); + + zbx_get_serialized_expression_functionids(trigger->expression, trigger->expression_bin, + &query->functionids); + + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == trigger->recovery_mode) + { + zbx_get_serialized_expression_functionids(trigger->recovery_expression, + trigger->recovery_expression_bin, &query->functionids); + } } for (j = 0; j < query->functionids.values_num; j++) diff --git a/src/libs/zbxdbcache/dbsync.c b/src/libs/zbxdbcache/dbsync.c index bf0ae011e73..04738e5fcb2 100644 --- a/src/libs/zbxdbcache/dbsync.c +++ b/src/libs/zbxdbcache/dbsync.c @@ -20,8 +20,9 @@ #include "common.h" #include "log.h" #include "dbcache.h" -#include "zbxserver.h" -#include "mutexs.h" +#include "zbxserialize.h" +#include "base64.h" +#include "zbxeval.h" #define ZBX_DBCONFIG_IMPL #include "dbconfig.h" @@ -418,6 +419,82 @@ int zbx_dbsync_next(zbx_dbsync_t *sync, zbx_uint64_t *rowid, char ***row, unsign /****************************************************************************** * * + * Function: encode_expression * + * * + * Purpose: encode serialized expression to be returned as db field * + * * + * Parameter: sync - [OUT] the changeset * + * * + * Return value: SUCCEED - the changeset was successfully calculated * + * FAIL - otherwise * + * * + ******************************************************************************/ +static char *encode_expression(const zbx_eval_context_t *ctx) +{ + unsigned char *data; + size_t len; + char *str = NULL; + + len = zbx_eval_serialize(ctx, NULL, &data); + str_base64_encode_dyn((const char *)data, &str, len); + zbx_free(data); + + return str; +} + + +/****************************************************************************** + * * + * Function: dbsync_compare_serialized_expression * + * * + * Purpose: compare serialized expression * + * * + * Parameter: col - [IN] the base64 encoded expression * + * data2 - [IN] the serialized expression in cache * + * * + * Return value: SUCCEED - the expressions are identical * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int dbsync_compare_serialized_expression(const char *col, const unsigned char *data2) +{ + zbx_uint32_t offset1, len1, offset2, len2; + unsigned char *data1; + int col_len, data1_len, ret = FAIL; + + if (NULL == data2) + { + if (NULL == col || '\0' == *col) + return SUCCEED; + return FAIL; + } + + if (NULL == col || '\0' == *col) + return FAIL; + + col_len = strlen(col); + data1 = zbx_malloc(NULL, col_len); + + str_base64_decode(col, (char *)data1, col_len, &data1_len); + + offset1 = zbx_deserialize_uint31_compact((const unsigned char *)data1, &len1); + offset2 = zbx_deserialize_uint31_compact((const unsigned char *)data2, &len2); + + if (offset1 != offset2 || len1 != len2) + goto out; + + if (0 != memcmp(data1 + offset1, data2 + offset2, len1)) + goto out; + + ret = SUCCEED; +out: + zbx_free(data1); + + return ret; +} + +/****************************************************************************** + * * * Function: zbx_dbsync_compare_config * * * * Purpose: compares config table with cached configuration data * @@ -1647,6 +1724,9 @@ static int dbsync_compare_item(const ZBX_DC_ITEM *item, const DB_ROW dbrow) if (FAIL == dbsync_compare_str(dbrow[11], calcitem->params)) return FAIL; + + if (FAIL == dbsync_compare_serialized_expression(dbrow[50], calcitem->formula_bin)) + return FAIL; } else if (NULL != calcitem) return FAIL; @@ -1780,7 +1860,7 @@ static char **dbsync_item_preproc_row(char **row) if (SUCCEED == dbsync_check_row_macros(row, 24)) flags |= ZBX_DBSYNC_ITEM_COLUMN_TRENDS; - if (ITEM_TYPE_CALCULATED == type && SUCCEED == dbsync_check_row_macros(row, 11)) + if (ITEM_TYPE_CALCULATED == type) flags |= ZBX_DBSYNC_ITEM_COLUMN_CALCITEM; if (0 == flags) @@ -1800,8 +1880,29 @@ static char **dbsync_item_preproc_row(char **row) if (0 != (flags & ZBX_DBSYNC_ITEM_COLUMN_TRENDS)) row[23] = dc_expand_user_macros(row[23], &hostid, 1); - if (0 != (flags & ZBX_DBSYNC_ITEM_COLUMN_CALCITEM)) - row[11] = dc_expand_user_macros_in_calcitem(row[11], hostid); + if (ITEM_TYPE_CALCULATED == type) + { + zbx_eval_context_t ctx; + char *error = NULL; + + if (FAIL == zbx_eval_parse_expression(&ctx, row[11], ZBX_EVAL_PARSE_CALC_EXPRESSSION, &error)) + { + zbx_eval_set_exception(&ctx, zbx_dsprintf(NULL, "Cannot parse formula: %s", error)); + zbx_free(error); + } + else + { + if (SUCCEED != zbx_eval_expand_user_macros(&ctx, &hostid, 1, dc_expand_user_macros_len, &error)) + { + zbx_eval_clear(&ctx); + zbx_eval_set_exception(&ctx, zbx_dsprintf(NULL, "Cannot evaluate formula: %s", error)); + zbx_free(error); + } + } + + row[50] = encode_expression(&ctx); + zbx_eval_clear(&ctx); + } return row; @@ -1838,7 +1939,7 @@ int zbx_dbsync_compare_items(zbx_dbsync_t *sync) "i.master_itemid,i.timeout,i.url,i.query_fields,i.posts,i.status_codes," "i.follow_redirects,i.post_type,i.http_proxy,i.headers,i.retrieve_mode," "i.request_method,i.output_format,i.ssl_cert_file,i.ssl_key_file,i.ssl_key_password," - "i.verify_peer,i.verify_host,i.allow_traps,i.templateid,id.parent_itemid" + "i.verify_peer,i.verify_host,i.allow_traps,i.templateid,id.parent_itemid,null" " from items i" " inner join hosts h on i.hostid=h.hostid" " left join item_discovery id on i.itemid=id.itemid" @@ -1850,7 +1951,7 @@ int zbx_dbsync_compare_items(zbx_dbsync_t *sync) return FAIL; } - dbsync_prepare(sync, 50, dbsync_item_preproc_row); + dbsync_prepare(sync, 51, dbsync_item_preproc_row); if (ZBX_DBSYNC_INIT == sync->mode) { @@ -2106,12 +2207,18 @@ static int dbsync_compare_trigger(const ZBX_DC_TRIGGER *trigger, const DB_ROW db if (FAIL == dbsync_compare_str(dbrow[15], trigger->event_name)) return FAIL; + if (FAIL == dbsync_compare_serialized_expression(dbrow[16], trigger->expression_bin)) + return FAIL; + + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == atoi(dbrow[10]) && + FAIL == dbsync_compare_serialized_expression(dbrow[17], trigger->recovery_expression_bin)) + { + return FAIL; + } + return SUCCEED; } -#define ZBX_DBSYNC_TRIGGER_COLUMN_EXPRESSION 0x01 -#define ZBX_DBSYNC_TRIGGER_COLUMN_RECOVERY_EXPRESSION 0x02 - /****************************************************************************** * * * Function: dbsync_trigger_preproc_row * @@ -2124,41 +2231,87 @@ static int dbsync_compare_trigger(const ZBX_DC_TRIGGER *trigger, const DB_ROW db * * * Comments: The row preprocessing can be used to expand user macros in * * some columns. * + * During preprocessing trigger expression/recovery expression are * + * parsed, serialized and stored as base64 strings into 16,17 * + * columns. * * * ******************************************************************************/ static char **dbsync_trigger_preproc_row(char **row) { zbx_vector_uint64_t hostids, functionids; - unsigned char flags = 0; - - /* return the original row if user macros are not used in target columns */ + zbx_eval_context_t ctx, ctx_r; + char *error = NULL; + unsigned char mode, timer = ZBX_TRIGGER_TIMER_DEFAULT; - if (SUCCEED == dbsync_check_row_macros(row, 2)) - flags |= ZBX_DBSYNC_TRIGGER_COLUMN_EXPRESSION; + zbx_vector_uint64_create(&hostids); + zbx_vector_uint64_create(&functionids); - if (SUCCEED == dbsync_check_row_macros(row, 11)) - flags |= ZBX_DBSYNC_TRIGGER_COLUMN_RECOVERY_EXPRESSION; + if (FAIL == zbx_eval_parse_expression(&ctx, row[2], ZBX_EVAL_TRIGGER_EXPRESSION, &error)) + { + zbx_eval_set_exception(&ctx, zbx_dsprintf(NULL, "cannot parse trigger expression: %s", error)); + zbx_free(error); + } + else + { + zbx_eval_get_functionids(&ctx, &functionids); - if (0 == flags) - return row; + if (SUCCEED == zbx_eval_check_timer_functions(&ctx)) + timer |= ZBX_TRIGGER_TIMER_EXPRESSION; + } - /* get associated host identifiers */ + ZBX_STR2UCHAR(mode, row[10]); - zbx_vector_uint64_create(&hostids); - zbx_vector_uint64_create(&functionids); + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == mode) + { + if (FAIL == zbx_eval_parse_expression(&ctx_r, row[11], ZBX_EVAL_TRIGGER_EXPRESSION, &error)) + { + zbx_eval_set_exception(&ctx_r, zbx_dsprintf(NULL, "cannot parse trigger recovery" + " expression: %s", error)); + zbx_free(error); + } + else + { + zbx_eval_get_functionids(&ctx_r, &functionids); - get_functionids(&functionids, row[2]); - get_functionids(&functionids, row[11]); + if (SUCCEED == zbx_eval_check_timer_functions(&ctx_r)) + timer |= ZBX_TRIGGER_TIMER_RECOVERY_EXPRESSION; + } + } dc_get_hostids_by_functionids(functionids.values, functionids.values_num, &hostids); - /* expand user macros */ + if (NULL != ctx.expression) + { + if (SUCCEED != zbx_eval_expand_user_macros(&ctx, hostids.values, hostids.values_num, + dc_expand_user_macros_len, &error)) + { + zbx_eval_clear(&ctx); + zbx_eval_set_exception(&ctx, zbx_dsprintf(NULL, "cannot evaluate expression: %s", error)); + zbx_free(error); + } + } + + row[16] = encode_expression(&ctx); + zbx_eval_clear(&ctx); - if (0 != (flags & ZBX_DBSYNC_TRIGGER_COLUMN_EXPRESSION)) - row[2] = dc_expand_user_macros_in_expression(row[2], hostids.values, hostids.values_num); + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == mode) + { + if (NULL != ctx_r.expression) + { + if (SUCCEED != zbx_eval_expand_user_macros(&ctx_r, hostids.values, hostids.values_num, + dc_expand_user_macros_len, &error)) + { + zbx_eval_clear(&ctx_r); + zbx_eval_set_exception(&ctx_r, zbx_dsprintf(NULL, "cannot evaluate recovery" + " expression: %s", error)); + zbx_free(error); + } + } + row[17] = encode_expression(&ctx_r); + zbx_eval_clear(&ctx_r); + } - if (0 != (flags & ZBX_DBSYNC_TRIGGER_COLUMN_RECOVERY_EXPRESSION)) - row[11] = dc_expand_user_macros_in_expression(row[11], hostids.values, hostids.values_num); + row[18] = zbx_dsprintf(NULL, "%d", timer); zbx_vector_uint64_destroy(&functionids); zbx_vector_uint64_destroy(&hostids); @@ -2177,6 +2330,11 @@ static char **dbsync_trigger_preproc_row(char **row) * Return value: SUCCEED - the changeset was successfully calculated * * FAIL - otherwise * * * + * Comment: The 16th and 17th fields (starting with 0) are placeholders for * + * serialized expression/recovery expression. * + * The 18th field is placeholder for trigger timer flag (set if * + * expression/recovery expression contains timer functions). * + * * ******************************************************************************/ int zbx_dbsync_compare_triggers(zbx_dbsync_t *sync) { @@ -2191,7 +2349,7 @@ int zbx_dbsync_compare_triggers(zbx_dbsync_t *sync) if (NULL == (result = DBselect( "select distinct t.triggerid,t.description,t.expression,t.error,t.priority,t.type,t.value," "t.state,t.lastchange,t.status,t.recovery_mode,t.recovery_expression," - "t.correlation_mode,t.correlation_tag,opdata,event_name" + "t.correlation_mode,t.correlation_tag,t.opdata,t.event_name,null,null,null" " from hosts h,items i,functions f,triggers t" " where h.hostid=i.hostid" " and i.itemid=f.itemid" @@ -2204,7 +2362,7 @@ int zbx_dbsync_compare_triggers(zbx_dbsync_t *sync) return FAIL; } - dbsync_prepare(sync, 16, dbsync_trigger_preproc_row); + dbsync_prepare(sync, 19, dbsync_trigger_preproc_row); if (ZBX_DBSYNC_INIT == sync->mode) { @@ -2382,6 +2540,13 @@ static int dbsync_compare_function(const ZBX_DC_FUNCTION *function, const DB_ROW static char **dbsync_function_preproc_row(char **row) { zbx_uint64_t hostid; + const char *row3; + + /* first parameter is /host/key placeholder $, don't cache it */ + if (NULL == (row3 = strchr(row[3], ','))) + row3 = ""; + else + row3++; /* return the original row if user macros are not used in target columns */ if (SUCCEED == dbsync_check_row_macros(row, 3)) @@ -2389,8 +2554,10 @@ static char **dbsync_function_preproc_row(char **row) /* get associated host identifier */ ZBX_STR2UINT64(hostid, row[5]); - row[3] = dc_expand_user_macros_in_func_params(row[3], hostid); + row[3] = dc_expand_user_macros_in_func_params(row3, hostid); } + else + row[3] = zbx_strdup(NULL, row3); return row; } @@ -3726,11 +3893,11 @@ int zbx_dbsync_compare_item_preprocs(zbx_dbsync_t *sync) " where pp.itemid=i.itemid" " and i.hostid=h.hostid" " and (h.proxy_hostid is null" - " or i.type in (%d,%d,%d,%d))" + " or i.type in (%d,%d,%d))" " and h.status in (%d,%d)" " and i.flags<>%d" " order by pp.itemid", - ITEM_TYPE_INTERNAL, ITEM_TYPE_AGGREGATE, ITEM_TYPE_CALCULATED, ITEM_TYPE_DEPENDENT, + ITEM_TYPE_INTERNAL, ITEM_TYPE_CALCULATED, ITEM_TYPE_DEPENDENT, HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED, ZBX_FLAG_DISCOVERY_PROTOTYPE))) { diff --git a/src/libs/zbxdbcache/dbsync.h b/src/libs/zbxdbcache/dbsync.h index 3d9207881f2..3c2ee0fa4c7 100644 --- a/src/libs/zbxdbcache/dbsync.h +++ b/src/libs/zbxdbcache/dbsync.h @@ -46,6 +46,8 @@ # define ZBX_HOST_TLS_OFFSET 0 #endif +#define ZBX_DBSYNC_TRIGGER_ERROR 0x80 + /****************************************************************************** * * * Function: zbx_dbsync_preproc_row_func_t * diff --git a/src/libs/zbxdbhigh/db.c b/src/libs/zbxdbhigh/db.c index 9b2b1fe7c17..7b4bdc7ab36 100644 --- a/src/libs/zbxdbhigh/db.c +++ b/src/libs/zbxdbhigh/db.c @@ -851,18 +851,65 @@ zbx_uint64_t DBget_maxid_num(const char *tablename, int num) /****************************************************************************** * * + * Function: DBextract_version * + * * + * Purpose: connects to DB and tries to detect DB version * + * * + ******************************************************************************/ +zbx_uint32_t DBextract_version(struct zbx_json *json) +{ + zbx_uint32_t ret; + + DBconnect(ZBX_DB_CONNECT_NORMAL); + ret = zbx_dbms_version_extract(json); + DBclose(); + + return ret; +} + +/****************************************************************************** + * * + * Function: DBflush_version_requirements * + * * + * Purpose: writes a json entry in DB with the result for the front-end * + * * + * Parameters: version - [IN] entry of DB versions * + * * + ******************************************************************************/ +void DBflush_version_requirements(const char *version) +{ + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + DBconnect(ZBX_DB_CONNECT_NORMAL); + + if (ZBX_DB_OK > DBexecute("update config set dbversion_status='%s'", version)) + zabbix_log(LOG_LEVEL_CRIT, "Failed to set dbversion_status"); + + DBclose(); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); +} + +/****************************************************************************** + * * * Function: DBcheck_capabilities * * * * Purpose: checks DBMS for optional features and exit if is not suitable * * * + * Parameters: db_version - [IN] version of DB * + * * + * Return value: SUCCEED - if optional features were checked successfully * + * FAIL - otherwise * + * * ******************************************************************************/ -void DBcheck_capabilities(void) +int DBcheck_capabilities(zbx_uint32_t db_version) { + int ret = SUCCEED; #ifdef HAVE_POSTGRESQL #define MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB 100002 #define MIN_TIMESCALEDB_VERSION 10500 - int postgresql_version, timescaledb_version; + int timescaledb_version; DB_RESULT result; DB_ROW row; @@ -880,22 +927,20 @@ void DBcheck_capabilities(void) if (0 != zbx_strcmp_null(row[0], ZBX_CONFIG_DB_EXTENSION_TIMESCALE)) goto clean; + ret = FAIL; /* In case of major upgrade, db_extension may be missing */ + /* Timescale compression feature is available in PostgreSQL 10.2 and TimescaleDB 1.5.0 */ - if (MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB > (postgresql_version = zbx_dbms_get_version())) + if (MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB > db_version) { - zabbix_log(LOG_LEVEL_CRIT, "PostgreSQL version %d is not supported with TimescaleDB, minimum is %d", - postgresql_version, MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB); - DBfree_result(result); - DBclose(); - exit(EXIT_FAILURE); + zabbix_log(LOG_LEVEL_CRIT, "PostgreSQL version %lu is not supported with TimescaleDB, minimum is %d", + (unsigned long)db_version, MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB); + goto clean; } if (0 == (timescaledb_version = zbx_tsdb_get_version())) { zabbix_log(LOG_LEVEL_CRIT, "Cannot determine TimescaleDB version"); - DBfree_result(result); - DBclose(); - exit(EXIT_FAILURE); + goto clean; } zabbix_log(LOG_LEVEL_INFORMATION, "TimescaleDB version: %d", timescaledb_version); @@ -904,15 +949,18 @@ void DBcheck_capabilities(void) { zabbix_log(LOG_LEVEL_CRIT, "TimescaleDB version %d is not supported, minimum is %d", timescaledb_version, MIN_TIMESCALEDB_VERSION); - DBfree_result(result); - DBclose(); - exit(EXIT_FAILURE); + goto clean; } + + ret = SUCCEED; clean: DBfree_result(result); out: DBclose(); +#else + ZBX_UNUSED(db_version); #endif + return ret; } #define MAX_EXPRESSIONS 950 diff --git a/src/libs/zbxdbhigh/event.c b/src/libs/zbxdbhigh/event.c index 5f93ca4a8f0..216c7c8ddb7 100644 --- a/src/libs/zbxdbhigh/event.c +++ b/src/libs/zbxdbhigh/event.c @@ -198,6 +198,7 @@ void zbx_db_get_events_by_eventids(zbx_vector_uint64_t *eventids, zbx_vector_ptr event->trigger.opdata = zbx_strdup(NULL, row[9]); event->trigger.event_name = ('\0' != *row[10] ? zbx_strdup(NULL, row[10]) : NULL); + event->trigger.cache = NULL; } } } @@ -212,29 +213,9 @@ void zbx_db_get_events_by_eventids(zbx_vector_uint64_t *eventids, zbx_vector_ptr /****************************************************************************** * * - * Function: zbx_db_trigger_clean * - * * - * Purpose: frees resources allocated to store trigger data * - * * - * Parameters: trigger - * - * * - ******************************************************************************/ -void zbx_db_trigger_clean(DB_TRIGGER *trigger) -{ - zbx_free(trigger->description); - zbx_free(trigger->expression); - zbx_free(trigger->recovery_expression); - zbx_free(trigger->comments); - zbx_free(trigger->url); - zbx_free(trigger->opdata); - zbx_free(trigger->event_name); -} - -/****************************************************************************** - * * * Function: zbx_db_free_event * * * - * Purpose: deallocate memory allocated in zbx_db_get_events_by_eventids() * + * Purpose: free the event with it's resources * * * * Parameters: event - [IN] event data * * * diff --git a/src/libs/zbxdbhigh/host.c b/src/libs/zbxdbhigh/host.c index a3af4b1925d..d6fffadc292 100644 --- a/src/libs/zbxdbhigh/host.c +++ b/src/libs/zbxdbhigh/host.c @@ -832,9 +832,9 @@ static int validate_host(zbx_uint64_t hostid, zbx_vector_uint64_t *templateids, zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select distinct type" " from items" - " where type not in (%d,%d,%d,%d,%d,%d,%d,%d)" + " where type not in (%d,%d,%d,%d,%d,%d,%d)" " and", - ITEM_TYPE_TRAPPER, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE, + ITEM_TYPE_TRAPPER, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_HTTPTEST, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_CALCULATED, ITEM_TYPE_DEPENDENT); DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "hostid", templateids->values, templateids->values_num); @@ -1761,21 +1761,14 @@ static int DBcopy_trigger_to_host(zbx_uint64_t *new_triggerid, zbx_uint64_t *cur const char *recovery_expression, unsigned char recovery_mode, unsigned char status, unsigned char type, unsigned char priority, const char *comments, const char *url, unsigned char flags, unsigned char correlation_mode, const char *correlation_tag, unsigned char manual_close, - const char *opdata, unsigned char discover, const char *event_name) + const char *opdata, unsigned char discover, const char *event_name, char **error) { DB_RESULT result; DB_ROW row; char *sql = NULL; size_t sql_alloc = 256, sql_offset = 0; zbx_uint64_t itemid, h_triggerid, functionid; - char *old_expression = NULL, - *new_expression = NULL, - *expression_esc = NULL, - *new_recovery_expression = NULL, - *recovery_expression_esc = NULL, - search[MAX_ID_LEN + 3], - replace[MAX_ID_LEN + 3], - *description_esc = NULL, + char *description_esc = NULL, *comments_esc = NULL, *url_esc = NULL, *function_esc = NULL, @@ -1838,12 +1831,10 @@ static int DBcopy_trigger_to_host(zbx_uint64_t *new_triggerid, zbx_uint64_t *cur /* create trigger if no updated triggers */ if (SUCCEED != res) { - res = SUCCEED; + zbx_eval_context_t ctx, ctx_r; *new_triggerid = DBget_maxid("triggers"); *cur_triggerid = 0; - new_expression = zbx_strdup(NULL, expression); - new_recovery_expression = zbx_strdup(NULL, recovery_expression); comments_esc = DBdyn_escape_string(comments); url_esc = DBdyn_escape_string(url); @@ -1865,6 +1856,19 @@ static int DBcopy_trigger_to_host(zbx_uint64_t *new_triggerid, zbx_uint64_t *cur zbx_free(url_esc); zbx_free(comments_esc); + if (SUCCEED != (res = zbx_eval_parse_expression(&ctx, expression, + ZBX_EVAL_PARSE_TRIGGER_EXPRESSSION | ZBX_EVAL_COMPOSE_FUNCTIONID, error))) + goto out; + + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == recovery_mode && + (SUCCEED != (res = zbx_eval_parse_expression(&ctx_r, recovery_expression, + ZBX_EVAL_PARSE_TRIGGER_EXPRESSSION | ZBX_EVAL_COMPOSE_FUNCTIONID, + error)))) + { + zbx_eval_clear(&ctx); + goto out; + } + /* Loop: functions */ result = DBselect( "select hi.itemid,tf.functionid,tf.name,tf.parameter,ti.key_" @@ -1880,13 +1884,12 @@ static int DBcopy_trigger_to_host(zbx_uint64_t *new_triggerid, zbx_uint64_t *cur { if (SUCCEED != DBis_null(row[0])) { + zbx_uint64_t old_functionid; + ZBX_STR2UINT64(itemid, row[0]); functionid = DBget_maxid("functions"); - zbx_snprintf(search, sizeof(search), "{%s}", row[1]); - zbx_snprintf(replace, sizeof(replace), "{" ZBX_FS_UI64 "}", functionid); - function_esc = DBdyn_escape_string(row[2]); parameter_esc = DBdyn_escape_string(row[3]); @@ -1898,52 +1901,62 @@ static int DBcopy_trigger_to_host(zbx_uint64_t *new_triggerid, zbx_uint64_t *cur functionid, itemid, *new_triggerid, function_esc, parameter_esc); - old_expression = new_expression; - new_expression = string_replace(new_expression, search, replace); - zbx_free(old_expression); - - old_expression = new_recovery_expression; - new_recovery_expression = string_replace(new_recovery_expression, search, replace); - zbx_free(old_expression); + ZBX_DBROW2UINT64(old_functionid, row[1]); + zbx_eval_replace_functionid(&ctx, old_functionid, functionid); + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == recovery_mode) + zbx_eval_replace_functionid(&ctx_r, old_functionid, functionid); zbx_free(parameter_esc); zbx_free(function_esc); } else { - zabbix_log(LOG_LEVEL_DEBUG, "Missing similar key '%s'" - " for host [" ZBX_FS_UI64 "]", + *error = zbx_dsprintf(*error, "Missing similar key '%s' for host [" ZBX_FS_UI64 "]", row[4], hostid); res = FAIL; } } DBfree_result(result); + if (SUCCEED == (res = zbx_eval_validate_replaced_functionids(&ctx, error)) && + TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == recovery_mode) + { + res = zbx_eval_validate_replaced_functionids(&ctx_r, error); + } + if (SUCCEED == res) { - expression_esc = DBdyn_escape_field("triggers", "expression", new_expression); - recovery_expression_esc = DBdyn_escape_field("triggers", "recovery_expression", - new_recovery_expression); + char *new_expression = NULL, *esc; - zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, - "update triggers" - " set expression='%s',recovery_expression='%s'" - " where triggerid=" ZBX_FS_UI64 ";\n", - expression_esc, recovery_expression_esc, *new_triggerid); + zbx_eval_compose_expression(&ctx, &new_expression); + esc = DBdyn_escape_field("triggers", "expression", new_expression); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set expression='%s'", esc); + zbx_free(esc); + zbx_free(new_expression); - zbx_free(recovery_expression_esc); - zbx_free(expression_esc); + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == recovery_mode) + { + zbx_eval_compose_expression(&ctx_r, &new_expression); + esc = DBdyn_escape_field("triggers", "recovery_expression", new_expression); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, ",recovery_expression='%s'", esc); + zbx_free(esc); + zbx_free(new_expression); + } + + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where triggerid=" ZBX_FS_UI64 ";\n", + *new_triggerid); } - zbx_free(new_recovery_expression); - zbx_free(new_expression); + zbx_eval_clear(&ctx); + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == recovery_mode) + zbx_eval_clear(&ctx_r); } DBend_multiple_update(&sql, &sql_alloc, &sql_offset); if (sql_offset > 16) /* In ORACLE always present begin..end; */ DBexecute("%s", sql); - +out: zbx_free(sql); zbx_free(correlation_tag_esc); zbx_free(event_name_esc); @@ -4223,6 +4236,7 @@ static void DBcopy_template_host_prototypes(zbx_uint64_t hostid, zbx_vector_uint * * * Parameters: hostid - [IN] host identificator from database * * templateids - [IN] array of template IDs * + * error - [IN] the error message * * * * Return value: upon successful completion return SUCCEED * * * @@ -4231,7 +4245,7 @@ static void DBcopy_template_host_prototypes(zbx_uint64_t hostid, zbx_vector_uint * Comments: !!! Don't forget to sync the code with PHP !!! * * * ******************************************************************************/ -static int DBcopy_template_triggers(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids) +static int DBcopy_template_triggers(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids, char **error) { char *sql = NULL; size_t sql_alloc = 512, sql_offset = 0; @@ -4283,7 +4297,8 @@ static int DBcopy_template_triggers(zbx_uint64_t hostid, const zbx_vector_uint64 (unsigned char)atoi(row[13]), /* manual_close */ row[14], /* opdata */ (unsigned char)atoi(row[15]), /* discover */ - row[16]); /* event_name */ + row[16], /* event_name */ + error); if (0 != new_triggerid) /* new trigger added */ zbx_vector_uint64_append(&new_triggerids, new_triggerid); @@ -4301,6 +4316,9 @@ static int DBcopy_template_triggers(zbx_uint64_t hostid, const zbx_vector_uint64 zbx_vector_uint64_destroy(&cur_triggerids); zbx_vector_uint64_destroy(&new_triggerids); + if (FAIL == res && NULL == *error) + *error = zbx_strdup(NULL, "unknown error while linking triggers"); + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(res)); return res; @@ -5580,7 +5598,7 @@ int DBcopy_template_elements(zbx_uint64_t hostid, zbx_vector_uint64_t *lnk_templ DBcopy_template_items(hostid, lnk_templateids); DBcopy_template_host_prototypes(hostid, lnk_templateids); - if (SUCCEED == (res = DBcopy_template_triggers(hostid, lnk_templateids))) + if (SUCCEED == (res = DBcopy_template_triggers(hostid, lnk_templateids, error))) { DBcopy_template_graphs(hostid, lnk_templateids); DBcopy_template_httptests(hostid, lnk_templateids); diff --git a/src/libs/zbxdbhigh/proxy.c b/src/libs/zbxdbhigh/proxy.c index c3e4391c1a3..0bfda980c45 100644 --- a/src/libs/zbxdbhigh/proxy.c +++ b/src/libs/zbxdbhigh/proxy.c @@ -1421,7 +1421,7 @@ static int process_proxyconfig_table(const ZBX_TABLE *table, struct zbx_json_par if (NULL == table_items) { - table_items = DBget_table("items"); + table_items = DBget_table("item_rtdata"); /* do not update existing lastlogsize and mtime fields */ zbx_vector_ptr_create(&skip_fields); @@ -3510,7 +3510,7 @@ static int proxy_item_validator(DC_ITEM *item, zbx_socket_t *sock, void *args, c return FAIL; /* don't process aggregate/calculated items coming from proxy */ - if (ITEM_TYPE_AGGREGATE == item->type || ITEM_TYPE_CALCULATED == item->type) + if (ITEM_TYPE_CALCULATED == item->type) return FAIL; return SUCCEED; diff --git a/src/libs/zbxdbhigh/trigger.c b/src/libs/zbxdbhigh/trigger.c index a881e01b69c..685c0ee3467 100644 --- a/src/libs/zbxdbhigh/trigger.c +++ b/src/libs/zbxdbhigh/trigger.c @@ -32,7 +32,6 @@ #define ZBX_FLAGS_TRIGGER_CREATE_EVENT \ (ZBX_FLAGS_TRIGGER_CREATE_TRIGGER_EVENT | ZBX_FLAGS_TRIGGER_CREATE_INTERNAL_EVENT) - /****************************************************************************** * * * Function: zbx_process_trigger * @@ -119,7 +118,7 @@ static int zbx_process_trigger(struct _DC_TRIGGER *trigger, zbx_vector_ptr_t *di { zbx_add_event(EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER, trigger->triggerid, &trigger->timespec, new_value, trigger->description, - trigger->expression_orig, trigger->recovery_expression_orig, + trigger->expression, trigger->recovery_expression, trigger->priority, trigger->type, &trigger->tags, trigger->correlation_mode, trigger->correlation_tag, trigger->value, trigger->opdata, trigger->event_name, NULL); @@ -128,8 +127,8 @@ static int zbx_process_trigger(struct _DC_TRIGGER *trigger, zbx_vector_ptr_t *di if (0 != (event_flags & ZBX_FLAGS_TRIGGER_CREATE_INTERNAL_EVENT)) { zbx_add_event(EVENT_SOURCE_INTERNAL, EVENT_OBJECT_TRIGGER, trigger->triggerid, - &trigger->timespec, new_state, NULL, trigger->expression_orig, - trigger->recovery_expression_orig, 0, 0, &trigger->tags, 0, NULL, 0, NULL, NULL, + &trigger->timespec, new_state, NULL, trigger->expression, + trigger->recovery_expression, 0, 0, &trigger->tags, 0, NULL, 0, NULL, NULL, new_error); } @@ -304,3 +303,516 @@ void zbx_append_trigger_diff(zbx_vector_ptr_t *trigger_diff, zbx_uint64_t trigge zbx_vector_ptr_append(trigger_diff, diff); } + + +/* temporary cache of trigger related data */ +typedef struct +{ + zbx_uint32_t init; + zbx_uint32_t done; + zbx_eval_context_t eval_ctx; + zbx_eval_context_t eval_ctx_r; + zbx_vector_uint64_t hostids; +} +zbx_trigger_cache_t; + +/* related trigger data caching states */ +typedef enum +{ + ZBX_TRIGGER_CACHE_EVAL_CTX, + ZBX_TRIGGER_CACHE_EVAL_CTX_R, + ZBX_TRIGGER_CACHE_EVAL_CTX_MACROS, + ZBX_TRIGGER_CACHE_HOSTIDS, +} +zbx_trigger_cache_state_t; + +/****************************************************************************** + * * + * Function: db_trigger_get_cache * + * * + * Purpose: get trigger cache with the requested data cached * + * * + * Parameters: trigger - [IN] the trigger * + * state - [IN] the required cache state * + * * + ******************************************************************************/ +static zbx_trigger_cache_t *db_trigger_get_cache(const DB_TRIGGER *trigger, zbx_trigger_cache_state_t state) +{ + zbx_trigger_cache_t *cache; + char *error = NULL; + zbx_uint32_t flag = 1 << state; + zbx_vector_uint64_t functionids; + + if (NULL == trigger->cache) + { + cache = (zbx_trigger_cache_t *)zbx_malloc(NULL, sizeof(zbx_trigger_cache_t)); + cache->init = cache->done = 0; + ((DB_TRIGGER *)trigger)->cache = cache; + } + else + cache = (zbx_trigger_cache_t *)trigger->cache; + + if (0 != (cache->init & flag)) + return 0 != (cache->done & flag) ? cache : NULL; + + cache->init |= flag; + + switch (state) + { + case ZBX_TRIGGER_CACHE_EVAL_CTX: + if ('\0' == *trigger->expression) + return NULL; + + if (FAIL == zbx_eval_parse_expression(&cache->eval_ctx, trigger->expression, + ZBX_EVAL_TRIGGER_EXPRESSION, &error)) + { + zbx_free(error); + return NULL; + } + break; + case ZBX_TRIGGER_CACHE_EVAL_CTX_R: + if ('\0' == *trigger->recovery_expression) + return NULL; + + if (FAIL == zbx_eval_parse_expression(&cache->eval_ctx_r, trigger->recovery_expression, + ZBX_EVAL_TRIGGER_EXPRESSION, &error)) + { + zbx_free(error); + return NULL; + } + break; + case ZBX_TRIGGER_CACHE_EVAL_CTX_MACROS: + if (NULL == db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX)) + return NULL; + zbx_dc_eval_expand_user_macros(&cache->eval_ctx); + break; + case ZBX_TRIGGER_CACHE_HOSTIDS: + zbx_vector_uint64_create(&cache->hostids); + zbx_vector_uint64_create(&functionids); + zbx_db_trigger_get_all_functionids(trigger, &functionids); + DCget_hostids_by_functionids(&functionids, &cache->hostids); + zbx_vector_uint64_destroy(&functionids); + break; + default: + return NULL; + } + + cache->done |= flag; + + return cache; +} + +/****************************************************************************** + * * + * Function: trigger_cache_free * + * * + * Purpose: free trigger cache * + * * + * Parameters: cache - [IN] the trigger cache * + * * + ******************************************************************************/ +static void trigger_cache_free(zbx_trigger_cache_t *cache) +{ + if (0 != (cache->done & (1 << ZBX_TRIGGER_CACHE_EVAL_CTX))) + zbx_eval_clear(&cache->eval_ctx); + + if (0 != (cache->done & (1 << ZBX_TRIGGER_CACHE_EVAL_CTX_R))) + zbx_eval_clear(&cache->eval_ctx_r); + + if (0 != (cache->done & (1 << ZBX_TRIGGER_CACHE_HOSTIDS))) + zbx_vector_uint64_destroy(&cache->hostids); + + zbx_free(cache); +} + +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_all_functionids * + * * + * Purpose: get functionids from trigger expression and recovery expression * + * * + * Parameters: trigger - [IN] the trigger * + * functionids - [OUT] the extracted functionids * + * * + * Comments: This function will cache parsed expressions in the trigger. * + * * + ******************************************************************************/ +void zbx_db_trigger_get_all_functionids(const DB_TRIGGER *trigger, zbx_vector_uint64_t *functionids) +{ + zbx_trigger_cache_t *cache; + + if (NULL != (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX))) + zbx_eval_get_functionids(&cache->eval_ctx, functionids); + + if (NULL != (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX_R))) + zbx_eval_get_functionids(&cache->eval_ctx_r, functionids); +} + +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_functionids * + * * + * Purpose: get functionids from trigger expression * + * * + * Parameters: trigger - [IN] the trigger * + * functionids - [OUT] the extracted functionids * + * * + * Comments: This function will cache parsed expressions in the trigger. * + * * + ******************************************************************************/ +void zbx_db_trigger_get_functionids(const DB_TRIGGER *trigger, zbx_vector_uint64_t *functionids) +{ + zbx_trigger_cache_t *cache; + + if (NULL != (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX))) + zbx_eval_get_functionids(&cache->eval_ctx, functionids); + + zbx_vector_uint64_sort(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); +} +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_constant * + * * + * Purpose: get trigger expression constant at the specified location * + * * + * Parameters: trigger - [IN] the trigger * + * index - [IN] the constant index, starting with 1 * + * out - [IN] the constant value, if exists * + * * + * Return value: SUCCEED - the expression was parsed and constant extracted * + * (if the index was valid) * + * FAIL - the expression failed to parse * + * * + * Comments: This function will cache parsed expressions in the trigger. * + * * + ******************************************************************************/ +int zbx_db_trigger_get_constant(const DB_TRIGGER *trigger, int index, char **out) +{ + zbx_trigger_cache_t *cache; + + if (NULL == (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX_MACROS))) + return FAIL; + + zbx_eval_get_constant(&cache->eval_ctx, index, out); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_itemid * + * * + * Purpose: get the Nth function item from trigger expression * + * * + * Parameters: trigger - [IN] the trigger * + * index - [IN] the function index * + * itemid - [IN] the function itemid * + * * + * Comments: SUCCEED - the itemid was extracted successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_db_trigger_get_itemid(const DB_TRIGGER *trigger, int index, zbx_uint64_t *itemid) +{ + int i, ret = FAIL; + zbx_trigger_cache_t *cache; + + if (NULL == (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX))) + return FAIL; + + for (i = 0; i < cache->eval_ctx.stack.values_num; i++) + { + zbx_eval_token_t *token = &cache->eval_ctx.stack.values[i]; + zbx_uint64_t functionid; + DC_FUNCTION function; + int errcode; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type || (int)token->opt + 1 != index) + continue; + + switch (token->value.type) + { + case ZBX_VARIANT_UI64: + functionid = token->value.data.ui64; + break; + case ZBX_VARIANT_NONE: + if (SUCCEED != is_uint64_n(cache->eval_ctx.expression + token->loc.l + 1, + token->loc.r - token->loc.l - 1, &functionid)) + { + return FAIL; + } + zbx_variant_set_ui64(&token->value, functionid); + break; + default: + return FAIL; + } + + DCconfig_get_functions_by_functionids(&function, &functionid, &errcode, 1); + + if (SUCCEED == errcode) + { + *itemid = function.itemid; + ret = SUCCEED; + } + + DCconfig_clean_functions(&function, &errcode, 1); + break; + } + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_itemids * + * * + * Purpose: get unique itemids of trigger functions in the order at they are * + * written in expression * + * * + * Parameters: trigger - [IN] the trigger * + * itemids - [IN] the function itemids * + * * + ******************************************************************************/ +void zbx_db_trigger_get_itemids(const DB_TRIGGER *trigger, zbx_vector_uint64_t *itemids) +{ + zbx_vector_uint64_t functionids, functionids_ordered; + zbx_trigger_cache_t *cache; + + if (NULL == (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX))) + return; + + zbx_vector_uint64_create(&functionids); + zbx_vector_uint64_create(&functionids_ordered); + + zbx_eval_get_functionids_ordered(&cache->eval_ctx, &functionids_ordered); + + if (0 != functionids_ordered.values_num) + { + DC_FUNCTION *functions; + int i, *errcodes, index; + + zbx_vector_uint64_append_array(&functionids, functionids_ordered.values, + functionids_ordered.values_num); + + zbx_vector_uint64_sort(&functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(&functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + + functions = (DC_FUNCTION *)zbx_malloc(NULL, sizeof(DC_FUNCTION) * functionids.values_num); + errcodes = (int *)zbx_malloc(NULL, sizeof(int) * functionids.values_num); + + DCconfig_get_functions_by_functionids(functions, functionids.values, errcodes, + functionids.values_num); + + for (i = 0; i < functionids_ordered.values_num; i++) + { + if (-1 == (index = zbx_vector_uint64_bsearch(&functionids, functionids_ordered.values[i], + ZBX_DEFAULT_UINT64_COMPARE_FUNC))) + { + THIS_SHOULD_NEVER_HAPPEN; + continue; + } + + if (SUCCEED != errcodes[index]) + continue; + + if (FAIL == zbx_vector_uint64_search(itemids, functions[index].itemid, + ZBX_DEFAULT_UINT64_COMPARE_FUNC)) + { + zbx_vector_uint64_append(itemids, functions[index].itemid); + } + } + + DCconfig_clean_functions(functions, errcodes, functionids.values_num); + zbx_free(functions); + zbx_free(errcodes); + } + + zbx_vector_uint64_destroy(&functionids_ordered); + zbx_vector_uint64_destroy(&functionids); +} + +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_all_hostids * + * * + * Purpose: get hostids from trigger expression and recovery expression * + * * + * Parameters: trigger - [IN] the trigger * + * hostids - [OUT] the extracted hostids * + * * + * Return value: SUCCEED - the hostids vector was returned (but can be empty * + * FAIL - otherwise * + * * + * Comments: This function will cache parsed expressions in the trigger. * + * * + ******************************************************************************/ +int zbx_db_trigger_get_all_hostids(const DB_TRIGGER *trigger, const zbx_vector_uint64_t **hostids) +{ + zbx_trigger_cache_t *cache; + + if (NULL == (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_HOSTIDS))) + return FAIL; + + *hostids = &cache->hostids; + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: zbx_db_trigger_clean * + * * + * Purpose: frees resources allocated to store trigger data * + * * + * Parameters: trigger - * + * * + ******************************************************************************/ +void zbx_db_trigger_clean(DB_TRIGGER *trigger) +{ + zbx_free(trigger->description); + zbx_free(trigger->expression); + zbx_free(trigger->recovery_expression); + zbx_free(trigger->comments); + zbx_free(trigger->url); + zbx_free(trigger->opdata); + zbx_free(trigger->event_name); + + if (NULL != trigger->cache) + trigger_cache_free((zbx_trigger_cache_t *)trigger->cache); +} + +/****************************************************************************** + * * + * Function: db_trigger_get_expression * + * * + * Purpose: get original trigger expression/recovery expression with expanded * + * functions * + * * + * Parameters: trigger - [IN] the trigger * + * expression - [OUT] the trigger expression * + * * + ******************************************************************************/ +static void db_trigger_get_expression(const zbx_eval_context_t *ctx, char **expression) +{ + int i; + zbx_eval_context_t local_ctx; + + zbx_eval_copy(&local_ctx, ctx, ctx->expression); + local_ctx.rules |= ZBX_EVAL_COMPOSE_MASK_ERROR; + + for (i = 0; i < local_ctx.stack.values_num; i++) + { + zbx_eval_token_t *token = &local_ctx.stack.values[i]; + zbx_uint64_t functionid; + DC_FUNCTION function; + DC_ITEM item; + int err_func, err_item; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) + { + /* reset cached token values to get the original expression */ + zbx_variant_clear(&token->value); + continue; + } + + switch (token->value.type) + { + case ZBX_VARIANT_UI64: + functionid = token->value.data.ui64; + break; + case ZBX_VARIANT_NONE: + if (SUCCEED != is_uint64_n(local_ctx.expression + token->loc.l + 1, + token->loc.r - token->loc.l - 1, &functionid)) + { + continue; + } + break; + default: + continue; + } + + DCconfig_get_functions_by_functionids(&function, &functionid, &err_func, 1); + + if (SUCCEED == err_func) + { + DCconfig_get_items_by_itemids(&item, &function.itemid, &err_item, 1); + + if (SUCCEED == err_item) + { + char *func = NULL; + size_t func_alloc = 0, func_offset = 0; + + zbx_snprintf_alloc(&func, &func_alloc, &func_offset, "%s(/%s/%s", + function.function, item.host.host, item.key_orig); + + if ('\0' != *function.parameter) + zbx_snprintf_alloc(&func, &func_alloc, &func_offset, ",%s", function.parameter); + + zbx_chrcpy_alloc(&func, &func_alloc, &func_offset,')'); + + zbx_variant_clear(&token->value); + zbx_variant_set_str(&token->value, func); + DCconfig_clean_items(&item, &err_item, 1); + } + else + { + zbx_variant_clear(&token->value); + zbx_variant_set_error(&token->value, zbx_dsprintf(NULL, "item id:" ZBX_FS_UI64 + " deleted", function.itemid)); + } + + DCconfig_clean_functions(&function, &err_func, 1); + } + else + { + zbx_variant_clear(&token->value); + zbx_variant_set_error(&token->value, zbx_dsprintf(NULL, "function id:" ZBX_FS_UI64 " deleted", + functionid)); + } + } + + zbx_eval_compose_expression(&local_ctx, expression); + + zbx_eval_clear(&local_ctx); +} + + +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_expression * + * * + * Purpose: get original trigger expression with expanded functions * + * * + * Parameters: trigger - [IN] the trigger * + * expression - [OUT] the trigger expression * + * * + ******************************************************************************/ +void zbx_db_trigger_get_expression(const DB_TRIGGER *trigger, char **expression) +{ + zbx_trigger_cache_t *cache; + + if (NULL == (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX))) + *expression = zbx_strdup(NULL, trigger->expression); + else + db_trigger_get_expression(&cache->eval_ctx, expression); +} + +/****************************************************************************** + * * + * Function: zbx_db_trigger_get_recovery_expression * + * * + * Purpose: get original trigger recovert expression with expanded functions * + * * + * Parameters: trigger - [IN] the trigger * + * expression - [OUT] the trigger expression * + * * + ******************************************************************************/ +void zbx_db_trigger_get_recovery_expression(const DB_TRIGGER *trigger, char **expression) +{ + zbx_trigger_cache_t *cache; + + if (NULL == (cache = db_trigger_get_cache(trigger, ZBX_TRIGGER_CACHE_EVAL_CTX_R))) + *expression = zbx_strdup(NULL, trigger->recovery_expression); + else + db_trigger_get_expression(&cache->eval_ctx_r, expression); +} diff --git a/src/libs/zbxdbupgrade/dbupgrade_5030.c b/src/libs/zbxdbupgrade/dbupgrade_5030.c index 6e51f68e727..47ceae7456a 100644 --- a/src/libs/zbxdbupgrade/dbupgrade_5030.c +++ b/src/libs/zbxdbupgrade/dbupgrade_5030.c @@ -21,12 +21,12 @@ #include "log.h" #include "db.h" #include "dbupgrade.h" -#include "zbxserver.h" -#include "zbxregexp.h" #include "dbupgrade_macros.h" +#include "zbxregexp.h" #include "zbxalgo.h" #include "zbxjson.h" #include "../zbxalgo/vectorimpl.h" +#include "sysinfo.h" /* * 5.4 development database patches @@ -3883,160 +3883,6 @@ static int DBpatch_5030123(void) static int DBpatch_5030124(void) { - DB_ROW row; - DB_RESULT result; - zbx_db_insert_t db_insert; - int ret; - - if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) - return SUCCEED; - - zbx_db_insert_prepare(&db_insert, "event_tag", "eventtagid", "eventid", "tag", "value", NULL); - - result = DBselect( - "select distinct e.eventid,it.tag,it.value from events e" - " join triggers t on e.objectid=t.triggerid" - " join functions f on t.triggerid=f.triggerid" - " join items i on i.itemid=f.itemid" - " join item_tag it on i.itemid=it.itemid" - " where e.source in (%d,%d) and e.object=%d and t.flags in (%d,%d) order by e.eventid", - EVENT_SOURCE_TRIGGERS, EVENT_SOURCE_INTERNAL, EVENT_OBJECT_TRIGGER, ZBX_FLAG_DISCOVERY_NORMAL, - ZBX_FLAG_DISCOVERY_CREATED); - - while (NULL != (row = DBfetch(result))) - { - DB_ROW rowN; - DB_RESULT resultN; - zbx_uint64_t eventid; - char *tag, *value, tmp[MAX_STRING_LEN]; - - ZBX_DBROW2UINT64(eventid, row[0]); - tag = DBdyn_escape_string(row[1]); - value = DBdyn_escape_string(row[2]); - zbx_snprintf(tmp, sizeof(tmp), - "select null from event_tag where eventid=" ZBX_FS_UI64 " and tag='%s' and value='%s'", - eventid, tag, value); - - resultN = DBselectN(tmp, 1); - - if (NULL == (rowN = DBfetch(resultN))) - zbx_db_insert_add_values(&db_insert, __UINT64_C(0), eventid, tag, value); - - DBfree_result(resultN); - zbx_free(tag); - zbx_free(value); - } - DBfree_result(result); - - zbx_db_insert_autoincrement(&db_insert, "eventtagid"); - ret = zbx_db_insert_execute(&db_insert); - zbx_db_insert_clean(&db_insert); - - return ret; -} - -static int DBpatch_5030125(void) -{ - DB_ROW row; - DB_RESULT result; - zbx_db_insert_t db_insert; - int ret; - - if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) - return SUCCEED; - - zbx_db_insert_prepare(&db_insert, "event_tag", "eventtagid", "eventid", "tag", "value", NULL); - - result = DBselect( - "select distinct e.eventid,it.tag,it.value from events e" - " join items i on i.itemid=e.objectid" - " join item_tag it on i.itemid=it.itemid" - " where e.source=%d and e.object=%d and i.flags in (%d,%d)", - EVENT_SOURCE_INTERNAL, EVENT_OBJECT_ITEM, ZBX_FLAG_DISCOVERY_NORMAL, - ZBX_FLAG_DISCOVERY_CREATED); - - while (NULL != (row = DBfetch(result))) - { - DB_ROW rowN; - DB_RESULT resultN; - zbx_uint64_t eventid; - char *tag, *value, tmp[MAX_STRING_LEN]; - - ZBX_DBROW2UINT64(eventid, row[0]); - tag = DBdyn_escape_string(row[1]); - value = DBdyn_escape_string(row[2]); - zbx_snprintf(tmp, sizeof(tmp), - "select null from event_tag where eventid=" ZBX_FS_UI64 " and tag='%s' and value='%s'", - eventid, tag, value); - - resultN = DBselectN(tmp, 1); - - if (NULL == (rowN = DBfetch(resultN))) - zbx_db_insert_add_values(&db_insert, __UINT64_C(0), eventid, tag, value); - - DBfree_result(resultN); - zbx_free(tag); - zbx_free(value); - } - DBfree_result(result); - - zbx_db_insert_autoincrement(&db_insert, "eventtagid"); - ret = zbx_db_insert_execute(&db_insert); - zbx_db_insert_clean(&db_insert); - - return ret; -} - -static int DBpatch_5030126(void) -{ - DB_ROW row; - DB_RESULT result; - zbx_db_insert_t db_insert; - int ret; - - if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) - return SUCCEED; - - zbx_db_insert_prepare(&db_insert, "problem_tag", "problemtagid", "eventid", "tag", "value", NULL); - - result = DBselect( - "select distinct e.eventid,e.tag,e.value from event_tag e" - " join problem p on e.eventid=p.eventid"); - - while (NULL != (row = DBfetch(result))) - { - DB_ROW rowN; - DB_RESULT resultN; - zbx_uint64_t eventid; - char *tag, *value, tmp[MAX_STRING_LEN]; - - ZBX_DBROW2UINT64(eventid, row[0]); - tag = DBdyn_escape_string(row[1]); - value = DBdyn_escape_string(row[2]); - zbx_snprintf(tmp, sizeof(tmp), - "select null from problem_tag where eventid=" ZBX_FS_UI64 " and tag='%s'" - " and value='%s'", eventid, tag, value); - - resultN = DBselectN(tmp, 1); - - if (NULL == (rowN = DBfetch(resultN))) - zbx_db_insert_add_values(&db_insert, __UINT64_C(0), eventid, tag, value); - - DBfree_result(resultN); - zbx_free(tag); - zbx_free(value); - } - DBfree_result(result); - - zbx_db_insert_autoincrement(&db_insert, "problemtagid"); - ret = zbx_db_insert_execute(&db_insert); - zbx_db_insert_clean(&db_insert); - - return ret; -} - -static int DBpatch_5030127(void) -{ #define CONDITION_TYPE_APPLICATION 15 if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) return SUCCEED; @@ -4051,7 +3897,7 @@ static int DBpatch_5030127(void) #undef CONDITION_TYPE_APPLICATION } -static int DBpatch_5030128(void) +static int DBpatch_5030125(void) { #define AUDIT_RESOURCE_APPLICATION 12 if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) @@ -4064,7 +3910,7 @@ static int DBpatch_5030128(void) #undef AUDIT_RESOURCE_APPLICATION } -static int DBpatch_5030129(void) +static int DBpatch_5030126(void) { if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) return SUCCEED; @@ -4253,7 +4099,7 @@ static int DBpatch_parse_applications_json(struct zbx_json_parse *jp, struct zbx return SUCCEED; } -static int DBpatch_5030130(void) +static int DBpatch_5030127(void) { DB_ROW row; DB_RESULT result; @@ -4314,7 +4160,7 @@ static int DBpatch_5030130(void) return ret; } -static int DBpatch_5030131(void) +static int DBpatch_5030128(void) { DB_ROW row; DB_RESULT result; @@ -4380,7 +4226,7 @@ out: return ret; } -static int DBpatch_5030132(void) +static int DBpatch_5030129(void) { if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) return SUCCEED; @@ -4392,57 +4238,57 @@ static int DBpatch_5030132(void) return SUCCEED; } -static int DBpatch_5030133(void) +static int DBpatch_5030130(void) { return DBdrop_foreign_key("httptest", 1); } -static int DBpatch_5030134(void) +static int DBpatch_5030131(void) { return DBdrop_index("httptest", "httptest_1"); } -static int DBpatch_5030135(void) +static int DBpatch_5030132(void) { return DBdrop_field("httptest", "applicationid"); } -static int DBpatch_5030136(void) +static int DBpatch_5030133(void) { return DBdrop_field("sysmaps_elements", "application"); } -static int DBpatch_5030137(void) +static int DBpatch_5030134(void) { return DBdrop_table("application_discovery"); } -static int DBpatch_5030138(void) +static int DBpatch_5030135(void) { return DBdrop_table("item_application_prototype"); } -static int DBpatch_5030139(void) +static int DBpatch_5030136(void) { return DBdrop_table("application_prototype"); } -static int DBpatch_5030140(void) +static int DBpatch_5030137(void) { return DBdrop_table("application_template"); } -static int DBpatch_5030141(void) +static int DBpatch_5030138(void) { return DBdrop_table("items_applications"); } -static int DBpatch_5030142(void) +static int DBpatch_5030139(void) { return DBdrop_table("applications"); } -static int DBpatch_5030143(void) +static int DBpatch_5030140(void) { DB_RESULT result; int ret; @@ -4462,7 +4308,7 @@ static int DBpatch_5030143(void) return ret; } -static int DBpatch_5030144(void) +static int DBpatch_5030141(void) { DB_RESULT result; int ret; @@ -4478,7 +4324,7 @@ static int DBpatch_5030144(void) return ret; } -static int DBpatch_5030145(void) +static int DBpatch_5030142(void) { const ZBX_TABLE table = {"report", "reportid", 0, @@ -4506,26 +4352,26 @@ static int DBpatch_5030145(void) return DBcreate_table(&table); } -static int DBpatch_5030146(void) +static int DBpatch_5030143(void) { return DBcreate_index("report", "report_1", "name", 1); } -static int DBpatch_5030147(void) +static int DBpatch_5030144(void) { const ZBX_FIELD field = {"userid", NULL, "users", "userid", 0, 0, 0, ZBX_FK_CASCADE_DELETE}; return DBadd_foreign_key("report", 1, &field); } -static int DBpatch_5030148(void) +static int DBpatch_5030145(void) { const ZBX_FIELD field = {"dashboardid", NULL, "dashboard", "dashboardid", 0, 0, 0, ZBX_FK_CASCADE_DELETE}; return DBadd_foreign_key("report", 2, &field); } -static int DBpatch_5030149(void) +static int DBpatch_5030146(void) { const ZBX_TABLE table = {"report_param", "reportparamid", 0, @@ -4542,19 +4388,19 @@ static int DBpatch_5030149(void) return DBcreate_table(&table); } -static int DBpatch_5030150(void) +static int DBpatch_5030147(void) { return DBcreate_index("report_param", "report_param_1", "reportid", 0); } -static int DBpatch_5030151(void) +static int DBpatch_5030148(void) { const ZBX_FIELD field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE}; return DBadd_foreign_key("report_param", 1, &field); } -static int DBpatch_5030152(void) +static int DBpatch_5030149(void) { const ZBX_TABLE table = {"report_user", "reportuserid", 0, @@ -4572,33 +4418,33 @@ static int DBpatch_5030152(void) return DBcreate_table(&table); } -static int DBpatch_5030153(void) +static int DBpatch_5030150(void) { return DBcreate_index("report_user", "report_user_1", "reportid", 0); } -static int DBpatch_5030154(void) +static int DBpatch_5030151(void) { const ZBX_FIELD field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE}; return DBadd_foreign_key("report_user", 1, &field); } -static int DBpatch_5030155(void) +static int DBpatch_5030152(void) { const ZBX_FIELD field = {"userid", NULL, "users", "userid", 0, 0, 0, ZBX_FK_CASCADE_DELETE}; return DBadd_foreign_key("report_user", 2, &field); } -static int DBpatch_5030156(void) +static int DBpatch_5030153(void) { const ZBX_FIELD field = {"access_userid", NULL, "users", "userid", 0, 0, 0, 0}; return DBadd_foreign_key("report_user", 3, &field); } -static int DBpatch_5030157(void) +static int DBpatch_5030154(void) { const ZBX_TABLE table = {"report_usrgrp", "reportusrgrpid", 0, @@ -4615,46 +4461,1837 @@ static int DBpatch_5030157(void) return DBcreate_table(&table); } -static int DBpatch_5030158(void) +static int DBpatch_5030155(void) { return DBcreate_index("report_usrgrp", "report_usrgrp_1", "reportid", 0); } -static int DBpatch_5030159(void) +static int DBpatch_5030156(void) { const ZBX_FIELD field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE}; return DBadd_foreign_key("report_usrgrp", 1, &field); } -static int DBpatch_5030160(void) +static int DBpatch_5030157(void) { const ZBX_FIELD field = {"usrgrpid", NULL, "usrgrp", "usrgrpid", 0, 0, 0, ZBX_FK_CASCADE_DELETE}; return DBadd_foreign_key("report_usrgrp", 2, &field); } -static int DBpatch_5030161(void) +static int DBpatch_5030158(void) { const ZBX_FIELD field = {"access_userid", NULL, "users", "userid", 0, 0, 0, 0}; return DBadd_foreign_key("report_usrgrp", 3, &field); } -static int DBpatch_5030162(void) +static int DBpatch_5030159(void) { const ZBX_FIELD field = {"url", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0}; return DBadd_field("config", &field); } -static int DBpatch_5030163(void) +static int DBpatch_5030160(void) { const ZBX_FIELD field = {"report_test_timeout", "60s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0}; return DBadd_field("config", &field); } +static int DBpatch_5030161(void) +{ + const ZBX_FIELD field = {"dbversion_status", "", NULL, NULL, 1024, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0}; + + return DBadd_field("config", &field); +} + +/* trigger function conversion to new syntax */ + +#define ZBX_DBPATCH_FUNCTION_UPDATE_NAME 0x01 +#define ZBX_DBPATCH_FUNCTION_UPDATE_PARAM 0x02 +#define ZBX_DBPATCH_FUNCTION_UPDATE (ZBX_DBPATCH_FUNCTION_UPDATE_NAME | \ + ZBX_DBPATCH_FUNCTION_UPDATE_PARAM) + +#define ZBX_DBPATCH_FUNCTION_CREATE 0x40 +#define ZBX_DBPATCH_FUNCTION_DELETE 0x80 + +#define ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION 0x01 +#define ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION 0x02 + +#define ZBX_DBPATCH_TRIGGER_UPDATE (ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION | \ + ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION) + +/* Function argument descriptors. */ +/* Used in varargs list to describe following parameter mapping to old position. */ +/* Terminated with ZBX_DBPATCH_ARG_NONE. */ +/* For example: */ +/* ..., ZBX_DBPATCH_ARG_NUM, 1, ZBX_DBPATCH_ARG_STR, 0, ZBX_DBPATCH_ARG_NONE) */ +/* meaning first numeric parameter copied from second parameter */ +/* second string parameter copied from first parameter */ +typedef enum +{ + ZBX_DBPATCH_ARG_NONE, /* terminating descriptor, must be put at the end of the list */ + ZBX_DBPATCH_ARG_HIST, /* history period followed by sec/num (int) and timeshift (int) indexes */ + ZBX_DBPATCH_ARG_TIME, /* time value followed by argument index (int) */ + ZBX_DBPATCH_ARG_NUM, /* number value followed by argument index (int) */ + ZBX_DBPATCH_ARG_STR, /* string value followed by argument index (int) */ + ZBX_DBPATCH_ARG_TREND, /* trend period, followed by period (int) and timeshift (int) indexes */ + ZBX_DBPATCH_ARG_CONST_STR, /* constant,fffffff followed by string (char *) value */ +} +zbx_dbpatch_arg_t; + +ZBX_VECTOR_DECL(loc, zbx_strloc_t) +ZBX_VECTOR_IMPL(loc, zbx_strloc_t) + +typedef struct +{ + zbx_uint64_t functionid; + zbx_uint64_t itemid; + + /* hostid for time based functions to track associated */ + /* hosts when replacing with history function with common function */ + zbx_uint64_t hostid; + + /* function location - expression|recovery expression */ + unsigned char location; + + char *name; + char *parameter; + + /* the first parameter (host:key) for calculated item */ + /* formula functions, NULL for trigger functions */ + char *arg0; + + unsigned char flags; +} +zbx_dbpatch_function_t; + +typedef struct +{ + zbx_uint64_t triggerid; + unsigned char recovery_mode; + unsigned char flags; + char *expression; + char *recovery_expression; +} +zbx_dbpatch_trigger_t; + +static void dbpatch_function_free(zbx_dbpatch_function_t *func) +{ + zbx_free(func->name); + zbx_free(func->parameter); + zbx_free(func->arg0); + zbx_free(func); +} + +static void dbpatch_trigger_clear(zbx_dbpatch_trigger_t *trigger) +{ + zbx_free(trigger->expression); + zbx_free(trigger->recovery_expression); +} + +static zbx_dbpatch_function_t *dbpatch_new_function(zbx_uint64_t functionid, zbx_uint64_t itemid, const char *name, + const char *parameter, unsigned char flags) +{ + zbx_dbpatch_function_t *func; + + func = (zbx_dbpatch_function_t *)zbx_malloc(NULL, sizeof(zbx_dbpatch_function_t)); + func->functionid = functionid; + func->itemid = itemid; + func->name = (NULL != name ? zbx_strdup(NULL, name) : NULL); + func->parameter = (NULL != parameter ? zbx_strdup(NULL, parameter) : NULL); + func->flags = flags; + func->arg0 = NULL; + + return func; +} + +static void dbpatch_add_function(const zbx_dbpatch_function_t *template, zbx_uint64_t functionid, const char *name, + const char *parameter, unsigned char flags, zbx_vector_ptr_t *functions) +{ + zbx_dbpatch_function_t *func; + + func = dbpatch_new_function(functionid, template->itemid, name, parameter, flags); + func->arg0 = (NULL != template->arg0 ? zbx_strdup(NULL, template->arg0) : NULL); + + zbx_vector_ptr_append(functions, func); +} + +static void dbpatch_update_function(zbx_dbpatch_function_t *func, const char *name, + const char *parameter, unsigned char flags) +{ + if (0 != (flags & ZBX_DBPATCH_FUNCTION_UPDATE_NAME)) + func->name = zbx_strdup(func->name, name); + + if (0 != (flags & ZBX_DBPATCH_FUNCTION_UPDATE_PARAM)) + func->parameter = zbx_strdup(func->parameter, parameter); + + func->flags = flags; +} + +/****************************************************************************** + * * + * Function: dbpatch_update_expression * + * * + * Purpose: replace {functionid} occurrences in expression with the specified * + * replacement string * + * * + * Return value: SUCCEED - expression was changed * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int dbpatch_update_expression(char **expression, zbx_uint64_t functionid, const char *replace) +{ + int pos = 0, last_pos = 0; + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0; + zbx_uint64_t id; + + for (; SUCCEED == zbx_token_find(*expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID); pos++) + { + switch (token.type) + { + case ZBX_TOKEN_OBJECTID: + if (SUCCEED == is_uint64_n(*expression + token.data.objectid.name.l, + token.data.objectid.name.r - token.data.objectid.name.l + 1, &id) && + functionid == id) + { + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, + *expression + last_pos, token.loc.l - last_pos); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, replace); + last_pos = token.loc.r + 1; + } + pos = token.loc.r; + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + break; + } + } + + if (NULL == out) + return FAIL; + + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, *expression + last_pos); + + zbx_free(*expression); + *expression = out; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: dbpatch_update_trigger * + * * + * Purpose: replace {functionid} occurrences in trigger expression and * + * recovery expression with the specified replacement string * + * * + ******************************************************************************/ +static void dbpatch_update_trigger(zbx_dbpatch_trigger_t *trigger, zbx_uint64_t functionid, const char *replace) +{ + if (SUCCEED == dbpatch_update_expression(&trigger->expression, functionid, replace)) + trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION; + + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == trigger->recovery_mode) + { + if (SUCCEED == dbpatch_update_expression(&trigger->recovery_expression, functionid, replace)) + trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION; + } +} + +static void dbpatch_update_func_abschange(zbx_dbpatch_function_t *function, char **replace) +{ + dbpatch_update_function(function, "change", "", ZBX_DBPATCH_FUNCTION_UPDATE); + *replace = zbx_dsprintf(NULL, "abs({" ZBX_FS_UI64 "})", function->functionid); +} + +static void dbpatch_update_func_delta(zbx_dbpatch_function_t *function, const char *parameter, char **replace, + zbx_vector_ptr_t *functions) +{ + zbx_uint64_t functionid2; + + dbpatch_update_function(function, "max", parameter, ZBX_DBPATCH_FUNCTION_UPDATE); + + functionid2 = (NULL == function->arg0 ? DBget_maxid("functions") : (zbx_uint64_t)functions->values_num); + dbpatch_add_function(function, functionid2, "min", parameter, ZBX_DBPATCH_FUNCTION_CREATE, functions); + + *replace = zbx_dsprintf(NULL, "({" ZBX_FS_UI64 "}-{" ZBX_FS_UI64 "})", function->functionid, functionid2); +} + +static void dbpatch_update_func_diff(zbx_dbpatch_function_t *function, char **replace, zbx_vector_ptr_t *functions) +{ + zbx_uint64_t functionid2; + + dbpatch_update_function(function, "last", "#1", ZBX_DBPATCH_FUNCTION_UPDATE); + + functionid2 = (NULL == function->arg0 ? DBget_maxid("functions") : (zbx_uint64_t)functions->values_num); + dbpatch_add_function(function, functionid2, "last", "#2", ZBX_DBPATCH_FUNCTION_CREATE, functions); + + *replace = zbx_dsprintf(NULL, "({" ZBX_FS_UI64 "}<>{" ZBX_FS_UI64 "})", function->functionid, functionid2); +} + +static void dbpatch_update_func_trenddelta(zbx_dbpatch_function_t *function, const char *parameter, char **replace, + zbx_vector_ptr_t *functions) +{ + zbx_uint64_t functionid2; + + dbpatch_update_function(function, "trendmax", parameter, ZBX_DBPATCH_FUNCTION_UPDATE); + + functionid2 = (NULL == function->arg0 ? DBget_maxid("functions") : (zbx_uint64_t)functions->values_num); + dbpatch_add_function(function, functionid2, "trendmin", parameter, ZBX_DBPATCH_FUNCTION_CREATE, functions); + + *replace = zbx_dsprintf(NULL, "({" ZBX_FS_UI64 "}-{" ZBX_FS_UI64 "})", function->functionid, functionid2); +} + +static void dbpatch_update_func_strlen(zbx_dbpatch_function_t *function, const char *parameter, char **replace) +{ + dbpatch_update_function(function, "last", parameter, ZBX_DBPATCH_FUNCTION_UPDATE); + + *replace = zbx_dsprintf(NULL, "length({" ZBX_FS_UI64 "})", function->functionid); +} + +static void dbpatch_update_hist2common(zbx_dbpatch_function_t *function, int extended, char **expression) +{ + char *str = NULL; + size_t str_alloc = 0, str_offset = 0; + + if (ZBX_DBPATCH_FUNCTION_DELETE == function->flags) + dbpatch_update_function(function, "last", "$", ZBX_DBPATCH_FUNCTION_UPDATE); + + if (0 == extended) + zbx_chrcpy_alloc(&str, &str_alloc, &str_offset, '('); + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, *expression); + if (0 == extended) + zbx_chrcpy_alloc(&str, &str_alloc, &str_offset, ')'); + + zbx_snprintf_alloc(&str, &str_alloc, &str_offset, " or ({" ZBX_FS_UI64 "}<>{" ZBX_FS_UI64 "})", + function->functionid, function->functionid); + + zbx_free(*expression); + *expression = str; +} + +/****************************************************************************** + * * + * Function: dbpatch_parse_function_params * + * * + * Purpose: parse function parameter string into parameter location vector * + * * + ******************************************************************************/ +static void dbpatch_parse_function_params(const char *parameter, zbx_vector_loc_t *params) +{ + const char *ptr; + size_t len, pos, sep = 0, eol; + zbx_strloc_t loc; + + eol = strlen(parameter); + + for (ptr = parameter; ptr < parameter + eol; ptr += sep + 1) + { + zbx_function_param_parse(ptr, &pos, &len, &sep); + + if (0 < len) + { + loc.l = ptr - parameter + pos; + loc.r = loc.l + len - 1; + } + else + { + loc.l = ptr - parameter + eol - (ptr - parameter); + loc.r = loc.l; + } + + zbx_vector_loc_append_ptr(params, &loc); + } + + while (0 < params->values_num && '\0' == parameter[params->values[params->values_num - 1].l]) + --params->values_num; +} + +/****************************************************************************** + * * + * Function: dbpatch_convert_params * + * * + * Purpose: convert function parameters into new syntax * + * * + * Parameters: out - [OUT] the converted parameter string * + * parameter - [IN] the original parameter string * + * params - [IN] the parameter locations in original parameter * + * string * + * ... - list of parameter descriptors with parameter data * + * (see zbx_dbpatch_arg_t enum for parameter list * + * description) * + * * + ******************************************************************************/ +static void dbpatch_convert_params(char **out, const char *parameter, const zbx_vector_loc_t *params, ...) +{ + size_t out_alloc = 0, out_offset = 0; + va_list args; + int index, type, param_num = 0; + const zbx_strloc_t *loc; + const char *ptr; + char *arg; + + va_start(args, params); + + while (ZBX_DBPATCH_ARG_NONE != (type = va_arg(args, int))) + { + if (0 != param_num++) + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, ','); + + switch (type) + { + case ZBX_DBPATCH_ARG_HIST: + if (-1 != (index = va_arg(args, int)) && index < params->values_num) + { + loc = ¶ms->values[index]; + arg = zbx_substr_unquote(parameter, loc->l, loc->r); + + if ('\0' != *arg) + { + zbx_strcpy_alloc(out, &out_alloc, &out_offset, arg); + + if ('#' != *arg && 0 != isdigit(arg[strlen(arg) - 1])) + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, 's'); + } + + zbx_free(arg); + } + + if (-1 != (index = va_arg(args, int)) && index < params->values_num) + { + loc = ¶ms->values[index]; + arg = zbx_substr_unquote(parameter, loc->l, loc->r); + + if ('\0' != *arg) + { + if (0 == out_offset) + zbx_strcpy_alloc(out, &out_alloc, &out_offset, "#1"); + + zbx_strcpy_alloc(out, &out_alloc, &out_offset, ":now-"); + zbx_strcpy_alloc(out, &out_alloc, &out_offset, arg); + if (0 != isdigit(arg[strlen(arg) - 1])) + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, 's'); + } + + zbx_free(arg); + } + + break; + case ZBX_DBPATCH_ARG_TIME: + if (params->values_num > (index = va_arg(args, int))) + { + char *str; + + loc = ¶ms->values[index]; + str = zbx_substr_unquote(parameter, loc->l, loc->r); + if ('\0' != *str) + { + zbx_strcpy_alloc(out, &out_alloc, &out_offset, str); + if (0 != isdigit(str[strlen(str) - 1])) + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, 's'); + } + zbx_free(str); + } + break; + case ZBX_DBPATCH_ARG_NUM: + if (params->values_num > (index = va_arg(args, int))) + { + char *str; + + loc = ¶ms->values[index]; + str = zbx_substr_unquote(parameter, loc->l, loc->r); + zbx_strcpy_alloc(out, &out_alloc, &out_offset, str); + zbx_free(str); + } + break; + case ZBX_DBPATCH_ARG_STR: + if (params->values_num > (index = va_arg(args, int))) + { + loc = ¶ms->values[index]; + if ('"' == parameter[loc->l]) + { + zbx_strncpy_alloc(out, &out_alloc, &out_offset, parameter + loc->l, + loc->r - loc->l + 1); + } + else if ('\0' != parameter[loc->l]) + { + char raw[FUNCTION_PARAM_LEN * 4 + 1], quoted[sizeof(raw)]; + + zbx_strlcpy(raw, parameter + loc->l, loc->r - loc->l + 2); + zbx_escape_string(quoted, sizeof(quoted), raw, "\"\\"); + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, '"'); + zbx_strcpy_alloc(out, &out_alloc, &out_offset, quoted); + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, '"'); + } + } + break; + case ZBX_DBPATCH_ARG_TREND: + if (params->values_num > (index = va_arg(args, int))) + { + char *str; + + loc = ¶ms->values[index]; + str = zbx_substr_unquote(parameter, loc->l, loc->r); + zbx_strcpy_alloc(out, &out_alloc, &out_offset, str); + zbx_free(str); + } + if (params->values_num > (index = va_arg(args, int))) + { + char *str; + + loc = ¶ms->values[index]; + str = zbx_substr_unquote(parameter, loc->l, loc->r); + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, ':'); + zbx_strcpy_alloc(out, &out_alloc, &out_offset, str); + zbx_free(str); + } + break; + case ZBX_DBPATCH_ARG_CONST_STR: + if (NULL != (ptr = va_arg(args, char *))) + { + char quoted[MAX_STRING_LEN]; + + zbx_escape_string(quoted, sizeof(quoted), ptr, "\"\\"); + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, '"'); + zbx_strcpy_alloc(out, &out_alloc, &out_offset, quoted); + zbx_chrcpy_alloc(out, &out_alloc, &out_offset, '"'); + } + break; + } + } + + va_end(args); + + if (0 != out_offset) + { + /* trim trailing empty parameters */ + while (0 < out_offset && ',' == (*out)[out_offset - 1]) + (*out)[--out_offset] = '\0'; + } + else + *out = zbx_strdup(*out, ""); +} + +static void dbpatch_update_func_bitand(zbx_dbpatch_function_t *function, const zbx_vector_loc_t *params, + char **replace) +{ + char *parameter = NULL, *mask = NULL; + int secnum = 0; + + if (2 <= params->values_num && '\0' != function->parameter[params->values[1].l]) + { + mask = zbx_substr_unquote(function->parameter, params->values[1].l, params->values[1].r); + *replace = zbx_dsprintf(NULL, "bitand({" ZBX_FS_UI64 "},%s)", function->functionid, mask); + zbx_free(mask); + } + else + *replace = zbx_dsprintf(NULL, "bitand({" ZBX_FS_UI64 "})", function->functionid); + + if (0 < params->values_num) + { + char *param; + + param = zbx_substr_unquote(function->parameter, params->values[0].l, params->values[0].r); + + if ('#' != *param && '{' != *param) + secnum = -1; + + zbx_free(param); + } + + dbpatch_convert_params(¶meter, function->parameter, params, + ZBX_DBPATCH_ARG_HIST, secnum, 2, + ZBX_DBPATCH_ARG_NONE); + + dbpatch_update_function(function, "last", parameter, ZBX_DBPATCH_FUNCTION_UPDATE); + + zbx_free(parameter); +} + +/****************************************************************************** + * * + * Function: dbpatch_convert_function * + * * + * Purpose: convert function to new parameter syntax/order * + * * + * Parameters: function - [IN/OUT] the function to convert * + * replace - [OUT] the replacement for {functionid} in the * + * expression * + * functions - [IN/OUT] the functions * + * * + * Comments: The function conversion can result in another function being * + * added. * + * * + ******************************************************************************/ +static void dbpatch_convert_function(zbx_dbpatch_function_t *function, char **replace, zbx_vector_ptr_t *functions) +{ + zbx_vector_loc_t params; + char *parameter = NULL; + + zbx_vector_loc_create(¶ms); + + dbpatch_parse_function_params(function->parameter, ¶ms); + + if (0 == strcmp(function->name, "abschange")) + { + dbpatch_update_func_abschange(function, replace); + } + else if (0 == strcmp(function->name, "change")) + { + dbpatch_update_function(function, NULL, "", ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "avg") || 0 == strcmp(function->name, "max") || + 0 == strcmp(function->name, "min") || 0 == strcmp(function->name, "sum")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 0, 1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "delta")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 0, 1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_func_delta(function, parameter, replace, functions); + } + else if (0 == strcmp(function->name, "diff")) + { + dbpatch_update_func_diff(function, replace, functions); + } + else if (0 == strcmp(function->name, "fuzzytime")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_TIME, 0, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "nodata")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_TIME, 0, + ZBX_DBPATCH_ARG_STR, 1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "percentile")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 0, 1, + ZBX_DBPATCH_ARG_NUM, 2, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "trendavg") || 0 == strcmp(function->name, "trendmin") || + 0 == strcmp(function->name, "trendmax") || 0 == strcmp(function->name, "trendsum") || + 0 == strcmp(function->name, "trendcount")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_TREND, 0, 1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "trenddelta")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_TREND, 0, 1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_func_trenddelta(function, parameter, replace, functions); + } + else if (0 == strcmp(function->name, "band")) + { + dbpatch_update_func_bitand(function, ¶ms, replace); + } + else if (0 == strcmp(function->name, "forecast")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 0, 1, + ZBX_DBPATCH_ARG_TIME, 2, + ZBX_DBPATCH_ARG_STR, 3, + ZBX_DBPATCH_ARG_STR, 4, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "timeleft")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 0, 1, + ZBX_DBPATCH_ARG_NUM, 2, + ZBX_DBPATCH_ARG_STR, 3, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "count")) + { + char *op = NULL; + + if (2 <= params.values_num) + { + if (3 <= params.values_num && '\0' != function->parameter[params.values[2].l]) + { + op = zbx_substr_unquote(function->parameter, params.values[2].l, params.values[2].r); + + if (0 == strcmp(op, "band")) + op = zbx_strdup(op, "bitand"); + else if ('\0' == *op && '"' != function->parameter[params.values[2].l]) + zbx_free(op); + } + } + + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 0, 3, + ZBX_DBPATCH_ARG_CONST_STR, op, + ZBX_DBPATCH_ARG_STR, 1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + + zbx_free(op); + } + else if (0 == strcmp(function->name, "iregexp") || 0 == strcmp(function->name, "regexp")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 1, -1, + ZBX_DBPATCH_ARG_CONST_STR, function->name, + ZBX_DBPATCH_ARG_STR, 0, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, "find", parameter, ZBX_DBPATCH_FUNCTION_UPDATE); + } + else if (0 == strcmp(function->name, "str")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, 1, -1, + ZBX_DBPATCH_ARG_CONST_STR, "like", + ZBX_DBPATCH_ARG_STR, 0, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, "find", parameter, ZBX_DBPATCH_FUNCTION_UPDATE); + } + else if (0 == strcmp(function->name, "last")) + { + int secnum = 0; + + if (0 < params.values_num) + { + char *param; + + param = zbx_substr_unquote(function->parameter, params.values[0].l, params.values[0].r); + + if ('#' != *param && '{' != *param) + secnum = -1; + + zbx_free(param); + } + + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, secnum, 1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "prev")) + { + dbpatch_update_function(function, "last", "#2", ZBX_DBPATCH_FUNCTION_UPDATE); + } + else if (0 == strcmp(function->name, "strlen")) + { + int secnum = 0; + + if (0 < params.values_num) + { + char *param; + + param = zbx_substr_unquote(function->parameter, params.values[0].l, params.values[0].r); + + if ('#' != *param && '{' != *param) + secnum = -1; + + zbx_free(param); + } + + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, secnum, 1, + ZBX_DBPATCH_ARG_NONE); + + dbpatch_update_func_strlen(function, parameter, replace); + } + else if (0 == strcmp(function->name, "logeventid") || 0 == strcmp(function->name, "logsource")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, -1, -1, + ZBX_DBPATCH_ARG_STR, 0, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, parameter, ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + else if (0 == strcmp(function->name, "logseverity")) + { + dbpatch_convert_params(¶meter, function->parameter, ¶ms, + ZBX_DBPATCH_ARG_HIST, -1, -1, + ZBX_DBPATCH_ARG_NONE); + dbpatch_update_function(function, NULL, "", ZBX_DBPATCH_FUNCTION_UPDATE_PARAM); + } + + zbx_free(parameter); + zbx_vector_loc_destroy(¶ms); +} + +static int dbpatch_is_time_function(const char *name, size_t len) +{ + const char *functions[] = {"date", "dayofmonth", "dayofweek", "now", "time", NULL}, **func; + size_t func_len; + + for (func = functions; NULL != *func; func++) + { + func_len = strlen(*func); + if (func_len == len && 0 == memcmp(*func, name, len)) + return SUCCEED; + } + + return FAIL; +} + +#define ZBX_DBPATCH_EXPRESSION 0x01 +#define ZBX_DBPATCH_RECOVERY_EXPRESSION 0x02 + +/****************************************************************************** + * * + * Function: dbpatch_find_function * + * * + * Purpose: check if the expression contains specified functionid * + * * + ******************************************************************************/ +static int dbpatch_find_function(const char *expression, zbx_uint64_t functionid) +{ + int pos = 0; + zbx_token_t token; + zbx_uint64_t id; + + for (; SUCCEED == zbx_token_find(expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID); pos++) + { + switch (token.type) + { + case ZBX_TOKEN_OBJECTID: + if (SUCCEED == is_uint64_n(expression + token.data.objectid.name.l, + token.data.objectid.name.r - token.data.objectid.name.l + 1, &id) && + functionid == id) + { + return SUCCEED; + } + pos = token.loc.r; + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + break; + } + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: dbpatch_get_function_location * + * * + * Purpose: return function location mask (expression | recovery expression) * + * * + ******************************************************************************/ +static unsigned char dbpatch_get_function_location(const zbx_dbpatch_trigger_t *trigger, zbx_uint64_t functionid) +{ + unsigned char mask = 0; + + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION != trigger->recovery_mode) + return ZBX_DBPATCH_EXPRESSION; + + if (SUCCEED == dbpatch_find_function(trigger->expression, functionid)) + mask |= ZBX_DBPATCH_EXPRESSION; + + if (SUCCEED == dbpatch_find_function(trigger->recovery_expression, functionid)) + mask |= ZBX_DBPATCH_RECOVERY_EXPRESSION; + + return mask; +} + +/****************************************************************************** + * * + * Function: dbpatch_convert_trigger * + * * + * Purpose: convert trigger and its functions to use new expression syntax * + * * + * Parameters: trigger - [IN/OUT] the trigger data/updates * + * functions - [OUT] the function updates * + * * + ******************************************************************************/ +static int dbpatch_convert_trigger(zbx_dbpatch_trigger_t *trigger, zbx_vector_ptr_t *functions) +{ + DB_ROW row; + DB_RESULT result; + int i, index; + zbx_uint64_t functionid, itemid, hostid; + zbx_vector_loc_t params; + zbx_vector_ptr_t common_functions, trigger_functions; + zbx_vector_uint64_t hostids, r_hostids; + zbx_dbpatch_function_t *func; + + index = functions->values_num; + + zbx_vector_loc_create(¶ms); + zbx_vector_ptr_create(&common_functions); + zbx_vector_ptr_create(&trigger_functions); + zbx_vector_uint64_create(&hostids); + zbx_vector_uint64_create(&r_hostids); + + result = DBselect("select f.functionid,f.itemid,f.name,f.parameter,h.hostid" + " from functions f" + " join items i" + " on f.itemid=i.itemid" + " join hosts h" + " on i.hostid=h.hostid" + " where triggerid=" ZBX_FS_UI64 + " order by functionid", + trigger->triggerid); + + while (NULL != (row = DBfetch(result))) + { + char *replace = NULL; + unsigned char location; + + ZBX_STR2UINT64(functionid, row[0]); + ZBX_STR2UINT64(itemid, row[1]); + ZBX_STR2UINT64(hostid, row[4]); + + if (SUCCEED == dbpatch_is_time_function(row[2], strlen(row[2]))) + { + char func_name[FUNCTION_NAME_LEN * 4 + 1]; + + func = dbpatch_new_function(functionid, itemid, row[2], row[3], 0); + func->hostid = hostid; + func->location = dbpatch_get_function_location(trigger, functionid); + zbx_vector_ptr_append(&common_functions, func); + + zbx_snprintf(func_name, sizeof(func_name), "%s()", row[2]); + dbpatch_update_trigger(trigger, functionid, func_name); + + continue; + } + + location = dbpatch_get_function_location(trigger, functionid); + + if (0 != (location & ZBX_DBPATCH_EXPRESSION)) + zbx_vector_uint64_append(&hostids, hostid); + + if (0 != (location & ZBX_DBPATCH_RECOVERY_EXPRESSION)) + zbx_vector_uint64_append(&r_hostids, hostid); + + func = dbpatch_new_function(functionid, itemid, row[2], row[3], 0); + zbx_vector_ptr_append(functions, func); + dbpatch_convert_function(func, &replace, functions); + + if (NULL != replace) + { + dbpatch_update_trigger(trigger, func->functionid, replace); + zbx_free(replace); + } + } + DBfree_result(result); + + for (i = index; i < functions->values_num; i++) + { + func = (zbx_dbpatch_function_t *)functions->values[i]; + + if (0 == (func->flags & ZBX_DBPATCH_FUNCTION_DELETE) && NULL != func->parameter) + { + if ('\0' != *func->parameter) + func->parameter = zbx_dsprintf(func->parameter, "$,%s", func->parameter); + else + func->parameter = zbx_strdup(func->parameter, "$"); + + func->flags |= ZBX_DBPATCH_FUNCTION_UPDATE_PARAM; + } + } + + /* ensure that with history time functions converted to common time functions */ + /* the trigger is still linked to the same hosts */ + if (0 != common_functions.values_num) + { + int extended = 0, r_extended = 0; + + zbx_vector_uint64_sort(&hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(&hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + + zbx_vector_uint64_sort(&r_hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(&r_hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + + for (i = 0; i < common_functions.values_num; i++) + { + func = (zbx_dbpatch_function_t *)common_functions.values[i]; + func->flags = ZBX_DBPATCH_FUNCTION_DELETE; + + if (0 != (func->location & ZBX_DBPATCH_EXPRESSION) && + (FAIL == zbx_vector_uint64_search(&hostids, func->hostid, + ZBX_DEFAULT_UINT64_COMPARE_FUNC))) + { + dbpatch_update_hist2common(func, extended, &trigger->expression); + extended = 1; + zbx_vector_uint64_append(&hostids, func->hostid); + trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION; + } + + if (0 != (func->location & ZBX_DBPATCH_RECOVERY_EXPRESSION) && + (FAIL == zbx_vector_uint64_search(&r_hostids, func->hostid, + ZBX_DEFAULT_UINT64_COMPARE_FUNC))) + { + dbpatch_update_hist2common(func, r_extended, &trigger->recovery_expression); + r_extended = 1; + zbx_vector_uint64_append(&r_hostids, func->hostid); + trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION; + } + + zbx_vector_ptr_append(functions, func); + } + } + + zbx_vector_uint64_destroy(&hostids); + zbx_vector_uint64_destroy(&r_hostids); + zbx_vector_ptr_destroy(&trigger_functions); + zbx_vector_ptr_destroy(&common_functions); + zbx_vector_loc_destroy(¶ms); + + if (0 != (trigger->flags & ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION)) + { + if (zbx_strlen_utf8(trigger->expression) > TRIGGER_EXPRESSION_LEN) + { + zabbix_log(LOG_LEVEL_WARNING, "trigger \"" ZBX_FS_UI64 "\" expression is too long: %s", + trigger->triggerid, trigger->expression); + return FAIL; + } + } + + if (0 != (trigger->flags & ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION)) + { + if (zbx_strlen_utf8(trigger->recovery_expression) > TRIGGER_EXPRESSION_LEN) + { + zabbix_log(LOG_LEVEL_WARNING, "trigger \"" ZBX_FS_UI64 "\" recovery expression is too long: %s", + trigger->triggerid, trigger->recovery_expression); + return FAIL; + } + } + + return SUCCEED; +} + +static int DBpatch_5030162(void) +{ + int i, ret = SUCCEED; + DB_ROW row; + DB_RESULT result; + char *sql; + size_t sql_alloc = 4096, sql_offset = 0; + zbx_db_insert_t db_insert_functions; + zbx_vector_ptr_t functions; + + if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) + return SUCCEED; + + zbx_vector_ptr_create(&functions); + + sql = zbx_malloc(NULL, sql_alloc); + + zbx_db_insert_prepare(&db_insert_functions, "functions", "functionid", "itemid", "triggerid", "name", + "parameter", NULL); + DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset); + + result = DBselect("select triggerid,recovery_mode,expression,recovery_expression from triggers" + " order by triggerid"); + + while (NULL != (row = DBfetch(result))) + { + char delim, *esc; + zbx_dbpatch_trigger_t trigger; + + ZBX_STR2UINT64(trigger.triggerid, row[0]); + ZBX_STR2UCHAR(trigger.recovery_mode, row[1]); + trigger.expression = zbx_strdup(NULL, row[2]); + trigger.recovery_expression = zbx_strdup(NULL, row[3]); + trigger.flags = 0; + + if (SUCCEED == dbpatch_convert_trigger(&trigger, &functions)) + { + for (i = 0; i < functions.values_num; i++) + { + zbx_dbpatch_function_t *func = (zbx_dbpatch_function_t *)functions.values[i]; + + if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_CREATE)) + { + zbx_db_insert_add_values(&db_insert_functions, func->functionid, + func->itemid, trigger.triggerid, func->name, func->parameter); + continue; + } + + if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_DELETE)) + { + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, + "delete from functions where functionid=" ZBX_FS_UI64 ";\n", + func->functionid); + + if (FAIL == (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset))) + break; + + continue; + } + + delim = ' '; + + zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update functions set"); + if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_UPDATE_NAME)) + { + esc = DBdyn_escape_field("functions", "name", func->name); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%cname='%s'", delim, esc); + zbx_free(esc); + delim = ','; + } + + if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_UPDATE_PARAM)) + { + esc = DBdyn_escape_field("functions", "parameter", func->parameter); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%cparameter='%s'", delim, esc); + zbx_free(esc); + } + + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where functionid=" ZBX_FS_UI64 + ";\n", func->functionid); + + if (FAIL == (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset))) + break; + } + + if (SUCCEED == ret && 0 != (trigger.flags & ZBX_DBPATCH_TRIGGER_UPDATE)) + { + delim = ' '; + zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set"); + + if (0 != (trigger.flags & ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION)) + { + esc = DBdyn_escape_field("triggers", "expression", trigger.expression); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%cexpression='%s'", delim, + esc); + zbx_free(esc); + delim = ','; + } + + if (0 != (trigger.flags & ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION)) + { + esc = DBdyn_escape_field("triggers", "recovery_expression", + trigger.recovery_expression); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%crecovery_expression='%s'", + delim, esc); + zbx_free(esc); + } + + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where triggerid=" ZBX_FS_UI64 + ";\n", trigger.triggerid); + + if (FAIL == (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset))) + break; + } + } + + zbx_vector_ptr_clear_ext(&functions, (zbx_clean_func_t)dbpatch_function_free); + dbpatch_trigger_clear(&trigger); + + if (SUCCEED != ret) + break; + } + + DBfree_result(result); + + DBend_multiple_update(&sql, &sql_alloc, &sql_offset); + + if (SUCCEED == ret && 16 < sql_offset) + { + if (ZBX_DB_OK > DBexecute("%s", sql)) + ret = FAIL; + } + + if (SUCCEED == ret) + zbx_db_insert_execute(&db_insert_functions); + + zbx_db_insert_clean(&db_insert_functions); + zbx_free(sql); + + zbx_vector_ptr_destroy(&functions); + + return ret; +} + +static int DBpatch_5030163(void) +{ + if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) + return SUCCEED; + + if (ZBX_DB_OK > DBexecute("update trigger_queue set type=4 where type=3")) + return FAIL; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: dbpatch_replace_functionids * + * * + * Purpose: replace functionids {<index in functions vector>} in expression * + * with their string format * + * * + * Parameters: expression - [IN/OUT] the expression * + * functions - [IN] the functions * + * * + ******************************************************************************/ +static void dbpatch_replace_functionids(char **expression, const zbx_vector_ptr_t *functions) +{ + zbx_uint64_t index; + int pos = 0, last_pos = 0; + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0; + + for (; SUCCEED == zbx_token_find(*expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID); pos++) + { + switch (token.type) + { + case ZBX_TOKEN_OBJECTID: + if (SUCCEED == is_uint64_n(*expression + token.loc.l + 1, + token.loc.r - token.loc.l - 1, &index) && + (int)index < functions->values_num) + { + zbx_dbpatch_function_t *func = functions->values[index]; + + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, + *expression + last_pos, token.loc.l - last_pos); + + zbx_snprintf_alloc(&out, &out_alloc, &out_offset, "%s(%s", + func->name, func->arg0); + if ('\0' != *func->parameter) + { + zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ','); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, func->parameter); + } + zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ')'); + last_pos = token.loc.r + 1; + } + pos = token.loc.r; + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + break; + } + } + + if (0 != out_alloc) + { + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, *expression + last_pos); + zbx_free(*expression); + *expression = out; + } +} + +/****************************************************************************** + * * + * Function: dbpatch_convert_simple_macro * + * * + * Purpose: convert simple macro {host.key:func(params)} to the new syntax * + * func(/host/key,params) * + * * + * Parameters: expression - [IN] the expression with simple macro * + * data - [IN] the simple macro token data * + * function - [OUT] the simple macro replacement function * + * * + ******************************************************************************/ +static void dbpatch_convert_simple_macro(const char *expression, const zbx_token_simple_macro_t *data, + char **function) +{ + zbx_dbpatch_function_t *func; + zbx_vector_ptr_t functions; + char *name, *host, *key; + + name = zbx_substr(expression, data->func.l, data->func_param.l - 1); + + if (SUCCEED == dbpatch_is_time_function(name, strlen(name))) + { + *function = zbx_dsprintf(NULL, "%s()", name); + zbx_free(name); + return; + } + + zbx_vector_ptr_create(&functions); + + func = (zbx_dbpatch_function_t *)zbx_malloc(NULL, sizeof(zbx_dbpatch_function_t)); + func->functionid = 0; + func->itemid = 0; + func->flags = 0; + func->name = name; + + if (data->func_param.l + 1 == data->func_param.r) + func->parameter = zbx_strdup(NULL, ""); + else + func->parameter = zbx_substr(expression, data->func_param.l + 1, data->func_param.r - 1); + + host = zbx_substr(expression, data->host.l, data->host.r); + key = zbx_substr(expression, data->key.l, data->key.r); + + if (0 == strcmp(host, "{HOST.HOST}")) + func->arg0 = zbx_dsprintf(NULL, "//%s", key); + else + func->arg0 = zbx_dsprintf(NULL, "/%s/%s", host, key); + + zbx_vector_ptr_append(&functions, func); + + dbpatch_convert_function(func, function, &functions); + if (NULL == *function) + *function = zbx_strdup(NULL, "{0}"); + dbpatch_replace_functionids(function, &functions); + + zbx_free(key); + zbx_free(host); + zbx_vector_ptr_clear_ext(&functions, (zbx_clean_func_t)dbpatch_function_free); + zbx_vector_ptr_destroy(&functions); +} + +/****************************************************************************** + * * + * Function: dbpatch_convert_expression_macro * + * * + * Purpose: convert simple macros in expression macro {? } to function calls * + * using new expression syntax * + * * + * Parameters: expression - [IN] the original expression * + * loc - [IN] the macro location within expression * + * replace - [OUT] the expression macro replacement expression * + * * + * Return value: SUCCEED - expression macro was converted * + * FAIL - expression macro does not contain simple macros * + * * + ******************************************************************************/ +static int dbpatch_convert_expression_macro(const char *expression, const zbx_strloc_t *loc, char **replace) +{ + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0, pos = loc->l + 2, last_pos = loc->l; + + for (; SUCCEED == zbx_token_find(expression, (int)pos, &token, ZBX_TOKEN_SEARCH_BASIC) && token.loc.r < loc->r; + pos++) + { + char *macro = NULL; + + switch (token.type) + { + case ZBX_TOKEN_SIMPLE_MACRO: + dbpatch_convert_simple_macro(expression, &token.data.simple_macro, ¯o); + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos, + token.loc.l - last_pos); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, macro); + zbx_free(macro); + last_pos = token.loc.r + 1; + pos = token.loc.r; + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_FUNC_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + break; + } + } + + if (0 == out_offset) + return FAIL; + + if (last_pos <= loc->r) + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos, loc->r - last_pos + 1); + *replace = out; + + return SUCCEED; +} + +static int DBpatch_5030164(void) +{ + DB_ROW row; + DB_RESULT result; + char *sql; + size_t sql_alloc = 4096, sql_offset = 0; + int ret = SUCCEED; + + if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) + return SUCCEED; + + sql = zbx_malloc(NULL, sql_alloc); + + DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset); + + result = DBselect("select triggerid,event_name from triggers order by triggerid"); + + while (NULL != (row = DBfetch(result))) + { + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0, pos = 0, last_pos = 0; + + for (; SUCCEED == zbx_token_find(row[1], (int)pos, &token, ZBX_TOKEN_SEARCH_EXPRESSION_MACRO); pos++) + { + char *replace = NULL; + zbx_strloc_t *loc = NULL; + + switch (token.type) + { + case ZBX_TOKEN_EXPRESSION_MACRO: + loc = &token.loc; + break; + case ZBX_TOKEN_FUNC_MACRO: + loc = &token.data.func_macro.macro; + if ('?' != row[1][loc->l + 1]) + { + pos = token.loc.r; + continue; + } + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + continue; + default: + continue; + } + + if (SUCCEED == dbpatch_convert_expression_macro(row[1], loc, &replace)) + { + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, row[1] + last_pos, loc->l - last_pos); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, replace); + zbx_free(replace); + last_pos = loc->r + 1; + } + pos = token.loc.r; + } + + if (0 == out_alloc) + continue; + + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, row[1] + last_pos); + + if (TRIGGER_EVENT_NAME_LEN < zbx_strlen_utf8(out)) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot convert trigger \"%s\" event name: too long expression", + row[0]); + } + else + { + char *esc; + + esc = DBdyn_escape_field("triggers", "event_name", out); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set event_name='%s'" + " where triggerid=%s;\n", esc, row[0]); + zbx_free(esc); + + ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset); + } + + zbx_free(out); + } + DBfree_result(result); + + DBend_multiple_update(&sql, &sql_alloc, &sql_offset); + + if (SUCCEED == ret && 16 < sql_offset) + { + if (ZBX_DB_OK > DBexecute("%s", sql)) + ret = FAIL; + } + + zbx_free(sql); + + return ret; +} + +static int dbpatch_validate_key_macro(const char *key) +{ + char *params, *macro; + + if (NULL == (macro = strchr(key, '{'))) + return SUCCEED; + + if (NULL != (params = strchr(key, '[')) && params < macro) + return SUCCEED; + + return FAIL; +} + +static char *dbpatch_formula_to_expression(zbx_uint64_t itemid, const char *formula, zbx_vector_ptr_t *functions) +{ + zbx_dbpatch_function_t *func; + const char *ptr; + char *exp = NULL, error[128]; + size_t exp_alloc = 0, exp_offset = 0, pos = 0, par_l, par_r; + + for (ptr = formula; SUCCEED == zbx_function_find(ptr, &pos, &par_l, &par_r, error, sizeof(error)); + ptr += par_r + 1) + { + size_t param_pos, param_len, sep_pos; + int quoted; + + /* copy the part of the string preceding function */ + zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, ptr, pos); + + if (SUCCEED != dbpatch_is_time_function(ptr + pos, par_l - pos)) + { + char *arg0, *host = NULL, *key = NULL; + int ret; + size_t arg0_len; + + zbx_function_param_parse(ptr + par_l + 1, ¶m_pos, ¶m_len, &sep_pos); + + arg0 = zbx_function_param_unquote_dyn(ptr + par_l + 1 + param_pos, param_len, "ed); + arg0_len = strlen(arg0); + zbx_remove_chars(arg0, "\t\n\r"); + if (strlen(arg0) != arg0_len) + { + zabbix_log(LOG_LEVEL_WARNING, "control characters were removed from calculated item \"" + ZBX_FS_UI64 "\" formula host:key parameter at %s", itemid, ptr); + } + + ret = parse_host_key(arg0, &host, &key); + zbx_free(arg0); + + if (FAIL == ret) + { + zbx_vector_ptr_clear_ext(functions, (zbx_clean_func_t)dbpatch_function_free); + zbx_free(exp); + return NULL; + } + + if (SUCCEED != dbpatch_validate_key_macro(key)) + { + zabbix_log(LOG_LEVEL_WARNING, "invalid key parameter \"%s\" in calculated item \"" + ZBX_FS_UI64 "\" formula: using macro within item key is not supported" + " anymore", key, itemid); + } + + func = (zbx_dbpatch_function_t *)zbx_malloc(NULL, sizeof(zbx_dbpatch_function_t)); + func->itemid = itemid; + func->name = zbx_substr(ptr, pos, par_l - 1); + func->flags = 0; + + func->arg0 = zbx_dsprintf(NULL, "/%s/%s", ZBX_NULL2EMPTY_STR(host), key); + zbx_free(host); + zbx_free(key); + + if (')' != ptr[par_l + 1 + sep_pos]) + func->parameter = zbx_substr(ptr, par_l + sep_pos + 2, par_r - 1); + else + func->parameter = zbx_strdup(NULL, ""); + + func->functionid = functions->values_num; + zbx_vector_ptr_append(functions, func); + + zbx_snprintf_alloc(&exp, &exp_alloc, &exp_offset, "{" ZBX_FS_UI64 "}", func->functionid); + } + else + { + zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, ptr + pos, par_l - pos); + zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, "()"); + } + } + + if (par_l <= par_r) + zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, ptr); + + return exp; +} + +static int DBpatch_5030165(void) +{ + DB_ROW row; + DB_RESULT result; + zbx_vector_ptr_t functions; + int i, ret = SUCCEED; + char *sql = NULL; + size_t sql_alloc = 0, sql_offset = 0; + + zbx_vector_ptr_create(&functions); + + DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset); + + result = DBselect("select i.itemid,i.params" + " from items i,hosts h" + " where i.type=15" + " and h.hostid=i.hostid" + " order by i.itemid"); + + while (SUCCEED == ret && NULL != (row = DBfetch(result))) + { + zbx_uint64_t itemid, index; + char *expression, *out = NULL; + int pos = 0, last_pos = 0; + zbx_token_t token; + size_t out_alloc = 0, out_offset = 0; + + ZBX_STR2UINT64(itemid, row[0]); + if (NULL == (expression = dbpatch_formula_to_expression(itemid, row[1], &functions))) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot convert calculated item \"" ZBX_FS_UI64 "\"formula", + itemid); + continue; + } + + for (i = 0; i < functions.values_num; i++) + { + char *replace = NULL; + zbx_dbpatch_function_t *func = functions.values[i]; + + dbpatch_convert_function(func, &replace, &functions); + if (NULL != replace) + { + dbpatch_update_expression(&expression, func->functionid, replace); + zbx_free(replace); + } + } + + for (; SUCCEED == zbx_token_find(expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID); pos++) + { + switch (token.type) + { + case ZBX_TOKEN_OBJECTID: + if (SUCCEED == is_uint64_n(expression + token.loc.l + 1, + token.loc.r - token.loc.l - 1, &index) && + (int)index < functions.values_num) + { + zbx_dbpatch_function_t *func = functions.values[index]; + + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, + expression + last_pos, token.loc.l - last_pos); + + zbx_snprintf_alloc(&out, &out_alloc, &out_offset, "%s(%s", + func->name, func->arg0); + if ('\0' != *func->parameter) + { + zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ','); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, func->parameter); + } + zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ')'); + last_pos = token.loc.r + 1; + } + pos = token.loc.r; + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + break; + } + } + + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos); + + if (ITEM_PARAM_LEN < zbx_strlen_utf8(out)) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot convert calculated item \"" ZBX_FS_UI64 "\" formula:" + " too long expression", itemid); + } + else + { + char *esc; + + esc = DBdyn_escape_field("items", "params", out); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update items set params='%s' where itemid=" + ZBX_FS_UI64 ";\n", esc, itemid); + zbx_free(esc); + + ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset); + } + + zbx_vector_ptr_clear_ext(&functions, (zbx_clean_func_t)dbpatch_function_free); + zbx_free(expression); + zbx_free(out); + } + + DBfree_result(result); + zbx_vector_ptr_destroy(&functions); + + DBend_multiple_update(&sql, &sql_alloc, &sql_offset); + + if (SUCCEED == ret && 16 < sql_offset) + { + if (ZBX_DB_OK > DBexecute("%s", sql)) + ret = FAIL; + } + + zbx_free(sql); + + return ret; +} + +static int dbpatch_aggregate2formula(const char *itemid, const AGENT_REQUEST *request, char **str, + size_t *str_alloc, size_t *str_offset, char **error) +{ + char *esc; + + if (3 > request->nparam) + { + *error = zbx_strdup(NULL, "invalid number of parameters"); + return FAIL; + } + + if (0 == strcmp(request->key, "grpavg")) + { + zbx_strcpy_alloc(str, str_alloc, str_offset, "avg"); + } + else if (0 == strcmp(request->key, "grpmax")) + { + zbx_strcpy_alloc(str, str_alloc, str_offset, "max"); + } + else if (0 == strcmp(request->key, "grpmin")) + { + zbx_strcpy_alloc(str, str_alloc, str_offset, "min"); + } + else if (0 == strcmp(request->key, "grpsum")) + { + zbx_strcpy_alloc(str, str_alloc, str_offset, "sum"); + } + else + { + *error = zbx_dsprintf(NULL, "unknown group function \"%s\"", request->key); + return FAIL; + } + + if (SUCCEED != dbpatch_validate_key_macro(request->params[1])) + { + zabbix_log(LOG_LEVEL_WARNING, "invalid key parameter \"%s\" when converting aggregate check \"%s\"" + " to calculated item: using macro within item key is not supported anymore", + request->params[1], itemid); + } + + zbx_rtrim(request->params[1], " "); + zbx_snprintf_alloc(str, str_alloc, str_offset, "(%s_foreach(/*/%s?[", request->params[2], request->params[1]); + + if (REQUEST_PARAMETER_TYPE_ARRAY == get_rparam_type(request, 0)) + { + int i, groups_num; + char *group; + zbx_request_parameter_type_t type; + + groups_num = num_param(request->params[0]); + + for (i = 1; i <= groups_num; i++) + { + if (NULL == (group = get_param_dyn(request->params[0], i, &type))) + continue; + + if ('[' != (*str)[*str_offset - 1]) + zbx_strcpy_alloc(str, str_alloc, str_offset, " or "); + + esc = zbx_dyn_escape_string(group, "\\\""); + zbx_snprintf_alloc(str, str_alloc, str_offset, "group=\"%s\"", esc); + zbx_free(esc); + zbx_free(group); + } + } + else + { + esc = zbx_dyn_escape_string(request->params[0], "\\\""); + zbx_snprintf_alloc(str, str_alloc, str_offset, "group=\"%s\"", esc); + zbx_free(esc); + } + + zbx_chrcpy_alloc(str, str_alloc, str_offset, ']'); + + if (4 == request->nparam) + zbx_snprintf_alloc(str, str_alloc, str_offset, ",%s", request->params[3]); + + zbx_strcpy_alloc(str, str_alloc, str_offset, "))"); + + if (ITEM_PARAM_LEN < zbx_strlen_utf8(*str)) + { + *error = zbx_strdup(NULL, "resulting formula is too long"); + return FAIL; + } + + return SUCCEED; +} + +static int DBpatch_5030166(void) +{ + DB_ROW row; + DB_RESULT result; + int ret = SUCCEED; + char *sql = NULL, *params = NULL; + size_t sql_alloc = 0, sql_offset = 0, params_alloc = 0, params_offset; + + DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset); + + /* ITEM_TYPE_AGGREGATE = 8 */ + result = DBselect("select itemid,key_ from items where type=8"); + + while (SUCCEED == ret && NULL != (row = DBfetch(result))) + { + AGENT_REQUEST request; + char *error = NULL, *esc; + int ret_formula; + + params_offset = 0; + + init_request(&request); + parse_item_key(row[1], &request); + + ret_formula = dbpatch_aggregate2formula(row[0], &request, ¶ms, ¶ms_alloc, ¶ms_offset, + &error); + free_request(&request); + + if (FAIL == ret_formula) + { + zabbix_log(LOG_LEVEL_WARNING, "Cannot convert aggregate checks item \"%s\": %s", row[0], error); + zbx_free(error); + continue; + } + + esc = DBdyn_escape_field("items", "params", params); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, + "update items set type=15,params='%s' where itemid=%s;\n", esc, row[0]); + zbx_free(esc); + + ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset); + } + + DBfree_result(result); + + DBend_multiple_update(&sql, &sql_alloc, &sql_offset); + + if (SUCCEED == ret && 16 < sql_offset) + { + if (ZBX_DB_OK > DBexecute("%s", sql)) + ret = FAIL; + } + + zbx_free(params); + zbx_free(sql); + + return ret; +} + +static int DBpatch_5030167(void) +{ +#ifdef HAVE_MYSQL + return DBcreate_index("items", "items_8", "key_(1024)", 0); +#else + return DBcreate_index("items", "items_8", "key_", 0); +#endif +} + #undef HOST_STATUS_TEMPLATE #define HOST_STATUS_TEMPLATE 3 #undef ZBX_FLAG_DISCOVERY_NORMAL @@ -4666,56 +6303,56 @@ static int DBpatch_5030163(void) #define ZBX_FIELD_UUID {"uuid", "", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0} -static int DBpatch_5030164(void) +static int DBpatch_5030168(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; return DBadd_field("items", &field); } -static int DBpatch_5030165(void) +static int DBpatch_5030169(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; return DBadd_field("hosts", &field); } -static int DBpatch_5030166(void) +static int DBpatch_5030170(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; return DBadd_field("triggers", &field); } -static int DBpatch_5030167(void) +static int DBpatch_5030171(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; return DBadd_field("dashboard", &field); } -static int DBpatch_5030168(void) +static int DBpatch_5030172(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; return DBadd_field("graphs", &field); } -static int DBpatch_5030169(void) +static int DBpatch_5030173(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; return DBadd_field("hstgrp", &field); } -static int DBpatch_5030170(void) +static int DBpatch_5030174(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; return DBadd_field("httptest", &field); } -static int DBpatch_5030171(void) +static int DBpatch_5030175(void) { const ZBX_FIELD field = ZBX_FIELD_UUID; @@ -4750,7 +6387,35 @@ static char *update_template_name(char *old) return ptr; } -static int DBpatch_5030172(void) +static char *DBpatch_make_trigger_function(const char *name, const char *tpl, const char *key, const char *param) +{ + char *template_name, *func = NULL; + size_t func_alloc = 0, func_offset = 0; + + template_name = zbx_strdup(NULL, tpl); + template_name = update_template_name(template_name); + + zbx_snprintf_alloc(&func, &func_alloc, &func_offset, "%s(/%s/%s", name, template_name, key); + + if ('\0' != *param) + { + if ('$' == *param) + { + if (',' == *++param) + param++; + } + + zbx_snprintf_alloc(&func, &func_alloc, &func_offset, "%s", param); + } + + zbx_chrcpy_alloc(&func, &func_alloc, &func_offset, ')'); + + zbx_free(template_name); + + return func; +} + +static int DBpatch_5030176(void) { int ret = SUCCEED; char *name, *uuid, *sql = NULL; @@ -4794,7 +6459,7 @@ out: return ret; } -static int DBpatch_5030173(void) +static int DBpatch_5030177(void) { int ret = SUCCEED; char *name, *uuid, *sql = NULL, *seed = NULL; @@ -4841,7 +6506,7 @@ out: return ret; } -static int DBpatch_5030174(void) +static int DBpatch_5030178(void) { int ret = SUCCEED; char *uuid, *sql = NULL, *seed = NULL; @@ -4855,7 +6520,7 @@ static int DBpatch_5030174(void) DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset); result = DBselect( - "select t.triggerid,t.description,t.expression,t.recovery_expression" + "select distinct t.triggerid,t.description,t.expression,t.recovery_expression" " from triggers t" " join functions f on f.triggerid=t.triggerid" " join items i on i.itemid=f.itemid" @@ -4865,24 +6530,52 @@ static int DBpatch_5030174(void) while (NULL != (row = DBfetch(result))) { - const char *pexpr, *pexpr_f, *pexpr_s; - char *trigger_expr, *expression = NULL; + char *trigger_expr; + char *composed_expr[] = { NULL, NULL }; int i; - size_t expression_alloc = 0, expression_offset = 0; - zbx_uint64_t functionid; DB_ROW row2; DB_RESULT result2; for (i = 0; i < 2; i++) { - int expr_start = 1; + int j; + char *error = NULL; + zbx_eval_context_t ctx; trigger_expr = zbx_strdup(NULL, row[i + 2]); - pexpr = pexpr_f = (const char *)trigger_expr; - while (SUCCEED == get_N_functionid(pexpr, 1, &functionid, (const char **)&pexpr_s, &pexpr_f)) + zabbix_log(LOG_LEVEL_DEBUG, "%s: trigger expression: %s", __func__, trigger_expr); + + if ('\0' == *trigger_expr) { - trigger_expr[pexpr_s - trigger_expr] = '\0'; + if (0 == i) + zabbix_log(LOG_LEVEL_WARNING, "%s: empty expression for trigger %s", __func__, row[0]); + continue; + } + + if (FAIL == zbx_eval_parse_expression(&ctx, trigger_expr, ZBX_EVAL_PARSE_TRIGGER_EXPRESSSION, &error)) + { + zabbix_log(LOG_LEVEL_CRIT, "%s: error parsing trigger expression for %s: %s", + __func__, row[0], error); + zbx_free(error); + return FAIL; + } + + for (j = 0; j < ctx.stack.values_num; j++) + { + zbx_eval_token_t *token = &ctx.stack.values[j]; + zbx_uint64_t functionid; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) + continue; + + if (SUCCEED != is_uint64_n(ctx.expression + token->loc.l + 1, token->loc.r - token->loc.l - 1, + &functionid)) + { + zabbix_log(LOG_LEVEL_CRIT, "%s: error parsing trigger expression %s, is_uint64_n error", + __func__, row[0]); + return FAIL; + } result2 = DBselect( "select h.host,i.key_,f.name,f.parameter" @@ -4892,41 +6585,39 @@ static int DBpatch_5030174(void) " where f.functionid=" ZBX_FS_UI64, functionid); - while (NULL != (row2 = DBfetch(result2))) + if (NULL != (row2 = DBfetch(result2))) { - char *template_name; + char *func; - if (1 == expr_start) - { - zbx_snprintf_alloc(&expression, &expression_alloc, &expression_offset, - "/"); - expr_start = 0; - } - template_name = zbx_strdup(NULL, row2[0]); - template_name = update_template_name(template_name); - zbx_snprintf_alloc(&expression, &expression_alloc, &expression_offset, - "%s{%s:%s.%s(%s)}",pexpr, template_name, - row2[1], row2[2], row2[3]); - pexpr = pexpr_f; - zbx_free(template_name); + func = DBpatch_make_trigger_function(row2[2], row2[0], row2[1], row2[3]); + zbx_variant_clear(&token->value); + zbx_variant_set_str(&token->value, func); } DBfree_result(result2); } - if (pexpr != trigger_expr) - zbx_snprintf_alloc(&expression, &expression_alloc, &expression_offset, "%s", pexpr); + zbx_eval_compose_expression(&ctx, &composed_expr[i]); + zbx_eval_clear(&ctx); + + zabbix_log(LOG_LEVEL_DEBUG, "%s: result expression '%s'", __func__, composed_expr[i]); zbx_free(trigger_expr); } - zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", row[1]); - zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", expression); + zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s/", row[1]); + zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", composed_expr[0]); + if (NULL != composed_expr[1]) + zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s", composed_expr[1]); + + zabbix_log(LOG_LEVEL_DEBUG, "%s: seed: %s", __func__, seed); uuid = zbx_gen_uuid4(seed); zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set uuid='%s'" " where triggerid=%s;\n", uuid, row[0]); - zbx_free(expression); + + zbx_free(composed_expr[0]); + zbx_free(composed_expr[1]); zbx_free(uuid); zbx_free(seed); @@ -4945,7 +6636,7 @@ out: return ret; } -static int DBpatch_5030175(void) +static int DBpatch_5030179(void) { int ret = SUCCEED; char *host_name, *uuid, *sql = NULL, *seed = NULL; @@ -5014,7 +6705,7 @@ out: return ret; } -static int DBpatch_5030176(void) +static int DBpatch_5030180(void) { int ret = SUCCEED; char *template_name, *uuid, *sql = NULL, *seed = NULL; @@ -5061,7 +6752,7 @@ out: return ret; } -static int DBpatch_5030177(void) +static int DBpatch_5030181(void) { int ret = SUCCEED; char *template_name, *uuid, *sql = NULL, *seed = NULL; @@ -5108,7 +6799,7 @@ out: return ret; } -static int DBpatch_5030178(void) +static int DBpatch_5030182(void) { int ret = SUCCEED; char *template_name, *uuid, *sql = NULL, *seed = NULL; @@ -5155,7 +6846,7 @@ out: return ret; } -static int DBpatch_5030179(void) +static int DBpatch_5030183(void) { int ret = SUCCEED; char *uuid, *sql = NULL; @@ -5192,7 +6883,7 @@ out: return ret; } -static int DBpatch_5030180(void) +static int DBpatch_5030184(void) { int ret = SUCCEED; char *template_name, *uuid, *sql = NULL, *seed = NULL; @@ -5241,13 +6932,13 @@ out: return ret; } -static int DBpatch_5030181(void) +static int DBpatch_5030185(void) { - int ret = SUCCEED; - char *uuid, *sql = NULL, *seed = NULL; - size_t sql_alloc = 0, sql_offset = 0, seed_alloc = 0, seed_offset = 0; - DB_ROW row; - DB_RESULT result; + int ret = SUCCEED; + char *uuid, *sql = NULL, *seed = NULL; + size_t sql_alloc = 0, sql_offset = 0, seed_alloc = 0, seed_offset = 0; + DB_ROW row; + DB_RESULT result; if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) return ret; @@ -5265,11 +6956,9 @@ static int DBpatch_5030181(void) while (NULL != (row = DBfetch(result))) { - const char *pexpr, *pexpr_f, *pexpr_s; - char *trigger_expr, *total_expr = NULL; + char *trigger_expr; + char *composed_expr[] = { NULL, NULL }; int i; - size_t total_expr_alloc = 0, total_expr_offset = 0; - zbx_uint64_t functionid; DB_ROW row2; DB_RESULT result2; @@ -5294,14 +6983,44 @@ static int DBpatch_5030181(void) for (i = 0; i < 2; i++) { - int expr_start = 1; + int j; + char *error = NULL; + zbx_eval_context_t ctx; trigger_expr = zbx_strdup(NULL, row[i + 2]); - pexpr = pexpr_f = (const char *)trigger_expr; - while (SUCCEED == get_N_functionid(pexpr, 1, &functionid, (const char **)&pexpr_s, &pexpr_f)) + zabbix_log(LOG_LEVEL_DEBUG, "trigger expression: %s", trigger_expr); + + if ('\0' == *trigger_expr) { - trigger_expr[pexpr_s - trigger_expr] = '\0'; + if (0 == i) + zabbix_log(LOG_LEVEL_WARNING, "%s: empty expression for trigger %s", __func__, row[0]); + continue; + } + + if (FAIL == zbx_eval_parse_expression(&ctx, trigger_expr, ZBX_EVAL_TRIGGER_EXPRESSION_LLD, &error)) + { + zabbix_log(LOG_LEVEL_CRIT, "%s: error parsing trigger expression for %s: %s", + __func__, row[0], error); + zbx_free(error); + return FAIL; + } + + for (j = 0; j < ctx.stack.values_num; j++) + { + zbx_eval_token_t *token = &ctx.stack.values[j]; + zbx_uint64_t functionid; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) + continue; + + if (SUCCEED != is_uint64_n(ctx.expression + token->loc.l + 1, token->loc.r - token->loc.l - 1, + &functionid)) + { + zabbix_log(LOG_LEVEL_CRIT, "%s: error parsing trigger expression %s, is_uint64_n error", + __func__, row[0]); + return FAIL; + } result2 = DBselect( "select h.host,i.key_,f.name,f.parameter" @@ -5311,43 +7030,39 @@ static int DBpatch_5030181(void) " where f.functionid=" ZBX_FS_UI64, functionid); - while (NULL != (row2 = DBfetch(result2))) + if (NULL != (row2 = DBfetch(result2))) { - char *template_name; - - if (1 == expr_start) - { - zbx_snprintf_alloc(&total_expr, &total_expr_alloc, &total_expr_offset, - "/"); - expr_start = 0; - } + char *func; - template_name = zbx_strdup(NULL, row2[0]); - template_name = update_template_name(template_name); - - zbx_snprintf_alloc(&total_expr, &total_expr_alloc, &total_expr_offset, - "%s{%s:%s.%s(%s)}",pexpr, template_name, - row2[1], row2[2], row2[3]); - pexpr = pexpr_f; - zbx_free(template_name); + func = DBpatch_make_trigger_function(row2[2], row2[0], row2[1], row2[3]); + zbx_variant_clear(&token->value); + zbx_variant_set_str(&token->value, func); } DBfree_result(result2); } - if (pexpr != trigger_expr) - zbx_snprintf_alloc(&total_expr, &total_expr_alloc, &total_expr_offset, "%s", pexpr); + zbx_eval_compose_expression(&ctx, &composed_expr[i]); + zbx_eval_clear(&ctx); + + zabbix_log(LOG_LEVEL_DEBUG, "result expression '%s'", composed_expr[i]); zbx_free(trigger_expr); } - zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s", row[1]); - zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", total_expr); + zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s/", row[1]); + zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", composed_expr[0]); + if (NULL != composed_expr[1]) + zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s", composed_expr[1]); + + zabbix_log(LOG_LEVEL_DEBUG, "seed: %s", seed); uuid = zbx_gen_uuid4(seed); zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set uuid='%s'" " where triggerid=%s;\n", uuid, row[0]); - zbx_free(total_expr); + + zbx_free(composed_expr[0]); + zbx_free(composed_expr[1]); zbx_free(uuid); zbx_free(seed); @@ -5366,7 +7081,7 @@ out: return ret; } -static int DBpatch_5030182(void) +static int DBpatch_5030186(void) { int ret = SUCCEED; char *templ_name, *uuid, *sql = NULL, *seed = NULL; @@ -5417,7 +7132,7 @@ out: return ret; } -static int DBpatch_5030183(void) +static int DBpatch_5030187(void) { int ret = SUCCEED; char *name_tmpl, *uuid, *seed = NULL, *sql = NULL; @@ -5661,5 +7376,9 @@ DBPATCH_ADD(5030180, 0, 1) DBPATCH_ADD(5030181, 0, 1) DBPATCH_ADD(5030182, 0, 1) DBPATCH_ADD(5030183, 0, 1) +DBPATCH_ADD(5030184, 0, 1) +DBPATCH_ADD(5030185, 0, 1) +DBPATCH_ADD(5030186, 0, 1) +DBPATCH_ADD(5030187, 0, 1) DBPATCH_END() diff --git a/src/libs/zbxembed/xml.c b/src/libs/zbxembed/xml.c index 7437fde6bec..8125f041120 100644 --- a/src/libs/zbxembed/xml.c +++ b/src/libs/zbxembed/xml.c @@ -17,7 +17,7 @@ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ -#include "common.h" +#include "zbxvariant.h" #include "zbxembed.h" #include "embed.h" diff --git a/src/libs/zbxeval/Makefile.am b/src/libs/zbxeval/Makefile.am new file mode 100644 index 00000000000..c6d4dc4c93a --- /dev/null +++ b/src/libs/zbxeval/Makefile.am @@ -0,0 +1,9 @@ +## Process this file with automake to produce Makefile.in + +noinst_LIBRARIES = libzbxeval.a + +libzbxeval_a_SOURCES = \ + parse.c \ + execute.c \ + misc.c \ + query.c diff --git a/src/zabbix_server/poller/checks_aggregate.h b/src/libs/zbxeval/eval.h index 8534832a1c8..d3456a1ef22 100644 --- a/src/zabbix_server/poller/checks_aggregate.h +++ b/src/libs/zbxeval/eval.h @@ -17,12 +17,14 @@ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ -#ifndef ZABBIX_CHECKS_AGGREGATE_H -#define ZABBIX_CHECKS_AGGREGATE_H +#ifndef ZABBIX_EVAL_H +#define ZABBIX_EVAL_H -#include "dbcache.h" -#include "sysinfo.h" +#include "common.h" -extern int get_value_aggregate(const DC_ITEM *item, AGENT_RESULT *result); +int eval_suffixed_number_parse(const char *value, char *suffix); +int eval_compare_token(const zbx_eval_context_t *ctx, const zbx_strloc_t *loc, const char *text, + size_t len); +size_t eval_parse_query(const char *str, const char **phost, const char **pkey, const char **pfilter); #endif diff --git a/src/libs/zbxeval/execute.c b/src/libs/zbxeval/execute.c new file mode 100644 index 00000000000..f8f16a6f806 --- /dev/null +++ b/src/libs/zbxeval/execute.c @@ -0,0 +1,1619 @@ +/* +** Zabbix +** Copyright (C) 2001-2020 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 "common.h" +#include "log.h" +#include "zbxalgo.h" +#include "zbxserver.h" +#include "eval.h" + +/* exit code in addition to SUCCEED/FAIL */ +#define UNKNOWN 1 + +/****************************************************************************** + * * + * Function: variant_convert_suffixed_num * + * * + * 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; + + if (ZBX_VARIANT_STR != value_num->type) + return FAIL; + + if (SUCCEED != eval_suffixed_number_parse(value_num->data.str, &suffix)) + return FAIL; + + zbx_variant_set_dbl(value, atof(value_num->data.str) * suffix2factor(suffix)); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_execute_op_unary * + * * + * 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; + } + + zbx_variant_clear(right); + zbx_variant_set_dbl(right, value); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_execute_op_logic_err * + * * + * 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 oeprator 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; +} + +/****************************************************************************** + * * + * Function: eval_variant_compare * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_op_binary * + * * + * 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_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; + } +finish: + zbx_variant_clear(left); + zbx_variant_clear(right); + zbx_variant_set_dbl(left, value); + output->values_num--; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_suffixed_number_parse * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_push_value * + * * + * 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 == 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_push_null * + * * + * 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); +} + +/****************************************************************************** + * * + * Function: eval_compare_token * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_function_return * + * * + * 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(int args_num, zbx_variant_t *value, zbx_vector_var_t *output) +{ + int i; + + for (i = output->values_num - args_num; i < output->values_num; i++) + zbx_variant_clear(&output->values[i]); + output->values_num -= args_num; + + zbx_vector_var_append_ptr(output, value); +} + +/****************************************************************************** + * * + * Function: eval_validate_function_args * + * * + * 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); + } +} + +/****************************************************************************** + * * + * Function: eval_convert_function_arg * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_prepare_math_function_args * + * * + * 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, "empty vector argument for function at \"%s\"", + ctx->expression + token->loc.l); + return FAIL; + } + } + + return UNKNOWN; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_min * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_max * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_sum * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_avg * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_abs * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_length * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_date * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_time * + * * + * 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; +} +/****************************************************************************** + * * + * Function: eval_execute_function_now * + * * + * 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; +} +/****************************************************************************** + * * + * Function: eval_execute_function_dayofweek * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_dayofmonth * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_execute_function_bitand * + * * + * Purpose: evaluate bitand() 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_bitand(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, + 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 != zbx_variant_convert(left, ZBX_VARIANT_UI64)) + { + *error = zbx_dsprintf(*error, "function argument \"%s\" is not an unsigned integer value at \"%s\"", + zbx_variant_value_desc(left), ctx->expression + token->loc.l); + return FAIL; + } + + if (SUCCEED != zbx_variant_convert(right, ZBX_VARIANT_UI64)) + { + *error = zbx_dsprintf(*error, "function argument \"%s\" is not an unsigned integer value at \"%s\"", + zbx_variant_value_desc(right), ctx->expression + token->loc.l); + return FAIL; + } + + zbx_variant_set_ui64(&value, left->data.ui64 & right->data.ui64); + eval_function_return(2, &value, output); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_execute_cb_function * + * * + * Purpose: evaluate function by calling custom callback (if configured) * + * * + * Parameters: ctx - [IN] the evaluation context * + * token - [IN] the function token * + * functio_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; +} + +/****************************************************************************** + * * + * Function: eval_execute_common_function * + * * + * 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_bitand(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; +} + +/****************************************************************************** + * * + * Function: eval_execute_history_function * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_throw_exception * + * * + * 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); +} + +/****************************************************************************** + * * + * Function: eval_execute * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: eval_init_execute_context * + * * + * 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; +} + +/****************************************************************************** + * * + * Function: zbx_eval_execute * + * * + * 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); +} + +/****************************************************************************** + * * + * Function: zbx_eval_execute_ext * + * * + * 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); +} diff --git a/src/libs/zbxeval/misc.c b/src/libs/zbxeval/misc.c new file mode 100644 index 00000000000..9f6b86fb14c --- /dev/null +++ b/src/libs/zbxeval/misc.c @@ -0,0 +1,1120 @@ +/* +** Zabbix +** Copyright (C) 2001-2020 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 "common.h" +#include "log.h" + +#include "zbxalgo.h" +#include "../zbxalgo/vectorimpl.h" +#include "zbxvariant.h" +#include "zbxserialize.h" +#include "zbxserver.h" +#include "eval.h" + +#define ZBX_EVAL_STATIC_BUFFER_SIZE 4096 + +/****************************************************************************** + * * + * Function: reserve_buffer * + * * + * Purpose: reserve number of bytes in the specified buffer, reallocating if * + * necessary * + * * + * Parameters: buffer - [IN/OUT] the buffer * + * buffer_size - [INT/OUT] the deserialized value * + * reserve - [IN] the number of bytes to reserve * + * ptr - [IN/OUT] a pointer to an offset in buffer * + * * + * Comments: Initially static buffer is used, allocating dynamic buffer when * + * static buffer is too small. * + * * + ******************************************************************************/ +static void reserve_buffer(unsigned char **buffer, size_t *buffer_size, size_t reserve, unsigned char **ptr) +{ + size_t offset = *ptr - *buffer, new_size; + + if (offset + reserve <= *buffer_size) + return; + + new_size = *buffer_size * 1.5; + + if (ZBX_EVAL_STATIC_BUFFER_SIZE == *buffer_size) + { + unsigned char *old = *buffer; + + *buffer = zbx_malloc(NULL, new_size); + memcpy(*buffer, old, offset); + } + else + *buffer = zbx_realloc(*buffer, new_size); + + *buffer_size = new_size; + *ptr = *buffer + offset; +} + +static void serialize_variant(unsigned char **buffer, size_t *size, const zbx_variant_t *value, + unsigned char **ptr) +{ + size_t len; + + reserve_buffer(buffer, size, 1, ptr); + **ptr = value->type; + (*ptr)++; + + switch (value->type) + { + case ZBX_VARIANT_UI64: + reserve_buffer(buffer, size, sizeof(value->data.ui64), ptr); + *ptr += zbx_serialize_uint64(*ptr, value->data.ui64); + break; + case ZBX_VARIANT_DBL: + reserve_buffer(buffer, size, sizeof(value->data.dbl), ptr); + *ptr += zbx_serialize_double(*ptr, value->data.dbl); + break; + case ZBX_VARIANT_STR: + len = strlen(value->data.str) + 1; + reserve_buffer(buffer, size, len, ptr); + memcpy(*ptr, value->data.str, len); + *ptr += len; + break; + case ZBX_VARIANT_NONE: + break; + default: + zabbix_log(LOG_LEVEL_DEBUG, "TYPE: %d", value->type); + THIS_SHOULD_NEVER_HAPPEN; + (*ptr)[-1] = ZBX_VARIANT_NONE; + break; + } +} + +static zbx_uint32_t deserialize_variant(const unsigned char *ptr, zbx_variant_t *value) +{ + const unsigned char *start = ptr; + unsigned char type; + zbx_uint64_t ui64; + double dbl; + char *str; + size_t len; + + ptr += zbx_deserialize_char(ptr, &type); + + switch (type) + { + case ZBX_VARIANT_UI64: + ptr += zbx_deserialize_uint64(ptr, &ui64); + zbx_variant_set_ui64(value, ui64); + break; + case ZBX_VARIANT_DBL: + ptr += zbx_deserialize_double(ptr, &dbl); + zbx_variant_set_dbl(value, dbl); + break; + case ZBX_VARIANT_STR: + len = strlen((const char *)ptr) + 1; + str = zbx_malloc(NULL, len); + memcpy(str, ptr, len); + zbx_variant_set_str(value, str); + ptr += len; + break; + case ZBX_VARIANT_NONE: + zbx_variant_set_none(value); + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + zbx_variant_set_none(value); + break; + } + + return ptr - start; +} + +/****************************************************************************** + * * + * Function: zbx_eval_serialize * + * * + * Purpose: serialize evaluation context into buffer * + * * + * Parameters: ctx - [IN] the evaluation context * + * malloc_func - [IN] the buffer memory allocation function, * + * optional (by default the buffer is * + * allocated in heap) * + * data - [OUT] the buffer with serialized evaluation context * + * * + * Comments: Location of the replaced tokens (with token.value set) are not * + * serialized, making it impossible to reconstruct the expression * + * text with replaced tokens. * + * Context serialization/deserialization must be used for * + * context caching. * + * * + * Return value: The size of serialized data. * + * * + ******************************************************************************/ +size_t zbx_eval_serialize(const zbx_eval_context_t *ctx, zbx_mem_malloc_func_t malloc_func, + unsigned char **data) +{ + int i; + unsigned char buffer_static[ZBX_EVAL_STATIC_BUFFER_SIZE], *buffer = buffer_static, *ptr = buffer, len_buff[6]; + size_t buffer_size = ZBX_EVAL_STATIC_BUFFER_SIZE; + zbx_uint32_t len, len_offset; + + if (NULL == malloc_func) + malloc_func = ZBX_DEFAULT_MEM_MALLOC_FUNC; + + ptr += zbx_serialize_uint31_compact(ptr, ctx->stack.values_num); + + for (i = 0; i < ctx->stack.values_num; i++) + { + const zbx_eval_token_t *token = &ctx->stack.values[i]; + + /* reserve space for maximum possible worst case scenario with empty variant: */ + /* 4 bytes token type, 6 bytes per compact uint31 and 1 byte empty variant (4+3*6+1) */ + reserve_buffer(&buffer, &buffer_size, 23, &ptr); + + ptr += zbx_serialize_value(ptr, token->type); + ptr += zbx_serialize_uint31_compact(ptr, token->opt); + ptr += zbx_serialize_uint31_compact(ptr, token->loc.l); + ptr += zbx_serialize_uint31_compact(ptr, token->loc.r); + + serialize_variant(&buffer, &buffer_size, &token->value, &ptr); + } + + len = ptr - buffer; + + len_offset = zbx_serialize_uint31_compact(len_buff, len); + + *data = malloc_func(NULL, len + len_offset); + memcpy(*data, len_buff, len_offset); + memcpy(*data + len_offset, buffer, len); + + if (buffer != buffer_static) + zbx_free(buffer); + + return len + len_offset; +} + +/****************************************************************************** + * * + * Function: zbx_eval_deserialize * + * * + * Purpose: deserialize evaluation context from buffer * + * * + * Parameters: ctx - [OUT] the evaluation context * + * expression - [IN] the expression the evaluation context was * + * created from * + * rules - [IN] the composition and evaluation rules * + * data - [IN] the buffer with serialized context * + * * + ******************************************************************************/ +void zbx_eval_deserialize(zbx_eval_context_t *ctx, const char *expression, zbx_uint64_t rules, + const unsigned char *data) +{ + zbx_uint32_t i, tokens_num, len, pos; + + memset(ctx, 0, sizeof(zbx_eval_context_t)); + ctx->expression = expression; + ctx->rules = rules; + + data += zbx_deserialize_uint31_compact(data, &len); + data += zbx_deserialize_uint31_compact(data, &tokens_num); + zbx_vector_eval_token_create(&ctx->stack); + zbx_vector_eval_token_reserve(&ctx->stack, tokens_num); + ctx->stack.values_num = tokens_num; + + for (i = 0; i < tokens_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + data += zbx_deserialize_value(data, &token->type); + data += zbx_deserialize_uint31_compact(data, &token->opt); + + data += zbx_deserialize_uint31_compact(data, &pos); + token->loc.l = pos; + data += zbx_deserialize_uint31_compact(data, &pos); + token->loc.r = pos; + + data += deserialize_variant(data, &token->value); + } +} + +static int compare_tokens_by_loc(const void *d1, const void *d2) +{ + const zbx_eval_token_t *t1 = *(const zbx_eval_token_t * const *)d1; + const zbx_eval_token_t *t2 = *(const zbx_eval_token_t * const *)d2; + + ZBX_RETURN_IF_NOT_EQUAL(t1->loc.l, t2->loc.l); + return 0; +} + +/****************************************************************************** + * * + * Function: eval_token_print_alloc * + * * + * Purpose: print token into string quoting/escaping if necessary * + * * + * Parameters: ctx - [IN] the evaluation context * + * str - [IN/OUT] the output buffer * + * str_alloc - [IN/OUT] the output buffer size * + * str_offset - [IN/OUT] the output buffer offset * + * token - [IN] the token to print * + * * + ******************************************************************************/ +static void eval_token_print_alloc(const zbx_eval_context_t *ctx, char **str, size_t *str_alloc, size_t *str_offset, + const zbx_eval_token_t *token) +{ + int quoted = 0, check_value = 0; + const char *value_str; + + if (ZBX_VARIANT_NONE == token->value.type) + return; + + if (ZBX_VARIANT_ERR == token->value.type) + { + if (0 == (ctx->rules & ZBX_EVAL_COMPOSE_MASK_ERROR)) + zbx_snprintf_alloc(str, str_alloc, str_offset, "ERROR(%s)", token->value.data.err); + else + zbx_strcpy_alloc(str, str_alloc, str_offset, "*ERROR*"); + return; + } + + switch (token->type) + { + case ZBX_EVAL_TOKEN_FUNCTIONID: + if (0 != (ctx->rules & ZBX_EVAL_COMPOSE_FUNCTIONID)) + { + zbx_variant_t functionid; + + zbx_variant_copy(&functionid, &token->value); + + if (SUCCEED == zbx_variant_convert(&functionid, ZBX_VARIANT_UI64)) + { + zbx_snprintf_alloc(str, str_alloc, str_offset, "{" ZBX_FS_UI64 "}", + functionid.data.ui64); + return; + } + zbx_variant_clear(&functionid); + } + break; + case ZBX_EVAL_TOKEN_VAR_STR: + quoted = 1; + break; + case ZBX_EVAL_TOKEN_VAR_MACRO: + if (0 == (ctx->rules & ZBX_EVAL_COMPOSE_LLD)) + check_value = 1; + break; + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + if (0 != (ctx->rules & ZBX_EVAL_COMPOSE_QUOTE)) + quoted = 1; + else if (0 == (ctx->rules & ZBX_EVAL_COMPOSE_LLD)) + check_value = 1; + break; + case ZBX_EVAL_TOKEN_VAR_LLDMACRO: + if (0 != (ctx->rules & ZBX_EVAL_COMPOSE_QUOTE)) + quoted = 1; + else if (0 != (ctx->rules & ZBX_EVAL_COMPOSE_LLD)) + check_value = 1; + break; + } + + if (0 != check_value) + { + if (ZBX_VARIANT_STR == token->value.type && + SUCCEED != eval_suffixed_number_parse(token->value.data.str, NULL)) + { + quoted = 1; + } + } + + value_str = zbx_variant_value_desc(&token->value); + + if (0 == quoted) + zbx_strcpy_alloc(str, str_alloc, str_offset, value_str); + else + zbx_strquote_alloc(str, str_alloc, str_offset, value_str); +} + +/****************************************************************************** + * * + * Function: zbx_eval_compose_expression * + * * + * Purpose: compose expression by replacing processed tokens (with values) in * + * the original expression * + * * + * Parameters: ctx - [IN] the evaluation context * + * expression - [OUT] the composed expression * + * * + ******************************************************************************/ +void zbx_eval_compose_expression(const zbx_eval_context_t *ctx, char **expression) +{ + zbx_vector_ptr_t tokens; + const zbx_eval_token_t *token; + int i; + size_t pos = 0, expression_alloc = 0, expression_offset = 0; + + /* Handle exceptions that are set when expression evaluation failed. */ + /* Exception stack consists of two tokens - error message and exception. */ + if (2 == ctx->stack.values_num && ZBX_EVAL_TOKEN_EXCEPTION == ctx->stack.values[1].type) + { + zbx_strcpy_alloc(expression, &expression_alloc, &expression_offset, "throw("); + eval_token_print_alloc(ctx, expression, &expression_alloc, &expression_offset, &ctx->stack.values[0]); + zbx_chrcpy_alloc(expression, &expression_alloc, &expression_offset, ')'); + return; + } + + zbx_vector_ptr_create(&tokens); + + for (i = 0; i < ctx->stack.values_num; i++) + { + if (ZBX_VARIANT_NONE != ctx->stack.values[i].value.type) + zbx_vector_ptr_append(&tokens, &ctx->stack.values[i]); + } + + zbx_vector_ptr_sort(&tokens, compare_tokens_by_loc); + + for (i = 0; i < tokens.values_num; i++) + { + token = (const zbx_eval_token_t *)tokens.values[i]; + + if (0 != token->loc.l) + { + zbx_strncpy_alloc(expression, &expression_alloc, &expression_offset, ctx->expression + pos, + token->loc.l - pos); + } + pos = token->loc.r + 1; + eval_token_print_alloc(ctx, expression, &expression_alloc, &expression_offset, token); + } + + if ('\0' != ctx->expression[pos]) + zbx_strcpy_alloc(expression, &expression_alloc, &expression_offset, ctx->expression + pos); + + zbx_vector_ptr_destroy(&tokens); +} + +/****************************************************************************** + * * + * Function: eval_has_usermacro * + * * + * Purpose: check if string has possible user macro * + * * + * Parameters: str - [IN] the string to check * + * len - [IN] the string length * + * * + * Return value: SUCCEED - the string might contain a user macro * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_has_usermacro(const char *str, size_t len) +{ + const char *ptr; + + if (4 > len) + return FAIL; + + /* stop earlier to account for at least one character macro name and terminating '}' */ + for (ptr = str; ptr < str + len - 3; ) + { + if ('{' == *ptr++) + { + if ('$' == *ptr) + return SUCCEED; + ptr++; + } + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_query_expand_user_macros * + * * + * Purpose: expand user macros in item query * + * * + * Parameters: itemquery - [IN] the evaluation context * + * len - [IN] the item query length * + * hostids - [IN] the linked hostids * + * hostids_num - [IN] the number of linked hostids * + * resolver_cb - [IN] the resolver callback * + * out - [OUT] the item query with expanded macros * + * error - [OUT] the error message, optional. If specified * + * the function will return failure at the * + * first failed macro expansion * + * * + * Return value: SUCCEED - the macros were expanded successfully * + * FAIL - error parameter was given and at least one of * + * macros was not expanded * + * * + ******************************************************************************/ +static int eval_query_expand_user_macros(const char *itemquery, size_t len, zbx_uint64_t *hostids, int hostids_num, + zbx_macro_resolve_func_t resolver_cb, char **out, char **error) +{ + zbx_eval_context_t ctx; + zbx_item_query_t query; + int i, ret = SUCCEED; + char *errmsg = NULL, *filter = NULL; + + if (len != zbx_eval_parse_query(itemquery, len, &query)) + { + if (NULL != error) + { + *error = zbx_strdup(NULL, "cannot parse item query"); + return FAIL; + } + return SUCCEED; + } + + if (NULL == query.filter) + goto out; + + if (SUCCEED != zbx_eval_parse_expression(&ctx, query.filter, + ZBX_EVAL_PARSE_QUERY_EXPRESSION | ZBX_EVAL_COMPOSE_QUOTE, &errmsg)) + { + if (NULL != error) + { + ret = FAIL; + *error = zbx_dsprintf(NULL, "cannot parse item query filter: %s", errmsg); + } + + zbx_free(errmsg); + goto out; + } + + for (i = 0; i < ctx.stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx.stack.values[i]; + char *value, *tmp; + + switch (token->type) + { + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + ret = resolver_cb(ctx.expression + token->loc.l, token->loc.r - token->loc.l + 1, + hostids, hostids_num, &value, error); + break; + case ZBX_EVAL_TOKEN_VAR_STR: + if (SUCCEED != eval_has_usermacro(ctx.expression + token->loc.l, + token->loc.r - token->loc.l + 1)) + { + continue; + } + tmp = zbx_substr_unquote(ctx.expression, token->loc.l, token->loc.r); + ret = resolver_cb(tmp, strlen(tmp), hostids, hostids_num, &value, error); + zbx_free(tmp); + break; + default: + continue; + } + + if (SUCCEED != ret) + { + zbx_eval_clear(&ctx); + goto out; + } + + zbx_variant_set_str(&token->value, value); + } + + zbx_eval_compose_expression(&ctx, &filter); + zbx_eval_clear(&ctx); + + *out = zbx_dsprintf(NULL, "/%s/%s?[%s]", ZBX_NULL2EMPTY_STR(query.host), query.key, filter); + +out: + zbx_free(filter); + zbx_eval_clear_query(&query); + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_eval_expand_user_macros * + * * + * Purpose: expand user macros in parsed expression * + * * + * Parameters: ctx - [IN] the evaluation context * + * hostids - [IN] the linked hostids * + * hostids_num - [IN] the number of linked hostids * + * resolver_cb - [IN] the resolver callback * + * error - [OUT] the error message, optional. If specified * + * the function will return failure at the * + * first failed macro expansion * + * * + * Return value: SUCCEED - the macros were expanded successfully * + * FAIL - error parameter was given and at least one of * + * macros was not expanded * + * * + ******************************************************************************/ +int zbx_eval_expand_user_macros(const zbx_eval_context_t *ctx, zbx_uint64_t *hostids, int hostids_num, + zbx_macro_resolve_func_t resolver_cb, char **error) +{ + int i, ret = SUCCEED; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + char *value = NULL, *tmp; + + switch (token->type) + { + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + ret = resolver_cb(ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1, + hostids, hostids_num, &value, error); + break; + case ZBX_EVAL_TOKEN_VAR_STR: + case ZBX_EVAL_TOKEN_VAR_NUM: + case ZBX_EVAL_TOKEN_ARG_PERIOD: + if (SUCCEED != eval_has_usermacro(ctx->expression + token->loc.l, + token->loc.r - token->loc.l + 1)) + { + continue; + } + tmp = zbx_substr_unquote(ctx->expression, token->loc.l, token->loc.r); + ret = resolver_cb(tmp, strlen(tmp), hostids, hostids_num, &value, error); + zbx_free(tmp); + break; + case ZBX_EVAL_TOKEN_ARG_QUERY: + if (SUCCEED != eval_has_usermacro(ctx->expression + token->loc.l, + token->loc.r - token->loc.l + 1)) + { + continue; + } + ret = eval_query_expand_user_macros(ctx->expression + token->loc.l, + token->loc.r - token->loc.l + 1, hostids, hostids_num, resolver_cb, + &value, error); + break; + default: + continue; + } + + if (SUCCEED != ret) + return FAIL; + + if (NULL != value) + zbx_variant_set_str(&token->value, value); + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: zbx_eval_set_exception * + * * + * Purpose: set eval context to exception that will be returned when executed * + * * + * Parameters: ctx - [IN] the evaluation context * + * message - [IN] the exception message (the memory is owned by * + * context) * + * * + ******************************************************************************/ +void zbx_eval_set_exception(zbx_eval_context_t *ctx, char *message) +{ + zbx_eval_token_t *token; + + memset(ctx, 0, sizeof(zbx_eval_context_t)); + zbx_vector_eval_token_create(&ctx->stack); + zbx_vector_eval_token_reserve(&ctx->stack, 2); + ctx->stack.values_num = 2; + + token = ctx->stack.values; + memset(token, 0, 2 * sizeof(zbx_eval_token_t)); + token->type = ZBX_EVAL_TOKEN_VAR_STR; + zbx_variant_set_str(&token->value, message); + (++token)->type = ZBX_EVAL_TOKEN_EXCEPTION; +} + +/****************************************************************************** + * * + * Function: expression_extract_functionid * + * * + * Purpose: extract functionid from token * + * * + * Parameters: expression - [IN] the original expression * + * token - [IN] the token * + * functionid - [OUT] the extracted functionid * + * * + * Return value: SUCCEED - functionid was extracted successfully * + * FAIL - otherwise (incorrect token or invalid data) * + * * + * Comment: The extracted functionid will be cached as token value, so the * + * next time it can be used without extracting the value from * + * expression. * + * * + ******************************************************************************/ +static int expression_extract_functionid(const char *expression, zbx_eval_token_t *token, zbx_uint64_t *functionid) +{ + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) + return FAIL; + + switch (token->value.type) + { + case ZBX_VARIANT_UI64: + *functionid = token->value.data.ui64; + return SUCCEED; + case ZBX_VARIANT_NONE: + if (SUCCEED != is_uint64_n(expression + token->loc.l + 1, token->loc.r - token->loc.l - 1, + functionid)) + { + THIS_SHOULD_NEVER_HAPPEN; + break; + } + zbx_variant_set_ui64(&token->value, *functionid); + return SUCCEED; + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: zbx_eval_deserialize_dyn * + * * + * Purpose: deserialize expression and extract specified tokens into values * + * * + * Parameters: data - [IN] the serialized expression * + * expression - [IN] the original expression * + * mask - [IN] the tokens to extract * + * * + * Return value: Expression evaluation context. * + * * + ******************************************************************************/ +zbx_eval_context_t *zbx_eval_deserialize_dyn(const unsigned char *data, const char *expression, + zbx_uint64_t mask) +{ + zbx_eval_context_t *ctx; + int i; + zbx_uint64_t functionid; + char *value; + + ctx = (zbx_eval_context_t *)zbx_malloc(NULL, sizeof(zbx_eval_context_t)); + zbx_eval_deserialize(ctx, expression, ZBX_EVAL_TRIGGER_EXPRESSION, data); + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + switch (token->type) + { + case ZBX_EVAL_TOKEN_FUNCTIONID: + if (0 == (mask & ZBX_EVAL_EXTRACT_FUNCTIONID)) + continue; + expression_extract_functionid(expression, token, &functionid); + break; + case ZBX_EVAL_TOKEN_VAR_STR: + if (0 != (mask & ZBX_EVAL_EXTRACT_VAR_STR) && ZBX_VARIANT_NONE == token->value.type) + { + /* extract string variable value for macro resolving */ + value = zbx_substr_unquote(expression, token->loc.l, token->loc.r); + zbx_variant_set_str(&token->value, value); + } + break; + case ZBX_EVAL_TOKEN_VAR_MACRO: + if (0 != (mask & ZBX_EVAL_EXTRACT_VAR_MACRO) && ZBX_VARIANT_NONE == token->value.type) + { + /* extract macro for resolving */ + value = zbx_substr_unquote(expression, token->loc.l, token->loc.r); + zbx_variant_set_str(&token->value, value); + } + break; + } + } + + return ctx; +} + +/****************************************************************************** + * * + * Function: zbx_eval_get_functionids * + * * + * Purpose: get functionids from parsed expression * + * * + * Parameters: ctx - [IN] the evaluation context * + * functionids - [OUT] the extracted functionids * + * * + ******************************************************************************/ +void zbx_eval_get_functionids(zbx_eval_context_t *ctx, zbx_vector_uint64_t *functionids) +{ + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + zbx_uint64_t functionid; + + if (SUCCEED == expression_extract_functionid(ctx->expression, token, &functionid)) + zbx_vector_uint64_append(functionids, functionid); + } + + zbx_vector_uint64_sort(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); +} + +/****************************************************************************** + * * + * Function: zbx_eval_get_functionids_ordered * + * * + * Purpose: get functionids from parsed expression in the order as they * + * were written + * * + * Parameters: ctx - [IN] the evaluation context * + * functionids - [OUT] the extracted functionids * + * * + ******************************************************************************/ +void zbx_eval_get_functionids_ordered(zbx_eval_context_t *ctx, zbx_vector_uint64_t *functionids) +{ + int i; + zbx_vector_ptr_t tokens; + + zbx_vector_ptr_create(&tokens); + + for (i = 0; i < ctx->stack.values_num; i++) + { + if (ZBX_EVAL_TOKEN_FUNCTIONID == ctx->stack.values[i].type) + zbx_vector_ptr_append(&tokens, &ctx->stack.values[i]); + } + + zbx_vector_ptr_sort(&tokens, compare_tokens_by_loc); + + for (i = 0; i < tokens.values_num; i++) + { + zbx_eval_token_t *token = (zbx_eval_token_t *)tokens.values[i]; + zbx_uint64_t functionid; + + if (SUCCEED == expression_extract_functionid(ctx->expression, token, &functionid)) + zbx_vector_uint64_append(functionids, functionid); + } + + zbx_vector_ptr_destroy(&tokens); +} + +/****************************************************************************** + * * + * Function: zbx_eval_check_timer_functions * + * * + * Purpose: check if expression contains timer function calls (date, time, * + * now, dayofweek, dayofmonth) * + * * + * Parameters: ctx - [IN] the evaluation context * + * * + * Return value: SUCCEED - expression contains timer function call(s) * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_eval_check_timer_functions(const zbx_eval_context_t *ctx) +{ + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + if (ZBX_EVAL_TOKEN_FUNCTION != token->type) + continue; + + if (SUCCEED == eval_compare_token(ctx, &token->loc, "date", ZBX_CONST_STRLEN("date"))) + return SUCCEED; + if (SUCCEED == eval_compare_token(ctx, &token->loc, "time", ZBX_CONST_STRLEN("time"))) + return SUCCEED; + if (SUCCEED == eval_compare_token(ctx, &token->loc, "now", ZBX_CONST_STRLEN("now"))) + return SUCCEED; + if (SUCCEED == eval_compare_token(ctx, &token->loc, "dayofmonth", ZBX_CONST_STRLEN("dayofmonth"))) + return SUCCEED; + if (SUCCEED == eval_compare_token(ctx, &token->loc, "dayofweek", ZBX_CONST_STRLEN("dayofweek"))) + return SUCCEED; + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: zbx_get_serialized_expression_functionids * + * * + * Purpose: extract functionids from serialized expression * + * * + * Parameters: expression - [IN] the original expression * + * data - [IN] the serialized expression * + * functionids - [OUT] the extracted functionids * + * * + ******************************************************************************/ +void zbx_get_serialized_expression_functionids(const char *expression, const unsigned char *data, + zbx_vector_uint64_t *functionids) +{ + zbx_uint32_t i, tokens_num, len, loc_l, loc_r, opt; + zbx_token_type_t type; + zbx_uint64_t functionid; + unsigned char var_type; + + data += zbx_deserialize_uint31_compact(data, &len); + data += zbx_deserialize_uint31_compact(data, &tokens_num); + + for (i = 0; i < tokens_num; i++) + { + data += zbx_deserialize_value(data, &type); + data += zbx_deserialize_uint31_compact(data, &opt); + data += zbx_deserialize_uint31_compact(data, &loc_l); + data += zbx_deserialize_uint31_compact(data, &loc_r); + + data += zbx_deserialize_char(data, &var_type); + + switch (var_type) + { + case ZBX_VARIANT_UI64: + data += sizeof(zbx_uint64_t); + break; + case ZBX_VARIANT_DBL: + data += sizeof(double); + break; + case ZBX_VARIANT_STR: + data += strlen((const char *)data) + 1; + break; + case ZBX_VARIANT_NONE: + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + return; + } + + if (ZBX_EVAL_TOKEN_FUNCTIONID == type) + { + if (SUCCEED == is_uint64_n(expression + loc_l + 1, loc_r - loc_l - 1, &functionid)) + zbx_vector_uint64_append(functionids, functionid); + else + THIS_SHOULD_NEVER_HAPPEN; + } + } + + zbx_vector_uint64_sort(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); +} + +/****************************************************************************** + * * + * Function: zbx_eval_get_constant * + * * + * Purpose: the Nth constant in expression * + * * + * Parameters: ctx - [IN] the evaluation context * + * index - [IN] the constant index * + * value - [OUT] the constant value * + * * + ******************************************************************************/ +void zbx_eval_get_constant(const zbx_eval_context_t *ctx, int index, char **value) +{ + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + switch (token->type) + { + case ZBX_EVAL_TOKEN_VAR_STR: + case ZBX_EVAL_TOKEN_VAR_NUM: + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + if (index == (int)token->opt + 1) + { + zbx_free(*value); + if (ZBX_VARIANT_NONE != token->value.type) + *value = zbx_strdup(NULL, zbx_variant_value_desc(&token->value)); + else + *value = zbx_substr_unquote(ctx->expression, token->loc.l, token->loc.r); + return; + } + break; + } + } +} + +/****************************************************************************** + * * + * Function: zbx_eval_replace_functionid * + * * + * Purpose: replace functionid in parsed expression with new functionid macro * + * * + * Parameters: ctx - [IN] the evaluation context * + * old_functionid - [IN] the constant index * + * new_functionid - [OUT] the constant value * + * * + ******************************************************************************/ +void zbx_eval_replace_functionid(zbx_eval_context_t *ctx, zbx_uint64_t old_functionid, zbx_uint64_t new_functionid) +{ + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + zbx_uint64_t token_functionid; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) + continue; + + if (ZBX_VARIANT_UI64 != token->value.type) + { + if (ZBX_VARIANT_NONE != token->value.type) + continue; + + if (SUCCEED != is_uint64_n(ctx->expression + token->loc.l + 1, token->loc.r - token->loc.l - 1, + &token_functionid)) + { + THIS_SHOULD_NEVER_HAPPEN; + continue; + } + zbx_variant_set_ui64(&token->value, token_functionid); + } + + if (token->value.data.ui64 == old_functionid) + { + zbx_variant_set_ui64(&token->value, new_functionid); + + /* mark functionid as replaced to check if any non-replaced functionids are left */ + token->opt = ZBX_MAX_UINT31_1; + } + } +} + +/****************************************************************************** + * * + * Function: zbx_eval_validate_replaced_functionids * + * * + * Purpose: validate parsed expression to check if all functionids were * + * replaced * + * * + * Parameters: ctx [IN] the evaluation context * + * error - [IN] the error message * + * * + ******************************************************************************/ +int zbx_eval_validate_replaced_functionids(zbx_eval_context_t *ctx, char **error) +{ + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) + continue; + + if (ZBX_MAX_UINT31_1 != token->opt) + { + *error = zbx_dsprintf(*error, "non-updated functionid found at \"%s\"", + ctx->expression + token->loc.l); + return FAIL; + } + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: zbx_eval_copy * + * * + * Purpose: copy parsed expression * + * * + * Parameters: dst - [OUT] the destination evaluation context * + * src - [IN] the source evaluation context * + * expression - [IN] copied destination expression * + * * + ******************************************************************************/ +void zbx_eval_copy(zbx_eval_context_t *dst, const zbx_eval_context_t *src, const char *expression) +{ + int i; + + dst->expression = expression; + dst->rules = src->rules; + zbx_vector_eval_token_create(&dst->stack); + zbx_vector_eval_token_reserve(&dst->stack, src->stack.values_num); + + zbx_vector_eval_token_append_array(&dst->stack, src->stack.values, src->stack.values_num); + for (i = 0; i < dst->stack.values_num; i++) + { + if (ZBX_VARIANT_NONE != src->stack.values[i].value.type) + zbx_variant_copy(&dst->stack.values[i].value, &src->stack.values[i].value); + } +} + +/****************************************************************************** + * * + * Function: zbx_eval_format_function_error * + * * + * Purpose: format function evaluation error message * + * * + * Parameters: function - [IN] the function name * + * host - [IN] the host name, can be NULL * + * key - [IN] the item key, can be NULL * + * parameter - [IN] the function parameters list * + * error - [IN] the error message * + * * + * Return value: The formatted error message. * + * * + ******************************************************************************/ +char *zbx_eval_format_function_error(const char *function, const char *host, const char *key, + const char *parameter, const char *error) +{ + char *msg = NULL; + size_t msg_alloc = 0, msg_offset = 0; + + zbx_snprintf_alloc(&msg, &msg_alloc, &msg_offset, "Cannot evaluate function %s(/%s/%s", + function, (NULL != host ? host : "?"), (NULL != key ? key : "?")); + + if (NULL != parameter && '\0' != *parameter) + zbx_snprintf_alloc(&msg, &msg_alloc, &msg_offset, ",%s", parameter); + + zbx_chrcpy_alloc(&msg, &msg_alloc, &msg_offset, ')'); + + if (NULL != error && '\0' != *error) + zbx_snprintf_alloc(&msg, &msg_alloc, &msg_offset, ": %s", error); + + zbx_chrcpy_alloc(&msg, &msg_alloc, &msg_offset, '.'); + + return msg; +} + +/****************************************************************************** + * * + * Function: zbx_eval_extract_history_queries * + * * + * Purpose: copy history query into vector and replace it with vector index * + * * + * Parameters: ctx - [IN] the evaluation context * + * refs - [OUT] the item references * + * * + ******************************************************************************/ +void zbx_eval_extract_item_refs(zbx_eval_context_t *ctx, zbx_vector_str_t *refs) +{ + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + if (ZBX_EVAL_TOKEN_ARG_QUERY != token->type) + continue; + + if (ZBX_VARIANT_STR == token->value.type) + { + zbx_vector_str_append(refs, token->value.data.str); + zbx_variant_set_none(&token->value); + } + else + zbx_vector_str_append(refs, zbx_substr(ctx->expression, token->loc.l, token->loc.r)); + + zbx_variant_clear(&token->value); + zbx_variant_set_ui64(&token->value, refs->values_num - 1); + } +} diff --git a/src/libs/zbxeval/parse.c b/src/libs/zbxeval/parse.c new file mode 100644 index 00000000000..6a2e4c7e561 --- /dev/null +++ b/src/libs/zbxeval/parse.c @@ -0,0 +1,1547 @@ +/* +** Zabbix +** Copyright (C) 2001-2020 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 "common.h" +#include "log.h" + +#include "zbxalgo.h" +#include "../zbxalgo/vectorimpl.h" +#include "zbxvariant.h" +#include "zbxserialize.h" +#include "zbxserver.h" +#include "eval.h" + +ZBX_VECTOR_IMPL(eval_token, zbx_eval_token_t) + +static int is_whitespace(char c) +{ + return 0 != isspace((unsigned char)c) ? SUCCEED : FAIL; +} + +/****************************************************************************** + * * + * Function: eval_get_whitespace_len * + * * + * Purpose: find the number of following whitespace characters * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * * + * Return value: The number of whitespace characters found. * + * * + ******************************************************************************/ +static size_t eval_get_whitespace_len(zbx_eval_context_t *ctx, size_t pos) +{ + const char *ptr = ctx->expression + pos; + + while (SUCCEED == is_whitespace(*ptr)) + ptr++; + + return ptr - ctx->expression - pos; +} + +/****************************************************************************** + * * + * Function: eval_update_const_variable * + * * + * Purpose: update constant variable index in the trigger expression * + * * + * Parameters: ctx - [IN] the evaluation context * + * token - [IN] the variable token * + * * + * Comments: The index is used to refer constant values by using $<N> in * + * trigger names. Function arguments are excluded. * + * * + ******************************************************************************/ +static void eval_update_const_variable(zbx_eval_context_t *ctx, zbx_eval_token_t *token) +{ + zbx_variant_set_none(&token->value); + + if (0 != (ctx->rules & ZBX_EVAL_PARSE_CONST_INDEX)) + { + int i; + + for (i = 0; i < ctx->ops.values_num; i++) + { + if (0 != (ctx->ops.values[i].type & ZBX_EVAL_CLASS_FUNCTION)) + return; + } + + token->opt = ctx->const_index++; + } +} + +/****************************************************************************** + * * + * Function: eval_is_compound_number_char * + * * + * Purpose: check if the character can be a part of a compound number * + * following a macro * + * * + ******************************************************************************/ +static int eval_is_compound_number_char(char c, int pos) +{ + if (0 != isdigit((unsigned char)c)) + return SUCCEED; + + switch (c) + { + case '.': + case '{': + return SUCCEED; + case 'e': + case 'E': + return (0 != pos ? SUCCEED : FAIL); + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_parse_functionid * + * * + * Purpose: parse functionid token ({<functionid>}) * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_parse_functionid(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token) +{ + zbx_token_t tok; + + if (SUCCEED == zbx_token_parse_objectid(ctx->expression, ctx->expression + pos, &tok)) + { + token->type = ZBX_EVAL_TOKEN_FUNCTIONID; + token->opt = ctx->functionid_index++; + token->loc = tok.loc; + return SUCCEED; + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_parse_macro * + * * + * Purpose: parse macro * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * tok - [OUT] the parsed token * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_parse_macro(zbx_eval_context_t *ctx, int pos, zbx_token_t *tok) +{ + if (0 != (ctx->rules & ZBX_EVAL_PARSE_MACRO) && + SUCCEED == zbx_token_parse_macro(ctx->expression, ctx->expression + pos, tok)) + { + return SUCCEED; + } + else if (0 != (ctx->rules & ZBX_EVAL_PARSE_USERMACRO) && '$' == ctx->expression[pos + 1] && + SUCCEED == zbx_token_parse_user_macro(ctx->expression, ctx->expression + pos, tok)) + { + return SUCCEED; + } + else if (0 != (ctx->rules & ZBX_EVAL_PARSE_LLDMACRO) && '#' == ctx->expression[pos + 1] && + SUCCEED == zbx_token_parse_lld_macro(ctx->expression, ctx->expression + pos, tok)) + { + return SUCCEED; + } + else if ('{' == ctx->expression[pos + 1] && SUCCEED == zbx_token_parse_nested_macro(ctx->expression, + ctx->expression + pos, tok)) + { + return SUCCEED; + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_parse_number * + * * + * Purpose: parse numeric value * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * tok - [OUT] the parsed token * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_parse_number(zbx_eval_context_t *ctx, size_t pos, size_t *pos_r) +{ + int len, offset = 0; + char *end; + double tmp; + + if ('-' == ctx->expression[pos]) + offset++; + + if (FAIL == zbx_suffixed_number_parse(ctx->expression + pos + offset, &len)) + return FAIL; + + len += offset; + + tmp = strtod(ctx->expression + pos, &end) * (double)suffix2factor(ctx->expression[(int)pos + len - 1]); + if (HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno) + return FAIL; + + *pos_r = pos + (size_t)len - 1; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_constant * + * * + * Purpose: parse constant value * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: A constant is a number or macro depending on parsing rules. * + * Number can be a compound value, consisting of several macros * + * digits etc. * + * * + ******************************************************************************/ +static int eval_parse_constant(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token, char **error) +{ + zbx_token_t tok; + size_t offset = pos; + zbx_token_type_t type = 0; + + do + { + if ('{' == (ctx->expression[offset])) + { + if (SUCCEED != eval_parse_macro(ctx, (int)offset, &tok)) + break; + + if (pos == offset) + { + switch (tok.type) + { + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_FUNC_MACRO: + case ZBX_TOKEN_SIMPLE_MACRO: + type = ZBX_EVAL_TOKEN_VAR_MACRO; + break; + case ZBX_TOKEN_USER_MACRO: + type = ZBX_EVAL_TOKEN_VAR_USERMACRO; + break; + case ZBX_TOKEN_LLD_MACRO: + case ZBX_TOKEN_LLD_FUNC_MACRO: + type = ZBX_EVAL_TOKEN_VAR_LLDMACRO; + break; + } + } + else + type = ZBX_EVAL_TOKEN_VAR_NUM; + + offset = tok.loc.r + 1; + + switch (ctx->expression[offset]) + { + case 's': + case 'm': + case 'h': + case 'd': + case 'w': + case 'K': + case 'M': + case 'G': + case 'T': + type = ZBX_EVAL_TOKEN_VAR_NUM; + offset++; + goto out; + } + } + else if (SUCCEED == eval_parse_number(ctx, offset, &offset)) + { + type = ZBX_EVAL_TOKEN_VAR_NUM; + offset++; + } + else if (SUCCEED == eval_is_compound_number_char(ctx->expression[offset], offset - pos)) + offset++; + else + break; + } + while (0 != (ctx->rules & ZBX_EVAL_PARSE_COMPOUND_CONST)); +out: + if (0 == type) + { + *error = zbx_dsprintf(*error, "invalid token starting with \"%s\"", ctx->expression + pos); + return FAIL; + } + + if (ZBX_EVAL_TOKEN_VAR_NUM == type && 0 == (ctx->rules & ZBX_EVAL_PARSE_VAR_NUM)) + { + *error = zbx_dsprintf(*error, "invalid token starting with \"%s\"", ctx->expression + pos); + return FAIL; + } + + if (ZBX_EVAL_TOKEN_VAR_NUM == type || ZBX_EVAL_TOKEN_VAR_USERMACRO == type) + eval_update_const_variable(ctx, token); + + token->type = type; + token->loc.l = pos; + token->loc.r = offset - 1; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_character_token * + * * + * Purpose: parse single character token * + * * + * Parameters: pos - [IN] the starting position * + * type - [IN] the token type * + * token - [OUT] the parsed token * + * * + ******************************************************************************/ +static void eval_parse_character_token(size_t pos, zbx_token_type_t type, zbx_eval_token_t *token) +{ + token->type = type; + token->loc.l = pos; + token->loc.r = pos; +} + +/****************************************************************************** + * * + * Function: eval_parse_less_character_token * + * * + * Purpose: parse token starting with '<' * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * * + * Return value: SUCCEED - the token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: Tokens starting with '<' are '<', '<=' and '<>'. * + * * + ******************************************************************************/ +static int eval_parse_less_character_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token) +{ + if (0 != (ctx->rules & ZBX_EVAL_PARSE_COMPARE_EQ) && '>' == ctx->expression[pos + 1]) + { + token->type = ZBX_EVAL_TOKEN_OP_NE; + } + else + { + if (0 == (ctx->rules & ZBX_EVAL_PARSE_COMPARE_SORT)) + return FAIL; + + if ('=' != ctx->expression[pos + 1]) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_LT, token); + return SUCCEED; + } + + token->type = ZBX_EVAL_TOKEN_OP_LE; + } + + token->loc.l = pos; + token->loc.r = pos + 1; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_greater_character_token * + * * + * Purpose: parse token starting with '>' * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * * + * Comments: Tokens starting with '>' are '>' and '>='. * + * * + ******************************************************************************/ +static void eval_parse_greater_character_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token) +{ + if ('=' == ctx->expression[pos + 1]) + { + token->type = ZBX_EVAL_TOKEN_OP_GE; + token->loc.l = pos; + token->loc.r = pos + 1; + } + else + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_GT, token); +} + +/****************************************************************************** + * * + * Function: eval_parse_string_token * + * * + * Purpose: parse string variable token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: String variable token is token starting with '"'. * + * * + ******************************************************************************/ +static int eval_parse_string_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token, char **error) +{ + const char *ptr = ctx->expression + pos + 1; + + for (; '\0' != *ptr; ptr++) + { + if (*ptr == '"') + { + token->type = ZBX_EVAL_TOKEN_VAR_STR; + token->loc.l = pos; + token->loc.r = ptr - ctx->expression; + eval_update_const_variable(ctx, token); + return SUCCEED; + } + + if ('\\' == *ptr) + { + if ('"' != ptr[1] && '\\' != ptr[1] ) + { + *error = zbx_dsprintf(*error, "invalid escape sequence in string starting with \"%s\"", + ptr); + return FAIL; + } + ptr++; + } + } + + *error = zbx_dsprintf(*error, "unterminated string at \"%s\"", ctx->expression + pos); + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_parse_number_token * + * * + * Purpose: parse numeric variable token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: Time suffixes s,m,h,d,w are supported. * + * * + ******************************************************************************/ +static int eval_parse_number_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token) +{ + int len, offset = 0; + char *end; + double tmp; + + if ('-' == ctx->expression[pos]) + offset++; + + if (FAIL == zbx_suffixed_number_parse(ctx->expression + pos + offset, &len)) + return FAIL; + + len += offset; + + token->type = ZBX_EVAL_TOKEN_VAR_NUM; + + tmp = strtod(ctx->expression + pos, &end) * suffix2factor(ctx->expression[pos + len - 1]); + if (HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno) + return FAIL; + + token->loc.l = pos; + token->loc.r = pos + len - 1; + eval_update_const_variable(ctx, token); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_logic_token * + * * + * Purpose: parse logical operation token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: Keywords are 'and', 'or' and 'not', followed by separator * + * character (whitespace or '('). * + * * + ******************************************************************************/ +static int eval_parse_logic_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token) +{ + if (0 == strncmp(ctx->expression + pos, "and", 3)) + { + token->loc.r = pos + 2; + token->type = ZBX_EVAL_TOKEN_OP_AND; + } + else if (0 == strncmp(ctx->expression + pos, "or", 2)) + { + token->loc.r = pos + 1; + token->type = ZBX_EVAL_TOKEN_OP_OR; + } + else if (0 == strncmp(ctx->expression + pos, "not", 3)) + { + token->loc.r = pos + 2; + token->type = ZBX_EVAL_TOKEN_OP_NOT; + } + else + return FAIL; + + /* keyword must be followed by whitespace or opening parenthesis */ + if ('(' != ctx->expression[token->loc.r + 1] && SUCCEED != is_whitespace(ctx->expression[token->loc.r + 1])) + return FAIL; + + token->loc.l = pos; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_function_token * + * * + * Purpose: parse function token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: Function token is non-keyword alpha characters followed by '('. * + * * + ******************************************************************************/ +static int eval_parse_function_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token) +{ + const char *ptr = ctx->expression + pos; + + while (0 != isalpha((unsigned char)*ptr) || '_' == *ptr) + ptr++; + + if ('(' == *ptr) + { + token->type = ZBX_EVAL_TOKEN_FUNCTION; + token->loc.l = pos; + token->loc.r = ptr - ctx->expression - 1; + token->opt = ctx->stack.values_num; + return SUCCEED; + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_parse_query_filter * + * * + * Purpose: parse item query filter (?[group="xyz"]) * + * * + * Parameters: ptr - [IN] - the filter to parse * + * [OUT] - a reference to the next character after filter * + * * + * Return value: SUCCEED - the filter was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_parse_query_filter(const char **ptr) +{ + const char *filter = *ptr; + + if ('[' != *(++filter)) + return FAIL; + + filter++; + + while (']' != *filter) + { + if ('\0' == *filter) + return FAIL; + + if ('"' == *filter) + { + while ('"' != *(++filter)) + { + if ('\0' == *filter) + return FAIL; + + if ('\\' == *filter && '\0' == *(++filter)) + return FAIL; + } + } + + filter++; + } + + *ptr = ++filter; + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_query * + * * + * Purpose: parse item query /host/key?[filter] into host, key and filter * + * components * + * * + * Parameters: str - [IN] the item query * + * phost - [OUT] a reference to host or NULL (optional) * + * pkey - [OUT] a reference to key * + * pfilter - [OUT] a reference to the filter or NULL * + * * + * Return value: The number of parsed characters, 0 if there was an error * + * * + ******************************************************************************/ +size_t eval_parse_query(const char *str, const char **phost, const char **pkey, const char **pfilter) +{ +#define MVAR_HOST_HOST "{HOST.HOST" + + const char *host = str + 1, *key, *filter, *end; + + key = host; + + if ('*' == *key) + { + key++; + } + else if ('{' == *key) + { + if (0 == strncmp(key, MVAR_HOST_HOST, ZBX_CONST_STRLEN(MVAR_HOST_HOST))) + { + int offset = 0; + + if ('}' == key[ZBX_CONST_STRLEN(MVAR_HOST_HOST)]) + { + offset = 1; + } + else if (0 != isdigit((unsigned char)key[ZBX_CONST_STRLEN(MVAR_HOST_HOST)]) && + '}' == key[ZBX_CONST_STRLEN(MVAR_HOST_HOST) + 1]) + { + offset = 2; + } + + if (0 != offset) + key += ZBX_CONST_STRLEN(MVAR_HOST_HOST) + offset; + } + } + else if ('/' != *key) + { + while (SUCCEED == is_hostname_char(*key)) + key++; + } + + if ('/' != *key) + return 0; + + end = ++key; + + if ('*' == *key) + end++; + else if (SUCCEED != parse_key(&end)) + return 0; + + if (*end == '?') + { + filter = end; + if (SUCCEED != eval_parse_query_filter(&end)) + return 0; + filter += 2; + } + else + filter = NULL; + + if (NULL != phost) + { + *phost = host; + *pkey = key; + *pfilter = filter; + } + + return end - str; + +#undef MVAR_HOST_HOST +} + +/****************************************************************************** + * * + * Function: eval_parse_query_token * + * * + * Purpose: parse history query token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: History query token is the first argument of history functions * + * to specify item(s) in format /host/key * + * * + ******************************************************************************/ +static int eval_parse_query_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token, char **error) +{ + size_t len; + + if (0 == (len = eval_parse_query(ctx->expression + pos, NULL, NULL, NULL))) + { + *error = zbx_dsprintf(*error, "invalid item query starting at \"%s\"", ctx->expression + pos); + return FAIL; + } + + token->type = ZBX_EVAL_TOKEN_ARG_QUERY; + token->loc.l = pos; + token->loc.r = pos + len - 1; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_time_token * + * * + * Purpose: parse time period token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: Time period token is the second argument of history functions * + * to specify the history range in format <period>[:<timeshift>] * + * * + ******************************************************************************/ +static int eval_parse_period_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token, char **error) +{ + size_t offset = pos; + + for (;'\0' != ctx->expression[offset]; offset++) + { + if ('{' == ctx->expression[offset] && 0 != (ctx->rules & ZBX_EVAL_PARSE_COMPOUND_CONST)) + { + zbx_token_t tok; + + if (SUCCEED == eval_parse_macro(ctx, offset, &tok)) + offset = tok.loc.r; + continue; + + } + if (',' == ctx->expression[offset] || ')' == ctx->expression[offset] || + SUCCEED == is_whitespace(ctx->expression[offset])) + { + token->type = ZBX_EVAL_TOKEN_ARG_PERIOD; + token->loc.l = pos; + token->loc.r = offset - 1; + zbx_variant_set_none(&token->value); + + return SUCCEED; + } + } + + *error = zbx_dsprintf(*error, "unterminated function at \"%s\"", ctx->expression + pos); + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_parse_property_token * + * * + * Purpose: parse property token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + * Comments: Keywords are 'and', 'or' and 'not', followed by separator * + * character (whitespace or '('). * + * * + ******************************************************************************/ +static int eval_parse_property_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token) +{ + if (0 != (ctx->rules & ZBX_EVAL_PARSE_PROP_TAG) && 0 == strncmp(ctx->expression + pos, "tag", 3)) + { + token->loc.r = pos + 2; + token->type = ZBX_EVAL_TOKEN_PROP_TAG; + } + else if (0 != (ctx->rules & ZBX_EVAL_PARSE_PROP_GROUP) && 0 == strncmp(ctx->expression + pos, "group", 5)) + { + token->loc.r = pos + 4; + token->type = ZBX_EVAL_TOKEN_PROP_GROUP; + } + else + return FAIL; + + token->loc.l = pos; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_parse_token * + * * + * Purpose: parse token * + * * + * Parameters: ctx - [IN] the evaluation context * + * pos - [IN] the starting position * + * token - [OUT] the parsed token * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - token was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_parse_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token, char **error) +{ + size_t skip; + + skip = eval_get_whitespace_len(ctx, pos); + pos += skip; + + switch (ctx->expression[pos]) + { + case '{': + if (ZBX_EVAL_TOKEN_COMMA == ctx->last_token_type && + ZBX_EVAL_TOKEN_ARG_QUERY == ctx->stack.values[ctx->stack.values_num - 1].type) + { + return eval_parse_period_token(ctx, pos, token, error); + } + + if (0 != (ctx->rules & ZBX_EVAL_PARSE_FUNCTIONID) && + SUCCEED == eval_parse_functionid(ctx, pos, token)) + { + return SUCCEED; + } + return eval_parse_constant(ctx, pos, token, error); + case '+': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_MATH)) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_ADD, token); + return SUCCEED; + } + break; + case '-': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_VAR)) + { + if (0 == (ctx->last_token_type & ZBX_EVAL_CLASS_OPERAND) && + SUCCEED == eval_parse_number_token(ctx, pos, token)) + { + return SUCCEED; + } + } + if (0 != (ctx->rules & ZBX_EVAL_PARSE_MATH)) + { + if (0 == (ctx->last_token_type & ZBX_EVAL_CLASS_OPERAND)) + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_MINUS, token); + else + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_SUB, token); + + return SUCCEED; + } + break; + case '/': + if (ZBX_EVAL_TOKEN_GROUP_OPEN == ctx->last_token_type && + 0 != (ctx->rules & ZBX_EVAL_PARSE_ITEM_QUERY)) + { + zbx_eval_token_t *func_token = NULL; + + if (2 <= ctx->ops.values_num) + func_token = &ctx->ops.values[ctx->ops.values_num - 2]; + + if (NULL == func_token || 0 == (func_token->type & ZBX_EVAL_CLASS_FUNCTION)) + { + *error = zbx_dsprintf(*error, "item query must be first argument of a" + " historical function at \"%s\"", ctx->expression + pos); + return FAIL; + } + func_token->type = ZBX_EVAL_TOKEN_HIST_FUNCTION; + return eval_parse_query_token(ctx, pos, token, error); + } + else if (0 != (ctx->rules & ZBX_EVAL_PARSE_MATH)) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_DIV, token); + return SUCCEED; + } + break; + case '*': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_MATH)) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_MUL, token); + return SUCCEED; + } + break; + case '<': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_COMPARE)) + { + if (SUCCEED == eval_parse_less_character_token(ctx, pos, token)) + return SUCCEED; + } + break; + case '>': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_COMPARE_SORT)) + { + eval_parse_greater_character_token(ctx, pos, token); + return SUCCEED; + } + break; + case '=': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_COMPARE_EQ)) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_OP_EQ, token); + return SUCCEED; + } + break; + case '(': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_GROUP)) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_GROUP_OPEN, token); + return SUCCEED; + } + break; + case ')': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_GROUP)) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_GROUP_CLOSE, token); + return SUCCEED; + } + break; + case '"': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_VAR_STR)) + return eval_parse_string_token(ctx, pos, token, error); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* after ',' there will be at least one value on the stack */ + if (ZBX_EVAL_TOKEN_COMMA == ctx->last_token_type && + ZBX_EVAL_TOKEN_ARG_QUERY == ctx->stack.values[ctx->stack.values_num - 1].type) + { + return eval_parse_period_token(ctx, pos, token, error); + } + ZBX_FALLTHROUGH; + case '.': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_VAR_NUM)) + return eval_parse_constant(ctx, pos, token, error); + break; + case '#': + if (ZBX_EVAL_TOKEN_COMMA == ctx->last_token_type && + ZBX_EVAL_TOKEN_ARG_QUERY == ctx->stack.values[ctx->stack.values_num - 1].type) + { + return eval_parse_period_token(ctx, pos, token, error); + } + break; + case ',': + if (0 != (ctx->rules & ZBX_EVAL_PARSE_FUNCTION_ARGS)) + { + eval_parse_character_token(pos, ZBX_EVAL_TOKEN_COMMA, token); + return SUCCEED; + } + break; + case '\0': + return SUCCEED; + default: + if (0 != isalpha((unsigned char)ctx->expression[pos])) + { + /* logical operation must be separated by whitespace or '(', ')', ',' characters */ + if (0 != (ctx->rules & ZBX_EVAL_PARSE_LOGIC) && + (0 != skip || 0 != (ctx->last_token_type & ZBX_EVAL_CLASS_SEPARATOR) || + ZBX_EVAL_TOKEN_GROUP_CLOSE == ctx->last_token_type)) + { + if (SUCCEED == eval_parse_logic_token(ctx, pos, token)) + return SUCCEED; + } + + if (0 != (ctx->rules & ZBX_EVAL_PARSE_FUNCTION_NAME) && + SUCCEED == eval_parse_function_token(ctx, pos, token)) + { + return SUCCEED; + } + + if (0 != (ctx->rules & ZBX_EVAL_PARSE_PROPERTY) && + SUCCEED == eval_parse_property_token(ctx, pos, token)) + { + return SUCCEED; + } + } + break; + } + + *error = zbx_dsprintf(*error, "invalid token starting with \"%s\"", ctx->expression + pos); + return FAIL; +} + +/****************************************************************************** + * * + * Function: eval_append_operator * + * * + * Purpose: add operator/function token to evaluation stack * + * * + * Parameters: ctx - [IN] the evaluation context * + * token - [IN] the token to add * + * * + ******************************************************************************/ +static int eval_append_operator(zbx_eval_context_t *ctx, zbx_eval_token_t *token, char **error) +{ + if (0 != (token->type & ZBX_EVAL_CLASS_FUNCTION)) + { + int i, params = 0; + + for (i = (int)token->opt; i < ctx->stack.values_num; i++) + { + if (0 != (ctx->stack.values[i].type & ZBX_EVAL_CLASS_FUNCTION)) + params -= (int)ctx->stack.values[i].opt - 1; + else if (0 != (ctx->stack.values[i].type & ZBX_EVAL_CLASS_OPERAND)) + params++; + else if (0 != (ctx->stack.values[i].type & ZBX_EVAL_CLASS_OPERATOR2)) + params--; + } + + token->opt = params; + } + + if (0 != (ctx->rules & ZBX_EVAL_PARSE_PROPERTY)) + { + zbx_eval_token_t *prop = NULL, *value = NULL; + + if (0 != (ctx->stack.values[ctx->stack.values_num - 1].type & ZBX_EVAL_CLASS_PROPERTY)) + { + prop = &ctx->stack.values[ctx->stack.values_num - 1]; + + if (2 > ctx->stack.values_num) + { + *error = zbx_dsprintf(*error, "missing comparison string for property at \"%s\"", + ctx->expression + prop->loc.l); + return FAIL; + } + + value = &ctx->stack.values[ctx->stack.values_num - 2]; + if (0 == (value->type & ZBX_EVAL_CLASS_OPERAND)) + { + *error = zbx_dsprintf(*error, "property must be compared with a constant value at" + " \"%s\"", ctx->expression + prop->loc.l); + return FAIL; + } + + } + + if (0 != (ctx->stack.values[ctx->stack.values_num - 1].type & ZBX_EVAL_CLASS_OPERAND) && + 0 != (token->type & ZBX_EVAL_CLASS_OPERATOR2)) + { + if (0 != (ctx->stack.values[ctx->stack.values_num - 2].type & ZBX_EVAL_CLASS_PROPERTY)) + { + prop = &ctx->stack.values[ctx->stack.values_num - 2]; + value = &ctx->stack.values[ctx->stack.values_num - 1]; + } + } + + if (NULL != prop) + { + if ((ZBX_EVAL_TOKEN_VAR_STR != value->type && ZBX_EVAL_TOKEN_VAR_USERMACRO != value->type && + ZBX_EVAL_TOKEN_VAR_LLDMACRO != value->type)) + { + *error = zbx_dsprintf(*error, "invalid value type compared with property at \"%s\"", + ctx->expression + prop->loc.l); + return FAIL; + } + + if (ZBX_EVAL_TOKEN_OP_EQ != token->type && ZBX_EVAL_TOKEN_OP_NE != token->type) + { + *error = zbx_dsprintf(*error, "invalid operator used with property at \"%s\"", + ctx->expression + prop->loc.l); + return FAIL; + } + } + } + + zbx_vector_eval_token_append_ptr(&ctx->stack, token); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: eval_append_operand * + * * + * Purpose: add operand token to evaluation stack * + * * + * Parameters: ctx - [IN] the evaluation context * + * token - [IN] the token to add * + * * + ******************************************************************************/ +static int eval_append_operand(zbx_eval_context_t *ctx, zbx_eval_token_t *token, char **error) +{ + if (0 == (ctx->last_token_type & ZBX_EVAL_BEFORE_OPERAND)) + { + *error = zbx_dsprintf(*error, "operand following another operand at \"%s\"", + ctx->expression + token->loc.l); + return FAIL; + } + + if (0 != (ctx->rules & ZBX_EVAL_PARSE_PROPERTY)) + { + int i; + zbx_eval_token_t *prop = NULL; + + for (i = ctx->stack.values_num - 1; i >= 0; i--) + { + if (0 != (ctx->stack.values[i].type & ZBX_EVAL_CLASS_PROPERTY)) + { + prop = &ctx->stack.values[i]; + continue; + } + + if (0 == (ctx->stack.values[i].type & ZBX_EVAL_CLASS_OPERAND)) + break; + } + + if (0 != (token->type & ZBX_EVAL_CLASS_PROPERTY)) + { + if (NULL != prop) + { + *error = zbx_dsprintf(*error, "property must be compared with a constant value at" + " \"%s\"", ctx->expression + prop->loc.l); + return FAIL; + } + prop = token; + } + + if (NULL != prop && 2 < ctx->stack.values_num - i) + { + *error = zbx_dsprintf(*error, "property must be compared with a constant value at" + " \"%s\"", ctx->expression + prop->loc.l); + return FAIL; + } + } + + zbx_vector_eval_token_append_ptr(&ctx->stack, token); + + return SUCCEED; +} + + +/****************************************************************************** + * * + * Function: eval_append_arg_null * + * * + * Purpose: add null argument token to evaluation stack * + * * + * Parameters: ctx - [IN] the evaluation context * + * * + ******************************************************************************/ +static void eval_append_arg_null(zbx_eval_context_t *ctx) +{ + zbx_eval_token_t null_token = {.type = ZBX_EVAL_TOKEN_ARG_NULL}; + + zbx_vector_eval_token_append_ptr(&ctx->stack, &null_token); +} + +/****************************************************************************** + * * + * Function: eval_clear * + * * + * Purpose: free resources allocated by evaluation context * + * * + * Parameters: ctx - [IN] the evaluation context * + * * + ******************************************************************************/ +static void eval_clear(zbx_eval_context_t *ctx) +{ + if (NULL != ctx->stack.values) + { + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + zbx_variant_clear(&ctx->stack.values[i].value); + + zbx_vector_eval_token_destroy(&ctx->stack); + } +} + +/****************************************************************************** + * * + * Function: eval_parse_expression * + * * + * Purpose: parse expression into tokens in postfix notation order * + * * + * Parameters: ctx - [OUT] the evaluation context * + * expression - [IN] the expression to parse * + * rules - [IN] the parsing rules * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - expression was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_parse_expression(zbx_eval_context_t *ctx, const char *expression, zbx_uint64_t rules, char **error) +{ + size_t pos = 0; + int ret = FAIL; + zbx_eval_token_t *optoken; + + ctx->expression = expression; + ctx->rules = rules; + ctx->last_token_type = ZBX_EVAL_CLASS_SEPARATOR; + ctx->const_index = 0; + ctx->functionid_index = 0; + zbx_vector_eval_token_create(&ctx->stack); + zbx_vector_eval_token_reserve(&ctx->stack, 16); + zbx_vector_eval_token_create(&ctx->ops); + zbx_vector_eval_token_reserve(&ctx->ops, 16); + + while ('\0' != expression[pos]) + { + zbx_eval_token_t token = {0}; + + if (SUCCEED != eval_parse_token(ctx, pos, &token, error)) + goto out; + + if (0 == token.type) + break; + + /* serialization used for parsed expression caching has limits expression to 0x7fffffff */ + if ((zbx_uint32_t)0x7fffffff < token.loc.r) + { + *error = zbx_strdup(*error, "too long expression"); + goto out; + } + + if (ZBX_EVAL_TOKEN_ARG_QUERY == ctx->last_token_type && ZBX_EVAL_TOKEN_COMMA != token.type && + ZBX_EVAL_TOKEN_GROUP_CLOSE != token.type) + { + *error = zbx_dsprintf(*error, "invalid expression following query token at \"%s\"", + ctx->expression + pos); + goto out; + } + + if (ZBX_EVAL_TOKEN_GROUP_CLOSE == token.type && ZBX_EVAL_TOKEN_COMMA == ctx->last_token_type) + eval_append_arg_null(ctx); + + if (ZBX_EVAL_TOKEN_GROUP_OPEN == token.type) + { + if (0 == (ctx->last_token_type & (ZBX_EVAL_BEFORE_OPERAND | ZBX_EVAL_CLASS_FUNCTION))) + { + *error = zbx_dsprintf(*error, "opening parenthesis must follow operator or function" + " at \"%s\"", ctx->expression + pos); + goto out; + } + + zbx_vector_eval_token_append_ptr(&ctx->ops, &token); + } + else if (0 != (token.type & ZBX_EVAL_CLASS_FUNCTION)) + { + if (0 == (ctx->last_token_type & ZBX_EVAL_BEFORE_OPERAND)) + { + *error = zbx_dsprintf(*error, "function must follow operand or unary operator" + " at \"%s\"", ctx->expression + pos); + goto out; + } + zbx_vector_eval_token_append_ptr(&ctx->ops, &token); + } + else if (ZBX_EVAL_TOKEN_COMMA == token.type) + { + /* comma must follow and operand, comma or function */ + if (0 == (ctx->last_token_type & ZBX_EVAL_CLASS_OPERAND) && + (0 == (ctx->last_token_type & ZBX_EVAL_CLASS_SEPARATOR))) + { + *error = zbx_dsprintf(*error, "comma must follow an operand or separator at \"%s\"", + ctx->expression + pos); + goto out; + } + + if (0 != (ctx->last_token_type & ZBX_EVAL_CLASS_SEPARATOR)) + eval_append_arg_null(ctx); + + for (optoken = NULL; 0 < ctx->ops.values_num; ctx->ops.values_num--) + { + optoken = &ctx->ops.values[ctx->ops.values_num - 1]; + + if (ZBX_EVAL_TOKEN_GROUP_OPEN == optoken->type) + break; + + if (FAIL == eval_append_operator(ctx, optoken, error)) + goto out; + } + + if (NULL == optoken) + { + *error = zbx_dsprintf(*error, "missing function argument separator for comma at\"%s\"", + ctx->expression + pos); + goto out; + } + } + else if (ZBX_EVAL_TOKEN_GROUP_CLOSE == token.type) + { + /* right parenthesis must follow and operand, right parenthesis or function */ + if (0 == (ctx->last_token_type & (ZBX_EVAL_CLASS_OPERAND | ZBX_EVAL_CLASS_PROPERTY | + ZBX_EVAL_CLASS_SEPARATOR)) && + (ctx->ops.values_num < 2 || + ZBX_EVAL_TOKEN_FUNCTION != ctx->ops.values[ctx->ops.values_num - 2].type)) + { + *error = zbx_dsprintf(*error, "right parenthesis must follow an operand or left" + " parenthesis at \"%s\"", ctx->expression + pos); + goto out; + } + + for (optoken = NULL; 0 < ctx->ops.values_num; ctx->ops.values_num--) + { + optoken = &ctx->ops.values[ctx->ops.values_num - 1]; + + if (ZBX_EVAL_TOKEN_GROUP_OPEN == optoken->type) + { + ctx->ops.values_num--; + break; + } + + if (FAIL == eval_append_operator(ctx, optoken, error)) + goto out; + } + + if (NULL == optoken) + { + *error = zbx_dsprintf(*error, "missing left parenthesis for right parenthesis at \"%s\"", + ctx->expression + pos); + goto out; + } + + if (0 != ctx->ops.values_num) + { + optoken = &ctx->ops.values[ctx->ops.values_num - 1]; + + if (0 != (optoken->type & ZBX_EVAL_CLASS_FUNCTION)) + { + if (FAIL == eval_append_operator(ctx, optoken, error)) + goto out; + ctx->ops.values_num--; + } + } + } + else if (0 != (token.type & (ZBX_EVAL_CLASS_OPERAND | ZBX_EVAL_CLASS_PROPERTY))) + { + if (FAIL == eval_append_operand(ctx, &token, error)) + goto out; + } + else if (0 != (token.type & ZBX_EVAL_CLASS_OPERATOR)) + { + /* binary operator cannot be used after operator */ + if (0 != (token.type & ZBX_EVAL_CLASS_OPERATOR2) && + 0 == (ctx->last_token_type & ZBX_EVAL_BEFORE_OPERATOR)) + { + *error = zbx_dsprintf(*error, "binary operator must be used after operand at \"%s\"", + ctx->expression + pos); + goto out; + } + + /* unary !,- operators cannot follow an operand */ + if (0 != (token.type & ZBX_EVAL_CLASS_OPERATOR1) && + 0 == (ctx->last_token_type & ZBX_EVAL_BEFORE_OPERAND)) + { + *error = zbx_dsprintf(*error, "unary operator cannot follow an operand at \"%s\"", + ctx->expression + pos); + goto out; + } + + for (; 0 < ctx->ops.values_num; ctx->ops.values_num--) + { + optoken = &ctx->ops.values[ctx->ops.values_num - 1]; + + if ((optoken->type & ZBX_EVAL_OP_PRIORITY) > (token.type & ZBX_EVAL_OP_PRIORITY) || + 0 != (token.type & ZBX_EVAL_CLASS_OPERATOR1)) + break; + + if (ZBX_EVAL_TOKEN_GROUP_OPEN == optoken->type) + break; + + if (FAIL == eval_append_operator(ctx, optoken, error)) + goto out; + } + + zbx_vector_eval_token_append_ptr(&ctx->ops, &token); + } + + ctx->last_token_type = token.type; + pos = token.loc.r + 1; + } + + if (0 != (ctx->last_token_type & ZBX_EVAL_CLASS_OPERATOR)) + { + *error = zbx_strdup(*error, "expression ends with operator"); + goto out; + } + + if (ZBX_EVAL_TOKEN_COMMA == ctx->last_token_type) + { + *error = zbx_strdup(*error, "expression ends with comma"); + goto out; + } + + for (; 0 < ctx->ops.values_num; ctx->ops.values_num--) + { + optoken = &ctx->ops.values[ctx->ops.values_num - 1]; + + if (ZBX_EVAL_TOKEN_GROUP_OPEN == optoken->type) + { + *error = zbx_dsprintf(*error, "mismatched () brackets in expression: %s", ctx->expression); + goto out; + } + + if (FAIL == eval_append_operator(ctx, optoken, error)) + goto out; + } + + if (0 == ctx->stack.values_num) + { + *error = zbx_strdup(*error, "empty expression"); + goto out; + } + + if (0 != (ctx->rules & ZBX_EVAL_PARSE_PROPERTY) && 1 == ctx->stack.values_num) + { + if (0 != (ctx->stack.values[ctx->stack.values_num - 1].type & ZBX_EVAL_CLASS_PROPERTY)) + { + zbx_eval_token_t *prop = &ctx->stack.values[ctx->stack.values_num - 1]; + + *error = zbx_dsprintf(*error, "missing comparison string for property at \"%s\"", + ctx->expression + prop->loc.l); + goto out; + } + } + + ret = SUCCEED; +out: + zbx_vector_eval_token_destroy(&ctx->ops); + + if (SUCCEED != ret) + eval_clear(ctx); + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_eval_parse_expression * + * * + * Purpose: parse expression into tokens in postfix notation order * + * * + * Parameters: ctx - [OUT] the evaluation context * + * expression - [IN] the expression to parse * + * rules - [IN] the parsing rules * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - expression was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_eval_parse_expression(zbx_eval_context_t *ctx, const char *expression, zbx_uint64_t rules, char **error) +{ + return eval_parse_expression(ctx, expression, rules, error); +} + +/****************************************************************************** + * * + * Function: zbx_eval_init * + * * + * Purpose: initialize context so it can be cleared without parsing * + * * + * Parameters: ctx - [IN] the evaluation context * + * * + ******************************************************************************/ +void zbx_eval_init(zbx_eval_context_t *ctx) +{ + memset(ctx, 0, sizeof(zbx_eval_context_t)); +} + +/****************************************************************************** + * * + * Function: zbx_eval_clear * + * * + * Purpose: free resources allocated by evaluation context * + * * + * Parameters: ctx - [IN] the evaluation context * + * * + ******************************************************************************/ +void zbx_eval_clear(zbx_eval_context_t *ctx) +{ + eval_clear(ctx); +} + +/****************************************************************************** + * * + * Function: zbx_eval_status * + * * + * Purpose: return evaluation context status * + * * + * Parameters: ctx - [IN] the evaluation context * + * * + * Return value: SUCCEED - contains parsed expression * + * FAIL - empty, either parsing failed or was initialized * + * without parsing * + * * + ******************************************************************************/ +int zbx_eval_status(const zbx_eval_context_t *ctx) +{ + return (NULL == ctx->expression ? FAIL : SUCCEED); +} diff --git a/src/libs/zbxeval/query.c b/src/libs/zbxeval/query.c new file mode 100644 index 00000000000..5fc64f9aad5 --- /dev/null +++ b/src/libs/zbxeval/query.c @@ -0,0 +1,507 @@ +/* +** Zabbix +** Copyright (C) 2001-2020 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 "common.h" +#include "log.h" +#include "zbxalgo.h" +#include "zbxserver.h" +#include "zbxeval.h" +#include "eval.h" + +/* The tag expression token is virtual token used during item query filter processing. */ +#define ZBX_EVAL_TOKEN_TAG_EXPRESSION (1000) + +/****************************************************************************** + * * + * Function: zbx_eval_parse_query * + * * + * Purpose: parse item query /host/key?[filter] into host, key and filter * + * components * + * * + * Parameters: str - [IN] the item query * + * len - [IN] the query length * + * query - [IN] the parsed item query * + * * + * Return value: The number of parsed characters. * + * * + ******************************************************************************/ +size_t zbx_eval_parse_query(const char *str, size_t len, zbx_item_query_t *query) +{ + size_t n; + const char *host, *key, *filter; + + if (0 == (n = eval_parse_query(str, &host, &key, &filter)) || n != len) + return 0; + + query->host = (host != key - 1 ? zbx_substr(host, 0, key - host - 2) : NULL); + + if (NULL != filter) + { + query->key = zbx_substr(key, 0, (filter - key) - 3); + query->filter = zbx_substr(filter, 0, (str + len - filter) - 2); + } + else + { + query->key = zbx_substr(key, 0, (str + len - key) - 1); + query->filter = NULL; + } + + return n; +} + +/****************************************************************************** + * * + * Function: zbx_eval_clear_filter * + * * + * Purpose: frees resources allocated by item reference * + * * + ******************************************************************************/ +void zbx_eval_clear_query(zbx_item_query_t *query) +{ + zbx_free(query->host); + zbx_free(query->key); + zbx_free(query->filter); +} + +/****************************************************************************** + * * + * Function: zbx_eval_prepare_filter * + * * + * Purpose: prepare filter expression by converting property comparisons * + * prop =/<> "value" to prop("value")/not prop("value") function * + * calls. * + * * + * Parameters: ctx - [IN] the evaluation context * + * * + ******************************************************************************/ +void zbx_eval_prepare_filter(zbx_eval_context_t *ctx) +{ + int i, j; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *prop = &ctx->stack.values[i]; + + if (0 == (prop->type & ZBX_EVAL_CLASS_PROPERTY)) + continue; + + for (j = i + 1; j < ctx->stack.values_num; j++) + { + zbx_eval_token_t *op = &ctx->stack.values[j]; + + if (ZBX_EVAL_TOKEN_OP_EQ == op->type || ZBX_EVAL_TOKEN_OP_NE == op->type) + { + zbx_eval_token_t *func; + + func = &ctx->stack.values[j - 1]; + + if (i != j - 1) + { + zbx_strloc_t loc; + + loc = prop->loc; + *prop = *func; + func->loc = loc; + } + + func->opt = 1; + func->type = ZBX_EVAL_TOKEN_FUNCTION; + + if (ZBX_EVAL_TOKEN_OP_NE == op->type) + op->type = ZBX_EVAL_TOKEN_OP_NOT; + else + op->type = ZBX_EVAL_TOKEN_NOP; + + break; + } + } + } +} + +/****************************************************************************** + * * + * Function: eval_filter_apply_op2 * + * * + * Purpose: apply binary operation to stack * + * * + * Parameters: token - [IN] the operation token * + * stack - [IN/OUT] the target stack * + * index - [IN/OUT] the stack index * + * * + ******************************************************************************/ +static void eval_filter_apply_op2(zbx_eval_token_t *token, zbx_vector_eval_token_t *stack, + zbx_vector_uint64_t *index) +{ + zbx_eval_token_t *left, *right; + int li, ri; + + li = (int)index->values[index->values_num - 2]; + left = &stack->values[li]; + ri = (int)index->values[index->values_num - 1]; + right = &stack->values[ri]; + + if (ZBX_EVAL_TOKEN_TAG_EXPRESSION == left->type || ZBX_EVAL_TOKEN_TAG_EXPRESSION == right->type) + { + switch (token->type) + { + case ZBX_EVAL_TOKEN_OP_AND: + if (ZBX_EVAL_TOKEN_TAG_EXPRESSION == left->type) + { + memmove(left, right, (stack->values_num - ri) * sizeof(zbx_eval_token_t)); + stack->values_num -= (ri - li); + } + else + stack->values_num--; + break; + default: + left->type = ZBX_EVAL_TOKEN_TAG_EXPRESSION; + stack->values_num = li + 1; + break; + } + + index->values_num--; + return; + } + + zbx_vector_eval_token_append_ptr(stack, token); + index->values_num--; +} + +/****************************************************************************** + * * + * Function: eval_filter_apply_op1 * + * * + * Purpose: apply unary operation to stack * + * * + * Parameters: token - [IN] the operation token * + * stack - [IN/OUT] the target stack * + * index - [IN/OUT] the stack index * + * * + ******************************************************************************/ +static void eval_filter_apply_op1(zbx_eval_token_t *token, zbx_vector_eval_token_t *stack, + const zbx_vector_uint64_t *index) +{ + zbx_eval_token_t *right; + int ri; + + ri = (int)index->values[index->values_num - 1]; + right = &stack->values[ri]; + + if (ZBX_EVAL_TOKEN_TAG_EXPRESSION != right->type) + zbx_vector_eval_token_append_ptr(stack, token); +} + +/****************************************************************************** + * * + * Function: eval_filter_apply_func * + * * + * Purpose: apply function to stack * + * * + * Parameters: ctx - [IN] the evaluation context * + * token - [IN] the function token * + * stack - [IN/OUT] the target stack * + * index - [IN/OUT] the stack index * + * * + ******************************************************************************/ +static void eval_filter_apply_func(zbx_eval_context_t *ctx, zbx_eval_token_t *token, + zbx_vector_eval_token_t *stack, const zbx_vector_uint64_t *index) +{ + zbx_eval_token_t *left; + int li; + + if (ZBX_CONST_STRLEN("tag") == token->loc.r - token->loc.l + 1 && + 0 == memcmp(ctx->expression + token->loc.l, "tag", ZBX_CONST_STRLEN("tag"))) + { + li = (int)index->values[index->values_num - 1]; + left = &stack->values[li]; + + left->type = ZBX_EVAL_TOKEN_TAG_EXPRESSION; + stack->values_num = li + 1; + } + else + zbx_vector_eval_token_append_ptr(stack, token); +} + +/****************************************************************************** + * * + * Function: eval_op_str * + * * + * Purpose: get operator in text format * + * * + * Parameters: op - [IN] the operator type * + * * + * Return value: The operator in text format. * + * * + * Comments: This function will return 'unsupported operator' for unsupported * + * operators, causing the expression evaluation to fail. However * + * this should not happen as the supported operators are verified * + * during expression parsing. * + * * + ******************************************************************************/ +static const char *eval_op_str(zbx_token_type_t op) +{ + switch (op) + { + case ZBX_EVAL_TOKEN_OP_EQ: + return "="; + case ZBX_EVAL_TOKEN_OP_NE: + return "<>"; + case ZBX_EVAL_TOKEN_OP_AND: + return " and "; + case ZBX_EVAL_TOKEN_OP_OR: + return " or "; + case ZBX_EVAL_TOKEN_OP_NOT: + return "not "; + default: + return "unsupported operator"; + } +} + +/****************************************************************************** + * * + * Function: eval_unquote_str * + * * + * Purpose: unquote string * + * * + * Parameters: str - [IN] the string to unquote * + * * + * Return value: The unquoted string. * + * * + * Comments: The string is unquoted in the same buffer. * + * * + ******************************************************************************/ +static char *eval_unquote_str(char *str) +{ + char *dst, *src; + + if ('\"' != *str) + return str; + + src = str; + dst = src++; + + while ('\0' != *src && '"' != *src) + { + if ('\\' == *src) + { + + if ('\0' == *(++src)) + break; + } + + *dst++ = *src++; + } + + *dst = '\0'; + + return str; +} + +/****************************************************************************** + * * + * Function: eval_generate_filter * + * * + * Purpose: generate filter expression from the specified stack * + * * + * Parameters: ctx - [IN] the evaluation context * + * stack - [IN] the expression stack * + * groups - [OUT] the group values to match * + * filter - [OUT] the generated filter * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - the filter expression was successfully generated * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int eval_generate_filter(const zbx_eval_context_t *ctx, const zbx_vector_eval_token_t *stack, + zbx_vector_str_t *groups, char **filter, char **error) +{ + zbx_vector_str_t out; + int i, ret = FAIL; + char *tmp; + + if (0 == stack->values_num) + { + *error = zbx_strdup(NULL, "invalid filter expression"); + return FAIL; + } + + if (ZBX_EVAL_TOKEN_TAG_EXPRESSION == stack->values[0].type) + { + *filter = NULL; + return SUCCEED; + } + + zbx_vector_str_create(&out); + + for (i = 0; i < stack->values_num; i++) + { + zbx_eval_token_t *token = &stack->values[i]; + + if (0 != (token->type & ZBX_EVAL_CLASS_OPERATOR2)) + { + if (2 > out.values_num) + { + *error = zbx_strdup(NULL, "not enough values on stack for binary operation"); + goto out; + } + + tmp = zbx_dsprintf(NULL, "(%s%s%s)", out.values[out.values_num - 2], eval_op_str(token->type), + out.values[out.values_num - 1]); + + zbx_free(out.values[out.values_num - 2]); + zbx_free(out.values[out.values_num - 1]); + out.values_num -= 1; + out.values[out.values_num - 1] = tmp; + } + else if (0 != (token->type & ZBX_EVAL_CLASS_OPERATOR1)) + { + if (1 > out.values_num) + { + *error = zbx_strdup(NULL, "not enough values on stack for unary operation"); + goto out; + } + + tmp = zbx_dsprintf(NULL, "%s%s", eval_op_str(token->type), out.values[out.values_num - 1]); + + zbx_free(out.values[out.values_num - 1]); + out.values[out.values_num - 1] = tmp; + } + else if (ZBX_EVAL_TOKEN_FUNCTION == token->type) + { + if (1 > out.values_num) + { + *error = zbx_strdup(NULL, "not enough values on stack for property comparison"); + goto out; + } + + tmp = zbx_dsprintf(NULL, "{%d}", groups->values_num); + zbx_vector_str_append(groups, eval_unquote_str(out.values[out.values_num - 1])); + out.values[out.values_num - 1] = tmp; + } + else if (ZBX_EVAL_TOKEN_NOP != token->type) + zbx_vector_str_append(&out, zbx_substr(ctx->expression, token->loc.l, token->loc.r)); + } + + if (1 != out.values_num) + { + *error = zbx_strdup(NULL, "too many values left on stack after generating filter expression"); + goto out; + } + + *filter = out.values[0]; + out.values_num = 0; + + ret = SUCCEED; +out: + zbx_vector_str_clear_ext(&out, zbx_str_free); + zbx_vector_str_destroy(&out); + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_eval_get_group_filter * + * * + * Purpose: generate group SQL filter expression from item filter * + * * + * Parameters: ctx - [IN] the filter expression evaluation context * + * groups - [OUT] the group values to match * + * filter - [OUT] the generated filter * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - the filter expression was successfully generated * + * FAIL - otherwise * + * * + * Comments: The filter SQL is generated in two steps. * + * 1) The filter expression token stack is simplified by removing * + * tag related expression parts, so tags do not affect selection * + * item candidates. * + * This is done by 'evaluating' the original filter expression * + * token stack according to the following rules: * + * * group comparisons are copied without changes * + * * tag comparisons are replaced with TAG_EXPRESSION token * + * * TAG_EXPRESSION and <expression> is replaced with * + * <expression> * + * * TAG_EXPRESSION or <expression> is replaced with * + * TAG_EXPRESSION * + * * not TAG_EXPRESSION is replaced with TAG_EXPRESSION * + * * + * 2) At this point the simplified stack will contain either only * + * group comparisons/logical operators or TAG_EXPRESSION token. * + * In the first case the filter is generated by replacing group * + * comparisons with {N}, where N is the index of value to * + * compare in groups vector. * + * In the second case it means that selection of item candidates * + * cannot be restricted by groups and the filter must be NULL. * + * * + ******************************************************************************/ +int zbx_eval_get_group_filter(zbx_eval_context_t *ctx, zbx_vector_str_t *groups, char **filter, char **error) +{ + zbx_vector_eval_token_t stack; /* simplified filter expression token stack */ + zbx_vector_uint64_t output; /* pseudo output stack, containing indexes of expression */ + /* fragments in the simplified filter expression token stack */ + int i, ret = FAIL; + + zbx_vector_eval_token_create(&stack); + zbx_vector_uint64_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_OPERATOR2)) + { + if (2 > output.values_num) + { + *error = zbx_strdup(NULL, "not enough values on stack for binary operation"); + goto out; + } + + eval_filter_apply_op2(token, &stack, &output); + } + else if (0 != (token->type & ZBX_EVAL_CLASS_OPERATOR1)) + { + if (1 > output.values_num) + { + *error = zbx_strdup(NULL, "not enough values on stack for unary operation"); + goto out; + } + + eval_filter_apply_op1(token, &stack, &output); + } + else if (ZBX_EVAL_TOKEN_FUNCTION == token->type) + { + eval_filter_apply_func(ctx, token, &stack, &output); + } + else if (ZBX_EVAL_TOKEN_NOP != token->type) + { + zbx_vector_uint64_append(&output, stack.values_num); + zbx_vector_eval_token_append_ptr(&stack, token); + } + } + + ret = eval_generate_filter(ctx, &stack, groups, filter, error); +out: + zbx_vector_uint64_destroy(&output); + zbx_vector_eval_token_destroy(&stack); + + return ret; +} diff --git a/src/libs/zbxhistory/history.c b/src/libs/zbxhistory/history.c index 8215ea49e18..3afcdabd790 100644 --- a/src/libs/zbxhistory/history.c +++ b/src/libs/zbxhistory/history.c @@ -426,3 +426,46 @@ int zbx_history_record_compare_desc_func(const zbx_history_record_t *d1, const z return d2->timestamp.sec - d1->timestamp.sec; } +/****************************************************************************** + * * + * Function: zbx_history_value2variant * + * * + * Purpose: converts history value to variant value * + * * + * Parameters: value - [IN] the value to convert * + * value_type - [IN] the history value type * + * var - [IN] the output value * + * * + ******************************************************************************/ +void zbx_history_value2variant(const history_value_t *value, unsigned char value_type, zbx_variant_t *var) +{ + switch (value_type) + { + case ITEM_VALUE_TYPE_FLOAT: + zbx_variant_set_dbl(var, value->dbl); + break; + case ITEM_VALUE_TYPE_UINT64: + zbx_variant_set_ui64(var, value->ui64); + break; + case ITEM_VALUE_TYPE_STR: + case ITEM_VALUE_TYPE_TEXT: + zbx_variant_set_str(var, zbx_strdup(NULL, value->str)); + break; + case ITEM_VALUE_TYPE_LOG: + zbx_variant_set_str(var, zbx_strdup(NULL, value->log->value)); + } +} + +/****************************************************************************** + * * + * Function: zbx_history_check_version * + * * + * Purpose: relays the version retrieval logic to the history implementation * + * functions * + * * + ******************************************************************************/ +void zbx_history_check_version(struct zbx_json *json) +{ + if (NULL != CONFIG_HISTORY_STORAGE_URL) + zbx_elastic_version_extract(json); +} diff --git a/src/libs/zbxhistory/history.h b/src/libs/zbxhistory/history.h index e086fa455db..710b9575d70 100644 --- a/src/libs/zbxhistory/history.h +++ b/src/libs/zbxhistory/history.h @@ -20,6 +20,8 @@ #ifndef ZABBIX_HISTORY_H #define ZABBIX_HISTORY_H +#include "zbxjson.h" + #define ZBX_HISTORY_IFACE_SQL 0 #define ZBX_HISTORY_IFACE_ELASTIC 1 @@ -48,5 +50,7 @@ int zbx_history_sql_init(zbx_history_iface_t *hist, unsigned char value_type, ch /* elastic hist */ int zbx_history_elastic_init(zbx_history_iface_t *hist, unsigned char value_type, char **error); +void zbx_elastic_version_extract(struct zbx_json *json); +zbx_uint32_t zbx_elastic_version_get(void); #endif diff --git a/src/libs/zbxhistory/history_elastic.c b/src/libs/zbxhistory/history_elastic.c index 313de56b563..c55e0a97a69 100644 --- a/src/libs/zbxhistory/history_elastic.c +++ b/src/libs/zbxhistory/history_elastic.c @@ -19,7 +19,6 @@ #include "common.h" #include "log.h" -#include "zbxjson.h" #include "zbxalgo.h" #include "dbcache.h" #include "zbxhistory.h" @@ -34,12 +33,13 @@ #define ZBX_IDX_JSON_ALLOCATE 256 #define ZBX_JSON_ALLOCATE 2048 - const char *value_type_str[] = {"dbl", "str", "log", "uint", "text"}; extern char *CONFIG_HISTORY_STORAGE_URL; extern int CONFIG_HISTORY_STORAGE_PIPELINES; +static zbx_uint32_t ZBX_ELASTIC_SVERSION = ZBX_DBVERSION_UNDEFINED; + typedef struct { char *base_url; @@ -479,7 +479,7 @@ try_again: { int fds; CURLMcode code; - char *error; + char *error; zbx_curlpage_t *curl_page; if (CURLM_OK != (code = curl_multi_perform(writer.handle, &running))) @@ -1015,15 +1015,134 @@ int zbx_history_elastic_init(zbx_history_iface_t *hist, unsigned char value_type return SUCCEED; } -#else +/************************************************************************************ + * * + * Function: zbx_elastic_version_extract * + * * + * Purpose: queries elastic search version and extracts the numeric version from * + * the response string * + * * + ************************************************************************************/ +void zbx_elastic_version_extract(struct zbx_json *json) +{ +#define RIGHT2(x) ((int)((zbx_uint32_t)(x) - ((zbx_uint32_t)((x)/100))*100)) + zbx_httppage_t page; + struct zbx_json_parse jp, jp_values, jp_sub; + struct curl_slist *curl_headers; + CURLcode err; + CURLoption opt; + CURL *handle; + size_t version_len = 0; + char *version_friendly = NULL, errbuf[CURL_ERROR_SIZE]; + int flag, major_num, minor_num, increment_num, ret = FAIL; + zbx_uint32_t version; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + memset(&page, 0, sizeof(zbx_httppage_t)); + + if (0 != curl_global_init(CURL_GLOBAL_ALL)) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot initialize cURL library"); + goto out; + } + + if (NULL == (handle = curl_easy_init())) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot initialize cURL session"); + goto out; + } + + curl_headers = curl_slist_append(NULL, "Content-Type: application/json"); + + if (CURLE_OK != (err = curl_easy_setopt(handle, opt = CURLOPT_URL, CONFIG_HISTORY_STORAGE_URL)) || + CURLE_OK != (err = curl_easy_setopt(handle, opt = CURLOPT_WRITEFUNCTION, curl_write_cb)) || + CURLE_OK != (err = curl_easy_setopt(handle, opt = CURLOPT_WRITEDATA, &page)) || + CURLE_OK != (err = curl_easy_setopt(handle, opt = CURLOPT_HTTPHEADER, curl_headers)) || + CURLE_OK != (err = curl_easy_setopt(handle, opt = CURLOPT_FAILONERROR, 1L)) || + CURLE_OK != (err = curl_easy_setopt(handle, opt = CURLOPT_ERRORBUFFER, errbuf))) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot set cURL option %d: [%s]", (int)opt, curl_easy_strerror(err)); + goto clean; + } + + *errbuf = '\0'; + + if (CURLE_OK != (err = curl_easy_perform(handle))) + { + elastic_log_error(handle, err, errbuf); + goto clean; + + } + if (SUCCEED != zbx_json_open(page.data, &jp) || + SUCCEED != zbx_json_brackets_open(jp.start, &jp_values) || + SUCCEED != zbx_json_brackets_by_name(&jp_values, "version", &jp_sub) || + SUCCEED != zbx_json_value_by_name_dyn(&jp_sub, "number", &version_friendly, &version_len, NULL)) + { + goto clean; + } + + ret = SUCCEED; +clean: + curl_slist_free_all(curl_headers); + curl_easy_cleanup(handle); +out: + if (FAIL == ret) + { + zabbix_log(LOG_LEVEL_CRIT, "Failed to extract ElasticDB version"); + version = ZBX_DBVERSION_UNDEFINED; + } + else + { + zabbix_log(LOG_LEVEL_DEBUG, "ElasticDB version retrieved unparsed: %s", version_friendly); + + if (3 != sscanf(version_friendly, "%d.%d.%d", &major_num, &minor_num, &increment_num) || + major_num >= 100 || major_num <= 0 || minor_num >= 100 || minor_num < 0 || + increment_num >= 100 || increment_num < 0) + { + zabbix_log(LOG_LEVEL_WARNING, "Failed to detect ElasticDB version from the " + "following query result: %s", version_friendly); + version = ZBX_DBVERSION_UNDEFINED; + } + else + { + version = major_num * 10000 + minor_num * 100 + increment_num; + } + } + + flag = zbx_db_version_check("ElasticDB", version, ZBX_ELASTIC_MIN_VERSION, ZBX_DBVERSION_UNDEFINED); + zbx_db_version_json_create(json, "ElasticDB", version_friendly, ZBX_ELASTIC_MIN_VERSION_FRIENDLY, "", flag); + ZBX_ELASTIC_SVERSION = version; + zbx_free(version_friendly); + zbx_free(page.data); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s version:%lu", __func__, zbx_result_string(ret), + (unsigned long)version); +} + +zbx_uint32_t zbx_elastic_version_get(void) +{ + return ZBX_ELASTIC_SVERSION; +} +#else int zbx_history_elastic_init(zbx_history_iface_t *hist, unsigned char value_type, char **error) { ZBX_UNUSED(hist); ZBX_UNUSED(value_type); *error = zbx_strdup(*error, "cURL library support >= 7.28.0 is required for Elasticsearch history backend"); + return FAIL; } +void zbx_elastic_version_extract(struct zbx_json *json) +{ + ZBX_UNUSED(json); +} + +zbx_uint32_t zbx_elastic_version_get(void) +{ + return ZBX_DBVERSION_UNDEFINED; +} #endif diff --git a/src/libs/zbxjson/json.c b/src/libs/zbxjson/json.c index 7f73a05c0aa..5b0e4a6860e 100644 --- a/src/libs/zbxjson/json.c +++ b/src/libs/zbxjson/json.c @@ -875,11 +875,11 @@ static const char *zbx_json_copy_string(const char *p, char *out, size_t size) while ('\0' != *p) { + unsigned int nbytes, i; + unsigned char uc[4]; /* decoded Unicode character takes 1-4 bytes in UTF-8 */ + switch (*p) { - unsigned int nbytes, i; - unsigned char uc[4]; /* decoded Unicode character takes 1-4 bytes in UTF-8 */ - case '\\': ++p; if (0 == (nbytes = zbx_json_decode_character(&p, uc))) diff --git a/src/libs/zbxjson/jsonpath.c b/src/libs/zbxjson/jsonpath.c index 4abe69e08c7..17b8342b426 100644 --- a/src/libs/zbxjson/jsonpath.c +++ b/src/libs/zbxjson/jsonpath.c @@ -25,12 +25,10 @@ #include "json.h" #include "json_parser.h" #include "jsonpath.h" +#include "zbxvariant.h" #include "../zbxalgo/vectorimpl.h" -ZBX_VECTOR_DECL(var, zbx_variant_t) -ZBX_VECTOR_IMPL(var, zbx_variant_t) - typedef struct { char *name; diff --git a/src/libs/zbxserver/Makefile.am b/src/libs/zbxserver/Makefile.am index c7fda611927..d0f0b47b169 100644 --- a/src/libs/zbxserver/Makefile.am +++ b/src/libs/zbxserver/Makefile.am @@ -6,14 +6,17 @@ libzbxserver_a_SOURCES = \ evalfunc.c \ evalfunc.h \ expression.c \ + expression.h \ macrofunc.c \ macrofunc.h \ zabbix_stats.c \ zabbix_stats.h \ + evalfunc2.c \ get_host_from_event.c \ get_host_from_event.h \ zabbix_users.c \ - zabbix_users.h + zabbix_users.h \ + expression_eval.c libzbxserver_server_a_SOURCES = \ zabbix_stats.h \ diff --git a/src/libs/zbxserver/evalfunc.c b/src/libs/zbxserver/evalfunc.c index 979bae94e48..a03303c8a9d 100644 --- a/src/libs/zbxserver/evalfunc.c +++ b/src/libs/zbxserver/evalfunc.c @@ -424,7 +424,7 @@ out: #define OP_LIKE 6 #define OP_REGEXP 7 #define OP_IREGEXP 8 -#define OP_BAND 9 +#define OP_BITAND 9 #define OP_MAX 10 static void count_one_ui64(int *count, int op, zbx_uint64_t value, zbx_uint64_t pattern, zbx_uint64_t mask) @@ -455,7 +455,7 @@ static void count_one_ui64(int *count, int op, zbx_uint64_t value, zbx_uint64_t if (value <= pattern) (*count)++; break; - case OP_BAND: + case OP_BITAND: if ((value & mask) == pattern) (*count)++; } @@ -627,7 +627,7 @@ static int evaluate_COUNT(char **value, DC_ITEM *item, const char *parameters, c else if (0 == strcmp(arg3, "iregexp")) op = OP_IREGEXP; else if (0 == strcmp(arg3, "band")) - op = OP_BAND; + op = OP_BITAND; if (OP_UNKNOWN == op) { @@ -652,14 +652,14 @@ static int evaluate_COUNT(char **value, DC_ITEM *item, const char *parameters, c goto out; } - if (OP_BAND == op && ITEM_VALUE_TYPE_FLOAT == item->value_type) + if (OP_BITAND == op && ITEM_VALUE_TYPE_FLOAT == item->value_type) { *error = zbx_dsprintf(*error, "operator \"%s\" is not supported for counting float values", arg3); goto out; } - if (OP_BAND == op && NULL != (arg2_2 = strchr(arg2, '/'))) + if (OP_BITAND == op && NULL != (arg2_2 = strchr(arg2, '/'))) { *arg2_2 = '\0'; /* end of the 1st part of the 2nd parameter (number to compare with) */ arg2_2++; /* start of the 2nd part of the 2nd parameter (mask) */ @@ -669,7 +669,7 @@ static int evaluate_COUNT(char **value, DC_ITEM *item, const char *parameters, c { if (ITEM_VALUE_TYPE_UINT64 == item->value_type) { - if (OP_BAND != op) + if (OP_BITAND != op) { if (SUCCEED != str2uint64(arg2, ZBX_UNIT_SYMBOLS, &arg2_ui64)) { @@ -834,7 +834,7 @@ out: #undef OP_LIKE #undef OP_REGEXP #undef OP_IREGEXP -#undef OP_BAND +#undef OP_BITAND #undef OP_MAX /****************************************************************************** @@ -2697,6 +2697,167 @@ out: /****************************************************************************** * * + * Function: trends_parse_range * + * * + * Purpose: parse trend function period arguments into time range using old * + * parameter format * + * * + * Parameters: from - [IN] the time the period shift is calculated * + * from * + * period - [IN] the history period * + * period_shift - [IN] the history period shift * + * start - [OUT] the period start time in seconds since * + * Epoch * + * end - [OUT] the period end time in seconds since * + * Epoch * + * error - [OUT] the error message if parsing failed * + * * + * Return value: SUCCEED - period was parsed successfully * + * FAIL - invalid time period was specified * + * * + * Comments: Daylight saving changes are applied when parsing ranges with * + * day+ used as period base (now/?). * + * * + * Example period_shift values: * + * now/d * + * now/d-1h * + * now/d+1h * + * now/d+1h/w * + * now/d/w/h+1h+2h * + * now-1d/h * + * * + * Comments: This is temporary solution to keep calculated checks working * + * until they are updated. * + * * + ******************************************************************************/ +static int trends_parse_range(time_t from, const char *period, const char *period_shift, int *start, int *end, + char **error) +{ + int period_num, period_hours[ZBX_TIME_UNIT_COUNT] = {0, 1, 24, 24 * 7, 24 * 30, 24 * 365}; + zbx_time_unit_t period_unit; + size_t len; + struct tm tm_end, tm_start; + const char *p; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() period:%s shift:%s", __func__, period, period_shift); + + /* parse period */ + + if (SUCCEED != zbx_tm_parse_period(period, &len, &period_num, &period_unit, error)) + return FAIL; + + if ('\0' != period[len]) + { + *error = zbx_dsprintf(*error, "unexpected character[s] in period \"%s\"", period + len); + return FAIL; + } + + if (period_hours[period_unit] * period_num > 24 * 366) + { + *error = zbx_strdup(*error, "period is too large"); + return FAIL; + } + + /* parse period shift */ + + p = period_shift; + + if (0 != strncmp(p, "now", ZBX_CONST_STRLEN("now"))) + { + *error = zbx_strdup(*error, "period shift must begin with \"now\""); + return FAIL; + } + + p += ZBX_CONST_STRLEN("now"); + + localtime_r(&from, &tm_end); + + while ('\0' != *p) + { + zbx_time_unit_t unit; + + if ('/' == *p) + { + if (ZBX_TIME_UNIT_UNKNOWN == (unit = zbx_tm_str_to_unit(++p))) + { + *error = zbx_dsprintf(*error, "unexpected character starting with \"%s\"", p); + return FAIL; + } + + if (unit < period_unit) + { + *error = zbx_dsprintf(*error, "time units in period shift must be greater or equal" + " to period time unit"); + return FAIL; + } + + zbx_tm_round_down(&tm_end, unit); + + /* unit is single character */ + p++; + } + else if ('+' == *p || '-' == *p) + { + int num; + char op = *(p++); + + if (FAIL == zbx_tm_parse_period(p, &len, &num, &unit, error)) + return FAIL; + + if (unit < period_unit) + { + *error = zbx_dsprintf(*error, "time units in period shift must be greater or equal" + " to period time unit"); + return FAIL; + } + + if ('+' == op) + zbx_tm_add(&tm_end, num, unit); + else + zbx_tm_sub(&tm_end, num, unit); + + p += len; + } + else + { + *error = zbx_dsprintf(*error, "unexpected character starting with \"%s\"", p); + return FAIL; + } + } + + tm_start = tm_end; + + /* trends clock refers to the beginning of the hourly interval - subtract */ + /* one hour to get the trends clock for the last hourly interval */ + zbx_tm_sub(&tm_end, 1, ZBX_TIME_UNIT_HOUR); + + if (-1 == (*end = mktime(&tm_end))) + { + *error = zbx_dsprintf(*error, "cannot calculate the period end time: %s", zbx_strerror(errno)); + return FAIL; + } + + if (abs((int)from - *end) > SEC_PER_YEAR * 26) + { + *error = zbx_strdup(*error, "period shift is too large"); + return FAIL; + } + + zbx_tm_sub(&tm_start, period_num, period_unit); + if (-1 == (*start = mktime(&tm_start))) + { + *error = zbx_dsprintf(*error, "cannot calculate the period start time: %s", zbx_strerror(errno)); + return FAIL; + } + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() start:%d end:%d", __func__, *start, *end); + + return SUCCEED; +} + + +/****************************************************************************** + * * * Function: evaluate_TREND * * * * Purpose: evaluate trend* functions for the item * @@ -2735,7 +2896,7 @@ static int evaluate_TREND(char **value, DC_ITEM *item, const char *func, const c goto out; } - if (SUCCEED != zbx_trends_parse_range(ts->sec, period, period_shift, &start, &end, error)) + if (SUCCEED != trends_parse_range(ts->sec, period, period_shift, &start, &end, error)) goto out; switch (item->value_type) @@ -3409,8 +3570,12 @@ int evaluate_macro_function(char **result, const char *host, const char *key, co (resolved_params = zbx_dc_expand_user_macros_in_func_params(parameter, item.host.hostid)), &ts, &error)) { - zabbix_log(LOG_LEVEL_DEBUG, "cannot evaluate function \"%s:%s.%s(%s)\": %s", host, key, function, - parameter, (NULL == error ? "item does not exist" : error)); + char *msg; + + msg = zbx_eval_format_function_error(function, host, key, parameter, + (NULL == error ? "item does not exist" : error)); + zabbix_log(LOG_LEVEL_DEBUG, "%s", msg); + zbx_free(msg); ret = FAIL; } else @@ -3487,21 +3652,12 @@ int evaluate_macro_function(char **result, const char *host, const char *key, co * FAIL - don't evaluate the function for NOTSUPPORTED items * * * ******************************************************************************/ -int evaluatable_for_notsupported(const char *fn) +int zbx_evaluatable_for_notsupported(const char *fn) { - /* functions date(), dayofmonth(), dayofweek(), now(), time() and nodata() are exceptions, */ - /* they should be evaluated for NOTSUPPORTED items, too */ - - if ('n' != *fn && 'd' != *fn && 't' != *fn) - return FAIL; - - if (('n' == *fn) && (0 == strcmp(fn, "nodata") || 0 == strcmp(fn, "now"))) - return SUCCEED; - - if (('d' == *fn) && (0 == strcmp(fn, "dayofweek") || 0 == strcmp(fn, "dayofmonth") || 0 == strcmp(fn, "date"))) - return SUCCEED; + /* function nodata() are exceptions, */ + /* and should be evaluated for NOTSUPPORTED items, too */ - if (0 == strcmp(fn, "time")) + if (0 == strcmp(fn, "nodata")) return SUCCEED; return FAIL; diff --git a/src/libs/zbxserver/evalfunc.h b/src/libs/zbxserver/evalfunc.h index 0de78f499d1..919da491f19 100644 --- a/src/libs/zbxserver/evalfunc.h +++ b/src/libs/zbxserver/evalfunc.h @@ -29,6 +29,6 @@ zbx_output_format_t; int evaluate_macro_function(char **result, const char *host, const char *key, const char *function, const char *parameter, zbx_output_format_t format); -int evaluatable_for_notsupported(const char *fn); +int zbx_evaluatable_for_notsupported(const char *fn); #endif diff --git a/src/libs/zbxserver/evalfunc2.c b/src/libs/zbxserver/evalfunc2.c new file mode 100644 index 00000000000..9b0a16f26eb --- /dev/null +++ b/src/libs/zbxserver/evalfunc2.c @@ -0,0 +1,2409 @@ +/* +** Zabbix +** Copyright (C) 2001-2021 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. +**/ + +/* + * NOTE!!!!! + * + * This is the new expression syntax support for trigger functions and calculated/aggregated + * checks. The old syntax is still used in simple macros. When the new expression syntax + * support is added to simple macros the evalfunc.c:evaluate_function (and related code) + * must be removed, this code must be copied over the old implementation and unused code removed. + */ + +#include "common.h" +#include "db.h" +#include "log.h" +#include "zbxserver.h" +#include "valuecache.h" +#include "evalfunc.h" +#include "zbxregexp.h" +#include "zbxtrends.h" + +typedef enum +{ + ZBX_PARAM_OPTIONAL, + ZBX_PARAM_MANDATORY +} +zbx_param_type_t; + +typedef enum +{ + ZBX_VALUE_NONE, + ZBX_VALUE_SECONDS, + ZBX_VALUE_NVALUES +} +zbx_value_type_t; + +static const char *zbx_type_string(zbx_value_type_t type) +{ + switch (type) + { + case ZBX_VALUE_NONE: + return "none"; + case ZBX_VALUE_SECONDS: + return "sec"; + case ZBX_VALUE_NVALUES: + return "num"; + default: + THIS_SHOULD_NEVER_HAPPEN; + return "unknown"; + } +} + +/****************************************************************************** + * * + * Function: get_function_parameter_int * + * * + * Purpose: get the value of sec|#num trigger function parameter * + * * + * Parameters: parameters - [IN] trigger function parameters * + * Nparam - [IN] specifies which parameter to extract * + * parameter_type - [IN] specifies whether parameter is mandatory * + * or optional * + * value - [OUT] parameter value (preserved as is if the * + * parameter is optional and empty) * + * type - [OUT] parameter value type (number of seconds * + * or number of values) * + * * + * Return value: SUCCEED - parameter is valid * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int get_function_parameter_int(const char *parameters, int Nparam, zbx_param_type_t parameter_type, + int *value, zbx_value_type_t *type) +{ + char *parameter; + int ret = FAIL; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() parameters:'%s' Nparam:%d", __func__, parameters, Nparam); + + if (NULL == (parameter = zbx_function_get_param_dyn(parameters, Nparam))) + goto out; + + if ('\0' == *parameter) + { + switch (parameter_type) + { + case ZBX_PARAM_OPTIONAL: + ret = SUCCEED; + break; + case ZBX_PARAM_MANDATORY: + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + } + else if ('#' == *parameter) + { + *type = ZBX_VALUE_NVALUES; + if (SUCCEED == is_uint31(parameter + 1, value) && 0 < *value) + ret = SUCCEED; + } + else if ('-' == *parameter) + { + if (SUCCEED == is_time_suffix(parameter + 1, value, ZBX_LENGTH_UNLIMITED)) + { + *value = -(*value); + *type = ZBX_VALUE_SECONDS; + ret = SUCCEED; + } + } + else if (SUCCEED == is_time_suffix(parameter, value, ZBX_LENGTH_UNLIMITED)) + { + *type = ZBX_VALUE_SECONDS; + ret = SUCCEED; + } + + if (SUCCEED == ret) + zabbix_log(LOG_LEVEL_DEBUG, "%s() type:%s value:%d", __func__, zbx_type_string(*type), *value); + + zbx_free(parameter); +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +static int get_function_parameter_uint64(const char *parameters, int Nparam, zbx_uint64_t *value) +{ + char *parameter; + int ret = FAIL; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() parameters:'%s' Nparam:%d", __func__, parameters, Nparam); + + if (NULL == (parameter = zbx_function_get_param_dyn(parameters, Nparam))) + goto out; + + if (SUCCEED == (ret = is_uint64(parameter, value))) + zabbix_log(LOG_LEVEL_DEBUG, "%s() value:" ZBX_FS_UI64, __func__, *value); + + zbx_free(parameter); +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +static int get_function_parameter_float(const char *parameters, int Nparam, unsigned char flags, double *value) +{ + char *parameter; + int ret = FAIL; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() parameters:'%s' Nparam:%d", __func__, parameters, Nparam); + + if (NULL == (parameter = zbx_function_get_param_dyn(parameters, Nparam))) + goto out; + + if (SUCCEED == (ret = is_double_suffix(parameter, flags))) + { + *value = str2double(parameter); + zabbix_log(LOG_LEVEL_DEBUG, "%s() value:" ZBX_FS_DBL, __func__, *value); + } + + zbx_free(parameter); +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +static int get_function_parameter_str(const char *parameters, int Nparam, char **value) +{ + int ret = FAIL; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() parameters:'%s' Nparam:%d", __func__, parameters, Nparam); + + if (NULL == (*value = zbx_function_get_param_dyn(parameters, Nparam))) + goto out; + + zabbix_log(LOG_LEVEL_DEBUG, "%s() value:'%s'", __func__, *value); + ret = SUCCEED; +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: get_function_parameter_hist_range * + * * + * Purpose: get the value of sec|num + timeshift trigger function parameter * + * * + * Parameters: from - [IN] the function calculation time * + * parameters - [IN] trigger function parameters * + * Nparam - [IN] specifies which parameter to extract * + * value - [OUT] parameter value (preserved as is if the * + * parameter is optional and empty) * + * type - [OUT] parameter value type (number of seconds * + * or number of values) * + * timeshift - [OUT] the timeshift value (0 if absent) * + * * + * Return value: SUCCEED - parameter is valid * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int get_function_parameter_hist_range(int from, const char *parameters, int Nparam, int *value, + zbx_value_type_t *type, int *timeshift) +{ + char *parameter = NULL, *shift; + int ret = FAIL; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() parameters:'%s' Nparam:%d", __func__, parameters, Nparam); + + if (NULL == (parameter = zbx_function_get_param_dyn(parameters, Nparam))) + goto out; + + if (NULL != (shift = strchr(parameter, ':'))) + *shift++ = '\0'; + + if ('\0' == *parameter) + { + *value = 0; + *type = ZBX_VALUE_NONE; + } + else if ('#' != *parameter) + { + if (SUCCEED != is_time_suffix(parameter, value, ZBX_LENGTH_UNLIMITED) || 0 > *value) + goto out; + + *type = ZBX_VALUE_SECONDS; + } + else + { + if (SUCCEED != is_uint31(parameter + 1, value) || 0 >= *value) + goto out; + *type = ZBX_VALUE_NVALUES; + } + + if (NULL != shift) + { + struct tm tm; + char *error = NULL; + int end; + + if (SUCCEED != zbx_parse_timeshift(from, shift, &tm, &error)) + { + zabbix_log(LOG_LEVEL_DEBUG, "%s() timeshift error:%s", __func__, error); + zbx_free(error); + goto out; + } + + if (-1 == (end = mktime(&tm))) + { + zabbix_log(LOG_LEVEL_DEBUG, "%s() invalid timeshift value:%s", __func__, zbx_strerror(errno)); + goto out; + } + + if (end >= from) + { + zabbix_log(LOG_LEVEL_DEBUG, "%s() timeshift produced time in future", __func__); + goto out; + } + + *timeshift = from - end; + } + else + *timeshift = 0; + + ret = SUCCEED; + zabbix_log(LOG_LEVEL_DEBUG, "%s() type:%s value:%d timeshift:%d", __func__, zbx_type_string(*type), *value, + *timeshift); +out: + zbx_free(parameter); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: get_last_n_value * + * * + * Purpose: get last Nth value defined by #num:now-timeshift first parameter * + * * + * Parameters: item - [IN] item (performance metric) * + * parameters - [IN] the parameter string with #sec|num/timeshift * + * in first parameter * + * ts - [IN] the starting timestamp * + * value - [OUT] the Nth value * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - value was found successfully copied * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int get_last_n_value(const DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + zbx_history_record_t *value, char **error) +{ + int arg1 = 1, ret = FAIL, time_shift; + zbx_value_type_t arg1_type = ZBX_VALUE_NVALUES; + zbx_vector_history_record_t values; + zbx_timespec_t ts_end = *ts; + + zbx_history_record_vector_create(&values); + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift)) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + if (ZBX_VALUE_NVALUES != arg1_type) + arg1 = 1; /* time or non parameter is defaulted to "last(0)" */ + + ts_end.sec -= time_shift; + + if (SUCCEED != zbx_vc_get_values(item->itemid, item->value_type, &values, 0, arg1, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (arg1 <= values.values_num) + { + *value = values.values[arg1 - 1]; + zbx_vector_history_record_remove(&values, arg1 - 1); + ret = SUCCEED; + } + else + *error = zbx_strdup(*error, "not enough data"); +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_LOGEVENTID * + * * + * Purpose: evaluate function 'logeventid' for the item * + * * + * Parameters: item - item (performance metric) * + * parameter - regex string for event id matching * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_LOGEVENTID(zbx_variant_t *value, DC_ITEM *item, const char *parameters, + const zbx_timespec_t *ts, char **error) +{ + char *pattern = NULL; + int ret = FAIL, nparams; + zbx_vector_ptr_t regexps; + zbx_history_record_t vc_value; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_vector_ptr_create(®exps); + + if (ITEM_VALUE_TYPE_LOG != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (2 < (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (2 == nparams) + { + if (SUCCEED != get_function_parameter_str(parameters, 2, &pattern)) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto out; + } + + if ('@' == *pattern) + { + DCget_expressions_by_name(®exps, pattern + 1); + + if (0 == regexps.values_num) + { + *error = zbx_dsprintf(*error, "global regular expression \"%s\" does not exist", + pattern + 1); + goto out; + } + } + } + else + pattern = zbx_strdup(NULL, ""); + + if (SUCCEED == get_last_n_value(item, parameters, ts, &vc_value, error)) + { + char logeventid[16]; + int regexp_ret; + + zbx_snprintf(logeventid, sizeof(logeventid), "%d", vc_value.value.log->logeventid); + + if (FAIL == (regexp_ret = regexp_match_ex(®exps, logeventid, pattern, ZBX_CASE_SENSITIVE))) + { + *error = zbx_dsprintf(*error, "invalid regular expression \"%s\"", pattern); + } + else + { + if (ZBX_REGEXP_MATCH == regexp_ret) + zbx_variant_set_dbl(value, 1); + else if (ZBX_REGEXP_NO_MATCH == regexp_ret) + zbx_variant_set_dbl(value, 0); + + ret = SUCCEED; + } + + zbx_history_record_clear(&vc_value, item->value_type); + } + else + zabbix_log(LOG_LEVEL_DEBUG, "result for LOGEVENTID is empty"); +out: + zbx_free(pattern); + + zbx_regexp_clean_expressions(®exps); + zbx_vector_ptr_destroy(®exps); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_LOGSOURCE * + * * + * Purpose: evaluate function 'logsource' for the item * + * * + * Parameters: item - item (performance metric) * + * parameter - ignored * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_LOGSOURCE(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + char **error) +{ + char *pattern = NULL; + int ret = FAIL, nparams; + zbx_vector_ptr_t regexps; + zbx_history_record_t vc_value; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_vector_ptr_create(®exps); + + if (ITEM_VALUE_TYPE_LOG != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (2 < (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (2 == nparams) + { + if (SUCCEED != get_function_parameter_str(parameters, 2, &pattern)) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto out; + } + + if ('@' == *pattern) + { + DCget_expressions_by_name(®exps, pattern + 1); + + if (0 == regexps.values_num) + { + *error = zbx_dsprintf(*error, "global regular expression \"%s\" does not exist", + pattern + 1); + goto out; + } + } + } + else + pattern = zbx_strdup(NULL, ""); + + if (SUCCEED == get_last_n_value(item, parameters, ts, &vc_value, error)) + { + switch (regexp_match_ex(®exps, vc_value.value.log->source, pattern, ZBX_CASE_SENSITIVE)) + { + case ZBX_REGEXP_MATCH: + zbx_variant_set_dbl(value, 1); + ret = SUCCEED; + break; + case ZBX_REGEXP_NO_MATCH: + zbx_variant_set_dbl(value, 0); + ret = SUCCEED; + break; + case FAIL: + *error = zbx_dsprintf(*error, "invalid regular expression"); + } + + zbx_history_record_clear(&vc_value, item->value_type); + } + else + zabbix_log(LOG_LEVEL_DEBUG, "result for LOGSOURCE is empty"); +out: + zbx_free(pattern); + + zbx_regexp_clean_expressions(®exps); + zbx_vector_ptr_destroy(®exps); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_LOGSEVERITY * + * * + * Purpose: evaluate function 'logseverity' for the item * + * * + * Parameters: item - item (performance metric) * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_LOGSEVERITY(zbx_variant_t *value, DC_ITEM *item, const char *parameters, + const zbx_timespec_t *ts, char **error) +{ + int ret = FAIL; + zbx_history_record_t vc_value; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + if (ITEM_VALUE_TYPE_LOG != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (1 < num_param(parameters)) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED == get_last_n_value(item, parameters, ts, &vc_value, error)) + { + zbx_variant_set_dbl(value, vc_value.value.log->severity); + zbx_history_record_clear(&vc_value, item->value_type); + + ret = SUCCEED; + } + else + zabbix_log(LOG_LEVEL_DEBUG, "result for LOGSEVERITY is empty"); +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +#define OP_UNKNOWN -1 +#define OP_EQ 0 +#define OP_NE 1 +#define OP_GT 2 +#define OP_GE 3 +#define OP_LT 4 +#define OP_LE 5 +#define OP_LIKE 6 +#define OP_REGEXP 7 +#define OP_IREGEXP 8 +#define OP_BITAND 9 + +static void count_one_ui64(int *count, int op, zbx_uint64_t value, zbx_uint64_t pattern, zbx_uint64_t mask) +{ + switch (op) + { + case OP_EQ: + if (value == pattern) + (*count)++; + break; + case OP_NE: + if (value != pattern) + (*count)++; + break; + case OP_GT: + if (value > pattern) + (*count)++; + break; + case OP_GE: + if (value >= pattern) + (*count)++; + break; + case OP_LT: + if (value < pattern) + (*count)++; + break; + case OP_LE: + if (value <= pattern) + (*count)++; + break; + case OP_BITAND: + if ((value & mask) == pattern) + (*count)++; + } +} + +static void count_one_dbl(int *count, int op, double value, double pattern) +{ + switch (op) + { + case OP_EQ: + if (value > pattern - ZBX_DOUBLE_EPSILON && value < pattern + ZBX_DOUBLE_EPSILON) + (*count)++; + break; + case OP_NE: + if (!(value > pattern - ZBX_DOUBLE_EPSILON && value < pattern + ZBX_DOUBLE_EPSILON)) + (*count)++; + break; + case OP_GT: + if (value >= pattern + ZBX_DOUBLE_EPSILON) + (*count)++; + break; + case OP_GE: + if (value > pattern - ZBX_DOUBLE_EPSILON) + (*count)++; + break; + case OP_LT: + if (value <= pattern - ZBX_DOUBLE_EPSILON) + (*count)++; + break; + case OP_LE: + if (value < pattern + ZBX_DOUBLE_EPSILON) + (*count)++; + } +} + +static void count_one_str(int *count, int op, const char *value, const char *pattern, zbx_vector_ptr_t *regexps) +{ + int res; + + switch (op) + { + case OP_EQ: + if (0 == strcmp(value, pattern)) + (*count)++; + break; + case OP_NE: + if (0 != strcmp(value, pattern)) + (*count)++; + break; + case OP_LIKE: + if (NULL != strstr(value, pattern)) + (*count)++; + break; + case OP_REGEXP: + if (ZBX_REGEXP_MATCH == (res = regexp_match_ex(regexps, value, pattern, ZBX_CASE_SENSITIVE))) + (*count)++; + else if (FAIL == res) + *count = FAIL; + break; + case OP_IREGEXP: + if (ZBX_REGEXP_MATCH == (res = regexp_match_ex(regexps, value, pattern, ZBX_IGNORE_CASE))) + (*count)++; + else if (FAIL == res) + *count = FAIL; + } +} + +/****************************************************************************** + * * + * Function: evaluate_COUNT * + * * + * Purpose: evaluate functions 'count' and 'find' for the item * + * * + * Parameters: item - [IN] item (performance metric) * + * parameters - [IN] up to three comma-separated fields: * + * (1) number of seconds/values + timeshift * + * (2) comparison operator (optional) * + * (3) value to compare with (optional) * + * Becomes mandatory for numeric items if 3rd * + * parameter is specified and is not "regexp" * + * or "iregexp". With "bitand" can take one of * + * 2 forms: * + * - value_to_compare_with/mask * + * - mask * + * ts - [IN] the function evaluation time * + * limit - [IN] the limit of counted values, will return * + * when the limit is reached * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_COUNT(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + int limit, char **error) +{ + int arg1, op = OP_UNKNOWN, numeric_search, nparams, count = 0, i, ret = FAIL; + int seconds = 0, nvalues = 0, time_shift; + char *operator = NULL, *pattern2 = NULL, *pattern = NULL, buf[ZBX_MAX_UINT64_LEN]; + double arg3_dbl; + zbx_uint64_t pattern_ui64, pattern2_ui64; + zbx_value_type_t arg1_type; + zbx_vector_ptr_t regexps; + zbx_vector_history_record_t values; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_vector_ptr_create(®exps); + zbx_history_record_vector_create(&values); + + numeric_search = (ITEM_VALUE_TYPE_UINT64 == item->value_type || ITEM_VALUE_TYPE_FLOAT == item->value_type); + + if (3 < (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift)) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + if (2 <= nparams && SUCCEED != get_function_parameter_str(parameters, 2, &operator)) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto out; + } + + if (3 <= nparams) + { + if (SUCCEED != get_function_parameter_str(parameters, 3, &pattern)) + { + *error = zbx_strdup(*error, "invalid fourth parameter"); + goto out; + } + } + else + pattern = zbx_strdup(NULL, ""); + + ts_end.sec -= time_shift; + + if (NULL == operator || '\0' == *operator) + op = (0 != numeric_search ? OP_EQ : OP_LIKE); + else if (0 == strcmp(operator, "eq")) + op = OP_EQ; + else if (0 == strcmp(operator, "ne")) + op = OP_NE; + else if (0 == strcmp(operator, "gt")) + op = OP_GT; + else if (0 == strcmp(operator, "ge")) + op = OP_GE; + else if (0 == strcmp(operator, "lt")) + op = OP_LT; + else if (0 == strcmp(operator, "le")) + op = OP_LE; + else if (0 == strcmp(operator, "like")) + op = OP_LIKE; + else if (0 == strcmp(operator, "regexp")) + op = OP_REGEXP; + else if (0 == strcmp(operator, "iregexp")) + op = OP_IREGEXP; + else if (0 == strcmp(operator, "bitand")) + op = OP_BITAND; + + if (OP_UNKNOWN == op) + { + *error = zbx_dsprintf(*error, "operator \"%s\" is not supported for function COUNT", operator); + goto out; + } + + numeric_search = (0 != numeric_search && OP_REGEXP != op && OP_IREGEXP != op); + + if (0 != numeric_search) + { + if (NULL != operator && '\0' != *operator && '\0' == *pattern) + { + *error = zbx_strdup(*error, "pattern must be provided along with operator for numeric values"); + goto out; + } + + if (OP_LIKE == op) + { + *error = zbx_dsprintf(*error, "operator \"%s\" is not supported for counting numeric values", + operator); + goto out; + } + + if (OP_BITAND == op && ITEM_VALUE_TYPE_FLOAT == item->value_type) + { + *error = zbx_dsprintf(*error, "operator \"%s\" is not supported for counting float values", + operator); + goto out; + } + + if (OP_BITAND == op && NULL != (pattern2 = strchr(pattern, '/'))) + { + *pattern2 = '\0'; /* end of the 1st part of the 2nd parameter (number to compare with) */ + pattern2++; /* start of the 2nd part of the 2nd parameter (mask) */ + } + + if (NULL != pattern && '\0' != *pattern) + { + if (ITEM_VALUE_TYPE_UINT64 == item->value_type) + { + if (OP_BITAND != op) + { + if (SUCCEED != str2uint64(pattern, ZBX_UNIT_SYMBOLS, &pattern_ui64)) + { + *error = zbx_dsprintf(*error, "\"%s\" is not a valid numeric unsigned" + " value", pattern); + goto out; + } + pattern2_ui64 = 0; + } + else + { + if (SUCCEED != is_uint64(pattern, &pattern_ui64)) + { + *error = zbx_dsprintf(*error, "\"%s\" is not a valid numeric unsigned" + " value", pattern); + goto out; + } + + if (NULL != pattern2) + { + if (SUCCEED != is_uint64(pattern2, &pattern2_ui64)) + { + *error = zbx_dsprintf(*error, "\"%s\" is not a valid numeric" + " unsigned value", pattern2); + goto out; + } + } + else + pattern2_ui64 = pattern_ui64; + } + } + else + { + if (SUCCEED != is_double_suffix(pattern, ZBX_FLAG_DOUBLE_SUFFIX)) + { + *error = zbx_dsprintf(*error, "\"%s\" is not a valid numeric float value", + pattern); + goto out; + } + + arg3_dbl = str2double(pattern); + } + } + } + else if (OP_LIKE != op && OP_REGEXP != op && OP_IREGEXP != op && OP_EQ != op && OP_NE != op) + { + *error = zbx_dsprintf(*error, "operator \"%s\" is not supported for counting textual values", operator); + goto out; + } + + if ((OP_REGEXP == op || OP_IREGEXP == op) && NULL != pattern && '@' == *pattern) + { + DCget_expressions_by_name(®exps, pattern + 1); + + if (0 == regexps.values_num) + { + *error = zbx_dsprintf(*error, "global regular expression \"%s\" does not exist", pattern + 1); + goto out; + } + } + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + case ZBX_VALUE_NONE: + nvalues = 1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + /* skip counting values one by one if both pattern and operator are empty or "" is searched in text values */ + if ((NULL != pattern && '\0' != *pattern) || (NULL != operator && '\0' != *operator && + OP_LIKE != op && OP_REGEXP != op && OP_IREGEXP != op)) + { + switch (item->value_type) + { + case ITEM_VALUE_TYPE_UINT64: + if (0 != numeric_search) + { + for (i = 0; i < values.values_num && count < limit; i++) + { + count_one_ui64(&count, op, values.values[i].value.ui64, pattern_ui64, + pattern2_ui64); + } + } + else + { + for (i = 0; i < values.values_num && FAIL != count && count < limit; i++) + { + zbx_snprintf(buf, sizeof(buf), ZBX_FS_UI64, + values.values[i].value.ui64); + count_one_str(&count, op, buf, pattern, ®exps); + } + } + break; + case ITEM_VALUE_TYPE_FLOAT: + if (0 != numeric_search) + { + for (i = 0; i < values.values_num && count < limit; i++) + count_one_dbl(&count, op, values.values[i].value.dbl, arg3_dbl); + } + else + { + for (i = 0; i < values.values_num && FAIL != count && count < limit; i++) + { + zbx_snprintf(buf, sizeof(buf), ZBX_FS_DBL_EXT(4), + values.values[i].value.dbl); + count_one_str(&count, op, buf, pattern, ®exps); + } + } + break; + case ITEM_VALUE_TYPE_LOG: + for (i = 0; i < values.values_num && FAIL != count && count < limit; i++) + count_one_str(&count, op, values.values[i].value.log->value, pattern, ®exps); + break; + default: + for (i = 0; i < values.values_num && FAIL != count && count < limit; i++) + count_one_str(&count, op, values.values[i].value.str, pattern, ®exps); + } + + if (FAIL == count) + { + *error = zbx_strdup(*error, "invalid regular expression"); + goto out; + } + } + else + { + if ((count = values.values_num) > limit) + count = limit; + } + + zbx_variant_set_dbl(value, count); + + ret = SUCCEED; +out: + zbx_free(operator); + zbx_free(pattern); + + zbx_regexp_clean_expressions(®exps); + zbx_vector_ptr_destroy(®exps); + + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +#undef OP_UNKNOWN +#undef OP_EQ +#undef OP_NE +#undef OP_GT +#undef OP_GE +#undef OP_LT +#undef OP_LE +#undef OP_LIKE +#undef OP_REGEXP +#undef OP_IREGEXP +#undef OP_BITAND + +/****************************************************************************** + * * + * Function: evaluate_SUM * + * * + * Purpose: evaluate function 'sum' for the item * + * * + * Parameters: item - item (performance metric) * + * parameters - number of seconds/values and time shift (optional)* + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_SUM(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error) +{ + int nparams, arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift; + zbx_value_type_t arg1_type; + zbx_vector_history_record_t values; + history_value_t result; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (1 != (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) || + ZBX_VALUE_NONE == arg1_type) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + ts_end.sec -= time_shift; + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (ITEM_VALUE_TYPE_FLOAT == item->value_type) + { + result.dbl = 0; + + for (i = 0; i < values.values_num; i++) + result.dbl += values.values[i].value.dbl; + } + else + { + result.ui64 = 0; + + for (i = 0; i < values.values_num; i++) + result.ui64 += values.values[i].value.ui64; + } + + zbx_history_value2variant(&result, item->value_type, value); + ret = SUCCEED; +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_AVG * + * * + * Purpose: evaluate function 'avg' for the item * + * * + * Parameters: item - item (performance metric) * + * parameters - number of seconds/values and time shift (optional)* + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_AVG(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error) +{ + int nparams, arg1, ret = FAIL, i, seconds = 0, nvalues = 0, time_shift; + zbx_value_type_t arg1_type; + zbx_vector_history_record_t values; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (1 != (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) || + ZBX_VALUE_NONE == arg1_type) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + ts_end.sec -= time_shift; + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (0 < values.values_num) + { + double avg = 0; + + if (ITEM_VALUE_TYPE_FLOAT == item->value_type) + { + for (i = 0; i < values.values_num; i++) + avg += values.values[i].value.dbl / (i + 1) - avg / (i + 1); + } + else + { + for (i = 0; i < values.values_num; i++) + avg += (double)values.values[i].value.ui64; + + avg = avg / values.values_num; + } + zbx_variant_set_dbl(value, avg); + + ret = SUCCEED; + } + else + { + zabbix_log(LOG_LEVEL_DEBUG, "result for AVG is empty"); + *error = zbx_strdup(*error, "not enough data"); + } +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_LAST * + * * + * Purpose: evaluate function 'last' for the item * + * * + * Parameters: value - dynamic buffer * + * item - item (performance metric) * + * parameters - Nth last value and time shift (optional) * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_LAST(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + char **error) +{ + int ret; + zbx_history_record_t vc_value; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + if (SUCCEED == (ret = get_last_n_value(item, parameters, ts, &vc_value, error))) + { + zbx_history_value2variant(&vc_value.value, item->value_type, value); + zbx_history_record_clear(&vc_value, item->value_type); + } + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_MIN * + * * + * Purpose: evaluate function 'min' for the item * + * * + * Parameters: item - item (performance metric) * + * parameters - number of seconds/values and time shift (optional)* + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_MIN(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error) +{ + int nparams, arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift; + zbx_value_type_t arg1_type; + zbx_vector_history_record_t values; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (1 != (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) || + ZBX_VALUE_NONE == arg1_type) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + ts_end.sec -= time_shift; + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (0 < values.values_num) + { + int index = 0; + + if (ITEM_VALUE_TYPE_UINT64 == item->value_type) + { + for (i = 1; i < values.values_num; i++) + { + if (values.values[i].value.ui64 < values.values[index].value.ui64) + index = i; + } + } + else + { + for (i = 1; i < values.values_num; i++) + { + if (values.values[i].value.dbl < values.values[index].value.dbl) + index = i; + } + } + + zbx_history_value2variant(&values.values[index].value, item->value_type, value); + ret = SUCCEED; + } + else + { + zabbix_log(LOG_LEVEL_DEBUG, "result for MIN is empty"); + *error = zbx_strdup(*error, "not enough data"); + } +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_MAX * + * * + * Purpose: evaluate function 'max' for the item * + * * + * Parameters: item - item (performance metric) * + * parameters - number of seconds/values and time shift (optional)* + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_MAX(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error) +{ + int nparams, arg1, ret = FAIL, i, seconds = 0, nvalues = 0, time_shift; + zbx_value_type_t arg1_type; + zbx_vector_history_record_t values; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (1 != (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) || + ZBX_VALUE_NONE == arg1_type) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + ts_end.sec -= time_shift; + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (0 < values.values_num) + { + int index = 0; + + if (ITEM_VALUE_TYPE_UINT64 == item->value_type) + { + for (i = 1; i < values.values_num; i++) + { + if (values.values[i].value.ui64 > values.values[index].value.ui64) + index = i; + } + } + else + { + for (i = 1; i < values.values_num; i++) + { + if (values.values[i].value.dbl > values.values[index].value.dbl) + index = i; + } + } + + zbx_history_value2variant(&values.values[index].value, item->value_type, value); + + ret = SUCCEED; + } + else + { + zabbix_log(LOG_LEVEL_DEBUG, "result for MAX is empty"); + *error = zbx_strdup(*error, "not enough data"); + } +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +static int __history_record_float_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2) +{ + ZBX_RETURN_IF_NOT_EQUAL(d1->value.dbl, d2->value.dbl); + + return 0; +} + +static int __history_record_uint64_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2) +{ + ZBX_RETURN_IF_NOT_EQUAL(d1->value.ui64, d2->value.ui64); + + return 0; +} + +/****************************************************************************** + * * + * Function: evaluate_PERCENTILE * + * * + * Purpose: evaluate function 'percentile' for the item * + * * + * Parameters: item - [IN] item (performance metric) * + * parameters - [IN] seconds/values, time shift (optional), * + * percentage * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in * + * 'value' * + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_PERCENTILE(zbx_variant_t *value, DC_ITEM *item, const char *parameters, + const zbx_timespec_t *ts, char **error) +{ + int nparams, arg1, time_shift, ret = FAIL, seconds = 0, nvalues = 0; + zbx_value_type_t arg1_type; + double percentage; + zbx_vector_history_record_t values; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (2 != (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) || + ZBX_VALUE_NONE == arg1_type) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + ts_end.sec -= time_shift; + + if (SUCCEED != get_function_parameter_float(parameters, 2, ZBX_FLAG_DOUBLE_PLAIN, &percentage) || + 0.0 > percentage || 100.0 < percentage) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto out; + } + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (0 < values.values_num) + { + int index; + + if (ITEM_VALUE_TYPE_FLOAT == item->value_type) + zbx_vector_history_record_sort(&values, (zbx_compare_func_t)__history_record_float_compare); + else + zbx_vector_history_record_sort(&values, (zbx_compare_func_t)__history_record_uint64_compare); + + if (0 == percentage) + index = 1; + else + index = (int)ceil(values.values_num * (percentage / 100)); + + zbx_history_value2variant(&values.values[index - 1].value, item->value_type, value); + + ret = SUCCEED; + } + else + { + zabbix_log(LOG_LEVEL_DEBUG, "result for PERCENTILE is empty"); + *error = zbx_strdup(*error, "not enough data"); + } +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_NODATA * + * * + * Purpose: evaluate function 'nodata' for the item * + * * + * Parameters: item - item (performance metric) * + * parameter - number of seconds * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_NODATA(zbx_variant_t *value, DC_ITEM *item, const char *parameters, char **error) +{ + int arg1, num, period, lazy = 1, ret = FAIL; + zbx_value_type_t arg1_type; + zbx_vector_history_record_t values; + zbx_timespec_t ts; + char *arg2 = NULL; + zbx_proxy_suppress_t nodata_win; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (2 < (num = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_int(parameters, 1, ZBX_PARAM_MANDATORY, &arg1, &arg1_type) || + ZBX_VALUE_SECONDS != arg1_type || 0 >= arg1) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + if (1 < num && (SUCCEED != get_function_parameter_str(parameters, 2, &arg2) || + ('\0' != *arg2 && 0 != (lazy = strcmp("strict", arg2))))) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto out; + } + + zbx_timespec(&ts); + nodata_win.flags = ZBX_PROXY_SUPPRESS_DISABLE; + + if (0 != item->host.proxy_hostid && 0 != lazy) + { + int lastaccess; + + if (SUCCEED != DCget_proxy_nodata_win(item->host.proxy_hostid, &nodata_win, &lastaccess)) + { + *error = zbx_strdup(*error, "cannot retrieve proxy last access"); + goto out; + } + + period = arg1 + (ts.sec - lastaccess); + } + else + period = arg1; + + if (SUCCEED == zbx_vc_get_values(item->itemid, item->value_type, &values, period, 1, &ts) && + 1 == values.values_num) + { + zbx_variant_set_dbl(value, 0); + } + else + { + int seconds; + + if (SUCCEED != DCget_data_expected_from(item->itemid, &seconds)) + { + *error = zbx_strdup(*error, "item does not exist, is disabled or belongs to a disabled host"); + goto out; + } + + if (seconds + arg1 > ts.sec) + { + *error = zbx_strdup(*error, + "item does not have enough data after server start or item creation"); + goto out; + } + + if (0 != (nodata_win.flags & ZBX_PROXY_SUPPRESS_ACTIVE)) + { + *error = zbx_strdup(*error, "historical data transfer from proxy is still in progress"); + goto out; + } + + zbx_variant_set_dbl(value, 1); + + if (0 != item->host.proxy_hostid && 0 != lazy) + { + zabbix_log(LOG_LEVEL_TRACE, "Nodata in %s() flag:%d values_num:%d start_time:%d period:%d", + __func__, nodata_win.flags, nodata_win.values_num, ts.sec - period, period); + } + } + + ret = SUCCEED; +out: + zbx_history_record_vector_destroy(&values, item->value_type); + zbx_free(arg2); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_CHANGE * + * * + * Purpose: evaluate function 'change' for the item * + * * + * Parameters: item - item (performance metric) * + * parameter - number of seconds * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_CHANGE(zbx_variant_t *value, DC_ITEM *item, const zbx_timespec_t *ts, char **error) +{ + int ret = FAIL; + zbx_vector_history_record_t values; + double result; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (SUCCEED != zbx_vc_get_values(item->itemid, item->value_type, &values, 0, 2, ts) || + 2 > values.values_num) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + switch (item->value_type) + { + case ITEM_VALUE_TYPE_FLOAT: + result = values.values[0].value.dbl - values.values[1].value.dbl; + break; + case ITEM_VALUE_TYPE_UINT64: + /* to avoid overflow */ + if (values.values[0].value.ui64 >= values.values[1].value.ui64) + result = values.values[0].value.ui64 - values.values[1].value.ui64; + else + result = -(double)(values.values[1].value.ui64 - values.values[0].value.ui64); + break; + case ITEM_VALUE_TYPE_LOG: + if (0 == strcmp(values.values[0].value.log->value, values.values[1].value.log->value)) + result = 0; + else + result = 1; + break; + + case ITEM_VALUE_TYPE_STR: + case ITEM_VALUE_TYPE_TEXT: + if (0 == strcmp(values.values[0].value.str, values.values[1].value.str)) + result = 0; + else + result = 1; + break; + default: + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + zbx_variant_set_dbl(value, result); + ret = SUCCEED; +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_FUZZYTIME * + * * + * Purpose: evaluate function 'fuzzytime' for the item * + * * + * Parameters: item - item (performance metric) * + * parameter - number of seconds * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_FUZZYTIME(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + char **error) +{ + int arg1, ret = FAIL; + zbx_value_type_t arg1_type; + zbx_history_record_t vc_value; + zbx_uint64_t fuzlow, fuzhig; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (1 < num_param(parameters)) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_int(parameters, 1, ZBX_PARAM_MANDATORY, &arg1, &arg1_type) || 0 >= arg1) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + if (ZBX_VALUE_SECONDS != arg1_type || ts->sec <= arg1) + { + *error = zbx_strdup(*error, "invalid argument type or value"); + goto out; + } + + if (SUCCEED != zbx_vc_get_value(item->itemid, item->value_type, ts, &vc_value)) + { + *error = zbx_strdup(*error, "cannot get value from value cache"); + goto out; + } + + fuzlow = (int)(ts->sec - arg1); + fuzhig = (int)(ts->sec + arg1); + + if (ITEM_VALUE_TYPE_UINT64 == item->value_type) + { + if (vc_value.value.ui64 >= fuzlow && vc_value.value.ui64 <= fuzhig) + zbx_variant_set_dbl(value, 1); + else + zbx_variant_set_dbl(value, 0); + } + else + { + if (vc_value.value.dbl >= fuzlow && vc_value.value.dbl <= fuzhig) + zbx_variant_set_dbl(value, 1); + else + zbx_variant_set_dbl(value, 0); + } + + zbx_history_record_clear(&vc_value, item->value_type); + + ret = SUCCEED; +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_BITAND * + * * + * Purpose: evaluate logical bitwise function 'and' for the item * + * * + * Parameters: value - dynamic buffer * + * item - item (performance metric) * + * parameters - up to 3 comma-separated fields: * + * (1) same as the 1st parameter for function * + * evaluate_LAST() (see documentation of * + * trigger function last()), * + * (2) mask to bitwise AND with (mandatory), * + * (3) same as the 2nd parameter for function * + * evaluate_LAST() (see documentation of * + * trigger function last()). * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_BITAND(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + char **error) +{ + char *last_parameters = NULL; + int nparams, ret = FAIL; + zbx_uint64_t mask; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + if (ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto clean; + } + + if (2 < (nparams = num_param(parameters))) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto clean; + } + + if (SUCCEED != get_function_parameter_uint64(parameters, 2, &mask)) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto clean; + } + + /* prepare the 1st and the 3rd parameter for passing to evaluate_LAST() */ + last_parameters = zbx_function_get_param_dyn(parameters, 1); + + if (SUCCEED == evaluate_LAST(value, item, last_parameters, ts, error)) + { + /* the evaluate_LAST() should return uint64 value, but just to be sure try to convert it */ + if (SUCCEED != zbx_variant_convert(value, ZBX_VARIANT_UI64)) + { + *error = zbx_strdup(*error, "invalid value type"); + goto clean; + } + zbx_variant_set_dbl(value, value->data.ui64 & (zbx_uint64_t)mask); + ret = SUCCEED; + } + + zbx_free(last_parameters); +clean: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_FORECAST * + * * + * Purpose: evaluate function 'forecast' for the item * + * * + * Parameters: item - item (performance metric) * + * parameters - number of seconds/values and time shift (optional)* + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_FORECAST(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + char **error) +{ + char *fit_str = NULL, *mode_str = NULL; + double *t = NULL, *x = NULL; + int nparams, time, arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift; + zbx_value_type_t time_type, arg1_type; + unsigned int k = 0; + zbx_vector_history_record_t values; + zbx_timespec_t zero_time; + zbx_fit_t fit; + zbx_mode_t mode; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (2 > (nparams = num_param(parameters)) || nparams > 4) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) || + ZBX_VALUE_NONE == arg1_type) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + if (SUCCEED != get_function_parameter_int(parameters, 2, ZBX_PARAM_MANDATORY, &time, &time_type) || + ZBX_VALUE_SECONDS != time_type) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto out; + } + + if (3 <= nparams) + { + if (SUCCEED != get_function_parameter_str(parameters, 3, &fit_str) || + SUCCEED != zbx_fit_code(fit_str, &fit, &k, error)) + { + *error = zbx_strdup(*error, "invalid fourth parameter"); + goto out; + } + } + else + { + fit = FIT_LINEAR; + } + + if (4 == nparams) + { + if (SUCCEED != get_function_parameter_str(parameters, 4, &mode_str) || + SUCCEED != zbx_mode_code(mode_str, &mode, error)) + { + *error = zbx_strdup(*error, "invalid fifth parameter"); + goto out; + } + } + else + { + mode = MODE_VALUE; + } + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + ts_end.sec -= time_shift; + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (0 < values.values_num) + { + t = (double *)zbx_malloc(t, values.values_num * sizeof(double)); + x = (double *)zbx_malloc(x, values.values_num * sizeof(double)); + + zero_time.sec = values.values[values.values_num - 1].timestamp.sec; + zero_time.ns = values.values[values.values_num - 1].timestamp.ns; + + if (ITEM_VALUE_TYPE_FLOAT == item->value_type) + { + for (i = 0; i < values.values_num; i++) + { + t[i] = values.values[i].timestamp.sec - zero_time.sec + 1.0e-9 * + (values.values[i].timestamp.ns - zero_time.ns + 1); + x[i] = values.values[i].value.dbl; + } + } + else + { + for (i = 0; i < values.values_num; i++) + { + t[i] = values.values[i].timestamp.sec - zero_time.sec + 1.0e-9 * + (values.values[i].timestamp.ns - zero_time.ns + 1); + x[i] = values.values[i].value.ui64; + } + } + + zbx_variant_set_dbl(value, zbx_forecast(t, x, values.values_num, + ts->sec - zero_time.sec - 1.0e-9 * (zero_time.ns + 1), time, fit, k, mode)); + } + else + { + zbx_variant_set_dbl(value, ZBX_MATH_ERROR); + } + + ret = SUCCEED; +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zbx_free(fit_str); + zbx_free(mode_str); + + zbx_free(t); + zbx_free(x); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_TIMELEFT * + * * + * Purpose: evaluate function 'timeleft' for the item * + * * + * Parameters: item - item (performance metric) * + * parameters - number of seconds/values and time shift (optional)* + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_TIMELEFT(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, + char **error) +{ + char *fit_str = NULL; + double *t = NULL, *x = NULL, threshold; + int nparams, arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift; + zbx_value_type_t arg1_type; + unsigned k = 0; + zbx_vector_history_record_t values; + zbx_timespec_t zero_time; + zbx_fit_t fit; + zbx_timespec_t ts_end = *ts; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + { + *error = zbx_strdup(*error, "invalid value type"); + goto out; + } + + if (2 > (nparams = num_param(parameters)) || nparams > 3) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) || + ZBX_VALUE_NONE == arg1_type) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + if (SUCCEED != get_function_parameter_float(parameters, 2, ZBX_FLAG_DOUBLE_SUFFIX, &threshold)) + { + *error = zbx_strdup(*error, "invalid third parameter"); + goto out; + } + + if (3 == nparams) + { + if (SUCCEED != get_function_parameter_str(parameters, 3, &fit_str) || + SUCCEED != zbx_fit_code(fit_str, &fit, &k, error)) + { + *error = zbx_strdup(*error, "invalid fourth parameter"); + goto out; + } + } + else + { + fit = FIT_LINEAR; + } + + if ((FIT_EXPONENTIAL == fit || FIT_POWER == fit) && 0.0 >= threshold) + { + *error = zbx_strdup(*error, "exponential and power functions are always positive"); + goto out; + } + + switch (arg1_type) + { + case ZBX_VALUE_SECONDS: + seconds = arg1; + break; + case ZBX_VALUE_NVALUES: + nvalues = arg1; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + } + + ts_end.sec -= time_shift; + + if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, nvalues, &ts_end)) + { + *error = zbx_strdup(*error, "cannot get values from value cache"); + goto out; + } + + if (0 < values.values_num) + { + t = (double *)zbx_malloc(t, values.values_num * sizeof(double)); + x = (double *)zbx_malloc(x, values.values_num * sizeof(double)); + + zero_time.sec = values.values[values.values_num - 1].timestamp.sec; + zero_time.ns = values.values[values.values_num - 1].timestamp.ns; + + if (ITEM_VALUE_TYPE_FLOAT == item->value_type) + { + for (i = 0; i < values.values_num; i++) + { + t[i] = values.values[i].timestamp.sec - zero_time.sec + 1.0e-9 * + (values.values[i].timestamp.ns - zero_time.ns + 1); + x[i] = values.values[i].value.dbl; + } + } + else + { + for (i = 0; i < values.values_num; i++) + { + t[i] = values.values[i].timestamp.sec - zero_time.sec + 1.0e-9 * + (values.values[i].timestamp.ns - zero_time.ns + 1); + x[i] = values.values[i].value.ui64; + } + } + + zbx_variant_set_dbl(value, zbx_timeleft(t, x, values.values_num, + ts->sec - zero_time.sec - 1.0e-9 * (zero_time.ns + 1), threshold, fit, k)); + } + else + { + zbx_variant_set_dbl(value, ZBX_MATH_ERROR); + } + + ret = SUCCEED; +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zbx_free(fit_str); + + zbx_free(t); + zbx_free(x); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_TREND * + * * + * Purpose: evaluate trend* functions for the item * + * * + * Parameters: value - [OUT] the function result * + * item - [IN] item (performance metric) * + * func - [IN] the trend function to evaluate * + * (avg, sum, count, delta, max, min) * + * parameters - [IN] function parameters * + * ts - [IN] the historical time when function must be * + * evaluated * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + ******************************************************************************/ +static int evaluate_TREND(zbx_variant_t *value, DC_ITEM *item, const char *func, const char *parameters, + const zbx_timespec_t *ts, char **error) +{ + int ret = FAIL, start, end; + char *period = NULL; + const char *table; + double value_dbl; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + if (1 != num_param(parameters)) + { + *error = zbx_strdup(*error, "invalid number of parameters"); + goto out; + } + + if (SUCCEED != get_function_parameter_str(parameters, 1, &period)) + { + *error = zbx_strdup(*error, "invalid second parameter"); + goto out; + } + + if (SUCCEED != zbx_trends_parse_range(ts->sec, period, &start, &end, error)) + goto out; + + switch (item->value_type) + { + case ITEM_VALUE_TYPE_FLOAT: + table = "trends"; + break; + case ITEM_VALUE_TYPE_UINT64: + table = "trends_uint"; + break; + default: + *error = zbx_strdup(*error, "unsupported value type"); + goto out; + } + + if (0 == strcmp(func, "avg")) + { + ret = zbx_trends_eval_avg(table, item->itemid, start, end, &value_dbl, error); + } + else if (0 == strcmp(func, "count")) + { + ret = zbx_trends_eval_count(table, item->itemid, start, end, &value_dbl, error); + } + else if (0 == strcmp(func, "max")) + { + ret = zbx_trends_eval_max(table, item->itemid, start, end, &value_dbl, error); + } + else if (0 == strcmp(func, "min")) + { + ret = zbx_trends_eval_min(table, item->itemid, start, end, &value_dbl, error); + } + else if (0 == strcmp(func, "sum")) + { + ret = zbx_trends_eval_sum(table, item->itemid, start, end, &value_dbl, error); + } + else + { + *error = zbx_strdup(*error, "unknown trend function"); + goto out; + } + + if (SUCCEED == ret) + zbx_variant_set_dbl(value, value_dbl); +out: + zbx_free(period); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * + * Function: evaluate_function * + * * + * Purpose: evaluate function * + * * + * Parameters: item - item to calculate function for * + * function - function (for example, 'max') * + * parameter - parameter of the function * + * * + * Return value: SUCCEED - evaluated successfully, value contains its value * + * FAIL - evaluation failed * + * * + ******************************************************************************/ +int evaluate_function2(zbx_variant_t *value, DC_ITEM *item, const char *function, const char *parameter, + const zbx_timespec_t *ts, char **error) +{ + int ret; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() function:'%s(/%s/%s,%s)' ts:'%s\'", __func__, + function, item->host.host, item->key_orig, parameter, zbx_timespec_str(ts)); + + if (0 == strcmp(function, "last")) + { + ret = evaluate_LAST(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "min")) + { + ret = evaluate_MIN(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "max")) + { + ret = evaluate_MAX(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "avg")) + { + ret = evaluate_AVG(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "sum")) + { + ret = evaluate_SUM(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "percentile")) + { + ret = evaluate_PERCENTILE(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "count")) + { + ret = evaluate_COUNT(value, item, parameter, ts, ZBX_MAX_UINT31_1, error); + } + else if (0 == strcmp(function, "nodata")) + { + ret = evaluate_NODATA(value, item, parameter, error); + } + else if (0 == strcmp(function, "change")) + { + ret = evaluate_CHANGE(value, item, ts, error); + } + else if (0 == strcmp(function, "find")) + { + ret = evaluate_COUNT(value, item, parameter, ts, 1, error); + } + else if (0 == strcmp(function, "fuzzytime")) + { + ret = evaluate_FUZZYTIME(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "logeventid")) + { + ret = evaluate_LOGEVENTID(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "logseverity")) + { + ret = evaluate_LOGSEVERITY(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "logsource")) + { + ret = evaluate_LOGSOURCE(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "bitand")) + { + ret = evaluate_BITAND(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "forecast")) + { + ret = evaluate_FORECAST(value, item, parameter, ts, error); + } + else if (0 == strcmp(function, "timeleft")) + { + ret = evaluate_TIMELEFT(value, item, parameter, ts, error); + } + else if (0 == strncmp(function, "trend", 5)) + { + ret = evaluate_TREND(value, item, function + 5, parameter, ts, error); + } + else + { + *error = zbx_strdup(*error, "function is not supported"); + ret = FAIL; + } + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s value:'%s' of type:'%s'", __func__, zbx_result_string(ret), + zbx_variant_value_desc(value), zbx_variant_type_desc(value)); + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_is_trigger_function * + * * + * Purpose: check if the specified function is a trigger function * + * * + * Parameters: name - [IN] the function name to check * + * len - [IN] the length of function name * + * * + * Return value: SUCCEED - the function is a trigger function * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_is_trigger_function(const char *name, size_t len) +{ + char *functions[] = {"last", "min", "max", "avg", "sum", "percentile", "count", "nodata", "change", "find", + "fuzzytime", "logeventid", "logseverity", "logsource", "bitand", "forecast", "timeleft", + "trendavg", "trendcount", "trendmax", "trendmin", "trendsum", + NULL}; + char **ptr; + + for (ptr = functions; NULL != *ptr; ptr++) + { + size_t compare_len; + + compare_len = strlen(*ptr); + if (compare_len == len && 0 == memcmp(*ptr, name, len)) + return SUCCEED; + } + + return FAIL; +} diff --git a/src/libs/zbxserver/expression.c b/src/libs/zbxserver/expression.c index 6d220f86601..8df3d2425c0 100644 --- a/src/libs/zbxserver/expression.c +++ b/src/libs/zbxserver/expression.c @@ -21,6 +21,8 @@ #include "evalfunc.h" #include "log.h" #include "zbxregexp.h" +#include "zbxvariant.h" +#include "zbxeval.h" #include "valuecache.h" #include "macrofunc.h" @@ -37,6 +39,8 @@ typedef struct zbx_libxml_error_t; #endif +#include "expression.h" + /* The following definitions are used to identify the request field */ /* for various value getters grouped by their scope: */ @@ -46,22 +50,6 @@ zbx_libxml_error_t; #define ZBX_REQUEST_HOST_CONN 3 #define ZBX_REQUEST_HOST_PORT 4 -/* DBget_item_value() */ -#define ZBX_REQUEST_HOST_ID 101 -#define ZBX_REQUEST_HOST_HOST 102 -#define ZBX_REQUEST_HOST_NAME 103 -#define ZBX_REQUEST_HOST_DESCRIPTION 104 -#define ZBX_REQUEST_ITEM_ID 105 -#define ZBX_REQUEST_ITEM_NAME 106 -#define ZBX_REQUEST_ITEM_NAME_ORIG 107 -#define ZBX_REQUEST_ITEM_KEY 108 -#define ZBX_REQUEST_ITEM_KEY_ORIG 109 -#define ZBX_REQUEST_ITEM_DESCRIPTION 110 -#define ZBX_REQUEST_ITEM_DESCRIPTION_ORIG 111 -#define ZBX_REQUEST_PROXY_NAME 112 -#define ZBX_REQUEST_PROXY_DESCRIPTION 113 -#define ZBX_REQUEST_ITEM_VALUETYPE 114 - /* DBget_history_log_value() */ #define ZBX_REQUEST_ITEM_LOG_DATE 201 #define ZBX_REQUEST_ITEM_LOG_TIME 202 @@ -82,293 +70,6 @@ static int substitute_key_macros_impl(char **data, zbx_uint64_t *hostid, DC_ITEM /****************************************************************************** * * - * Function: get_N_functionid * - * * - * Parameters: expression - [IN] null terminated trigger expression * - * '{11}=1 & {2346734}>5' * - * N_functionid - [IN] number of function in trigger expression * - * functionid - [OUT] ID of an N-th function in expression * - * start - [OUT] a pointer to text preceding the extracted * - * function id (can be NULL) * - * end - [OUT] a pointer to text following the extracted * - * function id (can be NULL) * - * * - ******************************************************************************/ -int get_N_functionid(const char *expression, int N_functionid, zbx_uint64_t *functionid, const char **start, - const char **end) -{ - enum state_t {NORMAL, ID} state = NORMAL; - int num = 0, ret = FAIL; - const char *c, *p_functionid = NULL; - - for (c = expression; '\0' != *c; c++) - { - if ('{' == *c) - { - /* skip user macros */ - if ('$' == c[1]) - { - int macro_r, context_l, context_r; - - if (SUCCEED == zbx_user_macro_parse(c, ¯o_r, &context_l, &context_r, NULL)) - c += macro_r; - else - c++; - - continue; - } - - state = ID; - p_functionid = c + 1; - if (NULL != start) - *start = c; - } - else if ('}' == *c && ID == state && NULL != p_functionid) - { - if (SUCCEED == is_uint64_n(p_functionid, c - p_functionid, functionid)) - { - if (++num == N_functionid) - { - if (NULL != end) - *end = c + 1; - - ret = SUCCEED; - break; - } - } - - state = NORMAL; - } - } - - return ret; -} - -/****************************************************************************** - * * - * Function: get_functionids * - * * - * Purpose: get identifiers of the functions used in expression * - * * - * Parameters: functionids - [OUT] the resulting vector of function ids * - * expression - [IN] null terminated trigger expression * - * '{11}=1 & {2346734}>5' * - * * - ******************************************************************************/ -void get_functionids(zbx_vector_uint64_t *functionids, const char *expression) -{ - zbx_token_t token; - int pos = 0; - zbx_uint64_t functionid; - - if ('\0' == *expression) - return; - - for (; SUCCEED == zbx_token_find(expression, pos, &token, ZBX_TOKEN_SEARCH_BASIC); pos++) - { - switch (token.type) - { - case ZBX_TOKEN_OBJECTID: - is_uint64_n(expression + token.loc.l + 1, token.loc.r - token.loc.l - 1, - &functionid); - zbx_vector_uint64_append(functionids, functionid); - ZBX_FALLTHROUGH; - case ZBX_TOKEN_USER_MACRO: - case ZBX_TOKEN_SIMPLE_MACRO: - case ZBX_TOKEN_MACRO: - pos = token.loc.r; - break; - } - } - - zbx_vector_uint64_sort(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); - zbx_vector_uint64_uniq(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); -} - -/****************************************************************************** - * * - * Function: get_N_itemid * - * * - * Parameters: expression - [IN] null terminated trigger expression * - * '{11}=1 & {2346734}>5' * - * N_functionid - [IN] number of function in trigger expression * - * itemid - [OUT] ID of an item of N-th function in * - * expression * - * * - ******************************************************************************/ -static int get_N_itemid(const char *expression, int N_functionid, zbx_uint64_t *itemid) -{ - zbx_uint64_t functionid; - DC_FUNCTION function; - int errcode, ret = FAIL; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s' N_functionid:%d", __func__, expression, N_functionid); - - if (SUCCEED == get_N_functionid(expression, N_functionid, &functionid, NULL, NULL)) - { - DCconfig_get_functions_by_functionids(&function, &functionid, &errcode, 1); - - if (SUCCEED == errcode) - { - *itemid = function.itemid; - ret = SUCCEED; - } - - DCconfig_clean_functions(&function, &errcode, 1); - } - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; -} - -/****************************************************************************** - * * - * Function: get_expanded_expression * - * * - * Purpose: get trigger expression with expanded user macros * - * * - * Comments: removes ' ', '\r', '\n' and '\t' for easier number search * - * * - ******************************************************************************/ -static char *get_expanded_expression(const char *expression) -{ - char *expression_ex; - - if (NULL != (expression_ex = DCexpression_expand_user_macros(expression))) - zbx_remove_whitespace(expression_ex); - - return expression_ex; -} - -/****************************************************************************** - * * - * Function: get_trigger_expression_constant * - * * - * Purpose: get constant from a trigger expression corresponding a given * - * reference from trigger name * - * * - * Parameters: expression - [IN] trigger expression, source of constants * - * reference - [IN] reference from a trigger name ($1, $2, ...) * - * constant - [OUT] the extracted constant or empty string, * - * must be freed by caller * - * * - ******************************************************************************/ -void get_trigger_expression_constant(const char *expression, const zbx_token_reference_t *reference, - char **constant) -{ - size_t pos; - zbx_strloc_t loc; - int index; - - for (pos = 0, index = 1; SUCCEED == zbx_expression_next_constant(expression, pos, &loc); - pos = loc.r + 1, index++) - { - if (index < reference->index) - continue; - - *constant = zbx_expression_extract_constant(expression, &loc); - return; - } - - *constant = zbx_strdup(*constant, ""); -} - -static void DCexpand_trigger_expression(char **expression) -{ - char *tmp = NULL; - size_t tmp_alloc = 256, tmp_offset = 0, l, r; - DC_FUNCTION function; - DC_ITEM item; - zbx_uint64_t functionid; - int errcode[2]; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, *expression); - - tmp = (char *)zbx_malloc(tmp, tmp_alloc); - - for (l = 0; '\0' != (*expression)[l]; l++) - { - if ('{' != (*expression)[l]) - { - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, (*expression)[l]); - continue; - } - - /* skip user macros */ - if ('$' == (*expression)[l + 1]) - { - int macro_r, context_l, context_r; - - if (SUCCEED == zbx_user_macro_parse(*expression + l, ¯o_r, &context_l, &context_r, NULL)) - { - zbx_strncpy_alloc(&tmp, &tmp_alloc, &tmp_offset, *expression + l, macro_r + 1); - l += macro_r; - continue; - } - - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, '{'); - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, '$'); - l++; - continue; - } - - for (r = l + 1; 0 != isdigit((*expression)[r]); r++) - ; - - if ('}' != (*expression)[r]) - { - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, (*expression)[l]); - continue; - } - - (*expression)[r] = '\0'; - - if (SUCCEED == is_uint64(&(*expression)[l + 1], &functionid)) - { - DCconfig_get_functions_by_functionids(&function, &functionid, &errcode[0], 1); - - if (SUCCEED == errcode[0]) - { - DCconfig_get_items_by_itemids(&item, &function.itemid, &errcode[1], 1); - - if (SUCCEED == errcode[1]) - { - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, '{'); - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, item.host.host); - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, ':'); - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, item.key_orig); - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, '.'); - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, function.function); - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, '('); - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, function.parameter); - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, ")}"); - } - - DCconfig_clean_items(&item, &errcode[1], 1); - } - - DCconfig_clean_functions(&function, &errcode[0], 1); - - if (SUCCEED != errcode[0] || SUCCEED != errcode[1]) - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, "*ERROR*"); - - l = r; - } - else - zbx_chrcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, (*expression)[l]); - - (*expression)[r] = '}'; - } - - zbx_free(*expression); - *expression = tmp; - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s() expression:'%s'", __func__, *expression); -} - -/****************************************************************************** - * * * Function: get_trigger_severity_name * * * * Purpose: get trigger severity name * @@ -1077,14 +778,14 @@ static int DBget_item_value(zbx_uint64_t itemid, char **replace_to, int request) * otherwise FAIL * * * ******************************************************************************/ -static int DBget_trigger_value(const char *expression, char **replace_to, int N_functionid, int request) +int DBget_trigger_value(const DB_TRIGGER *trigger, char **replace_to, int N_functionid, int request) { zbx_uint64_t itemid; int ret = FAIL; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - if (SUCCEED == get_N_itemid(expression, N_functionid, &itemid)) + if (SUCCEED == zbx_db_trigger_get_itemid(trigger, N_functionid, &itemid)) ret = DBget_item_value(itemid, replace_to, request); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); @@ -1488,7 +1189,7 @@ static int DBitem_get_value(zbx_uint64_t itemid, char **lastvalue, int raw, zbx_ * otherwise FAIL * * * ******************************************************************************/ -static int DBitem_value(const char *expression, char **value, int N_functionid, int clock, int ns, int raw) +static int DBitem_value(const DB_TRIGGER *trigger, char **value, int N_functionid, int clock, int ns, int raw) { zbx_uint64_t itemid; zbx_timespec_t ts = {clock, ns}; @@ -1496,7 +1197,7 @@ static int DBitem_value(const char *expression, char **value, int N_functionid, zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - if (SUCCEED == (ret = get_N_itemid(expression, N_functionid, &itemid))) + if (SUCCEED == (ret = zbx_db_trigger_get_itemid(trigger, N_functionid, &itemid))) ret = DBitem_get_value(itemid, value, raw, &ts); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); @@ -1517,13 +1218,13 @@ static int DBitem_value(const char *expression, char **value, int N_functionid, * otherwise FAIL * * * ******************************************************************************/ -static int DBitem_lastvalue(const char *expression, char **lastvalue, int N_functionid, int raw) +static int DBitem_lastvalue(const DB_TRIGGER *trigger, char **lastvalue, int N_functionid, int raw) { int ret; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - ret = DBitem_value(expression, lastvalue, N_functionid, time(NULL), 999999999, raw); + ret = DBitem_value(trigger, lastvalue, N_functionid, time(NULL), 999999999, raw); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); @@ -2221,7 +1922,7 @@ static int get_action_value(const char *macro, zbx_uint64_t actionid, char **rep * otherwise FAIL * * * ******************************************************************************/ -static int get_host_inventory(const char *macro, const char *expression, char **replace_to, +static int get_host_inventory(const char *macro, const DB_TRIGGER *trigger, char **replace_to, int N_functionid) { int i; @@ -2232,7 +1933,7 @@ static int get_host_inventory(const char *macro, const char *expression, char ** { zbx_uint64_t itemid; - if (SUCCEED != get_N_itemid(expression, N_functionid, &itemid)) + if (SUCCEED != zbx_db_trigger_get_itemid(trigger, N_functionid, &itemid)) return FAIL; return DCget_host_inventory_value_by_itemid(itemid, replace_to, inventory_fields[i].idx); @@ -2564,40 +2265,6 @@ static void get_event_value(const char *macro, const DB_EVENT *event, char **rep /****************************************************************************** * * - * Function: get_expression_macro_result * - * * - * Purpose: calculate result of expression macro * - * * - * Return value: upon successful completion return SUCCEED * - * otherwise FAIL * - * * - ******************************************************************************/ -static int get_expression_macro_result(const DB_EVENT *event, const DB_EVENT *r_event, char **expression, - char **replace_to, char *error, int maxerrlen) -{ - int ret; - double expression_result; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, *expression); - - substitute_simple_macros_impl(NULL, event, r_event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, expression, - MACRO_TYPE_EXPRESSION, error, maxerrlen); - - if (SUCCEED == (ret = evaluate(&expression_result, *expression, error, maxerrlen, NULL))) - { - char buffer[ZBX_MAX_DOUBLE_LEN + 1]; - - zbx_print_double(buffer, sizeof(buffer), expression_result); - *replace_to = zbx_strdup(*replace_to, buffer); - } - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; -} - -/****************************************************************************** - * * * Function: get_history_log_value * * * * Purpose: retrieve a particular attribute of a log value * @@ -2606,7 +2273,7 @@ static int get_expression_macro_result(const DB_EVENT *event, const DB_EVENT *r_ * otherwise FAIL * * * ******************************************************************************/ -static int get_history_log_value(const char *m, const char *expression, char **replace_to, int N_functionid, +static int get_history_log_value(const char *m, const DB_TRIGGER *trigger, char **replace_to, int N_functionid, int clock, int ns, const char *tz) { zbx_uint64_t itemid; @@ -2641,7 +2308,7 @@ static int get_history_log_value(const char *m, const char *expression, char **r else /* MVAR_ITEM_LOG_TIME */ request = ZBX_REQUEST_ITEM_LOG_TIME; - if (SUCCEED == (ret = get_N_itemid(expression, N_functionid, &itemid))) + if (SUCCEED == (ret = zbx_db_trigger_get_itemid(trigger, N_functionid, &itemid))) ret = DBget_history_log_value(itemid, replace_to, request, clock, ns, tz); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); @@ -2801,7 +2468,7 @@ static const char *func_macro_in_list(const char *str, zbx_token_func_macro_t *f * Comments: example: " {Zabbix server:{ITEM.KEY1}.last(0)} " to " 1.34 " * * * ******************************************************************************/ -static int get_trigger_function_value(const char *expression, char **replace_to, char *data, +static int get_trigger_function_value(const DB_TRIGGER *trigger, char **replace_to, char *data, const zbx_token_simple_macro_t *simple_macro, zbx_output_format_t format) { char *host = NULL, *key = NULL; @@ -2809,13 +2476,13 @@ static int get_trigger_function_value(const char *expression, char **replace_to, if (NULL != macro_in_list(data, simple_macro->host, simple_host_macros, &N_functionid)) { - if (SUCCEED != DBget_trigger_value(expression, &host, N_functionid, ZBX_REQUEST_HOST_HOST)) + if (SUCCEED != DBget_trigger_value(trigger, &host, N_functionid, ZBX_REQUEST_HOST_HOST)) goto out; } if (NULL != macro_in_list(data, simple_macro->key, simple_key_macros, &N_functionid)) { - if (SUCCEED != DBget_trigger_value(expression, &key, N_functionid, ZBX_REQUEST_ITEM_KEY_ORIG)) + if (SUCCEED != DBget_trigger_value(trigger, &key, N_functionid, ZBX_REQUEST_ITEM_KEY_ORIG)) goto out; } @@ -2841,29 +2508,62 @@ out: /****************************************************************************** * * - * Function: cache_trigger_hostids * + * Function: get_expression_macro_result * * * - * Purpose: cache host identifiers referenced by trigger expression * + * Purpose: calculate result of expression macro * * * - * Parameters: hostids - [OUT] the host identifier cache * - * expression - [IN] the trigger expression * - * recovery_expression - [IN] the trigger recovery expression * - * (can be empty) * + * Return value: upon successful completion return SUCCEED * + * otherwise FAIL * * * ******************************************************************************/ -static void cache_trigger_hostids(zbx_vector_uint64_t *hostids, const char *expression, - const char *recovery_expression) +static int get_expression_macro_result(const DB_EVENT *event, const DB_EVENT *r_event, const char *expression, + char **replace_to, char **error) { - if (0 == hostids->values_num) + int ret = FAIL; + zbx_eval_context_t ctx; + const zbx_vector_uint64_t *hostids; + zbx_timespec_t ts; + zbx_variant_t value; + zbx_expression_eval_t eval; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, expression); + + ZBX_UNUSED(r_event); + + if (SUCCEED != zbx_eval_parse_expression(&ctx, expression, ZBX_EVAL_PARSE_EXPRESSION_MACRO, error)) + goto out; + + if (SUCCEED != zbx_db_trigger_get_all_hostids(&event->trigger, &hostids)) { - zbx_vector_uint64_t functionids; + *error = zbx_strdup(NULL, "cannot obtain host identifiers for the expression macro"); + goto out; + } - zbx_vector_uint64_create(&functionids); - get_functionids(&functionids, expression); - get_functionids(&functionids, recovery_expression); - DCget_hostids_by_functionids(&functionids, hostids); - zbx_vector_uint64_destroy(&functionids); + if (SUCCEED != zbx_eval_expand_user_macros(&ctx, hostids->values, hostids->values_num, + zbx_dc_expand_user_macros_len, error)) + { + goto out; + } + + ts.sec = event->clock; + ts.ns = event->ns; + + zbx_expression_eval_init(&eval, ZBX_EXPRESSION_NORMAL, &ctx); + zbx_expression_eval_resolve_trigger_hosts(&eval, &event->trigger); + + if (SUCCEED == (ret = zbx_expression_eval_execute(&eval, &ts, &value, error))) + { + *replace_to = zbx_strdup(NULL, zbx_variant_value_desc(&value)); + zbx_variant_clear(&value); } + + zbx_expression_eval_clear(&eval); +out: + zbx_eval_clear(&ctx); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; } /****************************************************************************** @@ -2892,50 +2592,6 @@ static void cache_item_hostid(zbx_vector_uint64_t *hostids, zbx_uint64_t itemid) } } -/****************************************************************************** - * * - * Function: wrap_negative_double_suffix * - * * - * Purpose: wrap a replacement string that represents a negative number in * - * parentheses (for instance, turn "-123.456M" into "(-123.456M)") * - * * - * Parameters: replace_to - [IN/OUT] replacement string * - * replace_to_alloc - [IN/OUT] number of allocated bytes * - * * - ******************************************************************************/ -static void wrap_negative_double_suffix(char **replace_to, size_t *replace_to_alloc) -{ - size_t replace_to_len; - - if ('-' != (*replace_to)[0]) - return; - - replace_to_len = strlen(*replace_to); - - if (NULL != replace_to_alloc && *replace_to_alloc >= replace_to_len + 3) - { - memmove(*replace_to + 1, *replace_to, replace_to_len); - } - else - { - char *buffer; - - if (NULL != replace_to_alloc) - *replace_to_alloc = replace_to_len + 3; - - buffer = (char *)zbx_malloc(NULL, replace_to_len + 3); - - memcpy(buffer + 1, *replace_to, replace_to_len); - - zbx_free(*replace_to); - *replace_to = buffer; - } - - (*replace_to)[0] = '('; - (*replace_to)[replace_to_len + 1] = ')'; - (*replace_to)[replace_to_len + 2] = '\0'; -} - static const char *zbx_dobject_status2str(int st) { switch (st) @@ -2966,9 +2622,7 @@ static void resolve_opdata(const DB_EVENT *event, char **replace_to, const char if ('\0' == *event->trigger.opdata) { - int pos = 0; - zbx_token_t token; - zbx_uint64_t itemid; + int i; zbx_vector_uint64_t itemids; zbx_timespec_t ts; @@ -2976,38 +2630,22 @@ static void resolve_opdata(const DB_EVENT *event, char **replace_to, const char ts.ns = 999999999; zbx_vector_uint64_create(&itemids); + zbx_db_trigger_get_itemids(&event->trigger, &itemids); - for (; SUCCEED == zbx_token_find(event->trigger.expression, pos, &token, ZBX_TOKEN_SEARCH_BASIC); pos++) + for (i = 0; i < itemids.values_num; i++) { - switch (token.type) - { - case ZBX_TOKEN_OBJECTID: - if (SUCCEED == get_N_itemid(event->trigger.expression + token.loc.l, 1, - &itemid) && - FAIL == zbx_vector_uint64_search(&itemids, itemid, - ZBX_DEFAULT_UINT64_COMPARE_FUNC)) - { - char *val = NULL; - - zbx_vector_uint64_append(&itemids, itemid); + char *val = NULL; - if (NULL != *replace_to) - *replace_to = zbx_strdcat(*replace_to, ", "); + if (NULL != *replace_to) + *replace_to = zbx_strdcat(*replace_to, ", "); - if (SUCCEED == DBitem_get_value(itemid, &val, 0, &ts)) - { - *replace_to = zbx_strdcat(*replace_to, val); - zbx_free(val); - } - else - *replace_to = zbx_strdcat(*replace_to, STR_UNKNOWN_VARIABLE); - } - ZBX_FALLTHROUGH; - case ZBX_TOKEN_USER_MACRO: - case ZBX_TOKEN_SIMPLE_MACRO: - case ZBX_TOKEN_MACRO: - pos = token.loc.r; + if (SUCCEED == DBitem_get_value(itemids.values[i], &val, 0, &ts)) + { + *replace_to = zbx_strdcat(*replace_to, val); + zbx_free(val); } + else + *replace_to = zbx_strdcat(*replace_to, STR_UNKNOWN_VARIABLE); } zbx_vector_uint64_destroy(&itemids); @@ -3116,16 +2754,17 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ const DC_ITEM *dc_item, const DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data, int macro_type, char *error, int maxerrlen) { - char c, *replace_to = NULL, sql[64]; - const char *m; - int N_functionid, indexed_macro, require_address, ret, res = SUCCEED, - pos = 0, found, user_names_found = 0, raw_value; - size_t data_alloc, data_len; - DC_INTERFACE interface; - zbx_vector_uint64_t hostids; - zbx_token_t token, inner_token; - zbx_token_search_t token_search = ZBX_TOKEN_SEARCH_BASIC; - char *expression = NULL, *user_username = NULL, *user_name = NULL, *user_surname = NULL; + char c, *replace_to = NULL, sql[64]; + const char *m; + int N_functionid, indexed_macro, require_address, ret, res = SUCCEED, + pos = 0, found, user_names_found = 0, raw_value; + size_t data_alloc, data_len; + DC_INTERFACE interface; + zbx_vector_uint64_t hostids; + const zbx_vector_uint64_t *phostids; + zbx_token_t token, inner_token; + zbx_token_search_t token_search = ZBX_TOKEN_SEARCH_BASIC; + char *expression = NULL, *user_username = NULL, *user_name = NULL, *user_surname = NULL; if (NULL == data || NULL == *data || '\0' == **data) { @@ -3201,8 +2840,8 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ break; case ZBX_TOKEN_SIMPLE_MACRO: if (0 == (macro_type & (MACRO_TYPE_MESSAGE_NORMAL | MACRO_TYPE_MESSAGE_RECOVERY | - MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_EXPRESSION | - MACRO_TYPE_SCRIPT_NORMAL | MACRO_TYPE_SCRIPT_RECOVERY)) || + MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_SCRIPT_NORMAL | + MACRO_TYPE_SCRIPT_RECOVERY)) || EVENT_SOURCE_TRIGGERS != ((NULL != r_event) ? r_event : event)->source) { pos++; @@ -3243,9 +2882,12 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (NULL == dc_host) { - cache_trigger_hostids(&hostids, c_event->trigger.expression, - c_event->trigger.recovery_expression); - DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to); + if (SUCCEED == zbx_db_trigger_get_all_hostids(&c_event->trigger, + &phostids)) + { + DCget_user_macro(phostids->values, phostids->values_num, m, + &replace_to); + } } else DCget_user_macro(&dc_host->hostid, 1, m, &replace_to); @@ -3254,7 +2896,7 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } else if (ZBX_TOKEN_SIMPLE_MACRO == token.type) { - ret = get_trigger_function_value(c_event->trigger.expression, &replace_to, + ret = get_trigger_function_value(&c_event->trigger, &replace_to, *data, &token.data.simple_macro, ZBX_FORMAT_HUMAN); } else if (NULL != actionid && @@ -3325,113 +2967,112 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } else if (0 == strcmp(m, MVAR_HOST_ID)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_ID); } else if (0 == strcmp(m, MVAR_HOST_HOST) || 0 == strcmp(m, MVAR_HOSTNAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_HOST); } else if (0 == strcmp(m, MVAR_HOST_NAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_NAME); } else if (0 == strcmp(m, MVAR_HOST_DESCRIPTION)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_DESCRIPTION); } else if (0 == strcmp(m, MVAR_HOST_IP) || 0 == strcmp(m, MVAR_IPADDRESS)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_IP); } else if (0 == strcmp(m, MVAR_HOST_DNS)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_DNS); } else if (0 == strcmp(m, MVAR_HOST_CONN)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_CONN); } else if (0 == strcmp(m, MVAR_HOST_PORT)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_PORT); } else if (0 == strncmp(m, MVAR_INVENTORY, ZBX_CONST_STRLEN(MVAR_INVENTORY)) || 0 == strncmp(m, MVAR_PROFILE, ZBX_CONST_STRLEN(MVAR_PROFILE))) { - ret = get_host_inventory(m, c_event->trigger.expression, &replace_to, - N_functionid); + ret = get_host_inventory(m, &c_event->trigger, &replace_to, N_functionid); } else if (0 == strcmp(m, MVAR_ITEM_DESCRIPTION)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_DESCRIPTION); } else if (0 == strcmp(m, MVAR_ITEM_DESCRIPTION_ORIG)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_DESCRIPTION_ORIG); } else if (0 == strcmp(m, MVAR_ITEM_ID)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_ID); } else if (0 == strcmp(m, MVAR_ITEM_KEY) || 0 == strcmp(m, MVAR_TRIGGER_KEY)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_KEY); } else if (0 == strcmp(m, MVAR_ITEM_KEY_ORIG)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_KEY_ORIG); } else if (0 == strcmp(m, MVAR_ITEM_LASTVALUE)) { - ret = DBitem_lastvalue(c_event->trigger.expression, &replace_to, N_functionid, + ret = DBitem_lastvalue(&c_event->trigger, &replace_to, N_functionid, raw_value); } else if (0 == strcmp(m, MVAR_ITEM_NAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_NAME); } else if (0 == strcmp(m, MVAR_ITEM_NAME_ORIG)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_NAME_ORIG); } else if (0 == strcmp(m, MVAR_ITEM_VALUE)) { - ret = DBitem_value(c_event->trigger.expression, &replace_to, N_functionid, + ret = DBitem_value(&c_event->trigger, &replace_to, N_functionid, c_event->clock, c_event->ns, raw_value); } else if (0 == strncmp(m, MVAR_ITEM_LOG, ZBX_CONST_STRLEN(MVAR_ITEM_LOG))) { - ret = get_history_log_value(m, c_event->trigger.expression, &replace_to, + ret = get_history_log_value(m, &c_event->trigger, &replace_to, N_functionid, c_event->clock, c_event->ns, tz); } else if (0 == strcmp(m, MVAR_ITEM_VALUETYPE)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_VALUETYPE); } else if (0 == strcmp(m, MVAR_PROXY_NAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_PROXY_NAME); } else if (0 == strcmp(m, MVAR_PROXY_DESCRIPTION)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_PROXY_DESCRIPTION); } else if (0 == indexed_macro && 0 == strcmp(m, MVAR_TIME)) @@ -3464,16 +3105,13 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } else if (0 == strcmp(m, MVAR_TRIGGER_EXPRESSION)) { - replace_to = zbx_strdup(replace_to, c_event->trigger.expression); - DCexpand_trigger_expression(&replace_to); + zbx_db_trigger_get_expression(&c_event->trigger, &replace_to); } else if (0 == strcmp(m, MVAR_TRIGGER_EXPRESSION_RECOVERY)) { if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == c_event->trigger.recovery_mode) { - replace_to = zbx_strdup(replace_to, - c_event->trigger.recovery_expression); - DCexpand_trigger_expression(&replace_to); + zbx_db_trigger_get_recovery_expression(&c_event->trigger, &replace_to); } else replace_to = zbx_strdup(replace_to, ""); @@ -3569,9 +3207,8 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (ZBX_TOKEN_USER_MACRO == token.type) { - cache_trigger_hostids(&hostids, c_event->trigger.expression, - c_event->trigger.recovery_expression); - DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to); + if (SUCCEED == zbx_db_trigger_get_all_hostids(&c_event->trigger, &phostids)) + DCget_user_macro(phostids->values, phostids->values_num, m, &replace_to); pos = token.loc.r; } else if (NULL != actionid && @@ -3606,98 +3243,98 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } else if (0 == strcmp(m, MVAR_HOST_ID)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_ID); } else if (0 == strcmp(m, MVAR_HOST_HOST) || 0 == strcmp(m, MVAR_HOSTNAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_HOST); } else if (0 == strcmp(m, MVAR_HOST_NAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_NAME); } else if (0 == strcmp(m, MVAR_HOST_DESCRIPTION)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_DESCRIPTION); } else if (0 == strcmp(m, MVAR_HOST_IP) || 0 == strcmp(m, MVAR_IPADDRESS)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_IP); } else if (0 == strcmp(m, MVAR_HOST_DNS)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_DNS); } else if (0 == strcmp(m, MVAR_HOST_CONN)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_CONN); } else if (0 == strcmp(m, MVAR_HOST_PORT)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_PORT); } else if (0 == strncmp(m, MVAR_INVENTORY, ZBX_CONST_STRLEN(MVAR_INVENTORY)) || 0 == strncmp(m, MVAR_PROFILE, ZBX_CONST_STRLEN(MVAR_PROFILE))) { - ret = get_host_inventory(m, c_event->trigger.expression, &replace_to, + ret = get_host_inventory(m, &c_event->trigger, &replace_to, N_functionid); } else if (0 == strcmp(m, MVAR_ITEM_DESCRIPTION)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_DESCRIPTION); } else if (0 == strcmp(m, MVAR_ITEM_DESCRIPTION_ORIG)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_DESCRIPTION_ORIG); } else if (0 == strcmp(m, MVAR_ITEM_ID)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_ID); } else if (0 == strcmp(m, MVAR_ITEM_KEY) || 0 == strcmp(m, MVAR_TRIGGER_KEY)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_KEY); } else if (0 == strcmp(m, MVAR_ITEM_KEY_ORIG)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_KEY_ORIG); } else if (0 == strcmp(m, MVAR_ITEM_NAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_NAME); } else if (0 == strcmp(m, MVAR_ITEM_NAME_ORIG)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_NAME_ORIG); } else if (0 == strcmp(m, MVAR_ITEM_VALUETYPE)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_ITEM_VALUETYPE); } else if (0 == strcmp(m, MVAR_PROXY_NAME)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_PROXY_NAME); } else if (0 == strcmp(m, MVAR_PROXY_DESCRIPTION)) { - ret = DBget_trigger_value(c_event->trigger.expression, &replace_to, + ret = DBget_trigger_value(&c_event->trigger, &replace_to, N_functionid, ZBX_REQUEST_PROXY_DESCRIPTION); } else if (0 == indexed_macro && 0 == strcmp(m, MVAR_TIME)) @@ -3714,16 +3351,13 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } else if (0 == strcmp(m, MVAR_TRIGGER_EXPRESSION)) { - replace_to = zbx_strdup(replace_to, c_event->trigger.expression); - DCexpand_trigger_expression(&replace_to); + zbx_db_trigger_get_expression(&c_event->trigger, &replace_to); } else if (0 == strcmp(m, MVAR_TRIGGER_EXPRESSION_RECOVERY)) { if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == c_event->trigger.recovery_mode) { - replace_to = zbx_strdup(replace_to, - c_event->trigger.recovery_expression); - DCexpand_trigger_expression(&replace_to); + zbx_db_trigger_get_recovery_expression(&c_event->trigger, &replace_to); } else replace_to = zbx_strdup(replace_to, ""); @@ -4329,83 +3963,88 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (ZBX_TOKEN_USER_MACRO == token.type) { - cache_trigger_hostids(&hostids, event->trigger.expression, - event->trigger.recovery_expression); - DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to); + if (SUCCEED == zbx_db_trigger_get_all_hostids(&event->trigger, &phostids)) + DCget_user_macro(phostids->values, phostids->values_num, m, &replace_to); pos = token.loc.r; } else if (ZBX_TOKEN_REFERENCE == token.type) { - /* try to expand trigger expression if it hasn't been done yet */ - if (NULL == expression && NULL == (expression = - get_expanded_expression(event->trigger.expression))) + if (SUCCEED != zbx_db_trigger_get_constant(&event->trigger, + token.data.reference.index, &replace_to)) { /* expansion failed, reference substitution is impossible */ token_search &= ~ZBX_TOKEN_SEARCH_REFERENCES; continue; } - - get_trigger_expression_constant(expression, &token.data.reference, &replace_to); } else if (ZBX_TOKEN_EXPRESSION_MACRO == inner_token.type) { if (0 != (macro_type & MACRO_TYPE_EVENT_NAME)) { - char *exp = NULL; + char *exp = NULL, *errmsg = NULL; size_t exp_alloc = 0, exp_offset = 0; zbx_strloc_t *loc = &inner_token.data.expression_macro.expression; zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, *data + loc->l, loc->r - loc->l + 1); - ret = get_expression_macro_result(event, r_event, &exp, &replace_to, - error, maxerrlen); + ret = get_expression_macro_result(event, r_event, exp, &replace_to, + &errmsg); zbx_free(exp); + + if (SUCCEED != ret) + { + *errmsg = tolower(*errmsg); + zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot evaluate" + " expression macro: %s", __func__, errmsg); + zbx_strlcpy(error, errmsg, maxerrlen); + zbx_free(errmsg); + } } } else if (0 == strcmp(m, MVAR_HOST_HOST) || 0 == strcmp(m, MVAR_HOSTNAME)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_HOST); } else if (0 == strcmp(m, MVAR_HOST_NAME)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_NAME); } else if (0 == strcmp(m, MVAR_HOST_IP) || 0 == strcmp(m, MVAR_IPADDRESS)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_IP); } else if (0 == strcmp(m, MVAR_HOST_DNS)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_DNS); } else if (0 == strcmp(m, MVAR_HOST_CONN)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_CONN); } else if (0 == strcmp(m, MVAR_HOST_PORT)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_PORT); } else if (0 == strcmp(m, MVAR_ITEM_VALUE)) { - ret = DBitem_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBitem_value(&event->trigger, &replace_to, N_functionid, event->clock, event->ns, raw_value); } else if (0 == strncmp(m, MVAR_ITEM_LOG, ZBX_CONST_STRLEN(MVAR_ITEM_LOG))) { - ret = get_history_log_value(m, event->trigger.expression, &replace_to, + ret = get_history_log_value(m, &event->trigger, &replace_to, N_functionid, event->clock, event->ns, tz); } else if (0 == strcmp(m, MVAR_ITEM_LASTVALUE)) { - ret = DBitem_lastvalue(event->trigger.expression, &replace_to, N_functionid, + ret = DBitem_lastvalue(&event->trigger, &replace_to, N_functionid, raw_value); } else if (0 == strcmp(m, MVAR_TIME) && 0 != (macro_type & MACRO_TYPE_EVENT_NAME)) @@ -4418,22 +4057,7 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (EVENT_OBJECT_TRIGGER == event->object) { - if (ZBX_TOKEN_USER_MACRO == token.type) - { - /* When processing trigger expressions the user macros are already expanded. */ - /* An unexpanded user macro means either unknown macro or macro value */ - /* validation failure. */ - - if (NULL != error) - { - zbx_snprintf(error, maxerrlen, "Invalid macro '%.*s' value or type", - (int)(token.loc.r - token.loc.l + 1), - *data + token.loc.l); - } - - res = FAIL; - } - else if (0 == strcmp(m, MVAR_TRIGGER_VALUE)) + if (0 == strcmp(m, MVAR_TRIGGER_VALUE)) replace_to = zbx_dsprintf(replace_to, "%d", event->value); } } @@ -4443,44 +4067,43 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (ZBX_TOKEN_USER_MACRO == token.type) { - cache_trigger_hostids(&hostids, event->trigger.expression, - event->trigger.recovery_expression); - DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to); + if (SUCCEED == zbx_db_trigger_get_all_hostids(&event->trigger, &phostids)) + DCget_user_macro(phostids->values, phostids->values_num, m, &replace_to); pos = token.loc.r; } else if (0 == strcmp(m, MVAR_HOST_ID)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_ID); } else if (0 == strcmp(m, MVAR_HOST_HOST)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_HOST); } else if (0 == strcmp(m, MVAR_HOST_NAME)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_NAME); } else if (0 == strcmp(m, MVAR_HOST_IP)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_IP); } else if (0 == strcmp(m, MVAR_HOST_DNS)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_DNS); } else if (0 == strcmp(m, MVAR_HOST_CONN)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_CONN); } else if (0 == strcmp(m, MVAR_HOST_PORT)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_PORT); } else if (0 == strcmp(m, MVAR_TRIGGER_ID)) @@ -4489,17 +4112,17 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } else if (0 == strcmp(m, MVAR_ITEM_LASTVALUE)) { - ret = DBitem_lastvalue(event->trigger.expression, &replace_to, N_functionid, + ret = DBitem_lastvalue(&event->trigger, &replace_to, N_functionid, raw_value); } else if (0 == strcmp(m, MVAR_ITEM_VALUE)) { - ret = DBitem_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBitem_value(&event->trigger, &replace_to, N_functionid, event->clock, event->ns, raw_value); } else if (0 == strncmp(m, MVAR_ITEM_LOG, ZBX_CONST_STRLEN(MVAR_ITEM_LOG))) { - ret = get_history_log_value(m, event->trigger.expression, &replace_to, + ret = get_history_log_value(m, &event->trigger, &replace_to, N_functionid, event->clock, event->ns, tz); } else if (0 == strcmp(m, MVAR_EVENT_ID)) @@ -4808,49 +4431,48 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (ZBX_TOKEN_USER_MACRO == token.type) { - cache_trigger_hostids(&hostids, event->trigger.expression, - event->trigger.recovery_expression); - DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to); + if (SUCCEED == zbx_db_trigger_get_all_hostids(&event->trigger, &phostids)) + DCget_user_macro(phostids->values, phostids->values_num, m, &replace_to); pos = token.loc.r; } else if (0 == strncmp(m, MVAR_INVENTORY, ZBX_CONST_STRLEN(MVAR_INVENTORY))) { - ret = get_host_inventory(m, event->trigger.expression, &replace_to, + ret = get_host_inventory(m, &event->trigger, &replace_to, N_functionid); } else if (0 == strcmp(m, MVAR_HOST_ID)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_ID); } else if (0 == strcmp(m, MVAR_HOST_HOST)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_HOST); } else if (0 == strcmp(m, MVAR_HOST_NAME)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_NAME); } else if (0 == strcmp(m, MVAR_HOST_IP)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_IP); } else if (0 == strcmp(m, MVAR_HOST_DNS)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_DNS); } else if (0 == strcmp(m, MVAR_HOST_CONN)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_CONN); } else if (0 == strcmp(m, MVAR_HOST_PORT)) { - ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBget_trigger_value(&event->trigger, &replace_to, N_functionid, ZBX_REQUEST_HOST_PORT); } @@ -4858,17 +4480,17 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (0 == strcmp(m, MVAR_ITEM_LASTVALUE)) { - ret = DBitem_lastvalue(event->trigger.expression, &replace_to, - N_functionid, raw_value); + ret = DBitem_lastvalue(&event->trigger, &replace_to, N_functionid, + raw_value); } else if (0 == strcmp(m, MVAR_ITEM_VALUE)) { - ret = DBitem_value(event->trigger.expression, &replace_to, N_functionid, + ret = DBitem_value(&event->trigger, &replace_to, N_functionid, event->clock, event->ns, raw_value); } else if (0 == strncmp(m, MVAR_ITEM_LOG, ZBX_CONST_STRLEN(MVAR_ITEM_LOG))) { - ret = get_history_log_value(m, event->trigger.expression, &replace_to, + ret = get_history_log_value(m, &event->trigger, &replace_to, N_functionid, event->clock, event->ns, tz); } else if (0 == strcmp(m, MVAR_TRIGGER_ID)) @@ -4930,25 +4552,6 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } } } - else if (0 == indexed_macro && 0 != (macro_type & MACRO_TYPE_EXPRESSION)) - { - const DB_EVENT *c_event; - - c_event = ((NULL != r_event) ? r_event : event); - - if (ZBX_TOKEN_USER_MACRO == token.type) - { - cache_trigger_hostids(&hostids, c_event->trigger.expression, - c_event->trigger.recovery_expression); - DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to); - pos = token.loc.r; - } - else if (ZBX_TOKEN_SIMPLE_MACRO == token.type) - { - ret = get_trigger_function_value(c_event->trigger.expression, &replace_to, - *data, &token.data.simple_macro, ZBX_FORMAT_RAW); - } - } else if (0 == indexed_macro && 0 != (macro_type & MACRO_TYPE_REPORT)) { if (0 == strcmp(m, MVAR_TIME)) @@ -5022,61 +4625,24 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ return res; } -static int extract_expression_functionids(zbx_vector_uint64_t *functionids, const char *expression) -{ - const char *bl, *br; - zbx_uint64_t functionid; - - for (bl = strchr(expression, '{'); NULL != bl; bl = strchr(bl, '{')) - { - if (NULL == (br = strchr(bl, '}'))) - break; - - if (SUCCEED != is_uint64_n(bl + 1, br - bl - 1, &functionid)) - break; - - zbx_vector_uint64_append(functionids, functionid); - - bl = br + 1; - } - - return (NULL == bl ? SUCCEED : FAIL); -} - static void zbx_extract_functionids(zbx_vector_uint64_t *functionids, zbx_vector_ptr_t *triggers) { DC_TRIGGER *tr; - int i, values_num_save; + int i; zabbix_log(LOG_LEVEL_DEBUG, "In %s() tr_num:%d", __func__, triggers->values_num); for (i = 0; i < triggers->values_num; i++) { - const char *error_expression = NULL; - tr = (DC_TRIGGER *)triggers->values[i]; if (NULL != tr->new_error) continue; - values_num_save = functionids->values_num; - - if (SUCCEED != extract_expression_functionids(functionids, tr->expression)) - { - error_expression = tr->expression; - } - else if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == tr->recovery_mode && - SUCCEED != extract_expression_functionids(functionids, tr->recovery_expression)) - { - error_expression = tr->recovery_expression; - } + zbx_eval_get_functionids(tr->eval_ctx, functionids); - if (NULL != error_expression) - { - tr->new_error = zbx_dsprintf(tr->new_error, "Invalid expression [%s]", error_expression); - tr->new_value = TRIGGER_VALUE_UNKNOWN; - functionids->values_num = values_num_save; - } + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == tr->recovery_mode) + zbx_eval_get_functionids(tr->eval_ctx_r, functionids); } zbx_vector_uint64_sort(functionids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); @@ -5105,18 +4671,23 @@ zbx_trigger_func_position_t; * Author: Andrea Biscuola * * * ******************************************************************************/ -static int expand_trigger_macros(DB_EVENT *event, DC_TRIGGER *trigger, char *error, size_t maxerrlen) +static int expand_trigger_macros(zbx_eval_context_t *ctx, const DB_EVENT *event, char *error, size_t maxerrlen) { - if (FAIL == substitute_simple_macros_impl(NULL, event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &trigger->expression, MACRO_TYPE_TRIGGER_EXPRESSION, error, maxerrlen)) - { - return FAIL; - } + int i; - if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == trigger->recovery_mode) + for (i = 0; i < ctx->stack.values_num; i++) { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + if (ZBX_EVAL_TOKEN_VAR_MACRO != token->type && ZBX_EVAL_TOKEN_VAR_STR != token->type) + continue; + + /* all trigger macros macros are already extracted into strings */ + if (ZBX_VARIANT_STR != token->value.type) + continue; + if (FAIL == substitute_simple_macros_impl(NULL, event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &trigger->recovery_expression, MACRO_TYPE_TRIGGER_EXPRESSION, error, maxerrlen)) + &token->value.data.str, MACRO_TYPE_TRIGGER_EXPRESSION, error, maxerrlen)) { return FAIL; } @@ -5143,7 +4714,6 @@ static void zbx_link_triggers_with_functions(zbx_vector_ptr_t *triggers_func_pos { zbx_vector_uint64_t funcids; DC_TRIGGER *tr; - DB_EVENT ev; int i; zabbix_log(LOG_LEVEL_DEBUG, "In %s() trigger_order_num:%d", __func__, trigger_order->values_num); @@ -5151,8 +4721,6 @@ static void zbx_link_triggers_with_functions(zbx_vector_ptr_t *triggers_func_pos zbx_vector_uint64_create(&funcids); zbx_vector_uint64_reserve(&funcids, functionids->values_num); - ev.object = EVENT_OBJECT_TRIGGER; - for (i = 0; i < trigger_order->values_num; i++) { zbx_trigger_func_position_t *tr_func_pos; @@ -5162,20 +4730,14 @@ static void zbx_link_triggers_with_functions(zbx_vector_ptr_t *triggers_func_pos if (NULL != tr->new_error) continue; - ev.value = tr->value; + zbx_eval_get_functionids(tr->eval_ctx, &funcids); + tr_func_pos = (zbx_trigger_func_position_t *)zbx_malloc(NULL, sizeof(zbx_trigger_func_position_t)); + tr_func_pos->trigger = tr; + tr_func_pos->start_index = functionids->values_num; + tr_func_pos->count = funcids.values_num; - expand_trigger_macros(&ev, tr, NULL, 0); - - if (SUCCEED == extract_expression_functionids(&funcids, tr->expression)) - { - tr_func_pos = (zbx_trigger_func_position_t *)zbx_malloc(NULL, sizeof(zbx_trigger_func_position_t)); - tr_func_pos->trigger = tr; - tr_func_pos->start_index = functionids->values_num; - tr_func_pos->count = funcids.values_num; - - zbx_vector_uint64_append_array(functionids, funcids.values, funcids.values_num); - zbx_vector_ptr_append(triggers_func_pos, tr_func_pos); - } + zbx_vector_uint64_append_array(functionids, funcids.values, funcids.values_num); + zbx_vector_ptr_append(triggers_func_pos, tr_func_pos); zbx_vector_uint64_clear(&funcids); } @@ -5227,8 +4789,8 @@ void zbx_determine_items_in_expressions(zbx_vector_ptr_t *trigger_order, const z for (f = func_pos->start_index; f < func_pos->start_index + func_pos->count; f++) { - if (FAIL != zbx_vector_uint64_bsearch(&itemids_sorted, functions[f].itemid, - ZBX_DEFAULT_UINT64_COMPARE_FUNC)) + if (SUCCEED == errcodes[f] && FAIL != zbx_vector_uint64_bsearch(&itemids_sorted, + functions[f].itemid, ZBX_DEFAULT_UINT64_COMPARE_FUNC)) { func_pos->trigger->flags |= ZBX_DC_TRIGGER_PROBLEM_EXPRESSION; break; @@ -5259,7 +4821,7 @@ typedef struct zbx_timespec_t timespec; /* output data */ - char *value; + zbx_variant_t value; char *error; } zbx_func_t; @@ -5311,8 +4873,9 @@ static void func_clean(void *ptr) zbx_free(func->function); zbx_free(func->parameter); - zbx_free(func->value); zbx_free(func->error); + + zbx_variant_clear(&func->value); } /****************************************************************************** @@ -5340,7 +4903,7 @@ static void zbx_populate_function_items(const zbx_vector_uint64_t *functionids, zabbix_log(LOG_LEVEL_DEBUG, "In %s() functionids_num:%d", __func__, functionids->values_num); - func_local.value = NULL; + zbx_variant_set_none(&func_local.value); func_local.error = NULL; functions = (DC_FUNCTION *)zbx_malloc(functions, sizeof(DC_FUNCTION) * functionids->values_num); @@ -5375,6 +4938,7 @@ static void zbx_populate_function_items(const zbx_vector_uint64_t *functionids, func = (zbx_func_t *)zbx_hashset_insert(funcs, &func_local, sizeof(func_local)); func->function = zbx_strdup(NULL, func_local.function); func->parameter = zbx_strdup(NULL, func_local.parameter); + zbx_variant_set_none(&func->value); } ifunc_local.functionid = functions[i].functionid; @@ -5390,7 +4954,7 @@ static void zbx_populate_function_items(const zbx_vector_uint64_t *functionids, zabbix_log(LOG_LEVEL_DEBUG, "End of %s() ifuncs_num:%d", __func__, ifuncs->num_data); } -static void zbx_evaluate_item_functions(zbx_hashset_t *funcs, zbx_vector_ptr_t *unknown_msgs) +static void zbx_evaluate_item_functions(zbx_hashset_t *funcs) { DC_ITEM *items = NULL; char *error = NULL; @@ -5420,16 +4984,13 @@ static void zbx_evaluate_item_functions(zbx_hashset_t *funcs, zbx_vector_ptr_t * zbx_hashset_iter_reset(funcs, &iter); while (NULL != (func = (zbx_func_t *)zbx_hashset_iter_next(&iter))) { - int ret_unknown = 0; /* flag raised if current function evaluates to ZBX_UNKNOWN */ - char *unknown_msg; - i = zbx_vector_uint64_bsearch(&itemids, func->itemid, ZBX_DEFAULT_UINT64_COMPARE_FUNC); if (SUCCEED != errcodes[i]) { - func->error = zbx_dsprintf(func->error, "Cannot evaluate function \"%s(%s)\":" - " item does not exist.", - func->function, func->parameter); + zbx_free(func->error); + func->error = zbx_eval_format_function_error(func->function, NULL, NULL, func->parameter, + "item does not exist"); continue; } @@ -5437,72 +4998,39 @@ static void zbx_evaluate_item_functions(zbx_hashset_t *funcs, zbx_vector_ptr_t * if (ITEM_STATUS_ACTIVE != items[i].status) { - func->error = zbx_dsprintf(func->error, "Cannot evaluate function \"%s:%s.%s(%s)\":" - " item is disabled.", - items[i].host.host, items[i].key_orig, func->function, func->parameter); + zbx_free(func->error); + func->error = zbx_eval_format_function_error(func->function, items[i].host.host, + items[i].key_orig, func->parameter, "item is disabled"); continue; } if (HOST_STATUS_MONITORED != items[i].host.status) { - func->error = zbx_dsprintf(func->error, "Cannot evaluate function \"%s:%s.%s(%s)\":" - " item belongs to a disabled host.", - items[i].host.host, items[i].key_orig, func->function, func->parameter); + zbx_free(func->error); + func->error = zbx_eval_format_function_error(func->function, items[i].host.host, + items[i].key_orig, func->parameter, "item belongs to a disabled host"); continue; } - /* If the item is NOTSUPPORTED then evaluation is allowed for: */ - /* - time-based functions and nodata(). Their values can be */ - /* evaluated to regular numbers even for NOTSUPPORTED items. */ - /* - other functions. Result of evaluation is ZBX_UNKNOWN. */ - - if (ITEM_STATE_NOTSUPPORTED == items[i].state && FAIL == evaluatable_for_notsupported(func->function)) + if (ITEM_STATE_NOTSUPPORTED == items[i].state && + FAIL == zbx_evaluatable_for_notsupported(func->function)) { - /* compose and store 'unknown' message for future use */ - unknown_msg = zbx_dsprintf(NULL, - "Cannot evaluate function \"%s:%s.%s(%s)\": item is not supported.", - items[i].host.host, items[i].key_orig, func->function, func->parameter); - - zbx_free(func->error); - zbx_vector_ptr_append(unknown_msgs, unknown_msg); - ret_unknown = 1; + /* set 'unknown' error value */ + zbx_variant_set_error(&func->value, + zbx_eval_format_function_error(func->function, items[i].host.host, + items[i].key_orig, func->parameter, "item is not supported")); + continue; } - if (0 == ret_unknown && SUCCEED != evaluate_function(&func->value, &items[i], func->function, - func->parameter, &func->timespec, &error)) + if (SUCCEED != evaluate_function2(&func->value, &items[i], func->function, func->parameter, + &func->timespec, &error)) { /* compose and store error message for future use */ - if (NULL != error) - { - unknown_msg = zbx_dsprintf(NULL, - "Cannot evaluate function \"%s:%s.%s(%s)\": %s.", - items[i].host.host, items[i].key_orig, func->function, - func->parameter, error); - - zbx_free(func->error); - zbx_free(error); - } - else - { - unknown_msg = zbx_dsprintf(NULL, - "Cannot evaluate function \"%s:%s.%s(%s)\".", - items[i].host.host, items[i].key_orig, - func->function, func->parameter); - - zbx_free(func->error); - } - - zbx_vector_ptr_append(unknown_msgs, unknown_msg); - ret_unknown = 1; - } - - if (0 != ret_unknown) - { - char buffer[MAX_ID_LEN + 1]; - /* write a special token of unknown value with 'unknown' message number, like */ - /* ZBX_UNKNOWN0, ZBX_UNKNOWN1 etc. not wrapped in () */ - zbx_snprintf(buffer, sizeof(buffer), ZBX_UNKNOWN_STR "%d", unknown_msgs->values_num - 1); - func->value = zbx_strdup(func->value, buffer); + zbx_variant_set_error(&func->value, + zbx_eval_format_function_error(func->function, items[i].host.host, + items[i].key_orig, func->parameter, error)); + zbx_free(error); + continue; } } @@ -5517,34 +5045,30 @@ static void zbx_evaluate_item_functions(zbx_hashset_t *funcs, zbx_vector_ptr_t * zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } -static int substitute_expression_functions_results(zbx_hashset_t *ifuncs, char *expression, char **out, - size_t *out_alloc, char **error) +static int substitute_expression_functions_results(zbx_hashset_t *ifuncs, zbx_eval_context_t *ctx, char **error) { - char *br, *bl; - size_t out_offset = 0; zbx_uint64_t functionid; zbx_func_t *func; zbx_ifunc_t *ifunc; + int i; - for (br = expression, bl = strchr(expression, '{'); NULL != bl; bl = strchr(bl, '{')) + for (i = 0; i < ctx->stack.values_num; i++) { - *bl = '\0'; - zbx_strcpy_alloc(out, out_alloc, &out_offset, br); - *bl = '{'; + zbx_eval_token_t *token = &ctx->stack.values[i]; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) + continue; - if (NULL == (br = strchr(bl, '}'))) + if (ZBX_VARIANT_UI64 != token->value.type) { - *error = zbx_strdup(*error, "Invalid trigger expression"); + /* functionids should be already extracted into uint64 vars */ + THIS_SHOULD_NEVER_HAPPEN; + *error = zbx_dsprintf(*error, "Cannot parse function at: \"%s\"", + ctx->expression + token->loc.l); return FAIL; } - *br = '\0'; - - ZBX_STR2UINT64(functionid, bl + 1); - - *br++ = '}'; - bl = br; - + functionid = token->value.data.ui64; if (NULL == (ifunc = (zbx_ifunc_t *)zbx_hashset_search(ifuncs, &functionid))) { *error = zbx_dsprintf(*error, "Cannot obtain function" @@ -5560,39 +5084,39 @@ static int substitute_expression_functions_results(zbx_hashset_t *ifuncs, char * return FAIL; } - if (NULL == func->value) + if (ZBX_VARIANT_NONE == func->value.type) { *error = zbx_strdup(*error, "Unexpected error while processing a trigger expression"); return FAIL; } - if (SUCCEED != is_double_suffix(func->value, ZBX_FLAG_DOUBLE_SUFFIX) || '-' == *func->value) - { - zbx_chrcpy_alloc(out, out_alloc, &out_offset, '('); - zbx_strcpy_alloc(out, out_alloc, &out_offset, func->value); - zbx_chrcpy_alloc(out, out_alloc, &out_offset, ')'); - } - else - zbx_strcpy_alloc(out, out_alloc, &out_offset, func->value); + zbx_variant_copy(&token->value, &func->value); } - zbx_strcpy_alloc(out, out_alloc, &out_offset, br); - return SUCCEED; } +static void log_expression(const char *prefix, int index, const zbx_eval_context_t *ctx) +{ + if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG)) + { + char *expression = NULL; + + zbx_eval_compose_expression(ctx, &expression); + zabbix_log(LOG_LEVEL_DEBUG, "%s() expression[%d]:'%s' => '%s'", prefix, index, ctx->expression, + expression); + zbx_free(expression); + } +} + static void zbx_substitute_functions_results(zbx_hashset_t *ifuncs, zbx_vector_ptr_t *triggers) { DC_TRIGGER *tr; - char *out = NULL; - size_t out_alloc = TRIGGER_EXPRESSION_LEN_MAX; int i; zabbix_log(LOG_LEVEL_DEBUG, "In %s() ifuncs_num:%d tr_num:%d", __func__, ifuncs->num_data, triggers->values_num); - out = (char *)zbx_malloc(out, out_alloc); - for (i = 0; i < triggers->values_num; i++) { tr = (DC_TRIGGER *)triggers->values[i]; @@ -5600,35 +5124,26 @@ static void zbx_substitute_functions_results(zbx_hashset_t *ifuncs, zbx_vector_p if (NULL != tr->new_error) continue; - if( SUCCEED != substitute_expression_functions_results(ifuncs, tr->expression, &out, &out_alloc, - &tr->new_error)) + if( SUCCEED != substitute_expression_functions_results(ifuncs, tr->eval_ctx, &tr->new_error)) { tr->new_value = TRIGGER_VALUE_UNKNOWN; continue; } - zabbix_log(LOG_LEVEL_DEBUG, "%s() expression[%d]:'%s' => '%s'", __func__, i, tr->expression, out); - - tr->expression = zbx_strdup(tr->expression, out); + log_expression(__func__, i, tr->eval_ctx); if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == tr->recovery_mode) { - if (SUCCEED != substitute_expression_functions_results(ifuncs, - tr->recovery_expression, &out, &out_alloc, &tr->new_error)) + if (SUCCEED != substitute_expression_functions_results(ifuncs, tr->eval_ctx_r, &tr->new_error)) { tr->new_value = TRIGGER_VALUE_UNKNOWN; continue; } - zabbix_log(LOG_LEVEL_DEBUG, "%s() recovery_expression[%d]:'%s' => '%s'", __func__, i, - tr->recovery_expression, out); - - tr->recovery_expression = zbx_strdup(tr->recovery_expression, out); + log_expression(__func__, i, tr->eval_ctx_r); } } - zbx_free(out); - zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } @@ -5648,7 +5163,7 @@ static void zbx_substitute_functions_results(zbx_hashset_t *ifuncs, zbx_vector_p * Comments: example: "({15}>10) or ({123}=1)" => "(26.416>10) or (0=1)" * * * ******************************************************************************/ -static void substitute_functions(zbx_vector_ptr_t *triggers, zbx_vector_ptr_t *unknown_msgs) +static void substitute_functions(zbx_vector_ptr_t *triggers) { zbx_vector_uint64_t functionids; zbx_hashset_t ifuncs, funcs; @@ -5671,7 +5186,7 @@ static void substitute_functions(zbx_vector_ptr_t *triggers, zbx_vector_ptr_t *u if (0 != ifuncs.num_data) { - zbx_evaluate_item_functions(&funcs, unknown_msgs); + zbx_evaluate_item_functions(&funcs); zbx_substitute_functions_results(&ifuncs, triggers); } @@ -5685,6 +5200,65 @@ empty: /****************************************************************************** * * + * Function: prepare_triggers * + * * + * Purpose: prepare triggers for evaluation * + * * + * Parameters: triggers - [IN] array of DC_TRIGGER pointers * + * triggres_num - [IN] the number of triggers to prepare * + * * + ******************************************************************************/ +void prepare_triggers(DC_TRIGGER **triggers, int triggers_num) +{ + int i; + + for (i = 0; i < triggers_num; i++) + { + DC_TRIGGER *tr = triggers[i]; + + tr->eval_ctx = zbx_eval_deserialize_dyn(tr->expression_bin, tr->expression, ZBX_EVAL_EXCTRACT_ALL); + + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == tr->recovery_mode) + { + tr->eval_ctx_r = zbx_eval_deserialize_dyn(tr->recovery_expression_bin, tr->recovery_expression, + ZBX_EVAL_EXCTRACT_ALL); + } + } +} + +static int evaluate_expression(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, double *result, + char **error) +{ + zbx_variant_t value; + + if (SUCCEED != zbx_eval_execute(ctx, ts, &value, error)) + return FAIL; + + if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG)) + { + char *expression = NULL; + + zbx_eval_compose_expression(ctx, &expression); + zabbix_log(LOG_LEVEL_DEBUG, "%s(): %s => %s", __func__, expression, zbx_variant_value_desc(&value)); + zbx_free(expression); + } + + if (SUCCEED != zbx_variant_convert(&value, ZBX_VARIANT_DBL)) + { + *error = zbx_dsprintf(*error, "Cannot convert expression result of type \"%s\" to" + " floating point value", zbx_variant_type_desc(&value)); + zbx_variant_clear(&value); + + return FAIL; + } + + *result = value.data.dbl; + + return SUCCEED; +} + +/****************************************************************************** + * * * Function: evaluate_expressions * * * * Purpose: evaluate trigger expressions * @@ -5701,7 +5275,6 @@ void evaluate_expressions(zbx_vector_ptr_t *triggers) DC_TRIGGER *tr; int i; double expr_result; - zbx_vector_ptr_t unknown_msgs; /* pointers to messages about origins of 'unknown' values */ char err[MAX_STRING_LEN]; zabbix_log(LOG_LEVEL_DEBUG, "In %s() tr_num:%d", __func__, triggers->values_num); @@ -5714,18 +5287,21 @@ void evaluate_expressions(zbx_vector_ptr_t *triggers) event.value = tr->value; - if (SUCCEED != expand_trigger_macros(&event, tr, err, sizeof(err))) + if (SUCCEED != expand_trigger_macros(tr->eval_ctx, &event, err, sizeof(err))) { tr->new_error = zbx_dsprintf(tr->new_error, "Cannot evaluate expression: %s", err); tr->new_value = TRIGGER_VALUE_UNKNOWN; } - } - /* Assumption: most often there will be no NOTSUPPORTED items and function errors. */ - /* Therefore initialize error messages vector but do not reserve any space. */ - zbx_vector_ptr_create(&unknown_msgs); + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == tr->recovery_mode && + SUCCEED != expand_trigger_macros(tr->eval_ctx_r, &event, err, sizeof(err))) + { + tr->new_error = zbx_dsprintf(tr->new_error, "Cannot evaluate expression: %s", err); + tr->new_value = TRIGGER_VALUE_UNKNOWN; + } + } - substitute_functions(triggers, &unknown_msgs); + substitute_functions(triggers); /* calculate new trigger values based on their recovery modes and expression evaluations */ for (i = 0; i < triggers->values_num; i++) @@ -5735,12 +5311,8 @@ void evaluate_expressions(zbx_vector_ptr_t *triggers) if (NULL != tr->new_error) continue; - if (SUCCEED != evaluate(&expr_result, tr->expression, err, sizeof(err), &unknown_msgs)) - { - tr->new_error = zbx_strdup(tr->new_error, err); - tr->new_value = TRIGGER_VALUE_UNKNOWN; + if (SUCCEED != evaluate_expression(tr->eval_ctx, &tr->timespec, &expr_result, &tr->new_error)) continue; - } /* trigger expression evaluates to true, set PROBLEM value */ if (SUCCEED != zbx_double_compare(expr_result, 0.0)) @@ -5768,9 +5340,8 @@ void evaluate_expressions(zbx_vector_ptr_t *triggers) } /* processing recovery expression mode */ - if (SUCCEED != evaluate(&expr_result, tr->recovery_expression, err, sizeof(err), &unknown_msgs)) + if (SUCCEED != evaluate_expression(tr->eval_ctx_r, &tr->timespec, &expr_result, &tr->new_error)) { - tr->new_error = zbx_strdup(tr->new_error, err); tr->new_value = TRIGGER_VALUE_UNKNOWN; continue; } @@ -5786,9 +5357,6 @@ void evaluate_expressions(zbx_vector_ptr_t *triggers) tr->new_value = TRIGGER_VALUE_NONE; } - zbx_vector_ptr_clear_ext(&unknown_msgs, zbx_ptr_free); - zbx_vector_ptr_destroy(&unknown_msgs); - if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG)) { for (i = 0; i < triggers->values_num; i++) @@ -5873,21 +5441,14 @@ out: * flags - [IN] the flags passed to * * subtitute_discovery_macros() function * * jp_row - [IN] discovery data * - * error - [OUT] should be not NULL if * - * ZBX_MACRO_NUMERIC flag is set * - * error_len - [IN] the size of error buffer * * cur_token_inside_quote - [IN] used in autoquoting for trigger prototypes * * * - * Return value: Always SUCCEED if numeric flag is not set, otherwise SUCCEED * - * if all discovery macros resolved to numeric values, * - * otherwise FAIL with an error message. * - * * ******************************************************************************/ -static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, const struct zbx_json_parse *jp_row, - const zbx_vector_ptr_t *lld_macro_paths, char *error, size_t error_len, int cur_token_inside_quote) +static void process_lld_macro_token(char **data, zbx_token_t *token, int flags, const struct zbx_json_parse *jp_row, + const zbx_vector_ptr_t *lld_macro_paths, int cur_token_inside_quote) { char c, *replace_to = NULL; - int ret = SUCCEED, l ,r; + int l ,r; if (ZBX_TOKEN_LLD_FUNC_MACRO == token->type) { @@ -5907,16 +5468,10 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c { zabbix_log(LOG_LEVEL_DEBUG, "cannot substitute macro \"%s\": not found in value set", *data + l); - if (0 != (flags & ZBX_TOKEN_NUMERIC)) - { - zbx_snprintf(error, error_len, "no value for macro \"%s\"", *data + l); - ret = FAIL; - } - (*data)[r + 1] = c; zbx_free(replace_to); - return ret; + return; } (*data)[r + 1] = c; @@ -5930,37 +5485,13 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c zabbix_log(LOG_LEVEL_DEBUG, "cannot execute function \"%.*s\"", len, *data + token->data.lld_func_macro.func.l); - if (0 != (flags & ZBX_TOKEN_NUMERIC)) - { - zbx_snprintf(error, error_len, "unable to execute function \"%.*s\"", len, - *data + token->data.lld_func_macro.func.l); - ret = FAIL; - } - zbx_free(replace_to); - return ret; + return; } } - if (0 != (flags & ZBX_TOKEN_NUMERIC)) - { - if (SUCCEED == is_double_suffix(replace_to, ZBX_FLAG_DOUBLE_SUFFIX)) - { - size_t replace_to_alloc; - - replace_to_alloc = strlen(replace_to) + 1; - wrap_negative_double_suffix(&replace_to, &replace_to_alloc); - } - else - { - zbx_free(replace_to); - zbx_snprintf(error, error_len, "not numeric value in macro \"%.*s\"", - (int)(token->loc.r - token->loc.l + 1), *data + token->loc.l); - return FAIL; - } - } - else if (0 != (flags & ZBX_TOKEN_JSON)) + if (0 != (flags & ZBX_TOKEN_JSON)) { zbx_json_escape(&replace_to); } @@ -6024,36 +5555,6 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c zbx_free(replace_to); replace_to = replace_to_esc; } - else if (0 != (flags & ZBX_TOKEN_TRIGGER)) - { - char *tmp; - size_t sz; - - sz = zbx_get_escape_string_len(replace_to, "\"\\"); - - if (0 == cur_token_inside_quote && ZBX_INFINITY == evaluate_string_to_double(replace_to)) - { - /* autoquote */ - tmp = zbx_malloc(NULL, sz + 3); - tmp[0] = '\"'; - zbx_escape_string(tmp + 1, sz + 1, replace_to, "\"\\"); - tmp[sz + 1] = '\"'; - tmp[sz + 2] = '\0'; - zbx_free(replace_to); - replace_to = tmp; - } - else - { - if (sz != strlen(replace_to)) - { - tmp = zbx_malloc(NULL, sz + 1); - zbx_escape_string(tmp, sz + 1, replace_to, "\"\\"); - tmp[sz] = '\0'; - zbx_free(replace_to); - replace_to = tmp; - } - } - } if (NULL != replace_to) { @@ -6064,8 +5565,6 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c token->loc.r - token->loc.l + 1, replace_to, strlen(replace_to)); zbx_free(replace_to); } - - return SUCCEED; } /****************************************************************************** @@ -6111,74 +5610,246 @@ static void process_user_macro_token(char **data, zbx_token_t *token, const stru /****************************************************************************** * * - * Function: process_expression_macro_token * + * Function: substitute_query_filter_lld_macros * * * - * Purpose: expand discovery macro in expression macro * + * Purpose: substitute lld macros in calculated item query filter * * * - * Parameters: data - [IN/OUT] the expression containing lld macro * - * token - [IN/OUT] the token with user macro location data * - * jp_row - [IN] discovery data * - * lld_macro_paths - [IN] discovery data * - * error - [OUT] error message * - * max_error_len - [IN] the size of error buffer * + * Parameters: filter - [IN/OUT] the filter * + * jp_row - [IN] the lld data row * + * lld_macro_paths - [IN] use json path to extract from jp_row * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - the macros were expanded successfully. * + * FAIL - otherwise. * * * ******************************************************************************/ -static int process_expression_macro_token(char **data, zbx_token_t *token, - const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, char *error, - size_t error_len) +static int substitute_query_filter_lld_macros(char **filter, const struct zbx_json_parse *jp_row, + const zbx_vector_ptr_t *lld_macro_paths, char **error) { - zbx_token_t cur_token, tmp_token; - int pos, quoted = 0, last_pos; - size_t i; + char *errmsg = NULL, err[128], *new_filter = NULL; + int i, ret = FAIL; + zbx_eval_context_t ctx; - last_pos = pos = token->data.expression_macro.expression.l; + if (SUCCEED != zbx_eval_parse_expression(&ctx, *filter, + ZBX_EVAL_PARSE_QUERY_EXPRESSION | ZBX_EVAL_COMPOSE_QUOTE | ZBX_EVAL_PARSE_LLDMACRO, &errmsg)) + { + *error = zbx_dsprintf(NULL, "cannot parse item query filter: %s", errmsg); + zbx_free(errmsg); + goto out; + } - while (SUCCEED == zbx_token_find(*data, pos, &cur_token, ZBX_TOKEN_SEARCH_BASIC) && - cur_token.loc.l < token->loc.r) + for (i = 0; i < ctx.stack.values_num; i++) { - for (i = last_pos + 1; i < cur_token.loc.l; i++) + zbx_eval_token_t *token = &ctx.stack.values[i]; + char *value; + + switch (token->type) { - switch ((*data)[i]) - { - case '\\': - if (1 == quoted) - i++; - break; - case '"': - quoted = !quoted; - break; - } + case ZBX_EVAL_TOKEN_VAR_LLDMACRO: + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + case ZBX_EVAL_TOKEN_VAR_STR: + value = zbx_substr_unquote(ctx.expression, token->loc.l, token->loc.r); + + if (FAIL == substitute_lld_macros(&value, jp_row, lld_macro_paths, ZBX_MACRO_ANY, err, + sizeof(err))) + { + *error = zbx_strdup(NULL, err); + zbx_free(value); + + goto clean; + } + break; + default: + continue; } - tmp_token = cur_token; + zbx_variant_set_str(&token->value, value); + } - switch (cur_token.type) + zbx_eval_compose_expression(&ctx, &new_filter); + zbx_free(*filter); + *filter = new_filter; + + ret = SUCCEED; +clean: + zbx_eval_clear(&ctx); +out: + return ret; +} + +/****************************************************************************** + * * + * Function: substitute_item_query_macros * + * * + * Purpose: substitute lld macros in history function item query argument * + * /host/key?[filter] * + * * + * Parameters: ctx - [IN] the calculated item formula * + * token - [IN] the item query token * + * jp_row - [IN] the lld data row * + * lld_macro_paths - [IN] use json path to extract from jp_row * + * itemquery - [OUT] the item query with expanded macros * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - the macros were expanded successfully. * + * FAIL - otherwise. * + * * + ******************************************************************************/ +static int substitute_item_query_lld_macros(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, + const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, char **itemquery, + char **error) +{ + zbx_item_query_t query; + char err[128]; + int ret = FAIL; + size_t itemquery_alloc = 0, itemquery_offset = 0; + + if (0 == zbx_eval_parse_query(ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1, &query)) + { + *error = zbx_strdup(NULL, "invalid item reference"); + return FAIL; + } + + if (SUCCEED != substitute_key_macros(&query.key, NULL, NULL, jp_row, lld_macro_paths, MACRO_TYPE_ITEM_KEY, + err, sizeof(err))) + { + *error = zbx_strdup(NULL, err); + goto out; + } + + if (NULL != query.filter && SUCCEED != substitute_query_filter_lld_macros(&query.filter, jp_row, + lld_macro_paths, error)) + { + goto out; + } + + zbx_snprintf_alloc(itemquery, &itemquery_alloc, &itemquery_offset, "/%s/%s", ZBX_NULL2EMPTY_STR(query.host), + query.key); + if (NULL != query.filter) + zbx_snprintf_alloc(itemquery, &itemquery_alloc, &itemquery_offset, "?[%s]", query.filter); + + ret = SUCCEED; +out: + zbx_eval_clear_query(&query); + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_substitute_expression_macros * + * * + * Purpose: substitutes lld macros in an expression * + * * + * Parameters: data - [IN/OUT] the expression * + * jp_row - [IN] the lld data row * + * lld_macro_paths - [IN] use json path to extract from jp_row * + * error - [IN] pointer to string for reporting errors * + * max_error_len - [IN] size of 'error' string * + * * + ******************************************************************************/ +int zbx_substitute_expression_lld_macros(char **data, zbx_uint64_t rules, const struct zbx_json_parse *jp_row, + const zbx_vector_ptr_t *lld_macro_paths, char **error) +{ + char *exp = NULL; + int i, ret = FAIL; + zbx_eval_context_t ctx; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:%s", __func__, *data); + + if (SUCCEED != zbx_eval_parse_expression(&ctx, *data, rules, error)) + goto out; + + for (i = 0; i < ctx.stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx.stack.values[i]; + char *value = NULL, err[128]; + + switch(token->type) { - case ZBX_TOKEN_LLD_MACRO: - case ZBX_TOKEN_LLD_FUNC_MACRO: - if (FAIL == process_lld_macro_token(data, &cur_token, ZBX_TOKEN_STRING, jp_row, - lld_macro_paths, error, error_len, quoted)) + case ZBX_EVAL_TOKEN_ARG_QUERY: + if (FAIL == substitute_item_query_lld_macros(&ctx, token, jp_row, lld_macro_paths, + &value, error)) { - return FAIL; + goto clean; } - token->loc.r += cur_token.loc.r - tmp_token.loc.r; - pos = cur_token.loc.r; - break; - case ZBX_TOKEN_USER_MACRO: - process_user_macro_token(data, &cur_token, jp_row, lld_macro_paths); - token->loc.r += cur_token.loc.r - tmp_token.loc.r; - pos = cur_token.loc.r; break; - case ZBX_TOKEN_SIMPLE_MACRO: - process_simple_macro_token(data, &cur_token, jp_row, lld_macro_paths, error, error_len); - token->loc.r += cur_token.loc.r - tmp_token.loc.r; - pos = cur_token.loc.r; + case ZBX_EVAL_TOKEN_VAR_LLDMACRO: + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + case ZBX_EVAL_TOKEN_VAR_STR: + case ZBX_EVAL_TOKEN_VAR_NUM: + case ZBX_EVAL_TOKEN_ARG_PERIOD: + value = zbx_substr_unquote(ctx.expression, token->loc.l, token->loc.r); + + if (FAIL == substitute_lld_macros(&value, jp_row, lld_macro_paths, ZBX_MACRO_ANY, err, + sizeof(err))) + { + *error = zbx_strdup(NULL, err); + zbx_free(value); + goto clean; + } break; + default: + continue; } - last_pos = ++pos; + zbx_variant_clear(&token->value); + zbx_variant_set_str(&token->value, value); + } + + zbx_eval_compose_expression(&ctx, &exp); + + zbx_free(*data); + *data = exp; + exp = NULL; + + ret = SUCCEED; +clean: + zbx_free(exp); + zbx_eval_clear(&ctx); +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() expression:%s", __func__, *data); + + return ret; +} + +/****************************************************************************** + * * + * Function: process_expression_macro_token * + * * + * Purpose: expand discovery macro in expression macro * + * * + * Parameters: data - [IN/OUT] the expression containing macro * + * token - [IN/OUT] the macro token * + * jp_row - [IN] discovery data * + * lld_macro_paths - [IN] discovery data * + * error - [OUT] error message * + * max_error_len - [IN] the size of error buffer * + * * + ******************************************************************************/ +static int process_expression_macro_token(char **data, zbx_token_t *token, const struct zbx_json_parse *jp_row, + const zbx_vector_ptr_t *lld_macro_paths, char *error, size_t error_len) +{ + char *errmsg = NULL, *expression; + size_t right = token->data.expression_macro.expression.r; + + expression = zbx_substr(*data, token->data.expression_macro.expression.l, + token->data.expression_macro.expression.r); + + if (FAIL == zbx_substitute_expression_lld_macros(&expression, ZBX_EVAL_EXPRESSION_MACRO_LLD, jp_row, + lld_macro_paths, &errmsg)) + { + zbx_free(expression); + zbx_strlcpy(error, errmsg, error_len); + zbx_free(errmsg); + + return FAIL; } + zbx_replace_string(data, token->data.expression_macro.expression.l, &right, expression); + token->loc.r += right - token->data.expression_macro.expression.r; + zbx_free(expression); + return SUCCEED; } @@ -6201,19 +5872,32 @@ static int process_expression_macro_token(char **data, zbx_token_t *token, static int substitute_func_macro(char **data, zbx_token_t *token, const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, char *error, size_t max_error_len) { - int ret; - char *exp = NULL; - size_t exp_alloc = 0, exp_offset = 0; - size_t par_l = token->data.func_macro.func_param.l, par_r = token->data.func_macro.func_param.r; + int ret, offset = 0; + char *exp = NULL; + size_t exp_alloc = 0, exp_offset = 0, right; + size_t par_l = token->data.func_macro.func_param.l, par_r = token->data.func_macro.func_param.r; + zbx_token_t tok; + + if (SUCCEED == zbx_token_find(*data, (int)token->data.func_macro.macro.l, &tok, + ZBX_TOKEN_SEARCH_EXPRESSION_MACRO) && tok.loc.r <= token->data.func_macro.macro.r) + { + offset = (int)tok.loc.r; + + if (SUCCEED == process_expression_macro_token(data, &tok, jp_row, lld_macro_paths, error, + max_error_len)) + { + offset = tok.loc.r - offset; + } + } - ret = substitute_function_lld_param(*data + par_l + 1, par_r - (par_l + 1), 0, &exp, &exp_alloc, &exp_offset, - jp_row, lld_macro_paths, error, max_error_len); + ret = substitute_function_lld_param(*data + par_l + offset + 1, par_r - (par_l + 1), 0, &exp, &exp_alloc, + &exp_offset, jp_row, lld_macro_paths, error, max_error_len); if (SUCCEED == ret) { - /* copy what is left including closing parenthesis and replace function parameters */ - zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, *data + par_r, token->loc.r - (par_r - 1)); - zbx_replace_string(data, par_l + 1, &token->loc.r, exp); + right = par_r + offset - 1; + zbx_replace_string(data, par_l + offset + 1, &right, exp); + token->loc.r = right + 1; } zbx_free(exp); @@ -6282,8 +5966,8 @@ int substitute_lld_macros(char **data, const struct zbx_json_parse *jp_row, cons { case ZBX_TOKEN_LLD_MACRO: case ZBX_TOKEN_LLD_FUNC_MACRO: - ret = process_lld_macro_token(data, &token, flags, jp_row, lld_macro_paths, - error, max_error_len, cur_token_inside_quote); + process_lld_macro_token(data, &token, flags, jp_row, lld_macro_paths, + cur_token_inside_quote); pos = token.loc.r; break; case ZBX_TOKEN_USER_MACRO: @@ -6304,9 +5988,11 @@ int substitute_lld_macros(char **data, const struct zbx_json_parse *jp_row, cons } break; case ZBX_TOKEN_EXPRESSION_MACRO: - process_expression_macro_token(data, &token, jp_row, lld_macro_paths, error, - max_error_len); - pos = token.loc.r; + if (SUCCEED == process_expression_macro_token(data, &token, jp_row, + lld_macro_paths, error, max_error_len)) + { + pos = token.loc.r; + } break; } } @@ -6972,3 +6658,26 @@ int substitute_key_macros_unmasked(char **data, zbx_uint64_t *hostid, DC_ITEM *d zbx_dc_set_macro_env(old_macro_env); return ret; } + +/****************************************************************************** + * * + * Function: zbx_host_macro_index * + * * + * Purpose: extract index from valid indexed host macro * + * * + * Return value: The index or -1 if it was not valid indexed host macro * + * * + ******************************************************************************/ +int zbx_host_macro_index(const char *macro) +{ + zbx_strloc_t loc; + int func_num; + + loc.l = 0; + loc.r = strlen(macro) - 1; + + if (NULL != macro_in_list(macro, loc, simple_host_macros, &func_num)) + return func_num; + + return -1; +} diff --git a/src/libs/zbxserver/expression.h b/src/libs/zbxserver/expression.h new file mode 100644 index 00000000000..352366d560b --- /dev/null +++ b/src/libs/zbxserver/expression.h @@ -0,0 +1,42 @@ +/* +** Zabbix +** Copyright (C) 2001-2021 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. +**/ + +#ifndef ZABBIX_EXPRESSION_H +#define ZABBIX_EXPRESSION_H + +/* DBget_item_value() */ +#define ZBX_REQUEST_HOST_ID 101 +#define ZBX_REQUEST_HOST_HOST 102 +#define ZBX_REQUEST_HOST_NAME 103 +#define ZBX_REQUEST_HOST_DESCRIPTION 104 +#define ZBX_REQUEST_ITEM_ID 105 +#define ZBX_REQUEST_ITEM_NAME 106 +#define ZBX_REQUEST_ITEM_NAME_ORIG 107 +#define ZBX_REQUEST_ITEM_KEY 108 +#define ZBX_REQUEST_ITEM_KEY_ORIG 109 +#define ZBX_REQUEST_ITEM_DESCRIPTION 110 +#define ZBX_REQUEST_ITEM_DESCRIPTION_ORIG 111 +#define ZBX_REQUEST_PROXY_NAME 112 +#define ZBX_REQUEST_PROXY_DESCRIPTION 113 +#define ZBX_REQUEST_ITEM_VALUETYPE 114 + +int DBget_trigger_value(const DB_TRIGGER *trigger, char **replace_to, int N_functionid, int request); +int zbx_host_macro_index(const char *macro); + +#endif diff --git a/src/libs/zbxserver/expression_eval.c b/src/libs/zbxserver/expression_eval.c new file mode 100644 index 00000000000..f6181fefedd --- /dev/null +++ b/src/libs/zbxserver/expression_eval.c @@ -0,0 +1,1848 @@ +/* + ** Zabbix + ** Copyright (C) 2001-2021 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 "zbxserver.h" +#include "log.h" +#include "valuecache.h" +#include "evalfunc.h" +#include "zbxeval.h" +#include "expression.h" + +#define ZBX_ITEM_QUERY_UNSET 0x0000 + +#define ZBX_ITEM_QUERY_HOST_SELF 0x0001 +#define ZBX_ITEM_QUERY_HOST_ONE 0x0002 +#define ZBX_ITEM_QUERY_HOST_ANY 0x0004 + +#define ZBX_ITEM_QUERY_KEY_ONE 0x0010 +#define ZBX_ITEM_QUERY_KEY_SOME 0x0020 +#define ZBX_ITEM_QUERY_KEY_ANY 0x0040 +#define ZBX_ITEM_QUERY_FILTER 0x0100 + +#define ZBX_ITEM_QUERY_ERROR 0x8000 + +#define ZBX_ITEM_QUERY_MANY (ZBX_ITEM_QUERY_HOST_ANY |\ + ZBX_ITEM_QUERY_KEY_SOME | ZBX_ITEM_QUERY_KEY_ANY |\ + ZBX_ITEM_QUERY_FILTER) + +#define ZBX_ITEM_QUERY_ITEM_ANY (ZBX_ITEM_QUERY_HOST_ANY | ZBX_ITEM_QUERY_KEY_ANY) + +/* one item query data - index in hostkeys items */ +typedef struct +{ + int dcitem_hk_index; +} +zbx_expression_query_one_t; + +/* many item query data - matching itemids */ +typedef struct +{ + zbx_vector_uint64_t itemids; +} +zbx_expression_query_many_t; + +/* expression item query */ +typedef struct +{ + /* query flags, see see ZBX_ITEM_QUERY_* defines */ + zbx_uint32_t flags; + + /* the item query /host/key?[filter] */ + zbx_item_query_t ref; + + /* the query error */ + char *error; + + /* the expression item query data, zbx_expression_query_one_t or zbx_expression_query_many_t */ + void *data; +} +zbx_expression_query_t; + +/* group - hostids cache */ +typedef struct +{ + char *name; + zbx_vector_uint64_t hostids; +} +zbx_expression_group_t; + +/* item - tags cache */ +typedef struct +{ + zbx_uint64_t itemid; + zbx_vector_ptr_t tags; +} +zbx_expression_item_t; + +static void expression_query_free_one(zbx_expression_query_one_t *query) +{ + zbx_free(query); +} + +static void expression_query_free_many(zbx_expression_query_many_t *query) +{ + zbx_vector_uint64_destroy(&query->itemids); + zbx_free(query); +} + +static void expression_query_free(zbx_expression_query_t *query) +{ + zbx_eval_clear_query(&query->ref); + + if (ZBX_ITEM_QUERY_ERROR == query->flags) + zbx_free(query->error); + else if (0 != (query->flags & ZBX_ITEM_QUERY_MANY)) + expression_query_free_many((zbx_expression_query_many_t*) query->data); + else + expression_query_free_one((zbx_expression_query_one_t*) query->data); + + zbx_free(query); +} + +/****************************************************************************** + * * + * Function: test_key_param_wildcard_cb * + * * + * Purpose: check if key parameter is a wildcard '*' * + * * + ******************************************************************************/ +static int test_key_param_wildcard_cb(const char *data, int key_type, int level, int num, int quoted, + void *cb_data, char **param) +{ + ZBX_UNUSED(key_type); + ZBX_UNUSED(num); + ZBX_UNUSED(quoted); + ZBX_UNUSED(param); + + if (0 == level) + return SUCCEED; + + if ('*' == data[0] && '\0' == data[1]) + { + *(int *)cb_data = 1; + return FAIL; + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: expression_create_query * + * * + * Purpose: create expression item query from item query /host/key?[filter] * + * * + * Parameters: itemquery - [IN] the item query * + * * + * Return value: The created expression item query. * + * * + ******************************************************************************/ +static zbx_expression_query_t* expression_create_query(const char *itemquery) +{ + zbx_expression_query_t *query; + + query = (zbx_expression_query_t *)zbx_malloc(NULL, sizeof(zbx_expression_query_t)); + memset(query, 0, sizeof(zbx_expression_query_t)); + + query->flags = ZBX_ITEM_QUERY_UNSET; + + if (0 != zbx_eval_parse_query(itemquery, strlen(itemquery), &query->ref)) + { + if (NULL == query->ref.host) + query->flags |= ZBX_ITEM_QUERY_HOST_SELF; + else if ('*' == *query->ref.host) + query->flags |= ZBX_ITEM_QUERY_HOST_ANY; + else + query->flags |= ZBX_ITEM_QUERY_HOST_ONE; + + if (NULL != query->ref.filter) + query->flags |= ZBX_ITEM_QUERY_FILTER; + + if ('*' == *query->ref.key) + { + query->flags |= ZBX_ITEM_QUERY_KEY_ANY; + } + else if (NULL != strchr(query->ref.key, '*')) + { + int wildcard = 0; + + replace_key_params_dyn(&query->ref.key, ZBX_KEY_TYPE_ITEM, test_key_param_wildcard_cb, + &wildcard, NULL, 0); + + if (0 != wildcard) + query->flags |= ZBX_ITEM_QUERY_KEY_SOME; + else + query->flags |= ZBX_ITEM_QUERY_KEY_ONE; + } + else + query->flags |= ZBX_ITEM_QUERY_KEY_ONE; + } + + return query; +} + +/****************************************************************************** + * * + * Function: expression_group_free * + * * + ******************************************************************************/ +static void expression_group_free(zbx_expression_group_t *group) +{ + zbx_free(group->name); + zbx_vector_uint64_destroy(&group->hostids); + zbx_free(group); +} + +/****************************************************************************** + * * + * Function: expression_get_group * + * * + * Purpose: get group from cache by name * + * * + * Parameters: eval - [IN] the evaluation data * + * name - [IN] the group name * + * * + * Return value: The cached group. * + * * + * Comments: Cache group if necessary. * + * * + ******************************************************************************/ +static zbx_expression_group_t *expression_get_group(zbx_expression_eval_t *eval, const char *name) +{ + int i; + zbx_expression_group_t *group; + + for (i = 0; i < eval->groups.values_num; i++) + { + group = (zbx_expression_group_t *)eval->groups.values[i]; + + if (0 == strcmp(group->name, name)) + return group; + } + + group = (zbx_expression_group_t *)zbx_malloc(NULL, sizeof(zbx_expression_group_t)); + group->name = zbx_strdup(NULL, name); + zbx_vector_uint64_create(&group->hostids); + zbx_dc_get_hostids_by_group_name(name, &group->hostids); + zbx_vector_ptr_append(&eval->groups, group); + + return group; +} + +/****************************************************************************** + * * + * Function: expression_get_item * + * * + * Purpose: get item from cache by itemid * + * * + * Parameters: eval - [IN] the evaluation data * + * itemid - [IN] the item identifier * + * * + * Return value: The cached item. * + * * + * Comments: Cache item if necessary. * + * * + ******************************************************************************/ +static zbx_expression_item_t *expression_get_item(zbx_expression_eval_t *eval, zbx_uint64_t itemid) +{ + int i; + zbx_expression_item_t *item; + + for (i = 0; i < eval->itemtags.values_num; i++) + { + item = (zbx_expression_item_t *)eval->itemtags.values[i]; + + if (item->itemid == itemid) + return item; + } + + item = (zbx_expression_item_t *)zbx_malloc(NULL, sizeof(zbx_expression_group_t)); + item->itemid = itemid; + zbx_vector_ptr_create(&item->tags); + zbx_dc_get_item_tags(itemid, &item->tags); + zbx_vector_ptr_append(&eval->itemtags, item); + + return item; +} + +/****************************************************************************** + * * + * Function: expression_item_free * + * * + ******************************************************************************/ +static void expression_item_free(zbx_expression_item_t *item) +{ + zbx_vector_ptr_clear_ext(&item->tags, (zbx_clean_func_t) zbx_free_item_tag); + zbx_vector_ptr_destroy(&item->tags); + zbx_free(item); +} + +/****************************************************************************** + * * + * Function: expression_init_query_one * + * * + * Purpose: initialize one item query * + * * + * Parameters: eval - [IN] the evaluation data * + * query - [IN] the query to initialize * + * * + ******************************************************************************/ +static void expression_init_query_one(zbx_expression_eval_t *eval, zbx_expression_query_t *query) +{ + zbx_expression_query_one_t *data; + + data = (zbx_expression_query_one_t *)zbx_malloc(NULL, sizeof(zbx_expression_query_one_t)); + data->dcitem_hk_index = eval->one_num++; + query->data = data; +} + +/****************************************************************************** + * * + * Function: replace_key_param_wildcard_cb * + * * + * Purpose: replace wildcards '*'in key parameters with % and escape existing * + * %, \ characters for SQL like operation * + * * + ******************************************************************************/ +static int replace_key_param_wildcard_cb(const char *data, int key_type, int level, int num, int quoted, void *cb_data, + char **param) +{ + int ret; + char *tmp; + + ZBX_UNUSED(key_type); + ZBX_UNUSED(num); + ZBX_UNUSED(cb_data); + + if (0 == level) + return SUCCEED; + + if ('*' == data[0] && '\0' == data[1]) + { + *param = zbx_strdup(NULL, "%"); + return SUCCEED; + } + + if (NULL == strchr(data, '%') && NULL == strchr(data, '\\')) + return SUCCEED; + + tmp = zbx_strdup(NULL, data); + unquote_key_param(tmp); + *param = zbx_dyn_escape_string(tmp, "\\%%"); + zbx_free(tmp); + + /* escaping cannot result in unquotable parameter */ + if (FAIL == (ret = quote_key_param(param, quoted))) + { + THIS_SHOULD_NEVER_HAPPEN; + zbx_free(*param); + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: expression_match_item_key * + * * + * Purpose: check if item key matches the pattern * + * * + * Parameters: item_key - [IN] the item key to match * + * pattern - [IN] the pattern * + * * + ******************************************************************************/ +static int expression_match_item_key(const char *item_key, const AGENT_REQUEST *pattern) +{ + AGENT_REQUEST key; + int i, ret = FAIL; + + init_request(&key); + + if (SUCCEED != parse_item_key(item_key, &key)) + goto out; + + if (pattern->nparam != key.nparam) + goto out; + + if (0 != strcmp(pattern->key, key.key)) + goto out; + + for (i = 0; i < key.nparam; i++) + { + if (0 == strcmp(pattern->params[i], "*")) + continue; + + if (0 != strcmp(pattern->params[i], key.params[i])) + goto out; + } + + ret = SUCCEED; +out: + free_request(&key); + + return ret; +} + +typedef struct +{ + zbx_uint64_t itemid; + zbx_uint64_t hostid; + zbx_expression_eval_t *eval; +} +zbx_expression_eval_many_t; + +/****************************************************************************** + * * + * Function: expression_get_item_candidates * + * * + * Purpose: get itemids + hostids of items that might match query based on * + * host, key and filter groups * + * * + * Parameters: eval - [IN] the evaluation data * + * query - [IN] the expression item query * + * groups - [IN] the groups in filter template * + * filter_template - [IN] the group filter template with {index} * + * placeholders referring to a group in * + * groups vector * + * itemhosts - [out] itemid+hostid pairs matching query * + * * + ******************************************************************************/ +static void expression_get_item_candidates(zbx_expression_eval_t *eval, const zbx_expression_query_t *query, + const zbx_vector_str_t *groups, const char *filter_template, zbx_vector_uint64_pair_t *itemhosts) +{ + DB_RESULT result; + DB_ROW row; + char *sql = NULL, *esc, *clause = "where"; + size_t sql_alloc = 0, sql_offset = 0; + AGENT_REQUEST pattern; + + zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "select i.itemid,i.hostid"); + + if (0 != (query->flags & ZBX_ITEM_QUERY_KEY_SOME)) + { + init_request(&pattern); + parse_item_key(query->ref.key, &pattern); + + zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ",i.key_"); + } + + zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " from items i"); + + if (0 != (query->flags & ZBX_ITEM_QUERY_HOST_ONE)) + { + esc = DBdyn_escape_string(query->ref.host); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, ",hosts h" + " where h.hostid=i.hostid" + " and h.host='%s'", esc); + zbx_free(esc); + clause = "and"; + } + else if (0 != (query->flags & ZBX_ITEM_QUERY_HOST_SELF)) + { + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where i.hostid=" ZBX_FS_UI64, + eval->hostid); + clause = "and"; + } + + if (0 != (query->flags & ZBX_ITEM_QUERY_KEY_SOME)) + { + char *key; + + key = zbx_strdup(NULL, query->ref.key); + replace_key_params_dyn(&key, ZBX_KEY_TYPE_ITEM, replace_key_param_wildcard_cb, NULL, NULL, 0); + + esc = DBdyn_escape_string(key); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " %s i.key_ like '%s'", clause, esc); + zbx_free(esc); + zbx_free(key); + clause = "and"; + } + else if (0 != (query->flags & ZBX_ITEM_QUERY_KEY_ONE)) + { + esc = DBdyn_escape_string(query->ref.key); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " %s i.key_='%s'", clause, esc); + zbx_free(esc); + clause = "and"; + } + + if (0 != (query->flags & ZBX_ITEM_QUERY_FILTER) && NULL != filter_template && '\0' != *filter_template) + { + zbx_uint64_t index; + int pos = 0, last_pos = 0; + zbx_token_t token; + zbx_expression_group_t *group; + + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " %s ", clause); + + for (; SUCCEED == zbx_token_find(filter_template, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID); pos++) + { + if (ZBX_TOKEN_OBJECTID != token.type) + continue; + + if (SUCCEED != is_uint64_n(filter_template + token.loc.l + 1, token.loc.r - token.loc.l - 1, + &index) && (int)index < groups->values_num) + { + continue; + } + + group = expression_get_group(eval, groups->values[index]); + + zbx_strncpy_alloc(&sql, &sql_alloc, &sql_offset, filter_template + last_pos, + token.loc.l - last_pos); + + if (' ' == sql[sql_offset - 1]) + sql_offset--; + + if (0 < group->hostids.values_num) + { + DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.hostid", group->hostids.values, + group->hostids.values_num); + } + else + zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " 0"); + + last_pos = token.loc.r + 1; + pos = token.loc.r; + } + + if ('\0' != filter_template[last_pos]) + zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, filter_template + last_pos); + } + + result = DBselect("%s", sql); + + while (NULL != (row = DBfetch(result))) + { + zbx_uint64_pair_t pair; + + if (0 == (query->flags & ZBX_ITEM_QUERY_KEY_SOME) || + (NULL != pattern.key && SUCCEED == expression_match_item_key(row[2], &pattern))) + { + ZBX_STR2UINT64(pair.first, row[0]); + ZBX_STR2UINT64(pair.second, row[1]); + zbx_vector_uint64_pair_append(itemhosts, pair); + } + } + DBfree_result(result); + + if (0 != (query->flags & ZBX_ITEM_QUERY_KEY_SOME)) + free_request(&pattern); + + zbx_free(sql); +} + +/****************************************************************************** + * * + * Function: expression_item_check_tag * + * * + * Purpose: check if the item matches the tag * + * * + * Parameters: item - [IN] the item with tags * + * tag - [IN] the tag to match in format <tag name>[:<tag value>]* + * * + * Return value: SUCCEED - the item matches the specified tag * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int expression_item_check_tag(zbx_expression_item_t *item, const char *tag) +{ + int i; + size_t taglen; + const char *value; + + if (NULL != (value = strchr(tag, ':'))) + { + taglen = (value - tag); + value++; + } + else + taglen = strlen(tag); + + for (i = 0; i < item->tags.values_num; i++) + { + zbx_item_tag_t *itemtag = (zbx_item_tag_t *)item->tags.values[i]; + + if (taglen != strlen(itemtag->tag.tag) || 0 != memcmp(tag, itemtag->tag.tag, taglen)) + continue; + + if (NULL == value) + return SUCCEED; + + if (0 == strcmp(itemtag->tag.value, value)) + return SUCCEED; + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: expression_eval_filter * + * * + * Purpose: evaluate filter function * + * * + * Parameters: name - [IN] the function name (not zero terminated) * + * len - [IN] the function name length * + * args_num - [IN] the number of function arguments * + * args - [IN] an array of the function arguments. * + * data - [IN] the caller data used for function evaluation * + * ts - [IN] the function execution time * + * value - [OUT] the function return value * + * error - [OUT] the error message if function failed * + * * + * Return value: SUCCEED - the function was evaluated successfully * + * FAIL - otherwise * + * * + * Comments: The group/tag comparisons in filter are converted to function * + * calls that are evaluated by this callback. * + * * + ******************************************************************************/ +static int expression_eval_filter(const char *name, size_t len, int args_num, const zbx_variant_t *args, + void *data, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) +{ + zbx_expression_eval_many_t *many = (zbx_expression_eval_many_t *)data; + + ZBX_UNUSED(ts); + ZBX_UNUSED(len); + + if (1 != args_num) + { + *error = zbx_strdup(NULL, "invalid number of arguments"); + return FAIL; + } + + if (ZBX_VARIANT_STR != args[0].type) + { + *error = zbx_strdup(NULL, "invalid argument flags"); + return FAIL; + } + + if (0 == strncmp(name, "group", ZBX_CONST_STRLEN("group"))) + { + zbx_expression_group_t *group; + + group = expression_get_group(many->eval, args[0].data.str); + + if (FAIL != zbx_vector_uint64_bsearch(&group->hostids, many->hostid, ZBX_DEFAULT_UINT64_COMPARE_FUNC)) + zbx_variant_set_dbl(value, 1); + else + zbx_variant_set_dbl(value, 0); + + return SUCCEED; + } + else if (0 == strncmp(name, "tag", ZBX_CONST_STRLEN("tag"))) + { + zbx_expression_item_t *item; + + item = expression_get_item(many->eval, many->itemid); + + if (SUCCEED == expression_item_check_tag(item, args[0].data.str)) + zbx_variant_set_dbl(value, 1); + else + zbx_variant_set_dbl(value, 0); + + return SUCCEED; + } + else + { + *error = zbx_strdup(NULL, "unknown function"); + return FAIL; + } +} + +/****************************************************************************** + * * + * Function: expression_init_query_many * + * * + * Purpose: initialize many item query * + * * + * Parameters: eval - [IN] the evaluation data * + * query - [IN] the query to initialize * + * * + ******************************************************************************/ +static void expression_init_query_many(zbx_expression_eval_t *eval, zbx_expression_query_t *query) +{ + zbx_expression_query_many_t *data; + char *error = NULL, *errmsg = NULL, *filter_template = NULL; + int i, ret = FAIL; + zbx_eval_context_t ctx; + zbx_vector_uint64_pair_t itemhosts; + zbx_vector_str_t groups; + zbx_vector_uint64_t itemids; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() /%s/%s?[%s]", __func__, ZBX_NULL2EMPTY_STR(query->ref.host), + ZBX_NULL2EMPTY_STR(query->ref.key), ZBX_NULL2EMPTY_STR(query->ref.filter)); + + zbx_eval_init(&ctx); + + zbx_vector_uint64_create(&itemids); + zbx_vector_uint64_pair_create(&itemhosts); + zbx_vector_str_create(&groups); + + if (ZBX_ITEM_QUERY_ITEM_ANY == (query->flags & ZBX_ITEM_QUERY_ITEM_ANY)) + { + error = zbx_strdup(NULL, "item query must have at least a host or an item key defined"); + goto out; + } + + if (0 != (query->flags & ZBX_ITEM_QUERY_FILTER)) + { + if (SUCCEED != zbx_eval_parse_expression(&ctx, query->ref.filter, ZBX_EVAL_PARSE_QUERY_EXPRESSION, + &errmsg)) + { + error = zbx_dsprintf(NULL, "failed to parse item query filter: %s", errmsg); + zbx_free(errmsg); + goto out; + } + + zbx_eval_prepare_filter(&ctx); + + if (FAIL == zbx_eval_get_group_filter(&ctx, &groups, &filter_template, &errmsg)) + { + error = zbx_dsprintf(NULL, "failed to extract groups from item filter: %s", errmsg); + zbx_free(errmsg); + goto out; + } + } + + expression_get_item_candidates(eval, query, &groups, filter_template, &itemhosts); + + if (0 != (query->flags & ZBX_ITEM_QUERY_FILTER)) + { + zbx_expression_eval_many_t eval_data; + zbx_variant_t filter_value; + + eval_data.eval = eval; + + for (i = 0; i < itemhosts.values_num; i++) + { + eval_data.itemid = itemhosts.values[i].first; + eval_data.hostid = itemhosts.values[i].second; + + if (SUCCEED != zbx_eval_execute_ext(&ctx, NULL, expression_eval_filter, NULL, (void *)&eval_data, + &filter_value, &errmsg)) + { + zabbix_log(LOG_LEVEL_DEBUG, "failed to evaluate item query filter: %s", errmsg); + zbx_free(errmsg); + continue; + } + + if (SUCCEED != zbx_variant_convert(&filter_value, ZBX_VARIANT_DBL)) + { + zabbix_log(LOG_LEVEL_DEBUG, "unexpected item query filter evaluation result:" + " value:\"%s\" of type \"%s\"", zbx_variant_value_desc(&filter_value), + zbx_variant_type_desc(&filter_value)); + + zbx_variant_clear(&filter_value); + continue; + } + + if (SUCCEED != zbx_double_compare(filter_value.data.dbl, 0)) + zbx_vector_uint64_append(&itemids, eval_data.itemid); + } + } + else + { + for (i = 0; i < itemhosts.values_num; i++) + zbx_vector_uint64_append(&itemids, itemhosts.values[i].first); + } + + if (0 == itemids.values_num) + { + error = zbx_strdup(NULL, "no items matching query"); + goto out; + } + + if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG)) + { + for (i = 0; i < itemids.values_num; i++) + zabbix_log(LOG_LEVEL_DEBUG, "%s() itemid:" ZBX_FS_UI64, __func__, itemids.values[i]); + } + + data = (zbx_expression_query_many_t *)zbx_malloc(NULL, sizeof(zbx_expression_query_many_t)); + data->itemids = itemids; + query->data = data; + eval->many_num++; + + ret = SUCCEED; +out: + if (0 != (query->flags & ZBX_ITEM_QUERY_FILTER) && SUCCEED == zbx_eval_status(&ctx)) + zbx_eval_clear(&ctx); + + if (SUCCEED != ret) + { + query->error = error; + query->flags = ZBX_ITEM_QUERY_ERROR; + zbx_vector_uint64_destroy(&itemids); + } + + zbx_free(filter_template); + + zbx_vector_uint64_pair_destroy(&itemhosts); + + zbx_vector_str_clear_ext(&groups, zbx_str_free); + zbx_vector_str_destroy(&groups); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() items:%d", __func__, (SUCCEED == ret ? data->itemids.values_num : -1)); +} + +/****************************************************************************** + * * + * Function: expression_cache_dcitems_hk * + * * + * Purpose: cache items used in one item queries * + * * + * Parameters: eval - [IN] the evaluation data * + * * + ******************************************************************************/ +static void expression_cache_dcitems_hk(zbx_expression_eval_t *eval) +{ + int i; + + eval->hostkeys = (zbx_host_key_t *)zbx_malloc(NULL, sizeof(zbx_host_key_t) * eval->one_num); + eval->dcitems_hk = (DC_ITEM *)zbx_malloc(NULL, sizeof(DC_ITEM) * eval->one_num); + eval->errcodes_hk = (int *)zbx_malloc(NULL, sizeof(int) * eval->one_num); + + for (i = 0; i < eval->queries.values_num; i++) + { + zbx_expression_query_t *query = (zbx_expression_query_t *)eval->queries.values[i]; + zbx_expression_query_one_t *data; + + if (0 != (query->flags & ZBX_ITEM_QUERY_MANY) || ZBX_ITEM_QUERY_ERROR == query->flags) + continue; + + data = (zbx_expression_query_one_t *)query->data; + + eval->hostkeys[data->dcitem_hk_index].host = query->ref.host; + eval->hostkeys[data->dcitem_hk_index].key = query->ref.key; + } + + DCconfig_get_items_by_keys(eval->dcitems_hk, eval->hostkeys, eval->errcodes_hk, eval->one_num); +} + +/****************************************************************************** + * * + * Purpose: dcitem reference vector lookup functions * + * * + ******************************************************************************/ +static int compare_dcitems_by_itemid(const void *d1, const void *d2) +{ + DC_ITEM *dci1 = *(DC_ITEM **)d1; + DC_ITEM *dci2 = *(DC_ITEM **)d2; + + ZBX_RETURN_IF_NOT_EQUAL(dci1->itemid, dci2->itemid); + + return 0; +} + +static int expression_find_dcitem_by_itemid(const void *d1, const void *d2) +{ + zbx_uint64_t itemid = **(zbx_uint64_t **)d1; + DC_ITEM *dci = *(DC_ITEM **)d2; + + ZBX_RETURN_IF_NOT_EQUAL(itemid, dci->itemid); + + return 0; +} + +/****************************************************************************** + * * + * Function: expression_cache_dcitems * + * * + * Purpose: cache items used in many item queries * + * * + * Parameters: eval - [IN] the evaluation data * + * * + ******************************************************************************/ +static void expression_cache_dcitems(zbx_expression_eval_t *eval) +{ + int i, j; + zbx_vector_uint64_t itemids; + + zbx_vector_uint64_create(&itemids); + + if (0 != eval->one_num) + { + for (i = 0; i < eval->one_num; i++) + { + if (SUCCEED != eval->errcodes_hk[i]) + continue; + + zbx_vector_ptr_append(&eval->dcitem_refs, &eval->dcitems_hk[i]); + } + + zbx_vector_ptr_sort(&eval->dcitem_refs, compare_dcitems_by_itemid); + } + + for (i = 0; i < eval->queries.values_num; i++) + { + zbx_expression_query_t *query = (zbx_expression_query_t *)eval->queries.values[i]; + zbx_expression_query_many_t *data; + + if (0 == (query->flags & ZBX_ITEM_QUERY_MANY)) + continue; + + data = (zbx_expression_query_many_t *)query->data; + + for (j = 0; j < data->itemids.values_num; j++) + zbx_vector_uint64_append(&itemids, data->itemids.values[j]); + } + + zbx_vector_uint64_sort(&itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + zbx_vector_uint64_uniq(&itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + + for (i = 0; i < itemids.values_num;) + { + if (FAIL != zbx_vector_ptr_bsearch(&eval->dcitem_refs, &itemids.values[i], expression_find_dcitem_by_itemid)) + { + zbx_vector_uint64_remove(&itemids, i); + continue; + } + i++; + } + + if (0 != (eval->dcitems_num = itemids.values_num)) + { + zbx_vector_uint64_sort(&itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + + eval->dcitems = (DC_ITEM *)zbx_malloc(NULL, sizeof(DC_ITEM) * itemids.values_num); + eval->errcodes = (int *)zbx_malloc(NULL, sizeof(int) * itemids.values_num); + + DCconfig_get_items_by_itemids(eval->dcitems, itemids.values, eval->errcodes, itemids.values_num); + + for (i = 0; i < itemids.values_num; i++) + { + if (SUCCEED != eval->errcodes[i]) + continue; + + zbx_vector_ptr_append(&eval->dcitem_refs, &eval->dcitems[i]); + } + + zbx_vector_ptr_sort(&eval->dcitem_refs, compare_dcitems_by_itemid); + } + + zbx_vector_uint64_destroy(&itemids); +} + +/****************************************************************************** + * * + * Function: expression_eval_one * + * * + * Purpose: evaluate historical function for one item query * + * * + * Parameters: eval - [IN] the evaluation data * + * query - [IN] the item query * + * name - [IN] the function name (not zero terminated) * + * len - [IN] the function name length * + * args_num - [IN] the number of function arguments * + * args - [IN] an array of the function arguments. * + * data - [IN] the caller data used for function evaluation * + * ts - [IN] the function execution time * + * value - [OUT] the function return value * + * error - [OUT] the error message if function failed * + * * + * Return value: SUCCEED - the function was executed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int expression_eval_one(zbx_expression_eval_t *eval, zbx_expression_query_t *query, const char *name, + size_t len, int args_num, const zbx_variant_t *args, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) +{ + char func_name[MAX_STRING_LEN], *params = NULL; + size_t params_alloc = 0, params_offset = 0; + DC_ITEM *item; + int i, ret = FAIL; + zbx_expression_query_one_t *data; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() %.*s(/%s/%s?[%s],...)", __func__, (int )len, name, + ZBX_NULL2EMPTY_STR(query->ref.host), ZBX_NULL2EMPTY_STR(query->ref.key), + ZBX_NULL2EMPTY_STR(query->ref.filter)); + + data = (zbx_expression_query_one_t *)query->data; + + if (SUCCEED != eval->errcodes_hk[data->dcitem_hk_index]) + { + *error = zbx_dsprintf(NULL, "item \"/%s/%s\" does not exist", + eval->hostkeys[data->dcitem_hk_index].host, eval->hostkeys[data->dcitem_hk_index].key); + goto out; + } + + item = &eval->dcitems_hk[data->dcitem_hk_index]; + + /* do not evaluate if the item is disabled or belongs to a disabled host */ + + if (ITEM_STATUS_ACTIVE != item->status) + { + *error = zbx_dsprintf(NULL, "item \"/%s/%s\" is disabled", eval->hostkeys[data->dcitem_hk_index].host, + eval->hostkeys[data->dcitem_hk_index].key); + goto out; + } + + if (HOST_STATUS_MONITORED != item->host.status) + { + *error = zbx_dsprintf(NULL, "host \"%s\" is not monitored", eval->hostkeys[data->dcitem_hk_index].host); + goto out; + } + + memcpy(func_name, name, len); + func_name[len] = '\0'; + + /* If the item is NOTSUPPORTED then evaluation is allowed for: */ + /* - functions white-listed in evaluatable_for_notsupported(). */ + /* Their values can be evaluated to regular numbers even for */ + /* NOTSUPPORTED items. */ + /* - other functions. Result of evaluation is ZBX_UNKNOWN. */ + + if (ITEM_STATE_NOTSUPPORTED == item->state && FAIL == zbx_evaluatable_for_notsupported(func_name)) + { + /* compose and store 'unknown' message for future use */ + *error = zbx_dsprintf(NULL, "item \"/%s/%s\" is not supported", + eval->hostkeys[data->dcitem_hk_index].host, eval->hostkeys[data->dcitem_hk_index].key); + goto out; + } + + if (0 == args_num) + { + ret = evaluate_function2(value, item, func_name, "", ts, error); + goto out; + } + + for (i = 0; i < args_num; i++) + { + if (0 != i) + zbx_chrcpy_alloc(¶ms, ¶ms_alloc, ¶ms_offset, ','); + + switch (args[i].type) + { + case ZBX_VARIANT_DBL: + zbx_snprintf_alloc(¶ms, ¶ms_alloc, ¶ms_offset, ZBX_FS_DBL64, + args[i].data.dbl); + break; + case ZBX_VARIANT_STR: + zbx_strquote_alloc(¶ms, ¶ms_alloc, ¶ms_offset, args[i].data.str); + break; + case ZBX_VARIANT_UI64: + zbx_snprintf_alloc(¶ms, ¶ms_alloc, ¶ms_offset, ZBX_FS_UI64, + args[i].data.ui64); + break; + case ZBX_VARIANT_NONE: + break; + default: + *error = zbx_dsprintf(NULL, " unsupported argument #%d type \"%s\"", i + 1, + zbx_variant_type_desc(&args[i])); + goto out; + } + } + + ret = evaluate_function2(value, item, func_name, ZBX_NULL2EMPTY_STR(params), ts, error); +out: + zbx_free(params); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s value:%s flags:%s", __func__, zbx_result_string(ret), + zbx_variant_value_desc(value), zbx_variant_type_desc(value)); + + return ret; +} + +#define ZBX_VALUE_FUNC_UNKNOWN 0 +#define ZBX_VALUE_FUNC_MIN 1 +#define ZBX_VALUE_FUNC_AVG 2 +#define ZBX_VALUE_FUNC_MAX 3 +#define ZBX_VALUE_FUNC_SUM 4 +#define ZBX_VALUE_FUNC_COUNT 5 +#define ZBX_VALUE_FUNC_LAST 6 + + +#define MATCH_STRING(x, name, len) ZBX_CONST_STRLEN(x) == len && 0 == memcmp(name, x, len) + +static int get_function_by_name(const char *name, size_t len) +{ + + if (MATCH_STRING("avg_foreach", name, len)) + return ZBX_VALUE_FUNC_AVG; + + if (MATCH_STRING("count_foreach", name, len)) + return ZBX_VALUE_FUNC_COUNT; + + if (MATCH_STRING("last_foreach", name, len)) + return ZBX_VALUE_FUNC_LAST; + + if (MATCH_STRING("max_foreach", name, len)) + return ZBX_VALUE_FUNC_MAX; + + if (MATCH_STRING("min_foreach", name, len)) + return ZBX_VALUE_FUNC_MIN; + + if (MATCH_STRING("sum_foreach", name, len)) + return ZBX_VALUE_FUNC_SUM; + + return ZBX_VALUE_FUNC_UNKNOWN; +} + +/****************************************************************************** + * * + * Function: evaluate_history_func_min * + * * + * Purpose: calculate minimum value from the history value vector * + * * + * Parameters: values - [IN] a vector containing history values * + * value_type - [IN] the type of values. Only float/uint64 * + * values are supported. * + * result - [OUT] the resulting value * + * * + ******************************************************************************/ +static void evaluate_history_func_min(zbx_vector_history_record_t *values, int value_type, double *result) +{ + int i; + + if (ITEM_VALUE_TYPE_UINT64 == value_type) + { + *result = (double)values->values[0].value.ui64; + + for (i = 1; i < values->values_num; i++) + if ((double)values->values[i].value.ui64 < *result) + *result = (double)values->values[i].value.ui64; + } + else + { + *result = values->values[0].value.dbl; + + for (i = 1; i < values->values_num; i++) + if (values->values[i].value.dbl < *result) + *result = values->values[i].value.dbl; + } +} + +/****************************************************************************** + * * + * Function: evaluate_history_func_max * + * * + * Purpose: calculate maximum value from the history value vector * + * * + * Parameters: values - [IN] a vector containing history values * + * value_type - [IN] the type of values. Only float/uint64 * + * values are supported. * + * result - [OUT] the resulting value * + * * + ******************************************************************************/ +static void evaluate_history_func_max(zbx_vector_history_record_t *values, int value_type, double *result) +{ + int i; + + if (ITEM_VALUE_TYPE_UINT64 == value_type) + { + *result = (double)values->values[0].value.ui64; + + for (i = 1; i < values->values_num; i++) + if ((double)values->values[i].value.ui64 > *result) + *result = (double)values->values[i].value.ui64; + } + else + { + *result = values->values[0].value.dbl; + + for (i = 1; i < values->values_num; i++) + if (values->values[i].value.dbl > *result) + *result = values->values[i].value.dbl; + } +} + +/****************************************************************************** + * * + * Function: evaluate_history_func_sum * + * * + * Purpose: calculate sum of values from the history value vector * + * * + * Parameters: values - [IN] a vector containing history values * + * value_type - [IN] the type of values. Only float/uint64 * + * values are supported. * + * result - [OUT] the resulting value * + * * + ******************************************************************************/ +static void evaluate_history_func_sum(zbx_vector_history_record_t *values, int value_type, double *result) +{ + int i; + + *result = 0; + + if (ITEM_VALUE_TYPE_UINT64 == value_type) + { + for (i = 0; i < values->values_num; i++) + *result += (double)values->values[i].value.ui64; + } + else + { + for (i = 0; i < values->values_num; i++) + *result += values->values[i].value.dbl; + } +} + +/****************************************************************************** + * * + * Function: evaluate_history_func_avg * + * * + * Purpose: calculate average value of values from the history value vector * + * * + * Parameters: values - [IN] a vector containing history values * + * value_type - [IN] the type of values. Only float/uint64 * + * values are supported. * + * result - [OUT] the resulting value * + * * + ******************************************************************************/ +static void evaluate_history_func_avg(zbx_vector_history_record_t *values, int value_type, double *result) +{ + evaluate_history_func_sum(values, value_type, result); + *result /= values->values_num; +} + +/****************************************************************************** + * * + * Function: evaluate_history_func_count * + * * + * Purpose: calculate number of values in value vector * + * * + * Parameters: values - [IN] a vector containing history values * + * value_type - [IN] the type of values. Only float/uint64 * + * values are supported. * + * result - [OUT] the resulting value * + * * + ******************************************************************************/ +static void evaluate_history_func_count(zbx_vector_history_record_t *values, double *result) +{ + *result = (double)values->values_num; +} + +/****************************************************************************** + * * + * Function: evaluate_history_func_last * + * * + * Purpose: calculate the last (newest) value in value vector * + * * + * Parameters: values - [IN] a vector containing history values * + * result - [OUT] the resulting value * + * * + ******************************************************************************/ +static void evaluate_history_func_last(zbx_vector_history_record_t *values, int value_type, double *result) +{ + if (ITEM_VALUE_TYPE_UINT64 == value_type) + *result = (double)values->values[0].value.ui64; + else + *result = values->values[0].value.dbl; +} + +/****************************************************************************** + * * + * Function: evaluate_history_func * + * * + * Purpose: calculate function with values from value vector * + * * + * Parameters: values - [IN] a vector containing history values * + * value_type - [IN] the type of values. Only float/uint64 * + * values are supported. * + * func - [IN] the function to calculate. Only * + * ZBX_VALUE_FUNC_MIN, ZBX_VALUE_FUNC_AVG, * + * ZBX_VALUE_FUNC_MAX, ZBX_VALUE_FUNC_SUM, * + * ZBX_VALUE_FUNC_COUNT, ZBX_VALUE_FUNC_LAST * + * functions are supported. * + * result - [OUT] the resulting value * + * * + ******************************************************************************/ +static void evaluate_history_func(zbx_vector_history_record_t *values, int value_type, int func, + double *result) +{ + switch (func) + { + case ZBX_VALUE_FUNC_MIN: + evaluate_history_func_min(values, value_type, result); + break; + case ZBX_VALUE_FUNC_AVG: + evaluate_history_func_avg(values, value_type, result); + break; + case ZBX_VALUE_FUNC_MAX: + evaluate_history_func_max(values, value_type, result); + break; + case ZBX_VALUE_FUNC_SUM: + evaluate_history_func_sum(values, value_type, result); + break; + case ZBX_VALUE_FUNC_COUNT: + evaluate_history_func_count(values, result); + break; + case ZBX_VALUE_FUNC_LAST: + evaluate_history_func_last(values, value_type, result); + break; + } +} + +/****************************************************************************** + * * + * Function: get_dcitem * + * * + * Purpose: get item from cache by itemid * + * * + * Parameters: eval - [IN] the evaluation data * + * itemid - [IN] the item identifier * + * * + * Return value: The cached item. * + * * + ******************************************************************************/ +static DC_ITEM *get_dcitem(zbx_vector_ptr_t *dcitem_refs, zbx_uint64_t itemid) +{ + int index; + + if (FAIL == (index = zbx_vector_ptr_bsearch(dcitem_refs, &itemid, expression_find_dcitem_by_itemid))) + return NULL; + + return dcitem_refs->values[index]; +} + +/****************************************************************************** + * * + * Function: expression_eval_many * + * * + * Purpose: evaluate historical function for multiple items (aggregate checks)* + * * + * Parameters: eval - [IN] the evaluation data * + * query - [IN] the calculated item query * + * name - [IN] the function name (not zero terminated) * + * len - [IN] the function name length * + * args_num - [IN] the number of function arguments * + * args - [IN] an array of the function arguments. * + * data - [IN] the caller data used for function evaluation * + * ts - [IN] the function execution time * + * value - [OUT] the function return value * + * error - [OUT] the error message if function failed * + * * + * Return value: SUCCEED - the function was executed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int expression_eval_many(zbx_expression_eval_t *eval, zbx_expression_query_t *query, const char *name, + size_t len, int args_num, const zbx_variant_t *args, const zbx_timespec_t *ts, zbx_variant_t *value, + char **error) +{ + zbx_expression_query_many_t *data; + int ret = FAIL, item_func, count, seconds, i; + zbx_vector_history_record_t values; + zbx_vector_dbl_t *results; + double result; + zbx_variant_t arg; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() %.*s(/%s/%s?[%s],...)", __func__, (int)len, name, + ZBX_NULL2EMPTY_STR(query->ref.host), ZBX_NULL2EMPTY_STR(query->ref.key), + ZBX_NULL2EMPTY_STR(query->ref.filter)); + + ZBX_UNUSED(args_num); + + data = (zbx_expression_query_many_t *)query->data; + + if (ZBX_VALUE_FUNC_UNKNOWN == (item_func = get_function_by_name(name, len))) + { + *error = zbx_strdup(NULL, "unsupported function"); + goto out; + } + + if (ZBX_VALUE_FUNC_LAST == item_func) + { + count = 1; + seconds = 0; + } + else + { + if (1 != args_num) + { + *error = zbx_strdup(NULL, "invalid number of function parameters"); + goto out; + } + + if (ZBX_VARIANT_STR == args[0].type) + { + if (FAIL == is_time_suffix(args[0].data.str, &seconds, ZBX_LENGTH_UNLIMITED)) + { + *error = zbx_strdup(NULL, "invalid second parameter"); + goto out; + } + } + else + { + zbx_variant_copy(&arg, &args[0]); + + if (SUCCEED != zbx_variant_convert(&arg, ZBX_VARIANT_DBL)) + { + zbx_variant_clear(&arg); + *error = zbx_strdup(NULL, "invalid second parameter"); + goto out; + } + + seconds = arg.data.dbl; + zbx_variant_clear(&arg); + } + count = 0; + } + + results = (zbx_vector_dbl_t *)zbx_malloc(NULL, sizeof(zbx_vector_dbl_t)); + zbx_vector_dbl_create(results); + + for (i = 0; i < data->itemids.values_num; i++) + { + DC_ITEM *dcitem; + + if (NULL == (dcitem = get_dcitem(&eval->dcitem_refs, data->itemids.values[i]))) + continue; + + if (ITEM_STATUS_ACTIVE != dcitem->status) + continue; + + if (HOST_STATUS_MONITORED != dcitem->host.status) + continue; + + if (ITEM_VALUE_TYPE_FLOAT != dcitem->value_type && ITEM_VALUE_TYPE_UINT64 != dcitem->value_type) + continue; + + zbx_history_record_vector_create(&values); + + if (SUCCEED == zbx_vc_get_values(dcitem->itemid, dcitem->value_type, &values, seconds, count, ts) && + 0 < values.values_num) + { + evaluate_history_func(&values, dcitem->value_type, item_func, &result); + zbx_vector_dbl_append(results, result); + } + + zbx_history_record_vector_destroy(&values, dcitem->value_type); + } + + if (0 == results->values_num) + { + zbx_vector_dbl_destroy(results); + zbx_free(results); + + *error = zbx_strdup(NULL, "no data for query"); + goto out; + } + + zbx_variant_set_dbl_vector(value, results); + + ret = SUCCEED; +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s value:%s flags:%s", __func__, zbx_result_string(ret), + zbx_variant_value_desc(value), zbx_variant_type_desc(value)); + + return ret; +} + +/****************************************************************************** + * * + * Function: expression_eval_history * + * * + * Purpose: evaluate historical function * + * * + * Parameters: name - [IN] the function name (not zero terminated) * + * len - [IN] the function name length * + * args_num - [IN] the number of function arguments * + * args - [IN] an array of the function arguments. * + * data - [IN] the caller data used for function evaluation * + * ts - [IN] the function execution time * + * value - [OUT] the function return value * + * error - [OUT] the error message if function failed * + * * + * Return value: SUCCEED - the function was executed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int expression_eval_history(const char *name, size_t len, int args_num, const zbx_variant_t *args, + void *data, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) +{ + int ret = FAIL; + zbx_expression_eval_t *eval; + zbx_expression_query_t *query; + char *errmsg = NULL; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() function:%.*s", __func__, (int )len, name); + + zbx_variant_set_none(value); + + if (0 == args_num) + { + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid number of arguments"); + goto out; + } + + if (len >= MAX_STRING_LEN) + { + *error = zbx_strdup(NULL, "Cannot evaluate function: name too long"); + goto out; + } + + eval = (zbx_expression_eval_t *)data; + + /* the historical function item query argument is replaced with corresponding itemrefs index */ + query = (zbx_expression_query_t *)eval->queries.values[(int) args[0].data.ui64]; + + if (ZBX_ITEM_QUERY_ERROR == query->flags) + { + *error = zbx_dsprintf(NULL, "Cannot evaluate function: %s", query->error); + goto out; + } + + if (0 == (query->flags & ZBX_ITEM_QUERY_MANY)) + { + ret = expression_eval_one(eval, query, name, len, args_num - 1, args + 1, ts, value, &errmsg); + } + else if (ZBX_EXPRESSION_AGGREGATE == eval->mode) + { + ret = expression_eval_many(eval, query, name, len, args_num - 1, args + 1, ts, value, &errmsg); + } + else + { + errmsg = zbx_strdup(NULL, "aggregate queries are not supported"); + ret = FAIL; + } + + if (SUCCEED != ret) + { + *error = zbx_dsprintf(NULL, "Cannot evaluate function: %s", errmsg); + zbx_free(errmsg); + } +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s error:%s", __func__, zbx_result_string(ret), + ZBX_NULL2EMPTY_STR(*error)); + + return ret; +} + +/****************************************************************************** + * * + * Function: expression_eval_common * + * * + * Purpose: evaluate common function * + * * + * Parameters: name - [IN] the function name (not zero terminated) * + * len - [IN] the function name length * + * args_num - [IN] the number of function arguments * + * args - [IN] an array of the function arguments. * + * data - [IN] the caller data used for function evaluation * + * ts - [IN] the function execution time * + * value - [OUT] the function return value * + * error - [OUT] the error message if function failed * + * * + * Return value: SUCCEED - the function was executed successfully * + * FAIL - otherwise * + * * + * Comments: There are no custom common functions in expressions items, but * + * it's used to check for /host/key query quoting errors instead. * + * * + ******************************************************************************/ +static int expression_eval_common(const char *name, size_t len, int args_num, const zbx_variant_t *args, + void *data, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) +{ + ZBX_UNUSED(data); + ZBX_UNUSED(ts); + ZBX_UNUSED(value); + + if (SUCCEED != zbx_is_trigger_function(name, len)) + { + *error = zbx_strdup(NULL, "Cannot evaluate formula: unsupported function"); + return FAIL; + } + + if (0 == args_num) + { + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid number of arguments"); + return FAIL; + } + + if (ZBX_VARIANT_STR == args[0].type) + { + zbx_item_query_t query; + + if (0 != zbx_eval_parse_query(args[0].data.str, strlen(args[0].data.str), &query)) + { + zbx_eval_clear_query(&query); + *error = zbx_strdup(NULL, "Cannot evaluate function: quoted item query argument"); + return FAIL; + } + } + + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid first argument"); + return FAIL; +} + +/****************************************************************************** + * * + * Function: expression_eval_init * + * * + * Purpose: initialize expression evaluation data * + * * + * Parameters: eval - [IN] the evaluation data * + * mode - [IN] ZBX_EXPRESSION_NORMAL - support only single * + * item queries * + * ZBX_EXPRESSION_AGGREGATE - support aggregate * + * item queries * + * ctx - [IN] the parsed expression * + * * + ******************************************************************************/ +void zbx_expression_eval_init(zbx_expression_eval_t *eval, int mode, zbx_eval_context_t *ctx) +{ + int i; + zbx_expression_query_t *query; + zbx_vector_str_t filters; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_vector_str_create(&filters); + zbx_eval_extract_item_refs(ctx, &filters); + + zbx_vector_ptr_create(&eval->queries); + zbx_vector_ptr_create(&eval->groups); + zbx_vector_ptr_create(&eval->itemtags); + zbx_vector_ptr_create(&eval->dcitem_refs); + + eval->ctx = ctx; + eval->mode = mode; + eval->one_num = 0; + eval->many_num = 0; + eval->dcitems_num = 0; + eval->hostid = 0; + + for (i = 0; i < filters.values_num; i++) + { + query = expression_create_query(filters.values[i]); + zbx_vector_ptr_append(&eval->queries, query); + + if (ZBX_ITEM_QUERY_UNSET == query->flags) + { + query->error = zbx_strdup(NULL, "invalid item query filter"); + query->flags = ZBX_ITEM_QUERY_ERROR; + } + } + + zbx_vector_str_clear_ext(&filters, zbx_str_free); + zbx_vector_str_destroy(&filters); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); +} + +/****************************************************************************** + * * + * Function: expression_eval_clear * + * * + * Purpose: free resources allocated by expression evaluation data * + * * + * Parameters: eval - [IN] the evaluation data * + * * + ******************************************************************************/ +void zbx_expression_eval_clear(zbx_expression_eval_t *eval) +{ + if (0 != eval->one_num) + { + DCconfig_clean_items(eval->dcitems_hk, eval->errcodes_hk, eval->one_num); + zbx_free(eval->dcitems_hk); + zbx_free(eval->errcodes_hk); + zbx_free(eval->hostkeys); + } + + if (0 != eval->dcitems_num) + { + DCconfig_clean_items(eval->dcitems, eval->errcodes, eval->dcitems_num); + zbx_free(eval->dcitems); + zbx_free(eval->errcodes); + } + + zbx_vector_ptr_destroy(&eval->dcitem_refs); + + zbx_vector_ptr_clear_ext(&eval->itemtags, (zbx_clean_func_t) expression_item_free); + zbx_vector_ptr_destroy(&eval->itemtags); + + zbx_vector_ptr_clear_ext(&eval->groups, (zbx_clean_func_t) expression_group_free); + zbx_vector_ptr_destroy(&eval->groups); + + zbx_vector_ptr_clear_ext(&eval->queries, (zbx_clean_func_t) expression_query_free); + zbx_vector_ptr_destroy(&eval->queries); +} + +/****************************************************************************** + * * + * Function: zbx_expression_eval_resolve_item_hosts * + * * + * Purpose: resolve calculated item formula empty and macro host references * + * (// , {HOST.HOST}) to host names * + * * + * Parameters: eval - [IN] the evaluation data * + * item - [IN] the calculated item * + * * + ******************************************************************************/ +void zbx_expression_eval_resolve_item_hosts(zbx_expression_eval_t *eval, const DC_ITEM *item) +{ + int i; + + eval->hostid = item->host.hostid; + + for (i = 0; i < eval->queries.values_num; i++) + { + zbx_expression_query_t *query = (zbx_expression_query_t *)eval->queries.values[i]; + + if (0 != (ZBX_ITEM_QUERY_HOST_SELF & query->flags) || 0 == strcmp(query->ref.host, "{HOST.HOST}")) + query->ref.host = zbx_strdup(query->ref.host, item->host.host); + } +} + +typedef struct +{ + int num; + char *host; +} +zbx_host_index_t; + + +static int host_index_compare(const void *d1, const void *d2) +{ + const int *i1 = *(const int **)d1; + const int *i2 = *(const int **)d2; + + return *i1 - *i2; +} + +static void host_index_free(zbx_host_index_t *index) +{ + zbx_free(index->host); + zbx_free(index); +} + +/****************************************************************************** + * * + * Function: zbx_expression_eval_resolve_trigger_hosts * + * * + * Purpose: resolve expression macro empty and macro host references * + * (// , {HOST.HOST}, {HOST.HOST<N>}) to host names * + * * + * Parameters: eval - [IN] the evaluation data * + * trigger - [IN] the calculated item * + * * + ******************************************************************************/ +void zbx_expression_eval_resolve_trigger_hosts(zbx_expression_eval_t *eval, const DB_TRIGGER *trigger) +{ + int i, func_num, index; + zbx_vector_ptr_t hosts; + zbx_host_index_t *hi; + + zbx_vector_ptr_create(&hosts); + + for (i = 0; i < eval->queries.values_num; i++) + { + zbx_expression_query_t *query = (zbx_expression_query_t *)eval->queries.values[i]; + + if (0 != (ZBX_ITEM_QUERY_HOST_ONE & query->flags)) + func_num = zbx_host_macro_index(query->ref.host); + else if (0 != (ZBX_ITEM_QUERY_HOST_SELF & query->flags)) + func_num = 1; + else + func_num = -1; + + if (-1 == func_num) + continue; + + if (FAIL == (index = zbx_vector_ptr_search(&hosts, &func_num, host_index_compare))) + { + hi = (zbx_host_index_t *)zbx_malloc(NULL, sizeof(zbx_host_index_t)); + hi->num = func_num; + hi->host = NULL; + DBget_trigger_value(trigger, &hi->host, func_num, ZBX_REQUEST_HOST_HOST); + zbx_vector_ptr_append(&hosts, hi); + } + else + hi = (zbx_host_index_t *)hosts.values[index]; + + if (NULL != hi->host) + { + query->ref.host = zbx_strdup(query->ref.host, hi->host); + } + else + { + query->error = zbx_dsprintf(NULL, "invalid host \"%s\"", ZBX_NULL2EMPTY_STR(query->ref.host)); + query->flags = ZBX_ITEM_QUERY_ERROR; + } + } + + zbx_vector_ptr_clear_ext(&hosts, (zbx_clean_func_t)host_index_free); + zbx_vector_ptr_destroy(&hosts); +} + +/****************************************************************************** + * * + * Function: zbx_expression_eval_execute * + * * + * Purpose: execute expression containing history functions * + * * + * Parameters: eval - [IN] the evaluation data * + * ts - [IN] the calculated item * + * value - [OUT] the expression evaluation result * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - the expression was evaluated successfully. * + * FAIL - otherwise. * + * * + ******************************************************************************/ +int zbx_expression_eval_execute(zbx_expression_eval_t *eval, const zbx_timespec_t *ts, zbx_variant_t *value, + char **error) +{ + int i, ret; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG)) + { + char *expression = NULL; + + zbx_eval_compose_expression(eval->ctx, &expression); + zabbix_log(LOG_LEVEL_DEBUG, "%s() expression:'%s'", __func__, expression); + zbx_free(expression); + } + + for (i = 0; i < eval->queries.values_num; i++) + { + zbx_expression_query_t *query = (zbx_expression_query_t *)eval->queries.values[i]; + + if (ZBX_ITEM_QUERY_ERROR != query->flags) + { + if (0 != (query->flags & ZBX_ITEM_QUERY_MANY)) + expression_init_query_many(eval, query); + else + expression_init_query_one(eval, query); + } + } + + /* cache items for functions using one item queries */ + if (0 != eval->one_num) + expression_cache_dcitems_hk(eval); + + /* cache items for functions using many item queries */ + if (0 != eval->many_num) + expression_cache_dcitems(eval); + + zbx_variant_set_none(value); + + ret = zbx_eval_execute_ext(eval->ctx, ts, expression_eval_common, expression_eval_history, (void *)eval, value, + error); + + zbx_vc_flush_stats(); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s value:%s error:%s", __func__, zbx_result_string(ret), + zbx_variant_value_desc(value), ZBX_NULL2EMPTY_STR(*error)); + + return ret; +} diff --git a/src/libs/zbxtrends/cache.c b/src/libs/zbxtrends/cache.c index 797eb56dfc8..f44f37fad79 100644 --- a/src/libs/zbxtrends/cache.c +++ b/src/libs/zbxtrends/cache.c @@ -90,7 +90,7 @@ static void tfc_free_slot(zbx_tfc_slot_t *slot) cache->free_head = index; } -static zbx_tfc_slot_t *tfc_alloc_slot() +static zbx_tfc_slot_t *tfc_alloc_slot(void) { zbx_uint32_t index; @@ -289,7 +289,7 @@ static void tfc_free_data(zbx_tfc_data_t *data) * Purpose: ensure there is a free slot available * * * ******************************************************************************/ -static void tfc_reserve_slot() +static void tfc_reserve_slot(void) { if (UINT32_MAX == cache->free_head && cache->slots_num == cache->free_slot) { diff --git a/src/libs/zbxtrends/trends.c b/src/libs/zbxtrends/trends.c index 674dcd2d670..b0fdd9e26ec 100644 --- a/src/libs/zbxtrends/trends.c +++ b/src/libs/zbxtrends/trends.c @@ -90,93 +90,55 @@ static int trends_parse_base(const char *period_shift, zbx_time_unit_t *base, ch ******************************************************************************/ int zbx_trends_parse_base(const char *params, zbx_time_unit_t *base, char **error) { - char *period_shift; - int ret = FAIL; + const char *period_shift; + int ret; - if (NULL == (period_shift = zbx_function_get_param_dyn(params, 2))) + if (NULL == (period_shift = strchr(params, ':'))) { *error = zbx_strdup(*error, "missing period shift parameter"); return FAIL; } - ret = trends_parse_base(period_shift, base, error); - zbx_free(period_shift); + ret = trends_parse_base(period_shift + 1, base, error); return ret; } /****************************************************************************** * * - * Function: zbx_trends_parse_range * - * * - * Purpose: parse trend function period arguments into time range * - * * - * Parameters: from - [IN] the time the period shift is calculated * - * from * - * period - [IN] the history period * - * period_shift - [IN] the history period shift * - * start - [OUT] the period start time in seconds since * - * Epoch * - * end - [OUT] the period end time in seconds since * - * Epoch * - * error - [OUT] the error message if parsing failed * + * Function: trends_parse_timeshift * * * - * Return value: SUCCEED - period was parsed successfully * - * FAIL - invalid time period was specified * + * Purpose: parse timeshift * * * - * Comments: Daylight saving changes are applied when parsing ranges with * - * day+ used as period base (now/?). * + * Parameters: from - [IN] the start time * + * timeshift - [IN] the timeshift string * + * min_time_unit - [IN] minimum time unit that can be used * + * tm - [IN] the shifted time * + * error - [OUT] the error message if parsing failed * * * - * Example period_shift values: * - * now/d * - * now/d-1h * - * now/d+1h * - * now/d+1h/w * - * now/d/w/h+1h+2h * - * now-1d/h * + * Return value: SUCCEED - time shift was parsed successfully * + * FAIL - otherwise * * * ******************************************************************************/ -int zbx_trends_parse_range(time_t from, const char *period, const char *period_shift, int *start, int *end, +static int trends_parse_timeshift(time_t from, const char *timeshift, zbx_time_unit_t min_time_unit, struct tm *tm, char **error) { - int period_num, period_hours[ZBX_TIME_UNIT_COUNT] = {0, 1, 24, 24 * 7, 24 * 30, 24 * 365}; - zbx_time_unit_t period_unit; size_t len; - struct tm tm_end, tm_start; const char *p; - zabbix_log(LOG_LEVEL_DEBUG, "In %s() period:%s shift:%s", __func__, period, period_shift); + zabbix_log(LOG_LEVEL_DEBUG, "In %s() shift:%s", __func__, timeshift); - /* parse period */ - - if (SUCCEED != zbx_tm_parse_period(period, &len, &period_num, &period_unit, error)) - return FAIL; - - if ('\0' != period[len]) - { - *error = zbx_dsprintf(*error, "unexpected character[s] in period \"%s\"", period + len); - return FAIL; - } - - if (period_hours[period_unit] * period_num > 24 * 366) - { - *error = zbx_strdup(*error, "period is too large"); - return FAIL; - } - - /* parse period shift */ - - p = period_shift; + p = timeshift; if (0 != strncmp(p, "now", ZBX_CONST_STRLEN("now"))) { - *error = zbx_strdup(*error, "period shift must begin with \"now\""); + *error = zbx_strdup(*error, "time shift must begin with \"now\""); return FAIL; } p += ZBX_CONST_STRLEN("now"); - localtime_r(&from, &tm_end); + localtime_r(&from, tm); while ('\0' != *p) { @@ -190,14 +152,14 @@ int zbx_trends_parse_range(time_t from, const char *period, const char *period_s return FAIL; } - if (unit < period_unit) + if (unit < min_time_unit) { - *error = zbx_dsprintf(*error, "time units in period shift must be greater or equal" + *error = zbx_dsprintf(*error, "time units in time shift must be greater or equal" " to period time unit"); return FAIL; } - zbx_tm_round_down(&tm_end, unit); + zbx_tm_round_down(tm, unit); /* unit is single character */ p++; @@ -210,7 +172,7 @@ int zbx_trends_parse_range(time_t from, const char *period, const char *period_s if (FAIL == zbx_tm_parse_period(p, &len, &num, &unit, error)) return FAIL; - if (unit < period_unit) + if (unit < min_time_unit) { *error = zbx_dsprintf(*error, "time units in period shift must be greater or equal" " to period time unit"); @@ -218,9 +180,9 @@ int zbx_trends_parse_range(time_t from, const char *period, const char *period_s } if ('+' == op) - zbx_tm_add(&tm_end, num, unit); + zbx_tm_add(tm, num, unit); else - zbx_tm_sub(&tm_end, num, unit); + zbx_tm_sub(tm, num, unit); p += len; } @@ -231,6 +193,94 @@ int zbx_trends_parse_range(time_t from, const char *period, const char *period_s } } + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() %04d.%02d.%02d %02d:%02d:%02d", __func__, tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: zbx_parse_timeshift * + * * + * Purpose: parse timeshift * + * * + * Parameters: from - [IN] the start time * + * timeshift - [IN] the timeshift string * + * tm - [IN] the shifted time * + * error - [OUT] the error message if parsing failed * + * * + * Return value: SUCCEED - time shift was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_parse_timeshift(time_t from, const char *timeshift, struct tm *tm, char **error) +{ + return trends_parse_timeshift(from, timeshift, ZBX_TIME_UNIT_UNKNOWN, tm, error); +} + +/****************************************************************************** + * * + * Function: zbx_trends_parse_range * + * * + * Purpose: parse trend function period arguments into time range * + * * + * Parameters: from - [IN] the time the period shift is calculated * + * from * + * param - [IN] the history period parameter: * + * <period>:<period_shift> * + * start - [OUT] the period start time in seconds since * + * Epoch * + * end - [OUT] the period end time in seconds since * + * Epoch * + * error - [OUT] the error message if parsing failed * + * * + * Return value: SUCCEED - period was parsed successfully * + * FAIL - invalid time period was specified * + * * + * Comments: Daylight saving changes are applied when parsing ranges with * + * day+ used as period base (now/?). * + * * + * Example period_shift values: * + * now/d * + * now/d-1h * + * now/d+1h * + * now/d+1h/w * + * now/d/w/h+1h+2h * + * now-1d/h * + * * + ******************************************************************************/ +int zbx_trends_parse_range(time_t from, const char *param, int *start, int *end, char **error) +{ + int period_num, period_hours[ZBX_TIME_UNIT_COUNT] = {0, 0, 0, 1, 24, 24 * 7, 24 * 30, 24 * 365}; + zbx_time_unit_t period_unit; + size_t len; + struct tm tm_end, tm_start; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() param:%s", __func__, param); + + /* parse period */ + + if (SUCCEED != zbx_tm_parse_period(param, &len, &period_num, &period_unit, error)) + return FAIL; + + if ('\0' != param[len] && ':' != param[len]) + { + *error = zbx_dsprintf(*error, "unexpected character[s] in period \"%s\"", param + len); + return FAIL; + } + + if (period_hours[period_unit] * period_num > 24 * 366) + { + *error = zbx_strdup(*error, "period is too large"); + return FAIL; + } + + /* parse period shift */ + + if (SUCCEED != trends_parse_timeshift(from, param + len + 1, period_unit, &tm_end, error)) + return FAIL; + tm_start = tm_end; /* trends clock refers to the beginning of the hourly interval - subtract */ @@ -286,7 +336,7 @@ int zbx_trends_parse_nextcheck(time_t from, const char *period_shift, time_t *ne struct tm tm; zbx_time_unit_t base; - if (SUCCEED != trends_parse_base(period_shift, &base, error)) + if (SUCCEED != trends_parse_base(period_shift, &base, error) || ZBX_TIME_UNIT_HOUR > base) return FAIL; /* parse period shift */ diff --git a/src/zabbix_proxy/Makefile.am b/src/zabbix_proxy/Makefile.am index 53739114537..df10f2f7545 100644 --- a/src/zabbix_proxy/Makefile.am +++ b/src/zabbix_proxy/Makefile.am @@ -49,9 +49,10 @@ zabbix_proxy_LDADD = \ $(top_builddir)/src/libs/zbxsysinfo/simple/libsimplesysinfo.a \ $(top_builddir)/src/libs/zbxsysinfo/$(ARCH)/libspechostnamesysinfo.a \ $(top_builddir)/src/libs/zbxlog/libzbxlog.a \ + $(top_builddir)/src/libs/zbxdbcache/libzbxdbcache.a \ $(top_builddir)/src/libs/zbxserver/libzbxserver.a \ $(top_builddir)/src/libs/zbxserver/libzbxserver_proxy.a \ - $(top_builddir)/src/libs/zbxdbcache/libzbxdbcache.a \ + $(top_builddir)/src/libs/zbxeval/libzbxeval.a \ $(top_builddir)/src/libs/zbxhistory/libzbxhistory.a \ $(top_builddir)/src/libs/zbxmemory/libzbxmemory.a \ $(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \ diff --git a/src/zabbix_server/Makefile.am b/src/zabbix_server/Makefile.am index fdd79113ac5..834fe8dd2cd 100644 --- a/src/zabbix_server/Makefile.am +++ b/src/zabbix_server/Makefile.am @@ -77,8 +77,9 @@ zabbix_server_LDADD = \ $(top_builddir)/src/libs/zbxsysinfo/common/libcommonsysinfo.a \ $(top_builddir)/src/libs/zbxsysinfo/simple/libsimplesysinfo.a \ $(top_builddir)/src/libs/zbxlog/libzbxlog.a \ - $(top_builddir)/src/libs/zbxserver/libzbxserver.a \ $(top_builddir)/src/libs/zbxdbcache/libzbxdbcache.a \ + $(top_builddir)/src/libs/zbxserver/libzbxserver.a \ + $(top_builddir)/src/libs/zbxeval/libzbxeval.a \ $(top_builddir)/src/libs/zbxhistory/libzbxhistory.a \ $(top_builddir)/src/libs/zbxmemory/libzbxmemory.a \ $(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \ diff --git a/src/zabbix_server/alerter/alert_syncer.c b/src/zabbix_server/alerter/alert_syncer.c index 69e31deb58a..c985fb9436d 100644 --- a/src/zabbix_server/alerter/alert_syncer.c +++ b/src/zabbix_server/alerter/alert_syncer.c @@ -487,10 +487,12 @@ ZBX_PTR_VECTOR_IMPL(events_tags, zbx_event_tags_t*) static int zbx_event_tags_compare_func(const void *d1, const void *d2) { - const zbx_event_tags_t *event_tags_1 = (const zbx_event_tags_t *)d1; - const zbx_event_tags_t *event_tags_2 = (const zbx_event_tags_t *)d2; + const zbx_event_tags_t *event_tags_1 = *(const zbx_event_tags_t **)d1; + const zbx_event_tags_t *event_tags_2 = *(const zbx_event_tags_t **)d2; - return event_tags_1->eventid > event_tags_2->eventid; + ZBX_RETURN_IF_NOT_EQUAL(event_tags_1->eventid, event_tags_2->eventid); + + return 0; } static void event_tags_free(zbx_event_tags_t *event_tags) diff --git a/src/zabbix_server/escalator/escalator.c b/src/zabbix_server/escalator/escalator.c index b0e8a452caa..316709fb95c 100644 --- a/src/zabbix_server/escalator/escalator.c +++ b/src/zabbix_server/escalator/escalator.c @@ -1836,7 +1836,12 @@ static int check_escalation_trigger(zbx_uint64_t triggerid, unsigned char source zbx_vector_uint64_create(&functionids); zbx_vector_uint64_create(&itemids); - get_functionids(&functionids, trigger.expression_orig); + zbx_get_serialized_expression_functionids(trigger.expression, trigger.expression_bin, &functionids); + if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == trigger.recovery_mode) + { + zbx_get_serialized_expression_functionids(trigger.recovery_expression, trigger.recovery_expression_bin, + &functionids); + } functions = (DC_FUNCTION *)zbx_malloc(functions, sizeof(DC_FUNCTION) * functionids.values_num); errcodes = (int *)zbx_malloc(errcodes, sizeof(int) * functionids.values_num); diff --git a/src/zabbix_server/events.c b/src/zabbix_server/events.c index 27e2ea281f4..67e599fa174 100644 --- a/src/zabbix_server/events.c +++ b/src/zabbix_server/events.c @@ -155,12 +155,12 @@ static void process_item_tag(DB_EVENT* event, const zbx_item_tag_t *item_tag) validate_and_add_tag(event, t); } -static void get_item_tags_by_expression(const char *expression, zbx_vector_ptr_t *item_tags) +static void get_item_tags_by_expression(const DB_TRIGGER *trigger, zbx_vector_ptr_t *item_tags) { zbx_vector_uint64_t functionids; zbx_vector_uint64_create(&functionids); - get_functionids(&functionids, expression); + zbx_db_trigger_get_functionids(trigger, &functionids); zbx_dc_get_item_tags_by_functionids(functionids.values, functionids.values_num, item_tags); zbx_vector_uint64_destroy(&functionids); } @@ -240,6 +240,9 @@ DB_EVENT *zbx_add_event(unsigned char source, unsigned char object, zbx_uint64_t event->trigger.opdata = zbx_strdup(NULL, trigger_opdata); event->trigger.event_name = (NULL != event_name ? zbx_strdup(NULL, event_name) : NULL); event->name = zbx_strdup(NULL, (NULL != event_name ? event_name : trigger_description)); + event->trigger.cache = NULL; + event->trigger.url = NULL; + event->trigger.comments = NULL; substitute_simple_macros(NULL, event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &event->trigger.correlation_tag, MACRO_TYPE_TRIGGER_TAG, err, sizeof(err)); @@ -256,7 +259,7 @@ DB_EVENT *zbx_add_event(unsigned char source, unsigned char object, zbx_uint64_t } zbx_vector_ptr_create(&item_tags); - get_item_tags_by_expression(trigger_expression, &item_tags); + get_item_tags_by_expression(&event->trigger, &item_tags); for (i = 0; i < item_tags.values_num; i++) { @@ -277,20 +280,15 @@ DB_EVENT *zbx_add_event(unsigned char source, unsigned char object, zbx_uint64_t switch (object) { case EVENT_OBJECT_TRIGGER: - if (NULL != trigger_tags) - { - event->trigger.expression = zbx_strdup(NULL, trigger_expression); - event->trigger.recovery_expression = zbx_strdup(NULL, - trigger_recovery_expression); + memset(&event->trigger, 0, sizeof(DB_TRIGGER)); - for (i = 0; i < trigger_tags->values_num; i++) - process_trigger_tag(event, (const zbx_tag_t *)trigger_tags->values[i]); + event->trigger.expression = zbx_strdup(NULL, trigger_expression); + event->trigger.recovery_expression = zbx_strdup(NULL, trigger_recovery_expression); - zbx_free(event->trigger.expression); - zbx_free(event->trigger.recovery_expression); - } + for (i = 0; i < trigger_tags->values_num; i++) + process_trigger_tag(event, (const zbx_tag_t *)trigger_tags->values[i]); - get_item_tags_by_expression(trigger_expression, &item_tags); + get_item_tags_by_expression(&event->trigger, &item_tags); break; case EVENT_OBJECT_ITEM: zbx_dc_get_item_tags(objectid, &item_tags); @@ -1516,7 +1514,7 @@ static void flush_correlation_queue(zbx_vector_ptr_t *trigger_diff, zbx_vector_u close_trigger_event(recovery->eventid, recovery->objectid, &recovery->ts, 0, recovery->correlationid, recovery->c_eventid, trigger->description, - trigger->expression_orig, trigger->recovery_expression_orig, + trigger->expression, trigger->recovery_expression, trigger->priority, trigger->type, trigger->opdata, trigger->event_name); closed_num++; @@ -1734,19 +1732,16 @@ static void zbx_clean_event(DB_EVENT *event) { zbx_free(event->name); - switch (event->source) + if (EVENT_OBJECT_TRIGGER == event->object) + { + zbx_db_trigger_clean(&event->trigger); + zbx_free(event->trigger.correlation_tag); + } + + if (EVENT_SOURCE_TRIGGERS == event->source || EVENT_SOURCE_INTERNAL == event->source) { - case EVENT_SOURCE_TRIGGERS: - zbx_free(event->trigger.description); - zbx_free(event->trigger.expression); - zbx_free(event->trigger.recovery_expression); - zbx_free(event->trigger.correlation_tag); - zbx_free(event->trigger.opdata); - zbx_free(event->trigger.event_name); - ZBX_FALLTHROUGH; - case EVENT_SOURCE_INTERNAL: - zbx_vector_ptr_clear_ext(&event->tags, (zbx_clean_func_t)zbx_free_tag); - zbx_vector_ptr_destroy(&event->tags); + zbx_vector_ptr_clear_ext(&event->tags, (zbx_clean_func_t)zbx_free_tag); + zbx_vector_ptr_destroy(&event->tags); } zbx_free(event); @@ -1768,18 +1763,18 @@ void zbx_clean_events(void) /****************************************************************************** * * - * Function: get_hosts_by_expression * + * Function: db_trigger_get_hosts * * * - * Purpose: get hosts that are used in expression * + * Purpose: get hosts that are associated with trigger expression/recovery * + * expression * * * ******************************************************************************/ -static void get_hosts_by_expression(zbx_hashset_t *hosts, const char *expression, const char *recovery_expression) +static void db_trigger_get_hosts(zbx_hashset_t *hosts, DB_TRIGGER *trigger) { zbx_vector_uint64_t functionids; zbx_vector_uint64_create(&functionids); - get_functionids(&functionids, expression); - get_functionids(&functionids, recovery_expression); + zbx_db_trigger_get_all_functionids(trigger, &functionids); DCget_hosts_by_functionids(&functionids, hosts); zbx_vector_uint64_destroy(&functionids); } @@ -1834,9 +1829,9 @@ void zbx_export_events(void) zbx_json_addint64(&json, ZBX_PROTO_TAG_VALUE, event->value); zbx_json_adduint64(&json, ZBX_PROTO_TAG_EVENTID, event->eventid); zbx_json_addstring(&json, ZBX_PROTO_TAG_NAME, event->name, ZBX_JSON_TYPE_STRING); + zbx_json_addint64(&json, ZBX_PROTO_TAG_SEVERITY, event->severity); - get_hosts_by_expression(&hosts, event->trigger.expression, - event->trigger.recovery_expression); + db_trigger_get_hosts(&hosts, &event->trigger); zbx_json_addarray(&json, ZBX_PROTO_TAG_HOSTS); @@ -1943,8 +1938,7 @@ static void add_event_suppress_data(zbx_vector_ptr_t *event_refs, zbx_vector_uin query->eventid = event->eventid; zbx_vector_uint64_create(&query->functionids); - get_functionids(&query->functionids, event->trigger.expression); - get_functionids(&query->functionids, event->trigger.recovery_expression); + zbx_db_trigger_get_all_functionids(&event->trigger, &query->functionids); zbx_vector_ptr_create(&query->tags); if (0 != event->tags.values_num) @@ -2855,7 +2849,7 @@ int zbx_close_problem(zbx_uint64_t triggerid, zbx_uint64_t eventid, zbx_uint64_t DBbegin(); r_event = close_trigger_event(eventid, triggerid, &ts, userid, 0, 0, trigger.description, - trigger.expression_orig, trigger.recovery_expression_orig, trigger.priority, + trigger.expression, trigger.recovery_expression, trigger.priority, trigger.type, trigger.opdata, trigger.event_name); r_event->eventid = DBget_maxid_num("events", 1); diff --git a/src/zabbix_server/lld/lld_host.c b/src/zabbix_server/lld/lld_host.c index 03db434c061..e2ccbdd3603 100644 --- a/src/zabbix_server/lld/lld_host.c +++ b/src/zabbix_server/lld/lld_host.c @@ -3188,7 +3188,7 @@ static void lld_templates_link(const zbx_vector_ptr_t *hosts, char **error) { int i; zbx_lld_host_t *host; - char *err; + char *err = NULL; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); diff --git a/src/zabbix_server/lld/lld_item.c b/src/zabbix_server/lld/lld_item.c index e5b579fb513..371a7715e85 100644 --- a/src/zabbix_server/lld/lld_item.c +++ b/src/zabbix_server/lld/lld_item.c @@ -24,6 +24,7 @@ #include "zbxserver.h" #include "zbxregexp.h" #include "zbxprometheus.h" +#include "zbxvariant.h" typedef struct { @@ -1910,69 +1911,15 @@ static void lld_items_validate(zbx_uint64_t hostid, zbx_vector_ptr_t *items, zbx * * ******************************************************************************/ static int substitute_formula_macros(char **data, const struct zbx_json_parse *jp_row, - const zbx_vector_ptr_t *lld_macro_paths, char *error, size_t max_error_len) + const zbx_vector_ptr_t *lld_macro_paths, char **error) { - char *exp, *tmp, *e; - size_t exp_alloc = 128, exp_offset = 0, tmp_alloc = 128, tmp_offset = 0, f_pos, par_l, par_r; - int ret = FAIL; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - - exp = (char *)zbx_malloc(NULL, exp_alloc); - tmp = (char *)zbx_malloc(NULL, tmp_alloc); - - for (e = *data; SUCCEED == zbx_function_find(e, &f_pos, &par_l, &par_r, error, max_error_len); e += par_r + 1) - { - /* substitute LLD macros in the part of the string preceding function parameters */ - - zbx_strncpy_alloc(&tmp, &tmp_alloc, &tmp_offset, e, par_l + 1); - if (SUCCEED != substitute_lld_macros(&tmp, jp_row, lld_macro_paths, ZBX_MACRO_NUMERIC, error, - max_error_len)) - { - goto out; - } - - tmp_offset = strlen(tmp); - zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, tmp, tmp_offset); - - tmp_alloc = tmp_offset + 1; - tmp_offset = 0; - - /* substitute LLD macros in function parameters */ - - if (SUCCEED != substitute_function_lld_param(e + par_l + 1, par_r - (par_l + 1), 1, - &exp, &exp_alloc, &exp_offset, jp_row, lld_macro_paths, error, max_error_len)) - { - goto out; - } - - zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, ")"); - } - - if (par_l > par_r) - goto out; + int ret; - /* substitute LLD macros in the remaining part */ - - zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset, e); - if (SUCCEED != substitute_lld_macros(&tmp, jp_row, lld_macro_paths, ZBX_MACRO_NUMERIC, error, max_error_len)) - goto out; - - zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, tmp); - - ret = SUCCEED; -out: - zbx_free(tmp); + zabbix_log(LOG_LEVEL_DEBUG, "In %s() formula:%s", __func__, *data); - if (SUCCEED == ret) - { - zbx_free(*data); - *data = exp; - } - else - zbx_free(exp); + ret = zbx_substitute_expression_lld_macros(data, ZBX_EVAL_CALC_EXPRESSION_LLD, jp_row, lld_macro_paths, error); - zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() formula:%s", __func__, *data); return ret; } @@ -2062,10 +2009,12 @@ static zbx_lld_item_t *lld_item_make(const zbx_lld_item_prototype_t *item_protot if (ITEM_TYPE_CALCULATED == item_prototype->type) { + char *errmsg = NULL; if (SUCCEED == ret && FAIL == (ret = substitute_formula_macros(&item->params, jp_row, lld_macro_paths, - err, sizeof(err)))) + &errmsg))) { - *error = zbx_strdcatf(*error, "Cannot create item, error in formula: %s.\n", err); + *error = zbx_strdcatf(*error, "Cannot create item, error in formula: %s.\n", errmsg); + zbx_free(errmsg); } } else @@ -2305,7 +2254,9 @@ static void lld_item_update(const zbx_lld_item_prototype_t *item_prototype, cons if (ITEM_TYPE_CALCULATED == item_prototype->type) { - if (SUCCEED == substitute_formula_macros(&buffer, jp_row, lld_macro_paths, err, sizeof(err))) + char *errmsg = NULL; + + if (SUCCEED == substitute_formula_macros(&buffer, jp_row, lld_macro_paths, &errmsg)) { zbx_lrtrim(buffer, ZBX_WHITESPACE); @@ -2318,7 +2269,10 @@ static void lld_item_update(const zbx_lld_item_prototype_t *item_prototype, cons } } else - *error = zbx_strdcatf(*error, "Cannot update item, error in formula: %s.\n", err); + { + *error = zbx_strdcatf(*error, "Cannot update item, error in formula: %s.\n", errmsg); + zbx_free(errmsg); + } } else { diff --git a/src/zabbix_server/lld/lld_trigger.c b/src/zabbix_server/lld/lld_trigger.c index bb4a7c0dfd7..cceacc50de3 100644 --- a/src/zabbix_server/lld/lld_trigger.c +++ b/src/zabbix_server/lld/lld_trigger.c @@ -27,13 +27,13 @@ typedef struct { zbx_uint64_t triggerid; char *description; - char *expression; - char *recovery_expression; char *comments; char *url; char *correlation_tag; char *opdata; char *event_name; + char *expression_orig; + char *recovery_expression_orig; unsigned char status; unsigned char type; unsigned char priority; @@ -44,6 +44,8 @@ typedef struct zbx_vector_ptr_t functions; zbx_vector_ptr_t dependencies; zbx_vector_ptr_t tags; + zbx_eval_context_t eval_ctx; + zbx_eval_context_t eval_ctx_r; } zbx_lld_trigger_prototype_t; @@ -242,6 +244,9 @@ static void lld_function_free(zbx_lld_function_t *function) static void lld_trigger_prototype_free(zbx_lld_trigger_prototype_t *trigger_prototype) { + zbx_eval_clear(&trigger_prototype->eval_ctx); + zbx_eval_clear(&trigger_prototype->eval_ctx_r); + zbx_vector_ptr_clear_ext(&trigger_prototype->tags, (zbx_clean_func_t)lld_tag_free); zbx_vector_ptr_destroy(&trigger_prototype->tags); zbx_vector_ptr_clear_ext(&trigger_prototype->dependencies, zbx_ptr_free); @@ -253,8 +258,8 @@ static void lld_trigger_prototype_free(zbx_lld_trigger_prototype_t *trigger_prot zbx_free(trigger_prototype->correlation_tag); zbx_free(trigger_prototype->url); zbx_free(trigger_prototype->comments); - zbx_free(trigger_prototype->recovery_expression); - zbx_free(trigger_prototype->expression); + zbx_free(trigger_prototype->recovery_expression_orig); + zbx_free(trigger_prototype->expression_orig); zbx_free(trigger_prototype->description); zbx_free(trigger_prototype); } @@ -299,11 +304,12 @@ static void lld_trigger_free(zbx_lld_trigger_t *trigger) * trigger_prototypes - [OUT] sorted list of trigger prototypes * * * ******************************************************************************/ -static void lld_trigger_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *trigger_prototypes) +static void lld_trigger_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *trigger_prototypes, char **error) { DB_RESULT result; DB_ROW row; zbx_lld_trigger_prototype_t *trigger_prototype; + char *errmsg = NULL; result = DBselect( "select distinct t.triggerid,t.description,t.expression,t.status,t.type,t.priority,t.comments," @@ -323,8 +329,8 @@ static void lld_trigger_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t ZBX_STR2UINT64(trigger_prototype->triggerid, row[0]); trigger_prototype->description = zbx_strdup(NULL, row[1]); - trigger_prototype->expression = zbx_strdup(NULL, row[2]); - trigger_prototype->recovery_expression = zbx_strdup(NULL, row[8]); + trigger_prototype->expression_orig = zbx_strdup(NULL, row[2]); + trigger_prototype->recovery_expression_orig = zbx_strdup(NULL, row[8]); ZBX_STR2UCHAR(trigger_prototype->status, row[3]); ZBX_STR2UCHAR(trigger_prototype->type, row[4]); ZBX_STR2UCHAR(trigger_prototype->priority, row[5]); @@ -342,6 +348,30 @@ static void lld_trigger_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t zbx_vector_ptr_create(&trigger_prototype->dependencies); zbx_vector_ptr_create(&trigger_prototype->tags); + zbx_eval_init(&trigger_prototype->eval_ctx); + zbx_eval_init(&trigger_prototype->eval_ctx_r); + + if (SUCCEED != zbx_eval_parse_expression(&trigger_prototype->eval_ctx, + trigger_prototype->expression_orig, ZBX_EVAL_TRIGGER_EXPRESSION_LLD, &errmsg)) + { + *error = zbx_strdcatf(*error, "Invalid trigger prototype \"%s\" expression: %s\n", + trigger_prototype->description, errmsg); + zbx_free(errmsg); + lld_trigger_prototype_free(trigger_prototype); + continue; + } + + if ('\0' != *trigger_prototype->recovery_expression_orig && + SUCCEED != zbx_eval_parse_expression(&trigger_prototype->eval_ctx_r, + trigger_prototype->recovery_expression_orig, ZBX_EVAL_TRIGGER_EXPRESSION_LLD, &errmsg)) + { + *error = zbx_strdcatf(*error, "Invalid trigger prototype \"%s\" recovery expression: %s\n", + trigger_prototype->description, errmsg); + zbx_free(errmsg); + lld_trigger_prototype_free(trigger_prototype); + continue; + } + zbx_vector_ptr_append(trigger_prototypes, trigger_prototype); } DBfree_result(result); @@ -898,140 +928,242 @@ static zbx_lld_trigger_t *lld_trigger_get(zbx_uint64_t parent_triggerid, zbx_has return NULL; } -static void lld_expression_simplify(char **expression, zbx_vector_ptr_t *functions, zbx_uint64_t *function_index) +/****************************************************************************** + * * + * Function: lld_eval_expression_index_functions * + * * + * Purpose: set indexes for functionid tokens {<functionid>} from the * + * specified function vector * + * * + ******************************************************************************/ +static void lld_eval_expression_index_functions(zbx_eval_context_t *ctx, zbx_vector_ptr_t *functions) { - size_t l, r; - int index; + int i, index; zbx_uint64_t functionid; zbx_lld_function_t *function; - char buffer[ZBX_MAX_UINT64_LEN]; - for (l = 0; '\0' != (*expression)[l]; l++) + for (i = 0; i < ctx->stack.values_num; i++) { - if ('{' != (*expression)[l]) + zbx_eval_token_t *token = &ctx->stack.values[i]; + + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) continue; - if ('$' == (*expression)[l + 1]) + if (SUCCEED != is_uint64_n(ctx->expression + token->loc.l + 1, token->loc.r - token->loc.l - 1, + &functionid)) { - int macro_r, context_l, context_r; - - if (SUCCEED == zbx_user_macro_parse(*expression + l, ¯o_r, &context_l, &context_r, NULL)) - l += macro_r; - else - l++; - + THIS_SHOULD_NEVER_HAPPEN; continue; } - for (r = l + 1; '\0' != (*expression)[r] && '}' != (*expression)[r]; r++) - ; - - if ('}' != (*expression)[r]) - continue; - - /* ... > 0 | {12345} + ... */ - /* l r */ - - if (SUCCEED != is_uint64_n(*expression + l + 1, r - l - 1, &functionid)) - continue; - if (FAIL != (index = zbx_vector_ptr_bsearch(functions, &functionid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC))) { function = (zbx_lld_function_t *)functions->values[index]; - if (0 == function->index) - function->index = ++(*function_index); + function->index = index + 1; + zbx_variant_set_ui64(&token->value, function->index); + } + } +} + +/****************************************************************************** + * * + * Function: lld_eval_expression_simplify * + * * + * Purpose: simplify parsed expression by replacing {<functionid>} with * + * {<function index>} * + * * + ******************************************************************************/ +static void lld_eval_expression_simplify(zbx_eval_context_t *ctx, char **expression, zbx_vector_ptr_t *functions) +{ + zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, (NULL != expression ? *expression : "")); + + if (SUCCEED == zbx_eval_status(ctx)) + { + lld_eval_expression_index_functions(ctx, functions); - zbx_snprintf(buffer, sizeof(buffer), ZBX_FS_UI64, function->index); + if (NULL != expression) + { + char *new_expression = NULL; - r--; - zbx_replace_string(expression, l + 1, &r, buffer); - r++; + zbx_eval_compose_expression(ctx, &new_expression); + zbx_free(*expression); + *expression = new_expression; } - - l = r; } + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() expression:'%s'", __func__, (NULL != expression ? *expression : "")); } -static void lld_expressions_simplify(char **expression, char **recovery_expression, zbx_vector_ptr_t *functions) +/****************************************************************************** + * * + * Function: lld_trigger_expression_simplify * + * * + * Purpose: simplify trigger expression by replacing {<functionid>} with * + * {<function index>} * + * * + ******************************************************************************/ +static void lld_trigger_expression_simplify(const zbx_lld_trigger_t *trigger, char **expression, + zbx_vector_ptr_t *functions) { - zbx_uint64_t function_index = 0; + zbx_eval_context_t ctx; + char *errmsg = NULL; + + if ('\0' == **expression) + return; + + if (SUCCEED != zbx_eval_parse_expression(&ctx, *expression, ZBX_EVAL_TRIGGER_EXPRESSION_LLD, + &errmsg)) + { + const char *type; - zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s' recovery_expression:'%s'", __func__, - *expression, *recovery_expression); + type = (*expression == trigger->expression ? "" : " recovery"); + zabbix_log(LOG_LEVEL_DEBUG, "Invalid trigger \"%s\"%s expression: %s", trigger->description, type, + errmsg); + zbx_free(errmsg); - lld_expression_simplify(expression, functions, &function_index); - lld_expression_simplify(recovery_expression, functions, &function_index); + /* set empty expression so it's replaced with discovered one */ + *expression = zbx_strdup(*expression, ""); + return; + } - zabbix_log(LOG_LEVEL_DEBUG, "End of %s() expression:'%s' recovery_expression:'%s'", __func__, - *expression, *recovery_expression); + lld_eval_expression_simplify(&ctx, expression, functions); + zbx_eval_clear(&ctx); } -static char *lld_expression_expand(const char *expression, const zbx_vector_ptr_t *functions) +/****************************************************************************** + * * + * Function: lld_eval_expression_expand * + * * + * Purpose: expand parsed expression function indexes with function strings * + * in format itemid:func(params) * + * * + ******************************************************************************/ +static char *lld_eval_expression_expand(zbx_eval_context_t *ctx, const zbx_vector_ptr_t *functions) { - size_t l, r; - int i; + int i, j; + char *expression = NULL; zbx_uint64_t index; - char *buffer = NULL; - size_t buffer_alloc = 64, buffer_offset = 0; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, expression); - - buffer = (char *)zbx_malloc(buffer, buffer_alloc); - *buffer = '\0'; + zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, ctx->expression); - for (l = 0; '\0' != expression[l]; l++) + for (i = 0; i < ctx->stack.values_num; i++) { - zbx_chrcpy_alloc(&buffer, &buffer_alloc, &buffer_offset, expression[l]); + zbx_eval_token_t *token = &ctx->stack.values[i]; - if ('{' != expression[l]) + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) continue; - if ('$' == expression[l + 1]) + if (ZBX_VARIANT_UI64 != token->value.type) { - int macro_r, context_l, context_r; + if (SUCCEED != is_uint64_n(ctx->expression + token->loc.l + 1, token->loc.r - token->loc.l - 1, + &index)) + { + THIS_SHOULD_NEVER_HAPPEN; + continue; + } + } + else + index = token->value.data.ui64; - if (SUCCEED == zbx_user_macro_parse(expression + l, ¯o_r, &context_l, &context_r, NULL)) - l += macro_r; - else - l++; + for (j = 0; j < functions->values_num; j++) + { + const zbx_lld_function_t *function = (zbx_lld_function_t *)functions->values[j]; - continue; - } + if (function->index == index) + { + char *value; - for (r = l + 1; '\0' != expression[r] && '}' != expression[r]; r++) - ; + value = zbx_dsprintf(NULL, ZBX_FS_UI64 ":%s(%s)", function->itemid, + function->function, function->parameter); + zbx_variant_clear(&token->value); + zbx_variant_set_str(&token->value, value); + break; + } + } + } - if ('}' != expression[r]) - continue; + zbx_eval_compose_expression(ctx, &expression); - /* ... > 0 | {1} + ... */ - /* l r */ + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() expression:'%s'", __func__, expression); - if (SUCCEED != is_uint64_n(expression + l + 1, r - l - 1, &index)) - continue; + return expression; +} - for (i = 0; i < functions->values_num; i++) - { - const zbx_lld_function_t *function = (zbx_lld_function_t *)functions->values[i]; +/****************************************************************************** + * * + * Function: lld_trigger_expression_expand * + * * + * Purpose: expand trigger expression function indexes with function strings * + * in format itemid:func(params) * + * * + ******************************************************************************/ +static char *lld_trigger_expression_expand(const zbx_lld_trigger_t *trigger, const char *expression, + const zbx_vector_ptr_t *functions) +{ + zbx_eval_context_t ctx; + char *errmsg = NULL, *new_expression; - if (function->index != index) - continue; + if ('\0' == *expression) + return zbx_strdup(NULL, ""); - zbx_snprintf_alloc(&buffer, &buffer_alloc, &buffer_offset, ZBX_FS_UI64 ":%s(%s)", - function->itemid, function->function, function->parameter); + if (SUCCEED != zbx_eval_parse_expression(&ctx, expression, + ZBX_EVAL_TRIGGER_EXPRESSION_LLD & (~ZBX_EVAL_COMPOSE_FUNCTIONID), &errmsg)) + { + const char *type; - break; - } + type = (expression == trigger->expression ? "" : " recovery"); + zabbix_log(LOG_LEVEL_DEBUG, "Invalid trigger \"%s\"%s expression: %s", trigger->description, type, + errmsg); + zbx_free(errmsg); - l = r - 1; + return zbx_strdup(NULL, ""); } - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():'%s'", __func__, buffer); + new_expression = lld_eval_expression_expand(&ctx, functions); + zbx_eval_clear(&ctx); - return buffer; + return new_expression; +} + +/****************************************************************************** + * * + * Function: lld_trigger_expression_simplify_and_expand * + * * + * Purpose: set function indexes and expand them to function strings in * + * format itemid:func(params) * + * * + ******************************************************************************/ +static void lld_trigger_expression_simplify_and_expand(const zbx_lld_trigger_t *trigger, char **expression, + zbx_vector_ptr_t *functions) +{ + zbx_eval_context_t ctx; + char *errmsg = NULL, *new_expression; + + if ('\0' == **expression) + return; + + if (SUCCEED != zbx_eval_parse_expression(&ctx, *expression, + ZBX_EVAL_TRIGGER_EXPRESSION_LLD & (~ZBX_EVAL_COMPOSE_FUNCTIONID), &errmsg)) + { + const char *type; + + type = (*expression == trigger->expression ? "" : " recovery"); + zabbix_log(LOG_LEVEL_DEBUG, "Invalid trigger \"%s\"%s expression: %s", trigger->description, type, + errmsg); + zbx_free(errmsg); + + /* reset expression so it's replaced with discovered one */ + *expression = zbx_strdup(*expression, ""); + return; + } + + lld_eval_expression_index_functions(&ctx, functions); + new_expression = lld_eval_expression_expand(&ctx, functions); + zbx_eval_clear(&ctx); + zbx_free(*expression); + *expression = new_expression; } static int lld_parameter_make(const char *e, char **exp, const struct zbx_json_parse *jp_row, @@ -1196,6 +1328,60 @@ out: /****************************************************************************** * * + * Function: lld_eval_get_expanded_expression * + * * + * Purpose: return copy of the expression with expanded LLD macros * + * * + ******************************************************************************/ +static char *lld_eval_get_expanded_expression(const zbx_eval_context_t *src, const struct zbx_json_parse *jp_row, + const zbx_vector_ptr_t *lld_macros, char *err, size_t err_len) +{ + zbx_eval_context_t ctx; + int i; + char *expression = NULL; + + /* empty expression will not be parsed */ + if (SUCCEED != zbx_eval_status(src)) + return zbx_strdup(NULL, ""); + + zbx_eval_copy(&ctx, src, src->expression); + + for (i = 0; i < ctx.stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx.stack.values[i]; + char *value; + + switch(token->type) + { + case ZBX_EVAL_TOKEN_VAR_LLDMACRO: + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + case ZBX_EVAL_TOKEN_VAR_STR: + break; + default: + continue; + } + + value = zbx_substr_unquote(ctx.expression, token->loc.l, token->loc.r); + + if (FAIL == substitute_lld_macros(&value, jp_row, lld_macros, ZBX_MACRO_ANY, err, err_len)) + { + zbx_free(value); + goto out; + } + + zbx_variant_clear(&token->value); + zbx_variant_set_str(&token->value, value); + } + + zbx_eval_compose_expression(&ctx, &expression); +out: + zbx_eval_clear(&ctx); + + return expression; +} + +/****************************************************************************** + * * * Function: lld_trigger_make * * * * Purpose: create a trigger based on lld rule and add it to the list * @@ -1217,15 +1403,13 @@ static void lld_trigger_make(const zbx_lld_trigger_prototype_t *trigger_prototy trigger = lld_trigger_get(trigger_prototype->triggerid, items_triggers, &lld_row->item_links); operation_msg = NULL != trigger ? "update" : "create"; - expression = zbx_strdup(expression, trigger_prototype->expression); - recovery_expression = zbx_strdup(recovery_expression, trigger_prototype->recovery_expression); - - if (SUCCEED != substitute_lld_macros(&expression, jp_row, lld_macros, ZBX_MACRO_ANY | ZBX_TOKEN_TRIGGER, - err, sizeof(err)) || - SUCCEED != substitute_lld_macros(&recovery_expression, jp_row, lld_macros, - ZBX_MACRO_ANY | ZBX_TOKEN_TRIGGER, err, sizeof(err))) + if (NULL == (expression = lld_eval_get_expanded_expression(&trigger_prototype->eval_ctx, jp_row, lld_macros, + err, sizeof(err))) || + NULL == (recovery_expression = lld_eval_get_expanded_expression(&trigger_prototype->eval_ctx_r, + jp_row, lld_macros, err, sizeof(err)))) { - *error = zbx_strdcatf(*error, "Cannot %s trigger: %s.\n", operation_msg, err); + *error = zbx_strdcatf(*error, "Cannot %s trigger: failed to expand LLD macros in %s expression: %s.\n", + operation_msg, (NULL == expression ? "" : " recovery"), err); goto out; } @@ -1320,7 +1504,8 @@ static void lld_trigger_make(const zbx_lld_trigger_prototype_t *trigger_prototy } buffer = zbx_strdup(buffer, trigger_prototype->event_name); - substitute_lld_macros(&buffer, jp_row, lld_macros, ZBX_MACRO_ANY | ZBX_TOKEN_EXPRESSION_MACRO, NULL, 0); + substitute_lld_macros(&buffer, jp_row, lld_macros, + ZBX_MACRO_ANY | ZBX_TOKEN_EXPRESSION_MACRO | ZBX_MACRO_FUNC, NULL, 0); zbx_lrtrim(buffer, ZBX_WHITESPACE); if (0 != strcmp(trigger->event_name, buffer)) { @@ -1950,34 +2135,28 @@ static int lld_trigger_changed(const zbx_lld_trigger_t *trigger) * the triggers are identical; FAIL - otherwise * * * ******************************************************************************/ -static int lld_triggers_equal(const zbx_lld_trigger_t *trigger, const zbx_lld_trigger_t *trigger_b) +static int lld_triggers_equal(const zbx_lld_trigger_t *trigger, const zbx_lld_trigger_t *db_trigger) { int ret = FAIL; + char *expression = NULL; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - if (0 == strcmp(trigger->description, trigger_b->description)) - { - char *expression, *expression_b; - - expression = lld_expression_expand(trigger->expression, &trigger->functions); - expression_b = lld_expression_expand(trigger_b->expression, &trigger_b->functions); + if (0 != strcmp(trigger->description, db_trigger->description)) + goto out; - if (0 == strcmp(expression, expression_b)) - { - zbx_free(expression); - zbx_free(expression_b); + expression = lld_trigger_expression_expand(trigger, trigger->expression, &trigger->functions); - expression = lld_expression_expand(trigger->recovery_expression, &trigger->functions); - expression_b = lld_expression_expand(trigger_b->recovery_expression, &trigger_b->functions); + if (0 != strcmp(expression, db_trigger->expression)) + goto out; - if (0 == strcmp(expression, expression_b)) - ret = SUCCEED; - } + zbx_free(expression); + expression = lld_trigger_expression_expand(trigger, trigger->recovery_expression, &trigger->functions); - zbx_free(expression); - zbx_free(expression_b); - } + if (0 == strcmp(expression, db_trigger->recovery_expression)) + ret = SUCCEED; +out: + zbx_free(expression); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); @@ -2122,7 +2301,9 @@ static void lld_triggers_validate(zbx_uint64_t hostid, zbx_vector_ptr_t *trigger { db_trigger = (zbx_lld_trigger_t *)db_triggers.values[i]; - lld_expressions_simplify(&db_trigger->expression, &db_trigger->recovery_expression, + lld_trigger_expression_simplify_and_expand(db_trigger, &db_trigger->expression, + &db_trigger->functions); + lld_trigger_expression_simplify_and_expand(db_trigger, &db_trigger->recovery_expression, &db_trigger->functions); for (j = 0; j < triggers->values_num; j++) @@ -2309,64 +2490,73 @@ static void lld_trigger_tags_validate(zbx_vector_ptr_t *triggers, char **error) * internal function index * * * ******************************************************************************/ -static void lld_expression_create(char **expression, const zbx_vector_ptr_t *functions) +static int lld_expression_create(const zbx_lld_trigger_t *trigger, char **expression, + const zbx_vector_ptr_t *functions) { - size_t l, r; - int i; - zbx_uint64_t function_index; - char buffer[ZBX_MAX_UINT64_LEN]; + int i, j, ret = FAIL; + zbx_uint64_t function_index; + zbx_eval_context_t ctx; + char *errmsg = NULL, *new_expression = NULL; zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, *expression); - for (l = 0; '\0' != (*expression)[l]; l++) + if ('\0' == **expression) { - if ('{' != (*expression)[l]) - continue; - - if ('$' == (*expression)[l + 1]) - { - int macro_r, context_l, context_r; + ret = SUCCEED; + goto out; + } - if (SUCCEED == zbx_user_macro_parse(*expression + l, ¯o_r, &context_l, &context_r, NULL)) - l += macro_r; - else - l++; + if (SUCCEED != zbx_eval_parse_expression(&ctx, *expression, ZBX_EVAL_TRIGGER_EXPRESSION_LLD, &errmsg)) + { + const char *type; - continue; - } + type = (*expression == trigger->expression ? "" : " recovery"); + zabbix_log(LOG_LEVEL_DEBUG, "Invalid trigger \"%s\"%s expression: %s", trigger->description, type, + errmsg); + zbx_free(errmsg); - for (r = l + 1; '\0' != (*expression)[r] && '}' != (*expression)[r]; r++) - ; + THIS_SHOULD_NEVER_HAPPEN; - if ('}' != (*expression)[r]) - continue; + goto out; + } - /* ... > 0 | {1} + ... */ - /* l r */ + for (i = 0; i < ctx.stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx.stack.values[i]; - if (SUCCEED != is_uint64_n(*expression + l + 1, r - l - 1, &function_index)) + if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type) continue; - for (i = 0; i < functions->values_num; i++) + if (SUCCEED != is_uint64_n(ctx.expression + token->loc.l + 1, token->loc.r - token->loc.l - 1, + &function_index)) { - const zbx_lld_function_t *function = (zbx_lld_function_t *)functions->values[i]; - - if (function->index != function_index) - continue; - - zbx_snprintf(buffer, sizeof(buffer), ZBX_FS_UI64, function->functionid); + THIS_SHOULD_NEVER_HAPPEN; + continue; + } - r--; - zbx_replace_string(expression, l + 1, &r, buffer); - r++; + for (j = 0; j < functions->values_num; j++) + { + const zbx_lld_function_t *function = (zbx_lld_function_t *)functions->values[j]; - break; + if (function->index == function_index) + { + zbx_variant_set_ui64(&token->value, function->functionid); + break; + } } - - l = r; } + zbx_eval_compose_expression(&ctx, &new_expression); + zbx_free(*expression); + *expression = new_expression; + + zbx_eval_clear(&ctx); + + ret = SUCCEED; +out: zabbix_log(LOG_LEVEL_DEBUG, "End of %s() expression:'%s'", __func__, *expression); + + return ret; } /****************************************************************************** @@ -2583,10 +2773,26 @@ static int lld_triggers_save(zbx_uint64_t hostid, const zbx_vector_ptr_t *trigge } if (0 == trigger->triggerid || 0 != (trigger->flags & ZBX_FLAG_LLD_TRIGGER_UPDATE_EXPRESSION)) - lld_expression_create(&trigger->expression, &trigger->functions); + { + if (FAIL == lld_expression_create(trigger, &trigger->expression, &trigger->functions)) + { + /* further updates will fail, so there is unnecessary overhead, */ + /* but lld_expression_create() can fail only because of bugs, */ + /* so better to leave unoptimized handling of 'impossible' */ + /* errors than unnecessary complicate cod */ + DBrollback(); + ret = FAIL; + } + } if (0 == trigger->triggerid || 0 != (trigger->flags & ZBX_FLAG_LLD_TRIGGER_UPDATE_RECOVERY_EXPRESSION)) - lld_expression_create(&trigger->recovery_expression, &trigger->functions); + { + if (FAIL == lld_expression_create(trigger, &trigger->recovery_expression, &trigger->functions)) + { + DBrollback(); + ret = FAIL; + } + } if (0 == trigger->triggerid) { @@ -3552,7 +3758,7 @@ int lld_update_triggers(zbx_uint64_t hostid, zbx_uint64_t lld_ruleid, const zbx_ zbx_vector_ptr_create(&trigger_prototypes); - lld_trigger_prototypes_get(lld_ruleid, &trigger_prototypes); + lld_trigger_prototypes_get(lld_ruleid, &trigger_prototypes, error); if (0 == trigger_prototypes.values_num) goto out; @@ -3573,15 +3779,16 @@ int lld_update_triggers(zbx_uint64_t hostid, zbx_uint64_t lld_ruleid, const zbx_ { trigger_prototype = (zbx_lld_trigger_prototype_t *)trigger_prototypes.values[i]; - lld_expressions_simplify(&trigger_prototype->expression, &trigger_prototype->recovery_expression, - &trigger_prototype->functions); + lld_eval_expression_simplify(&trigger_prototype->eval_ctx, NULL, &trigger_prototype->functions); + lld_eval_expression_simplify(&trigger_prototype->eval_ctx_r, NULL, &trigger_prototype->functions); } for (i = 0; i < triggers.values_num; i++) { trigger = (zbx_lld_trigger_t *)triggers.values[i]; - lld_expressions_simplify(&trigger->expression, &trigger->recovery_expression, &trigger->functions); + lld_trigger_expression_simplify(trigger, &trigger->expression, &trigger->functions); + lld_trigger_expression_simplify(trigger, &trigger->recovery_expression, &trigger->functions); } /* making triggers */ diff --git a/src/zabbix_server/operations.c b/src/zabbix_server/operations.c index 79bdb3d9e9c..f9fb075e118 100644 --- a/src/zabbix_server/operations.c +++ b/src/zabbix_server/operations.c @@ -885,7 +885,7 @@ void op_groups_del(const DB_EVENT *event, zbx_vector_uint64_t *groupids) void op_template_add(const DB_EVENT *event, zbx_vector_uint64_t *lnk_templateids) { zbx_uint64_t hostid; - char *error; + char *error = NULL; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); diff --git a/src/zabbix_server/poller/Makefile.am b/src/zabbix_server/poller/Makefile.am index 7a6ef5b5236..ffbcd935146 100644 --- a/src/zabbix_server/poller/Makefile.am +++ b/src/zabbix_server/poller/Makefile.am @@ -5,8 +5,6 @@ noinst_LIBRARIES = libzbxpoller.a libzbxpoller_server.a libzbxpoller_proxy.a libzbxpoller_a_SOURCES = \ checks_agent.c \ checks_agent.h \ - checks_aggregate.c \ - checks_aggregate.h \ checks_calculated.c \ checks_calculated.h \ checks_db.c \ diff --git a/src/zabbix_server/poller/checks_aggregate.c b/src/zabbix_server/poller/checks_aggregate.c index 7c38358ef04..ba4d9178a98 100644 --- a/src/zabbix_server/poller/checks_aggregate.c +++ b/src/zabbix_server/poller/checks_aggregate.c @@ -510,146 +510,3 @@ clean1: return ret; } - -/****************************************************************************** - * * - * Function: get_value_aggregate * - * * - * Purpose: retrieve data from Zabbix server (aggregate items) * - * * - * Parameters: item - item we are interested in * - * * - * Return value: SUCCEED - data successfully retrieved and stored in result * - * and result_str (as string) * - * NOTSUPPORTED - requested item is not supported * - * * - * Author: Alexei Vladishev * - * * - ******************************************************************************/ -int get_value_aggregate(const DC_ITEM *item, AGENT_RESULT *result) -{ - AGENT_REQUEST request; - int ret = NOTSUPPORTED; - const char *tmp, *groups, *itemkey, *funcp = NULL; - int grp_func, item_func, params_num; - zbx_vector_str_t group_names; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s'", __func__, item->key_orig); - - init_request(&request); - zbx_vector_str_create(&group_names); - - if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) - { - SET_MSG_RESULT(result, zbx_strdup(NULL, "Value type must be Numeric for aggregate items")); - goto out; - } - - if (SUCCEED != parse_item_key(item->key, &request)) - { - SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid item key format.")); - goto out; - } - - if (0 == strcmp(get_rkey(&request), "grpmin")) - { - grp_func = ZBX_VALUE_FUNC_MIN; - } - else if (0 == strcmp(get_rkey(&request), "grpavg")) - { - grp_func = ZBX_VALUE_FUNC_AVG; - } - else if (0 == strcmp(get_rkey(&request), "grpmax")) - { - grp_func = ZBX_VALUE_FUNC_MAX; - } - else if (0 == strcmp(get_rkey(&request), "grpsum")) - { - grp_func = ZBX_VALUE_FUNC_SUM; - } - else - { - SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid item key.")); - goto out; - } - - params_num = get_rparams_num(&request); - - if (3 > params_num || params_num > 4) - { - SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters.")); - goto out; - } - - groups = get_rparam(&request, 0); - itemkey = get_rparam(&request, 1); - tmp = get_rparam(&request, 2); - - if (REQUEST_PARAMETER_TYPE_ARRAY == get_rparam_type(&request, 0)) - { - int i, groups_num; - char *group; - zbx_request_parameter_type_t type; - - groups_num = num_param(groups); - - for (i = 1; i <= groups_num; i++) - { - if (NULL == (group = get_param_dyn(groups, i, &type))) - continue; - - zbx_vector_str_append(&group_names, group); - - if (REQUEST_PARAMETER_TYPE_STRING != type) - { - SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Invalid host group list: %s", groups)); - goto out; - } - } - } - else - zbx_vector_str_append(&group_names, zbx_strdup(NULL, groups)); - - zabbix_log(LOG_LEVEL_DEBUG, "Host groups: '%s', Item key: '%s', Item function: '%s'", groups, itemkey, tmp); - - if (0 == strcmp(tmp, "min")) - item_func = ZBX_VALUE_FUNC_MIN; - else if (0 == strcmp(tmp, "avg")) - item_func = ZBX_VALUE_FUNC_AVG; - else if (0 == strcmp(tmp, "max")) - item_func = ZBX_VALUE_FUNC_MAX; - else if (0 == strcmp(tmp, "sum")) - item_func = ZBX_VALUE_FUNC_SUM; - else if (0 == strcmp(tmp, "count")) - item_func = ZBX_VALUE_FUNC_COUNT; - else if (0 == strcmp(tmp, "last")) - item_func = ZBX_VALUE_FUNC_LAST; - else - { - SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter.")); - goto out; - } - - if (4 == params_num) - { - funcp = get_rparam(&request, 3); - } - else if (3 == params_num && ZBX_VALUE_FUNC_LAST != item_func) - { - SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters.")); - goto out; - } - - if (SUCCEED != evaluate_aggregate(item, result, grp_func, &group_names, itemkey, item_func, funcp)) - goto out; - - ret = SUCCEED; -out: - zbx_vector_str_clear_ext(&group_names, zbx_str_free); - zbx_vector_str_destroy(&group_names); - free_request(&request); - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; -} diff --git a/src/zabbix_server/poller/checks_calculated.c b/src/zabbix_server/poller/checks_calculated.c index 88738c61f65..91e0cec59d0 100644 --- a/src/zabbix_server/poller/checks_calculated.c +++ b/src/zabbix_server/poller/checks_calculated.c @@ -1,361 +1,90 @@ /* -** Zabbix -** Copyright (C) 2001-2021 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. -**/ + ** Zabbix + ** Copyright (C) 2001-2021 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 "checks_calculated.h" #include "zbxserver.h" #include "log.h" -#include "../../libs/zbxserver/evalfunc.h" -#include "valuecache.h" -typedef struct +int get_value_calculated(DC_ITEM *dc_item, AGENT_RESULT *result) { - int functionid; - char *host; - char *key; - char *func; - char *params; - char *value; -} -function_t; + int ret = NOTSUPPORTED; + char *error = NULL; + zbx_eval_context_t ctx; + zbx_variant_t value; + zbx_timespec_t ts; + zbx_expression_eval_t eval; -typedef struct -{ - char *exp; - function_t *functions; - int functions_alloc; - int functions_num; -} -expression_t; - -static void free_expression(expression_t *exp) -{ - function_t *f; - int i; + zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s' expression:'%s'", __func__, dc_item->key_orig, dc_item->params); - for (i = 0; i < exp->functions_num; i++) + if (NULL == dc_item->formula_bin) { - f = &exp->functions[i]; - zbx_free(f->host); - zbx_free(f->key); - zbx_free(f->func); - zbx_free(f->params); - zbx_free(f->value); + zabbix_log(LOG_LEVEL_DEBUG, "%s() serialized formula is not set", __func__); + SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot evaluate calculated item:" + " serialized formula is not set")); + goto out; } - zbx_free(exp->exp); - zbx_free(exp->functions); - exp->functions_alloc = 0; - exp->functions_num = 0; -} + zbx_eval_deserialize(&ctx, dc_item->params, ZBX_EVAL_PARSE_CALC_EXPRESSSION, dc_item->formula_bin); + zbx_timespec(&ts); -static int calcitem_add_function(expression_t *exp, char *host, char *key, char *func, char *params) -{ - function_t *f; + zbx_expression_eval_init(&eval, ZBX_EXPRESSION_AGGREGATE, &ctx); + zbx_expression_eval_resolve_item_hosts(&eval, dc_item); - if (exp->functions_alloc == exp->functions_num) + if (SUCCEED != zbx_expression_eval_execute(&eval, &ts, &value, &error)) { - exp->functions_alloc += 8; - exp->functions = (function_t *)zbx_realloc(exp->functions, exp->functions_alloc * sizeof(function_t)); + zabbix_log(LOG_LEVEL_DEBUG, "%s() error:%s", __func__, error); + SET_MSG_RESULT(result, error); + error = NULL; } - - f = &exp->functions[exp->functions_num++]; - f->functionid = exp->functions_num; - f->host = host; - f->key = key; - f->func = func; - f->params = params; - f->value = NULL; - - return f->functionid; -} - -static int calcitem_parse_expression(DC_ITEM *dc_item, expression_t *exp, char *error, int max_error_len) -{ - char *e, *buf = NULL, *tmp_exp; - size_t exp_alloc = 128, exp_offset = 0, f_pos, par_l = 0, par_r = 0; - int ret = NOTSUPPORTED; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, dc_item->params); - - tmp_exp = (char *)zbx_malloc(NULL, exp_alloc); - - for (e = dc_item->params; SUCCEED == zbx_function_find(e, &f_pos, &par_l, &par_r, error, max_error_len); - e += par_r + 1) + else { - char *func, *params, *host = NULL, *key = NULL; - size_t param_pos, param_len, sep_pos; - int functionid, quoted; - - /* copy the part of the string preceding function */ - zbx_strncpy_alloc(&tmp_exp, &exp_alloc, &exp_offset, e, f_pos); - - /* extract the first function parameter and <host:>key reference from it */ + zabbix_log(LOG_LEVEL_DEBUG, "%s() value:%s", __func__, zbx_variant_value_desc(&value)); - zbx_function_param_parse(e + par_l + 1, ¶m_pos, ¶m_len, &sep_pos); - - zbx_free(buf); - buf = zbx_function_param_unquote_dyn(e + par_l + 1 + param_pos, param_len, "ed); - - if (SUCCEED != parse_host_key(buf, &host, &key)) + switch (value.type) { - zbx_snprintf(error, max_error_len, "Invalid first parameter in function [%.*s].", - (int)(par_r - f_pos + 1), e + f_pos); - goto out; + case ZBX_VARIANT_DBL: + SET_DBL_RESULT(result, value.data.dbl); + break; + case ZBX_VARIANT_UI64: + SET_UI64_RESULT(result, value.data.ui64); + break; + case ZBX_VARIANT_STR: + SET_TEXT_RESULT(result, value.data.str); + break; + default: + SET_MSG_RESULT(result, zbx_dsprintf(NULL, "unsupported calculated value result \"%s\"" + " of type \"%s\"", zbx_variant_value_desc(&value), + zbx_variant_type_desc(&value))); + zbx_variant_clear(&value); + break; } - if (NULL == host) - host = zbx_strdup(NULL, dc_item->host.host); - - /* extract function name and remaining parameters */ - e[par_l] = '\0'; - func = zbx_strdup(NULL, e + f_pos); - e[par_l] = '('; - - if (')' != e[par_l + 1 + sep_pos]) /* first parameter is not the only one */ + if (ZBX_VARIANT_NONE != value.type) { - e[par_r] = '\0'; - params = zbx_strdup(NULL, e + par_l + 1 + sep_pos + 1); - e[par_r] = ')'; + zbx_variant_set_none(&value); + ret = SUCCEED; } - else /* the only parameter of the function was <host:>key reference */ - params = zbx_strdup(NULL, ""); - - functionid = calcitem_add_function(exp, host, key, func, params); - - zabbix_log(LOG_LEVEL_DEBUG, "%s() functionid:%d function:'%s:%s.%s(%s)'", - __func__, functionid, host, key, func, params); - - /* substitute function with id in curly brackets */ - zbx_snprintf_alloc(&tmp_exp, &exp_alloc, &exp_offset, "{%d}", functionid); } - if (par_l > par_r) - goto out; - - /* copy the remaining part */ - zbx_strcpy_alloc(&tmp_exp, &exp_alloc, &exp_offset, e); - - exp->exp = tmp_exp; - tmp_exp = NULL; - - zabbix_log(LOG_LEVEL_DEBUG, "%s() expression:'%s'", __func__, exp->exp); - - ret = SUCCEED; + zbx_expression_eval_clear(&eval); + zbx_eval_clear(&ctx); out: - zbx_free(buf); - zbx_free(tmp_exp); - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; -} - -static int calcitem_evaluate_expression(expression_t *exp, char *error, size_t max_error_len, - zbx_vector_ptr_t *unknown_msgs) -{ - function_t *f = NULL; - char *buf, replace[16], *errstr = NULL; - int i, ret = SUCCEED; - zbx_host_key_t *keys = NULL; - DC_ITEM *items = NULL; - int *errcodes = NULL; - zbx_timespec_t ts; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - - if (0 == exp->functions_num) - return ret; - - keys = (zbx_host_key_t *)zbx_malloc(keys, sizeof(zbx_host_key_t) * (size_t)exp->functions_num); - items = (DC_ITEM *)zbx_malloc(items, sizeof(DC_ITEM) * (size_t)exp->functions_num); - errcodes = (int *)zbx_malloc(errcodes, sizeof(int) * (size_t)exp->functions_num); - - for (i = 0; i < exp->functions_num; i++) - { - keys[i].host = exp->functions[i].host; - keys[i].key = exp->functions[i].key; - } - - DCconfig_get_items_by_keys(items, keys, errcodes, exp->functions_num); - - zbx_timespec(&ts); - - for (i = 0; i < exp->functions_num; i++) - { - int ret_unknown = 0; /* flag raised if current function evaluates to ZBX_UNKNOWN */ - char *unknown_msg; - - f = &exp->functions[i]; - - if (SUCCEED != errcodes[i]) - { - zbx_snprintf(error, max_error_len, - "Cannot evaluate function \"%s(%s)\":" - " item \"%s:%s\" does not exist.", - f->func, f->params, f->host, f->key); - ret = NOTSUPPORTED; - break; - } - - /* do not evaluate if the item is disabled or belongs to a disabled host */ - - if (ITEM_STATUS_ACTIVE != items[i].status) - { - zbx_snprintf(error, max_error_len, - "Cannot evaluate function \"%s(%s)\":" - " item \"%s:%s\" is disabled.", - f->func, f->params, f->host, f->key); - ret = NOTSUPPORTED; - break; - } - - if (HOST_STATUS_MONITORED != items[i].host.status) - { - zbx_snprintf(error, max_error_len, - "Cannot evaluate function \"%s(%s)\":" - " item \"%s:%s\" belongs to a disabled host.", - f->func, f->params, f->host, f->key); - ret = NOTSUPPORTED; - break; - } - - /* If the item is NOTSUPPORTED then evaluation is allowed for: */ - /* - functions white-listed in evaluatable_for_notsupported(). */ - /* Their values can be evaluated to regular numbers even for */ - /* NOTSUPPORTED items. */ - /* - other functions. Result of evaluation is ZBX_UNKNOWN. */ - - if (ITEM_STATE_NOTSUPPORTED == items[i].state && FAIL == evaluatable_for_notsupported(f->func)) - { - /* compose and store 'unknown' message for future use */ - unknown_msg = zbx_dsprintf(NULL, - "Cannot evaluate function \"%s(%s)\": item \"%s:%s\" not supported.", - f->func, f->params, f->host, f->key); - - zbx_vector_ptr_append(unknown_msgs, unknown_msg); - ret_unknown = 1; - } - - if (0 == ret_unknown && - SUCCEED != evaluate_function(&(f->value), &items[i], f->func, f->params, &ts, &errstr)) - { - /* compose and store error message for future use */ - if (NULL != errstr) - { - unknown_msg = zbx_dsprintf(NULL, "Cannot evaluate function \"%s(%s)\": %s.", - f->func, f->params, errstr); - zbx_free(errstr); - } - else - { - unknown_msg = zbx_dsprintf(NULL, "Cannot evaluate function \"%s(%s)\".", - f->func, f->params); - } - - zbx_vector_ptr_append(unknown_msgs, unknown_msg); - ret_unknown = 1; - } - - if (1 == ret_unknown || SUCCEED != is_double_suffix(f->value, ZBX_FLAG_DOUBLE_SUFFIX) || '-' == *f->value) - { - char *wrapped; - - if (0 == ret_unknown) - { - wrapped = zbx_dsprintf(NULL, "(%s)", f->value); - } - else - { - /* write a special token of unknown value with 'unknown' message number, like */ - /* ZBX_UNKNOWN0, ZBX_UNKNOWN1 etc. not wrapped in () */ - wrapped = zbx_dsprintf(NULL, ZBX_UNKNOWN_STR "%d", unknown_msgs->values_num - 1); - } - - zbx_free(f->value); - f->value = wrapped; - } - else - f->value = (char *)zbx_realloc(f->value, strlen(f->value) + 1); - - zbx_snprintf(replace, sizeof(replace), "{%d}", f->functionid); - buf = string_replace(exp->exp, replace, f->value); - zbx_free(exp->exp); - exp->exp = buf; - } - - zbx_vc_flush_stats(); - - DCconfig_clean_items(items, errcodes, exp->functions_num); - - zbx_free(errcodes); - zbx_free(items); - zbx_free(keys); - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; -} - -int get_value_calculated(DC_ITEM *dc_item, AGENT_RESULT *result) -{ - expression_t exp; - int ret; - char error[MAX_STRING_LEN]; - double value; - zbx_vector_ptr_t unknown_msgs; /* pointers to messages about origins of 'unknown' values */ - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s' expression:'%s'", __func__, dc_item->key_orig, dc_item->params); - - memset(&exp, 0, sizeof(exp)); - - if (SUCCEED != (ret = calcitem_parse_expression(dc_item, &exp, error, sizeof(error)))) - { - SET_MSG_RESULT(result, strdup(error)); - goto clean1; - } - - /* Assumption: most often there will be no NOTSUPPORTED items and function errors. */ - /* Therefore initialize error messages vector but do not reserve any space. */ - zbx_vector_ptr_create(&unknown_msgs); - - if (SUCCEED != (ret = calcitem_evaluate_expression(&exp, error, sizeof(error), &unknown_msgs))) - { - SET_MSG_RESULT(result, strdup(error)); - goto clean; - } - - if (SUCCEED != evaluate(&value, exp.exp, error, sizeof(error), &unknown_msgs)) - { - SET_MSG_RESULT(result, strdup(error)); - ret = NOTSUPPORTED; - goto clean; - } - - zabbix_log(LOG_LEVEL_DEBUG, "%s() value:" ZBX_FS_DBL, __func__, value); - - SET_DBL_RESULT(result, value); -clean: - zbx_vector_ptr_clear_ext(&unknown_msgs, zbx_ptr_free); - zbx_vector_ptr_destroy(&unknown_msgs); -clean1: - free_expression(&exp); - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); return ret; diff --git a/src/zabbix_server/poller/poller.c b/src/zabbix_server/poller/poller.c index 3879c6ac54b..303ba5a9d89 100644 --- a/src/zabbix_server/poller/poller.c +++ b/src/zabbix_server/poller/poller.c @@ -30,7 +30,6 @@ #include "poller.h" #include "checks_agent.h" -#include "checks_aggregate.h" #include "checks_external.h" #include "checks_internal.h" #include "checks_script.h" @@ -312,9 +311,6 @@ static int get_value(DC_ITEM *item, AGENT_RESULT *result, zbx_vector_ptr_t *add_ res = CONFIG_ERROR; #endif break; - case ITEM_TYPE_AGGREGATE: - res = get_value_aggregate(item, result); - break; case ITEM_TYPE_EXTERNAL: /* external checks use their own timeouts */ res = get_value_external(item, result); diff --git a/src/zabbix_server/preprocessor/preproc_history.h b/src/zabbix_server/preprocessor/preproc_history.h index 66ca5e539fc..70179a586b0 100644 --- a/src/zabbix_server/preprocessor/preproc_history.h +++ b/src/zabbix_server/preprocessor/preproc_history.h @@ -22,6 +22,7 @@ #include "common.h" #include "dbcache.h" +#include "zbxvariant.h" typedef struct { diff --git a/src/zabbix_server/server.c b/src/zabbix_server/server.c index f6b8b348f06..acf3b21a482 100644 --- a/src/zabbix_server/server.c +++ b/src/zabbix_server/server.c @@ -1058,6 +1058,23 @@ static void zbx_main_sigusr_handler(int flags) } +static void zbx_check_db(void) +{ + struct zbx_json db_ver; + + zbx_json_initarray(&db_ver, ZBX_JSON_STAT_BUF_LEN); + + if (SUCCEED != DBcheck_capabilities(DBextract_version(&db_ver)) || SUCCEED != DBcheck_version()) + { + zbx_json_free(&db_ver); + exit(EXIT_FAILURE); + } + + zbx_history_check_version(&db_ver); + DBflush_version_requirements(db_ver.buffer); + zbx_json_free(&db_ver); +} + int MAIN_ZABBIX_ENTRY(int flags) { zbx_socket_t listen_sock; @@ -1252,10 +1269,8 @@ int MAIN_ZABBIX_ENTRY(int flags) exit(EXIT_FAILURE); } - DBcheck_capabilities(); + zbx_check_db(); - if (SUCCEED != DBcheck_version()) - exit(EXIT_FAILURE); DBcheck_character_set(); if (SUCCEED == DBcheck_double_type()) diff --git a/src/zabbix_server/trapper/trapper_expressions_evaluate.c b/src/zabbix_server/trapper/trapper_expressions_evaluate.c index 537cef406b6..9742d420e2d 100644 --- a/src/zabbix_server/trapper/trapper_expressions_evaluate.c +++ b/src/zabbix_server/trapper/trapper_expressions_evaluate.c @@ -61,38 +61,68 @@ out: return ret; } +static int trapper_expression_evaluate(const char *expression, const zbx_timespec_t *ts, double *result, + char **error) +{ + zbx_eval_context_t ctx; + int ret; + zbx_variant_t value; + + if (SUCCEED != zbx_eval_parse_expression(&ctx, expression, ZBX_EVAL_PARSE_TRIGGER_EXPRESSSION, error)) + return FAIL; + + if (SUCCEED == (ret = zbx_eval_execute(&ctx, ts, &value, error))) + { + if (SUCCEED == zbx_variant_convert(&value, ZBX_VARIANT_DBL)) + { + *result = value.data.dbl; + } + else + { + *error = zbx_dsprintf(NULL, "invalid result \"%s\" of type \"%s\"", + zbx_variant_value_desc(&value), zbx_variant_type_desc(&value)); + zbx_variant_clear(&value); + ret = FAIL; + } + } + + zbx_eval_clear(&ctx); + + return ret; +} + static int trapper_expressions_evaluate_run(const struct zbx_json_parse *jp, struct zbx_json *json, char **error) { - int ret = FAIL, i; - zbx_vector_ptr_t expressions; + int ret = FAIL, i; + zbx_vector_ptr_t expressions; + zbx_timespec_t ts; zbx_vector_ptr_create(&expressions); - if (FAIL == trapper_parse_expressions_evaluate(jp, &expressions, error)) + if (FAIL == trapper_parse_expressions_evaluate(jp, &expressions, error)) goto out; zbx_json_addstring(json, ZBX_PROTO_TAG_RESPONSE, "success", ZBX_JSON_TYPE_STRING); zbx_json_addarray(json, ZBX_PROTO_TAG_DATA); + zbx_timespec(&ts); + for (i = 0; i < expressions.values_num; i++) { - double expr_result; - zbx_vector_ptr_t unknown_msgs; - char evaluate_error[MAX_STRING_LEN]; - - evaluate_error[0] = '\0'; + double expr_result; + char *errmsg = NULL; zbx_json_addobject(json, NULL); zbx_json_addstring(json, ZBX_PROTO_TAG_EXPRESSION, expressions.values[i], ZBX_JSON_TYPE_STRING); - if (SUCCEED != evaluate(&expr_result, expressions.values[i], evaluate_error, sizeof(evaluate_error), - &unknown_msgs)) + if (SUCCEED != trapper_expression_evaluate(expressions.values[i], &ts, &expr_result, &errmsg)) { - zbx_json_addstring(json, ZBX_PROTO_TAG_ERROR, evaluate_error, ZBX_JSON_TYPE_STRING); + zbx_json_addstring(json, ZBX_PROTO_TAG_ERROR, errmsg, ZBX_JSON_TYPE_STRING); + zbx_free(errmsg); } else { - zbx_uint64_t res = (ZBX_INFINITY == expr_result || + zbx_uint64_t res = (ZBX_INFINITY == expr_result || SUCCEED == zbx_double_compare(expr_result, 0.0)) ? 0 : 1; zbx_json_adduint64(json, ZBX_PROTO_TAG_VALUE, res); diff --git a/src/zabbix_server/trapper/trapper_item_test.c b/src/zabbix_server/trapper/trapper_item_test.c index 43608bf400e..00ef393cbe9 100644 --- a/src/zabbix_server/trapper/trapper_item_test.c +++ b/src/zabbix_server/trapper/trapper_item_test.c @@ -333,6 +333,21 @@ int zbx_trapper_item_test_run(const struct zbx_json_parse *jp_data, zbx_uint64_t if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE)) dump_item(&item); + if (ITEM_TYPE_CALCULATED == item.type) + { + zbx_eval_context_t ctx; + char *error = NULL; + + if (FAIL == zbx_eval_parse_expression(&ctx, item.params, ZBX_EVAL_PARSE_CALC_EXPRESSSION, &error)) + { + zbx_eval_set_exception(&ctx, zbx_dsprintf(NULL, "Cannot parse formula: %s", error)); + zbx_free(error); + } + + zbx_eval_serialize(&ctx, NULL, &item.formula_bin); + zbx_eval_clear(&ctx); + } + zbx_check_items(&item, &errcode, 1, &result, &add_results, ZBX_NO_POLLER); switch (errcode) @@ -385,6 +400,7 @@ out: zbx_free(item.snmpv3_privpassphrase); zbx_free(item.snmpv3_contextname); zbx_free(item.script_params); + zbx_free(item.formula_bin); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); |