Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlexander Shubin <aleksandrs.subins@zabbix.com>2021-05-10 16:36:00 +0300
committerAlexander Shubin <aleksandrs.subins@zabbix.com>2021-05-10 16:36:00 +0300
commite222c02f21443c63e44745fe481342c7df5cf010 (patch)
tree600938ae7f9e38ffda1b88822c05caf1f42b386a /src
parentb559fd0b02b03665b538232b7a9c7cfa6408b369 (diff)
parent17c7dcad60169b733b6852fd34ff06e4b4947f61 (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')
-rw-r--r--src/go/cmd/zabbix_agent2/service_windows.go37
-rw-r--r--src/go/plugins/windows/services/services_windows.go2
-rw-r--r--src/libs/Makefile.am9
-rw-r--r--src/libs/zbxalgo/Makefile.am3
-rw-r--r--src/libs/zbxalgo/evaluate.c2
-rw-r--r--src/libs/zbxalgo/serialize.c100
-rw-r--r--src/libs/zbxalgo/vector.c1
-rw-r--r--src/libs/zbxcommon/misc.c2
-rw-r--r--src/libs/zbxcommon/str.c444
-rw-r--r--src/libs/zbxcommon/time.c14
-rw-r--r--src/libs/zbxcommon/variant.c124
-rw-r--r--src/libs/zbxcommon/variant_misc.c1
-rw-r--r--src/libs/zbxcommon/xml.c1
-rw-r--r--src/libs/zbxdb/db.c301
-rw-r--r--src/libs/zbxdbcache/dbcache.c11
-rw-r--r--src/libs/zbxdbcache/dbconfig.c813
-rw-r--r--src/libs/zbxdbcache/dbconfig.h33
-rw-r--r--src/libs/zbxdbcache/dbconfig_maintenance.c11
-rw-r--r--src/libs/zbxdbcache/dbsync.c235
-rw-r--r--src/libs/zbxdbcache/dbsync.h2
-rw-r--r--src/libs/zbxdbhigh/db.c76
-rw-r--r--src/libs/zbxdbhigh/event.c23
-rw-r--r--src/libs/zbxdbhigh/host.c102
-rw-r--r--src/libs/zbxdbhigh/proxy.c4
-rw-r--r--src/libs/zbxdbhigh/trigger.c520
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_5030.c2271
-rw-r--r--src/libs/zbxembed/xml.c2
-rw-r--r--src/libs/zbxeval/Makefile.am9
-rw-r--r--src/libs/zbxeval/eval.h (renamed from src/zabbix_server/poller/checks_aggregate.h)12
-rw-r--r--src/libs/zbxeval/execute.c1619
-rw-r--r--src/libs/zbxeval/misc.c1120
-rw-r--r--src/libs/zbxeval/parse.c1547
-rw-r--r--src/libs/zbxeval/query.c507
-rw-r--r--src/libs/zbxhistory/history.c43
-rw-r--r--src/libs/zbxhistory/history.h4
-rw-r--r--src/libs/zbxhistory/history_elastic.c127
-rw-r--r--src/libs/zbxjson/json.c6
-rw-r--r--src/libs/zbxjson/jsonpath.c4
-rw-r--r--src/libs/zbxserver/Makefile.am5
-rw-r--r--src/libs/zbxserver/evalfunc.c202
-rw-r--r--src/libs/zbxserver/evalfunc.h2
-rw-r--r--src/libs/zbxserver/evalfunc2.c2409
-rw-r--r--src/libs/zbxserver/expression.c1555
-rw-r--r--src/libs/zbxserver/expression.h42
-rw-r--r--src/libs/zbxserver/expression_eval.c1848
-rw-r--r--src/libs/zbxtrends/cache.c4
-rw-r--r--src/libs/zbxtrends/trends.c176
-rw-r--r--src/zabbix_proxy/Makefile.am3
-rw-r--r--src/zabbix_server/Makefile.am3
-rw-r--r--src/zabbix_server/alerter/alert_syncer.c8
-rw-r--r--src/zabbix_server/escalator/escalator.c7
-rw-r--r--src/zabbix_server/events.c68
-rw-r--r--src/zabbix_server/lld/lld_host.c2
-rw-r--r--src/zabbix_server/lld/lld_item.c80
-rw-r--r--src/zabbix_server/lld/lld_trigger.c533
-rw-r--r--src/zabbix_server/operations.c2
-rw-r--r--src/zabbix_server/poller/Makefile.am2
-rw-r--r--src/zabbix_server/poller/checks_aggregate.c143
-rw-r--r--src/zabbix_server/poller/checks_calculated.c393
-rw-r--r--src/zabbix_server/poller/poller.c4
-rw-r--r--src/zabbix_server/preprocessor/preproc_history.h1
-rw-r--r--src/zabbix_server/server.c21
-rw-r--r--src/zabbix_server/trapper/trapper_expressions_evaluate.c54
-rw-r--r--src/zabbix_server/trapper/trapper_item_test.c16
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, &macro_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, &macro_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, &param_pos, &param_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, &quoted);
- 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 = &params->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 = &params->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 = &params->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 = &params->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 = &params->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 = &params->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 = &params->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(&parameter, 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(&params);
+
+ dbpatch_parse_function_params(function->parameter, &params);
+
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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, &params, replace);
+ }
+ else if (0 == strcmp(function->name, "forecast"))
+ {
+ dbpatch_convert_params(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&parameter, function->parameter, &params,
+ 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(&params);
+}
+
+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(&params);
+ 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(&params);
+
+ 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, &macro);
+ 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, &param_pos, &param_len, &sep_pos);
+
+ arg0 = zbx_function_param_unquote_dyn(ptr + par_l + 1 + param_pos, param_len, &quoted);
+ 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, &params, &params_alloc, &params_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(&regexps);
+
+ 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(&regexps, 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(&regexps, 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(&regexps);
+ zbx_vector_ptr_destroy(&regexps);
+
+ 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(&regexps);
+
+ 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(&regexps, 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(&regexps, 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(&regexps);
+ zbx_vector_ptr_destroy(&regexps);
+
+ 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(&regexps);
+ 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(&regexps, 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, &regexps);
+ }
+ }
+ 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, &regexps);
+ }
+ }
+ 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, &regexps);
+ 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, &regexps);
+ }
+
+ 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(&regexps);
+ zbx_vector_ptr_destroy(&regexps);
+
+ 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, &macro_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, &macro_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(&params, &params_alloc, &params_offset, ',');
+
+ switch (args[i].type)
+ {
+ case ZBX_VARIANT_DBL:
+ zbx_snprintf_alloc(&params, &params_alloc, &params_offset, ZBX_FS_DBL64,
+ args[i].data.dbl);
+ break;
+ case ZBX_VARIANT_STR:
+ zbx_strquote_alloc(&params, &params_alloc, &params_offset, args[i].data.str);
+ break;
+ case ZBX_VARIANT_UI64:
+ zbx_snprintf_alloc(&params, &params_alloc, &params_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, &macro_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, &macro_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, &macro_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, &param_pos, &param_len, &sep_pos);
-
- zbx_free(buf);
- buf = zbx_function_param_unquote_dyn(e + par_l + 1 + param_pos, param_len, &quoted);
-
- 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));