diff options
author | Vladimirs Maksimovs <vladimirs.maksimovs@zabbix.com> | 2022-11-04 17:52:15 +0300 |
---|---|---|
committer | Vladimirs Maksimovs <vladimirs.maksimovs@zabbix.com> | 2022-11-04 17:52:15 +0300 |
commit | 4f4d9097321fddf2267c4d9ddd9837ec77491032 (patch) | |
tree | 2912c10ec62dc429a95a773f77eac493a114ba40 | |
parent | e27a0bcd3dcdf27d3108e1f7c9b61d975167cb07 (diff) | |
parent | b2b8335f76e81892cce2f555caf25b2ffa04432d (diff) |
.......... [ZBXNEXT-6470,ZBXNEXT-6980] updated to latest from master; no conflicts
57 files changed, 2579 insertions, 935 deletions
diff --git a/.gitignore b/.gitignore index a7b68a17a62..1f5a959de20 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,7 @@ tests/libs/zbxcommon/zbx_function_find tests/libs/zbxcommon/zbx_function_get_param_dyn tests/libs/zbxcommon/zbx_get_week_number tests/libs/zbxcommon/zbx_interval_preproc +tests/libs/zbxcommon/zbx_iso8601_utc tests/libs/zbxcommon/zbx_json_to_xml tests/libs/zbxcommon/zbx_ltrim_utf8 tests/libs/zbxcommon/zbx_rtrim_utf8 @@ -190,8 +191,8 @@ tests/libs/zbxhistory/zbx_history_get_values tests/libs/zbxjson/zbx_json_decodevalue tests/libs/zbxjson/zbx_json_decodevalue_dyn tests/libs/zbxjson/zbx_json_open_path +tests/libs/zbxjson/zbx_jsonobj_query tests/libs/zbxjson/zbx_jsonpath_compile -tests/libs/zbxjson/zbx_jsonpath_query tests/libs/zbxprometheus/prometheus_filter_init tests/libs/zbxprometheus/prometheus_parse_row tests/libs/zbxprometheus/zbx_prometheus_pattern @@ -205,6 +206,8 @@ tests/libs/zbxserver/macro_fmttime tests/libs/zbxserver/substitute_lld_macros tests/libs/zbxserver/valuemaps tests/libs/zbxsysinfo/check_key_access_rules +tests/libs/zbxsysinfo/zbx_execute_agent_check +tests/libs/zbxsysinfo/zbx_execute_agent_check_http tests/libs/zbxsysinfo/common/system_localtime tests/libs/zbxsysinfo/common/vfs_file_exists tests/libs/zbxsysinfo/common/web_page_get @@ -226,6 +229,7 @@ tests/libs/zbxcommon/zbx_tm_add tests/libs/zbxcommon/zbx_tm_sub tests/libs/zbxcommon/zbx_tm_round_up tests/libs/zbxcommon/zbx_tm_round_down +tests/libs/zbxtime/zbx_iso8601_utc tests/libs/zbxtrends/zbx_baseline_get_data tests/libs/zbxtrends/zbx_trends_parse_range tests/libs/zbxsysinfo/process_http diff --git a/ChangeLog.d/bugfix/ZBX-21655 b/ChangeLog.d/bugfix/ZBX-21655 new file mode 100644 index 00000000000..beffcf71e44 --- /dev/null +++ b/ChangeLog.d/bugfix/ZBX-21655 @@ -0,0 +1 @@ +.......PS. [ZBX-21655] fixed VMware datastore discovery to not return same datastore multiple times (asestakovs) diff --git a/ChangeLog.d/bugfix/ZBX-21677 b/ChangeLog.d/bugfix/ZBX-21677 new file mode 100644 index 00000000000..56d9cc310f2 --- /dev/null +++ b/ChangeLog.d/bugfix/ZBX-21677 @@ -0,0 +1 @@ +..F....... [ZBX-21677] fixed checkbox resetting in Monitoring->Latest data and Monitoring->Problems (rdetlavs) diff --git a/ChangeLog.d/bugfix/ZBX-21687 b/ChangeLog.d/bugfix/ZBX-21687 new file mode 100644 index 00000000000..111d856e167 --- /dev/null +++ b/ChangeLog.d/bugfix/ZBX-21687 @@ -0,0 +1 @@ +..F....... [ZBX-21687] fixed persistent preloader icons over dashboard widgets on Safari 16 (averza) diff --git a/ChangeLog.d/bugfix/ZBX-21689 b/ChangeLog.d/bugfix/ZBX-21689 new file mode 100644 index 00000000000..0a25ef361b5 --- /dev/null +++ b/ChangeLog.d/bugfix/ZBX-21689 @@ -0,0 +1 @@ +...G...... [ZBX-21689] fixed proc.num to not crash agent on Windows when 'user' parameter is set (asestakovs) diff --git a/ChangeLog.d/feature/ZBXNEXT-7940 b/ChangeLog.d/feature/ZBXNEXT-7940 new file mode 100644 index 00000000000..4cefde4bc30 --- /dev/null +++ b/ChangeLog.d/feature/ZBXNEXT-7940 @@ -0,0 +1 @@ +.........T [ZBXNEXT-7940] added template OS processes by Zabbix agent (egordymov) diff --git a/ChangeLog.d/feature/ZBXNEXT-8040 b/ChangeLog.d/feature/ZBXNEXT-8040 new file mode 100644 index 00000000000..15238ae2c93 --- /dev/null +++ b/ChangeLog.d/feature/ZBXNEXT-8040 @@ -0,0 +1 @@ +.......PS. [ZBXNEXT-8040] added object based json parsing and jsonpath optimizations (wiper) diff --git a/build/mingw/Makefile b/build/mingw/Makefile index 66dabc2c9e6..dd7c35a73b1 100644 --- a/build/mingw/Makefile +++ b/build/mingw/Makefile @@ -34,6 +34,7 @@ OBJS = \ $(OUTPUTDIR)\md5.o \ $(OUTPUTDIR)\sysinfo.o \ $(OUTPUTDIR)\vector.o \ + $(OUTPUTDIR)\hashset.o \ $(OUTPUTDIR)\zbxregexp.o \ $(OUTPUTDIR)\persistent_state.o \ $(OUTPUTDIR)\logfiles.o \ @@ -42,6 +43,7 @@ OBJS = \ $(OUTPUTDIR)\json.o \ $(OUTPUTDIR)\json_parser.o \ $(OUTPUTDIR)\jsonpath.o \ + $(OUTPUTDIR)\jsonobj.o \ $(OUTPUTDIR)\sha256crypt.o \ $(OUTPUTDIR)\variant.o \ $(OUTPUTDIR)\sysinfo_system.o \ @@ -179,6 +181,9 @@ $(OUTPUTDIR)\json_parser.o: $(TOPDIR)\src\libs\zbxjson\json_parser.c $(OUTPUTDIR)\jsonpath.o: $(TOPDIR)\src\libs\zbxjson\jsonpath.c $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ +$(OUTPUTDIR)\jsonobj.o: $(TOPDIR)\src\libs\zbxjson\jsonobj.c + $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ + $(OUTPUTDIR)\sha256crypt.o: $(TOPDIR)\src\libs\zbxhash\sha256crypt.c $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ @@ -209,6 +214,9 @@ $(OUTPUTDIR)\sysinfo_alias.o: $(TOPDIR)\src\libs\zbxsysinfo\alias\alias.c $(OUTPUTDIR)\vector.o: $(TOPDIR)\src\libs\zbxalgo\vector.c $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@ +$(OUTPUTDIR)\hashset.o: $(TOPDIR)\src\libs\zbxalgo\hashset.c + $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@ + $(OUTPUTDIR)\version.o: $(TOPDIR)\src\libs\zbxversion\version.c $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@ diff --git a/build/win32/project/Makefile_agent b/build/win32/project/Makefile_agent index ae7cde69102..8b37be7e784 100644 --- a/build/win32/project/Makefile_agent +++ b/build/win32/project/Makefile_agent @@ -37,6 +37,7 @@ ADD_RFLAGS = /d "ZABBIX_AGENT" OBJS = \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxip\ip.o \ ..\..\..\src\libs\zbxip\iprange.o \ @@ -66,6 +67,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ diff --git a/build/win32/project/Makefile_get b/build/win32/project/Makefile_get index d10b275b2d4..8fe19a2417a 100644 --- a/build/win32/project/Makefile_get +++ b/build/win32/project/Makefile_get @@ -29,6 +29,7 @@ ADD_RFLAGS = /d "ZABBIX_GET" OBJS = \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxip\ip.o \ ..\..\..\src\libs\zbxip\iprange.o \ @@ -56,6 +57,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ diff --git a/build/win32/project/Makefile_sender b/build/win32/project/Makefile_sender index d7443a2560e..e22e8ce79df 100644 --- a/build/win32/project/Makefile_sender +++ b/build/win32/project/Makefile_sender @@ -56,6 +56,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ @@ -64,6 +65,7 @@ OBJS = \ ..\..\..\src\libs\zbxwin32\fatal.o \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxregexp\zbxregexp.o \ ..\..\..\src\libs\zbxversion\version.o \ ..\..\..\src\libs\zbxxml\xml.o \ diff --git a/build/win32/project/Makefile_sender_dll b/build/win32/project/Makefile_sender_dll index eb8275cba64..0a5d1c05fd8 100644 --- a/build/win32/project/Makefile_sender_dll +++ b/build/win32/project/Makefile_sender_dll @@ -60,6 +60,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ @@ -68,6 +69,7 @@ OBJS = \ ..\..\..\src\libs\zbxwin32\fatal.o \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxregexp\zbxregexp.o \ ..\..\..\src\libs\zbxversion\version.o \ ..\..\..\src\libs\zbxxml\xml.o \ diff --git a/include/zbxjson.h b/include/zbxjson.h index 28ed6fd68a6..e5c91376b14 100644 --- a/include/zbxjson.h +++ b/include/zbxjson.h @@ -21,6 +21,7 @@ #define ZABBIX_ZJSON_H #include "zbxtypes.h" +#include "zbxalgo.h" #define ZBX_PROTO_TAG_CLOCK "clock" #define ZBX_PROTO_TAG_NS "ns" @@ -250,7 +251,8 @@ typedef enum ZBX_JSON_TYPE_OBJECT, ZBX_JSON_TYPE_NULL, ZBX_JSON_TYPE_TRUE, - ZBX_JSON_TYPE_FALSE + ZBX_JSON_TYPE_FALSE, + ZBX_JSON_TYPE_NUMBER } zbx_json_type_t; @@ -330,11 +332,51 @@ typedef struct /* set to 1 when jsonpath points at single location */ unsigned char definite; + unsigned char first_match; /* set to 1 if first match must be returned */ } zbx_jsonpath_t; -void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath); +typedef struct zbx_jsonobj zbx_jsonobj_t; + +ZBX_PTR_VECTOR_DECL(jsonobj_ptr, zbx_jsonobj_t *) + +typedef union +{ + char *string; + double number; + zbx_hashset_t object; + zbx_vector_jsonobj_ptr_t array; +} +zbx_jsonobj_data_t; + +typedef struct +{ + char *path; /* the path that was indexed - for example @.a.b.c */ + zbx_hashset_t objects; +} +zbx_jsonobj_index_t; + +struct zbx_jsonobj +{ + zbx_json_type_t type; + zbx_jsonobj_data_t data; + zbx_jsonobj_index_t *index; +}; + +typedef struct +{ + char *name; + zbx_jsonobj_t value; +} +zbx_jsonobj_el_t; + int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath); int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output); +void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath); + +int zbx_jsonobj_open(const char *data, zbx_jsonobj_t *obj); +void zbx_jsonobj_clear(zbx_jsonobj_t *obj); +int zbx_jsonobj_query(zbx_jsonobj_t *obj, const char *path, char **output); +int zbx_jsonobj_to_string(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj); #endif /* ZABBIX_ZJSON_H */ diff --git a/src/go/pkg/zbxlib/globals_windows.go b/src/go/pkg/zbxlib/globals_windows.go index 12873d2aa62..f01106f489f 100644 --- a/src/go/pkg/zbxlib/globals_windows.go +++ b/src/go/pkg/zbxlib/globals_windows.go @@ -49,6 +49,7 @@ package zbxlib #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/md5.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/vector.o +#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/hashset.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/zbxregexp.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/algodefs.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/persistent_state.o @@ -56,6 +57,7 @@ package zbxlib #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json_parser.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonpath.o +#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonobj.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sha256crypt.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/variant.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo_system.o diff --git a/src/libs/zbxjson/Makefile.am b/src/libs/zbxjson/Makefile.am index ad27f80c98e..f7981730a30 100644 --- a/src/libs/zbxjson/Makefile.am +++ b/src/libs/zbxjson/Makefile.am @@ -8,4 +8,6 @@ libzbxjson_a_SOURCES = \ json_parser.c \ json_parser.h \ jsonpath.c \ - jsonpath.h + jsonpath.h \ + jsonobj.c \ + jsonobj.h diff --git a/src/libs/zbxjson/json.c b/src/libs/zbxjson/json.c index e819f23e892..539c4d834bc 100644 --- a/src/libs/zbxjson/json.c +++ b/src/libs/zbxjson/json.c @@ -841,7 +841,7 @@ static unsigned int zbx_json_decode_character(const char **p, unsigned char *byt * string copying failed. * * * ******************************************************************************/ -static const char *zbx_json_copy_string(const char *p, char *out, size_t size) +const char *json_copy_string(const char *p, char *out, size_t size) { char *start = out; @@ -920,7 +920,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j /* only primitive values are decoded */ return NULL; default: - if (0 == (len = json_parse_value(p, NULL))) + if (0 == (len = json_parse_value(p, NULL, NULL))) return NULL; } @@ -930,7 +930,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j switch (type_local) { case ZBX_JSON_TYPE_STRING: - return zbx_json_copy_string(p, string, size); + return json_copy_string(p, string, size); case ZBX_JSON_TYPE_NULL: if (0 == size) return NULL; @@ -954,7 +954,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin /* only primitive values are decoded */ return NULL; default: - if (0 == (len = json_parse_value(p, NULL))) + if (0 == (len = json_parse_value(p, NULL, NULL))) return NULL; } @@ -970,7 +970,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin switch (type_local) { case ZBX_JSON_TYPE_STRING: - return zbx_json_copy_string(p, *string, *string_alloc); + return json_copy_string(p, *string, *string_alloc); case ZBX_JSON_TYPE_NULL: **string = '\0'; return p + len; @@ -987,7 +987,7 @@ const char *zbx_json_pair_next(const struct zbx_json_parse *jp, const char *p, c if (ZBX_JSON_TYPE_STRING != __zbx_json_type(p)) return NULL; - if (NULL == (p = zbx_json_copy_string(p, name, len))) + if (NULL == (p = json_copy_string(p, name, len))) return NULL; SKIP_WHITESPACE(p); @@ -1219,7 +1219,7 @@ int zbx_json_open_path(const struct zbx_json_parse *jp, const char *path, struct object.start = p; if (NULL == (object.end = __zbx_json_rbracket(p))) - object.end = p + json_parse_value(p, NULL) - 1; + object.end = p + json_parse_value(p, NULL, NULL) - 1; } *out = object; diff --git a/src/libs/zbxjson/json.h b/src/libs/zbxjson/json.h index e0c02e174ab..27006bc5f01 100644 --- a/src/libs/zbxjson/json.h +++ b/src/libs/zbxjson/json.h @@ -32,4 +32,6 @@ void zbx_set_json_strerror(const char *fmt, ...) __zbx_attr_format_printf(1, 2); +const char *json_copy_string(const char *p, char *out, size_t size); + #endif diff --git a/src/libs/zbxjson/json_parser.c b/src/libs/zbxjson/json_parser.c index 403a3ca2753..0ea71203ec8 100644 --- a/src/libs/zbxjson/json_parser.c +++ b/src/libs/zbxjson/json_parser.c @@ -21,27 +21,31 @@ #include "zbxcommon.h" #include "json.h" - -static zbx_int64_t json_parse_object(const char *start, char **error); +#include "jsonobj.h" /****************************************************************************** * * * Purpose: Prepares JSON parsing error message * * * - * Parameters: message - [IN] the error message * - * json_buffer - [IN] the failing data fragment * - * error - [OUT] the parsing error message (can be NULL) * + * Parameters: message - [IN] the error message * + * ptr - [IN] the failing data fragment * + * error - [OUT] the parsing error message (can be NULL) * * * * Return value: 0 - the json_error() function always returns 0 value * * so it can be used to return from failed parses * * * ******************************************************************************/ -static zbx_int64_t json_error(const char *message, const char *json_buffer, char **error) +zbx_int64_t json_error(const char *message, const char *ptr, char **error) { if (NULL != error) { - if (NULL != json_buffer) - *error = zbx_dsprintf(*error, "%s at: '%s'", message, json_buffer); + if (NULL != ptr) + { + if (128 < strlen(ptr)) + *error = zbx_dsprintf(*error, "%s at: '%128s...'", message, ptr); + else + *error = zbx_dsprintf(*error, "%s at: '%s'", message, ptr); + } else *error = zbx_strdup(*error, message); } @@ -54,6 +58,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char * Purpose: Parses JSON string value or object name * * * * Parameters: start - [IN] the JSON data without leading whitespace * + * str - [OUT] the parsed unquoted string (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -61,7 +66,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_string(const char *start, char **error) +static zbx_int64_t json_parse_string(const char *start, char **str, char **error) { const char *ptr = start; @@ -119,6 +124,12 @@ static zbx_int64_t json_parse_string(const char *start, char **error) ptr++; } + if (NULL != str) + { + *str = (char *)zbx_malloc(NULL, (size_t)(ptr - start)); + json_copy_string(start, *str, (size_t)(ptr - start)); + } + return ptr - start + 1; } @@ -127,6 +138,7 @@ static zbx_int64_t json_parse_string(const char *start, char **error) * Purpose: Parses JSON array value * * * * Parameters: start - [IN] the JSON data without leading whitespace * + * obj - [IN/OUT] the JSON object (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -134,11 +146,14 @@ static zbx_int64_t json_parse_string(const char *start, char **error) * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_array(const char *start, char **error) +zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error) { const char *ptr = start; zbx_int64_t len; + if (NULL != obj) + jsonobj_init(obj, ZBX_JSON_TYPE_ARRAY); + ptr++; SKIP_WHITESPACE(ptr); @@ -146,9 +161,29 @@ static zbx_int64_t json_parse_array(const char *start, char **error) { while (1) { + zbx_jsonobj_t *value; + + if (NULL != obj) + { + value = zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + jsonobj_init(value, ZBX_JSON_TYPE_UNKNOWN); + } + else + value = NULL; + /* json_parse_value strips leading whitespace, so we don't have to do it here */ - if (0 == (len = json_parse_value(ptr, error))) + if (0 == (len = json_parse_value(ptr, value, error))) + { + if (NULL != obj) + { + zbx_jsonobj_clear(value); + zbx_free(value); + } return 0; + } + + if (NULL != obj) + zbx_vector_jsonobj_ptr_append(&obj->data.array, value); ptr += len; SKIP_WHITESPACE(ptr); @@ -171,15 +206,16 @@ static zbx_int64_t json_parse_array(const char *start, char **error) * * * Purpose: Parses JSON number value * * * - * Parameters: start - [IN] the JSON data without leading whitespace * - * error - [OUT] the parsing error message (can be NULL) * + * Parameters: start - [IN] the JSON data without leading whitespace * + * number - [OUT] the parsed number (can be NULL) * + * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * * error parameter (if not NULL) contains allocated error * * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_number(const char *start, char **error) +static zbx_int64_t json_parse_number(const char *start, double *number, char **error) { const char *ptr = start; char first_digit; @@ -235,6 +271,9 @@ static zbx_int64_t json_parse_number(const char *start, char **error) } } + if (NULL != number) + *number = atof(start); + return ptr - start; } @@ -281,10 +320,12 @@ static zbx_int64_t json_parse_literal(const char *start, const char *text, char * message. * * * ******************************************************************************/ -zbx_int64_t json_parse_value(const char *start, char **error) +zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error) { const char *ptr = start; zbx_int64_t len; + char *str = NULL; + double number; SKIP_WHITESPACE(ptr); @@ -293,28 +334,40 @@ zbx_int64_t json_parse_value(const char *start, char **error) case '\0': return json_error("unexpected end of object value", NULL, error); case '"': - if (0 == (len = json_parse_string(ptr, error))) + if (0 == (len = json_parse_string(ptr, (NULL != obj ? &str : NULL), error))) return 0; + + if (NULL != obj) + jsonobj_set_string(obj, str); break; case '{': - if (0 == (len = json_parse_object(ptr, error))) + if (0 == (len = json_parse_object(ptr, obj, error))) return 0; break; case '[': - if (0 == (len = json_parse_array(ptr, error))) + if (0 == (len = json_parse_array(ptr, obj, error))) return 0; break; case 't': if (0 == (len = json_parse_literal(ptr, "true", error))) return 0; + + if (NULL != obj) + jsonobj_set_true(obj); break; case 'f': if (0 == (len = json_parse_literal(ptr, "false", error))) return 0; + + if (NULL != obj) + jsonobj_set_false(obj); break; case 'n': if (0 == (len = json_parse_literal(ptr, "null", error))) return 0; + + if (NULL != obj) + jsonobj_set_null(obj); break; case '0': case '1': @@ -327,8 +380,12 @@ zbx_int64_t json_parse_value(const char *start, char **error) case '8': case '9': case '-': - if (0 == (len = json_parse_number(ptr, error))) + if (0 == (len = json_parse_number(ptr, (NULL != obj ? &number : NULL), error))) return 0; + + if (NULL != obj) + jsonobj_set_number(obj, number); + break; default: return json_error("invalid JSON object value starting character", ptr, error); @@ -342,6 +399,7 @@ zbx_int64_t json_parse_value(const char *start, char **error) * Purpose: Parses JSON object * * * * Parameters: start - [IN] the JSON data * + * obj - [IN/OUT] the JSON object (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -349,10 +407,13 @@ zbx_int64_t json_parse_value(const char *start, char **error) * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_object(const char *start, char **error) +zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error) { - const char *ptr = start; - zbx_int64_t len; + const char *ptr = start; + zbx_int64_t len; + + if (NULL != obj) + jsonobj_init(obj, ZBX_JSON_TYPE_OBJECT); /* parse object name */ SKIP_WHITESPACE(ptr); @@ -364,11 +425,15 @@ static zbx_int64_t json_parse_object(const char *start, char **error) { while (1) { + zbx_jsonobj_el_t el; + if ('"' != *ptr) return json_error("invalid object name", ptr, error); + jsonobj_el_init(&el); + /* cannot parse object name, failing */ - if (0 == (len = json_parse_string(ptr, error))) + if (0 == (len = json_parse_string(ptr, (NULL != obj ? &el.name : NULL), error))) return 0; ptr += len; @@ -377,11 +442,21 @@ static zbx_int64_t json_parse_object(const char *start, char **error) SKIP_WHITESPACE(ptr); if (':' != *ptr) + { + jsonobj_el_clear(&el); return json_error("invalid object name/value separator", ptr, error); + } + ptr++; - if (0 == (len = json_parse_value(ptr, error))) + if (0 == (len = json_parse_value(ptr, (NULL != obj ? &el.value : NULL), error))) + { + jsonobj_el_clear(&el); return 0; + } + + if (NULL != obj) + zbx_hashset_insert(&obj->data.object, &el, sizeof(el)); ptr += len; @@ -426,11 +501,11 @@ zbx_int64_t zbx_json_validate(const char *start, char **error) switch (*start) { case '{': - if (0 == (len = json_parse_object(start, error))) + if (0 == (len = json_parse_object(start, NULL, error))) return 0; break; case '[': - if (0 == (len = json_parse_array(start, error))) + if (0 == (len = json_parse_array(start, NULL, error))) return 0; break; default: diff --git a/src/libs/zbxjson/json_parser.h b/src/libs/zbxjson/json_parser.h index f9fb3bb8cf5..cd54939eb7d 100644 --- a/src/libs/zbxjson/json_parser.h +++ b/src/libs/zbxjson/json_parser.h @@ -21,9 +21,15 @@ #define ZABBIX_JSON_PARSER_H #include "zbxtypes.h" +#include "jsonobj.h" zbx_int64_t zbx_json_validate(const char *start, char **error); -zbx_int64_t json_parse_value(const char *start, char **error); +zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error); + +zbx_int64_t json_error(const char *message, const char *ptr, char **error); + +zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error); +zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error); #endif diff --git a/src/libs/zbxjson/jsonobj.c b/src/libs/zbxjson/jsonobj.c new file mode 100644 index 00000000000..2a7c365284c --- /dev/null +++ b/src/libs/zbxjson/jsonobj.c @@ -0,0 +1,366 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 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 "jsonobj.h" + +#include "json_parser.h" +#include "json.h" +#include "zbxstr.h" + +ZBX_PTR_VECTOR_IMPL(jsonobj_ptr, zbx_jsonobj_t *) +ZBX_VECTOR_IMPL(jsonobj_ref, zbx_jsonobj_ref_t) + +/* jsonobject index hashset support */ + +static zbx_hash_t jsonobj_index_el_hash(const void *v) +{ + const zbx_jsonobj_index_el_t *el = (const zbx_jsonobj_index_el_t *)v; + + return ZBX_DEFAULT_STRING_HASH_FUNC(el->value); +} + +static int jsonobj_index_el_compare(const void *v1, const void *v2) +{ + const zbx_jsonobj_index_el_t *el1 = (const zbx_jsonobj_index_el_t *)v1; + const zbx_jsonobj_index_el_t *el2 = (const zbx_jsonobj_index_el_t *)v2; + + return strcmp(el1->value, el2->value); +} + +/* jsonobject values hashset support */ + +static zbx_hash_t jsonobj_el_hash(const void *v) +{ + const zbx_jsonobj_el_t *el = (const zbx_jsonobj_el_t *)v; + + return ZBX_DEFAULT_STRING_HASH_FUNC(el->name); +} + +static int jsonobj_el_compare(const void *v1, const void *v2) +{ + const zbx_jsonobj_el_t *el1 = (const zbx_jsonobj_el_t *)v1; + const zbx_jsonobj_el_t *el2 = (const zbx_jsonobj_el_t *)v2; + + return strcmp(el1->name, el2->name); +} + +/****************************************************************************** + * * + * Purpose: initialize json object structure * + * * + * Parameters: obj - [IN/OUT] the json object to initialize * + * type - [IN] the json object type * + * * + ******************************************************************************/ +void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type) +{ + obj->type = type; + + switch (type) + { + case ZBX_JSON_TYPE_ARRAY: + zbx_vector_jsonobj_ptr_create(&obj->data.array); + break; + case ZBX_JSON_TYPE_OBJECT: + zbx_hashset_create(&obj->data.object, 0, jsonobj_el_hash, jsonobj_el_compare); + break; + default: + memset(&obj->data, 0, sizeof(obj->data)); + break; + } + + obj->index = NULL; +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object index element * + * * + * Parameters: v - [IN] the json index element * + * * + ******************************************************************************/ +static void jsonobj_index_el_clear(void *v) +{ + zbx_jsonobj_index_el_t *el = (zbx_jsonobj_index_el_t *)v; + int i; + + zbx_free(el->value); + for (i = 0; i < el->objects.values_num; i++) + { + zbx_free(el->objects.values[i].name); + + if (0 != el->objects.values[i].external) + { + zbx_jsonobj_clear(el->objects.values[i].value); + zbx_free(el->objects.values[i].value); + } + } + + zbx_vector_jsonobj_ref_destroy(&el->objects); +} + +/****************************************************************************** + * * + * Purpose: initialize json object index * + * * + * Parameters: obj - [IN/OUT] the json object * + * path - [IN] the indexed relative path * + * * + ******************************************************************************/ +void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path) +{ + obj->index = (zbx_jsonobj_index_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_index_t)); + obj->index->path = zbx_strdup(NULL, path); + zbx_hashset_create_ext(&obj->index->objects, 0, jsonobj_index_el_hash, jsonobj_index_el_compare, + jsonobj_index_el_clear, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, + ZBX_DEFAULT_MEM_FREE_FUNC); +} + +/****************************************************************************** + * * + * Purpose: set string value to json object * + * * + ******************************************************************************/ +void jsonobj_set_string(zbx_jsonobj_t *obj, char *str) +{ + obj->type = ZBX_JSON_TYPE_STRING; + obj->data.string = str; +} + +/****************************************************************************** + * * + * Purpose: set numeric value to json object * + * * + ******************************************************************************/ +void jsonobj_set_number(zbx_jsonobj_t *obj, double number) +{ + obj->type = ZBX_JSON_TYPE_NUMBER; + obj->data.number = number; +} + +/****************************************************************************** + * * + * Purpose: set true value to json object * + * * + ******************************************************************************/ +void jsonobj_set_true(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_TRUE; +} + +/****************************************************************************** + * * + * Purpose: set false value to json object * + * * + ******************************************************************************/ +void jsonobj_set_false(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_FALSE; +} + +/****************************************************************************** + * * + * Purpose: set null value to json object * + * * + ******************************************************************************/ +void jsonobj_set_null(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_NULL; +} + +/****************************************************************************** + * * + * Purpose: initialize json object element * + * * + ******************************************************************************/ +void jsonobj_el_init(zbx_jsonobj_el_t *el) +{ + el->name = NULL; + jsonobj_init(&el->value, ZBX_JSON_TYPE_UNKNOWN); +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object element * + * * + ******************************************************************************/ +void jsonobj_el_clear(zbx_jsonobj_el_t *el) +{ + zbx_free(el->name); + zbx_jsonobj_clear(&el->value); +} + +/****************************************************************************** + * * + * Purpose: free json object * + * * + ******************************************************************************/ +static void jsonobj_free(zbx_jsonobj_t *obj) +{ + zbx_jsonobj_clear(obj); + zbx_free(obj); +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object * + * * + ******************************************************************************/ +void zbx_jsonobj_clear(zbx_jsonobj_t *obj) +{ + zbx_jsonobj_el_t *el; + zbx_hashset_iter_t iter; + + switch (obj->type) + { + case ZBX_JSON_TYPE_STRING: + zbx_free(obj->data.string); + break; + case ZBX_JSON_TYPE_ARRAY: + zbx_vector_jsonobj_ptr_clear_ext(&obj->data.array, jsonobj_free); + zbx_vector_jsonobj_ptr_destroy(&obj->data.array); + break; + case ZBX_JSON_TYPE_OBJECT: + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + zbx_free(el->name); + zbx_jsonobj_clear(&el->value); + } + zbx_hashset_destroy(&obj->data.object); + break; + default: + break; + } + + if (NULL != obj->index) + { + zbx_free(obj->index->path); + zbx_hashset_destroy(&obj->index->objects); + zbx_free(obj->index); + } +} + +/****************************************************************************** + * * + * Purpose: convert json object to text format * + * * + ******************************************************************************/ +int zbx_jsonobj_to_string(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj) +{ + char *tmp, buf[32], delim; + int i; + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; + + switch (obj->type) + { + case ZBX_JSON_TYPE_TRUE: + zbx_strcpy_alloc(str, str_alloc, str_offset, "true"); + break; + case ZBX_JSON_TYPE_FALSE: + zbx_strcpy_alloc(str, str_alloc, str_offset, "false"); + break; + case ZBX_JSON_TYPE_NULL: + zbx_strcpy_alloc(str, str_alloc, str_offset, "null"); + break; + case ZBX_JSON_TYPE_STRING: + tmp = zbx_strdup(NULL, obj->data.string); + zbx_json_escape(&tmp); + zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp); + zbx_free(tmp); + break; + case ZBX_JSON_TYPE_NUMBER: + zbx_print_double(buf, sizeof(buf), obj->data.number); + zbx_strcpy_alloc(str, str_alloc, str_offset, buf); + break; + case ZBX_JSON_TYPE_ARRAY: + delim = '['; + for (i = 0; i < obj->data.array.values_num; i++) + { + zbx_chrcpy_alloc(str, str_alloc, str_offset, delim); + delim = ','; + zbx_jsonobj_to_string(str, str_alloc, str_offset, obj->data.array.values[i]); + } + zbx_chrcpy_alloc(str, str_alloc, str_offset, ']'); + break; + case ZBX_JSON_TYPE_OBJECT: + delim = '{'; + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + zbx_chrcpy_alloc(str, str_alloc, str_offset, delim); + delim = ','; + + tmp = zbx_strdup(NULL, el->name); + zbx_json_escape(&tmp); + zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp); + zbx_chrcpy_alloc(str, str_alloc, str_offset, ':'); + zbx_free(tmp); + + zbx_jsonobj_to_string(str, str_alloc, str_offset, &el->value); + } + zbx_chrcpy_alloc(str, str_alloc, str_offset, '}'); + break; + default: + zbx_set_json_strerror("unknown json object with type: %u", obj->type); + return FAIL; + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Purpose: parses json formatted data into json object structure * + * * + ******************************************************************************/ +int zbx_jsonobj_open(const char *data, zbx_jsonobj_t *obj) +{ + int ret = FAIL; + char *error = NULL; + + switch (*data) + { + case '{': + if (0 == json_parse_object(data, obj, &error)) + goto out; + break; + case '[': + if (0 == json_parse_array(data, obj, &error)) + goto out; + break; + default: + /* not json data, failing */ + jsonobj_init(obj, ZBX_JSON_TYPE_UNKNOWN); + (void)json_error("invalid object format, expected opening character '{' or '['", data, &error); + goto out; + } + + ret = SUCCEED; +out: + if (FAIL == ret) + { + zbx_jsonobj_clear(obj); + zbx_set_json_strerror("%s", error); + zbx_free(error); + } + + return ret; +} diff --git a/src/libs/zbxjson/jsonobj.h b/src/libs/zbxjson/jsonobj.h new file mode 100644 index 00000000000..dfd12df6b9b --- /dev/null +++ b/src/libs/zbxjson/jsonobj.h @@ -0,0 +1,56 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 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_JSONOBJ_H +#define ZABBIX_JSONOBJ_H + +#include "zbxjson.h" + +typedef struct +{ + char *name; + zbx_jsonobj_t *value; + unsigned char external; /* 1 - the reference is to an external object. */ + /* 0 - the reference is to a local object which must be freed */ + /* when reference is destroyed */ +} +zbx_jsonobj_ref_t; + +ZBX_VECTOR_DECL(jsonobj_ref, zbx_jsonobj_ref_t) + +typedef struct +{ + char *value; /* the value found at indexed path */ + zbx_vector_jsonobj_ref_t objects; /* the objects matching value at indexed path */ +} +zbx_jsonobj_index_el_t; + +void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type); + +void jsonobj_el_init(zbx_jsonobj_el_t *el); +void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path); +void jsonobj_el_clear(zbx_jsonobj_el_t *el); + +void jsonobj_set_string(zbx_jsonobj_t *obj, char *str); +void jsonobj_set_number(zbx_jsonobj_t *obj, double number); +void jsonobj_set_true(zbx_jsonobj_t *obj); +void jsonobj_set_false(zbx_jsonobj_t *obj); +void jsonobj_set_null(zbx_jsonobj_t *obj); + +#endif diff --git a/src/libs/zbxjson/jsonpath.c b/src/libs/zbxjson/jsonpath.c index b57aff6f8d5..eec2ffc6935 100644 --- a/src/libs/zbxjson/jsonpath.c +++ b/src/libs/zbxjson/jsonpath.c @@ -18,7 +18,6 @@ **/ #include "jsonpath.h" -#include "zbxjson.h" #include "zbxregexp.h" #include "zbxvariant.h" @@ -26,21 +25,17 @@ #include "zbxexpr.h" #include "json.h" #include "json_parser.h" +#include "jsonobj.h" typedef struct { - char *name; - const char *value; + zbx_jsonobj_t *root; /* the root object */ + zbx_jsonpath_t *path; + unsigned char found; /* set to 1 when one object was matched and */ + /* no more matches are required */ + zbx_vector_jsonobj_ref_t objects; /* the matched objects */ } -zbx_json_element_t; - -ZBX_VECTOR_DECL(json, zbx_json_element_t) -ZBX_VECTOR_IMPL(json, zbx_json_element_t) - -static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects); -static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects); +zbx_jsonpath_context_t; typedef struct { @@ -49,6 +44,10 @@ typedef struct } zbx_jsonpath_token_def_t; +static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth); +static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth); +static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj); + /* define token groups and precedence */ static zbx_jsonpath_token_def_t jsonpath_tokens[] = { {0, 0}, @@ -74,6 +73,7 @@ static zbx_jsonpath_token_def_t jsonpath_tokens[] = { {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7} /* ZBX_JSONPATH_TOKEN_OP_REGEXP */ }; + static int jsonpath_token_precedence(int type) { return jsonpath_tokens[type].precedence; @@ -84,31 +84,118 @@ static int jsonpath_token_group(int type) return jsonpath_tokens[type].group; } -/* json element vector support */ -static void zbx_vector_json_add_element(zbx_vector_json_t *elements, const char *name, const char *value) +/****************************************************************************** + * * + * Purpose: add external json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * name - [IN] the json object name or array index * + * value - [IN] the json object * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add_object(zbx_vector_jsonobj_ref_t *refs, const char *name, + zbx_jsonobj_t *value) { - zbx_json_element_t el; + zbx_jsonobj_ref_t ref; - el.name = zbx_strdup(NULL, name); - el.value = value; - zbx_vector_json_append(elements, el); + ref.name = zbx_strdup(NULL, name); + ref.external = 1; + + ref.value = value; + zbx_vector_jsonobj_ref_append(refs, ref); +} + +/****************************************************************************** + * * + * Purpose: add internal json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * name - [IN] the json object name or array index * + * str - [IN] the string value of the object * + * * + * Comments: This function will create json object and add internal reference,* + * meaning the object will be destroyed together with its reference.* + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add_string(zbx_vector_jsonobj_ref_t *refs, const char *name, + const char *str) +{ + zbx_jsonobj_ref_t ref; + + ref.name = zbx_strdup(NULL, name); + ref.external = 0; + + ref.value = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + jsonobj_init(ref.value, ZBX_JSON_TYPE_STRING); + jsonobj_set_string(ref.value, zbx_strdup(NULL, str)); + + zbx_vector_jsonobj_ref_append(refs, ref); +} + +/****************************************************************************** + * * + * Purpose: add a copy of json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * ref - [IN] the json object reference * + * * + * Comments: For internal references a new internal json object will be * + * created. * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add(zbx_vector_jsonobj_ref_t *refs, zbx_jsonobj_ref_t *ref) +{ + if (0 != ref->external) + zbx_vector_jsonobj_ref_add_object(refs, ref->name, ref->value); + else + zbx_vector_jsonobj_ref_add_string(refs, ref->name, ref->value->data.string); } -static void zbx_vector_json_copy(zbx_vector_json_t *dst, const zbx_vector_json_t *src) +/****************************************************************************** + * * + * Purpose: copy json object references from one vector to other * + * * + * Parameters: dst - [IN/OUT] the destination json object reference vector * + * src - [IN] the source json object reference vector * + * * + * Comments: For internal references a new internal json object will be * + * created. * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_copy(zbx_vector_jsonobj_ref_t *dst, const zbx_vector_jsonobj_ref_t *src) { int i; for (i = 0; i < src->values_num; i++) - zbx_vector_json_add_element(dst, src->values[i].name, src->values[i].value); + { + if (0 != src->values[i].external) + zbx_vector_jsonobj_ref_add_object(dst, src->values[i].name, src->values[i].value); + else + zbx_vector_jsonobj_ref_add_string(dst, src->values[i].name, src->values[i].value->data.string); + } } -static void zbx_vector_json_clear_ext(zbx_vector_json_t *elements) +/****************************************************************************** + * * + * Purpose: free resources allocated by json object reference * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_clear_ext(zbx_vector_jsonobj_ref_t *refs) { int i; - for (i = 0; i < elements->values_num; i++) - zbx_free(elements->values[i].name); - zbx_vector_json_clear(elements); + for (i = 0; i < refs->values_num; i++) + { + zbx_jsonobj_ref_t *ref = &refs->values[i]; + + zbx_free(ref->name); + if (0 == ref->external) + { + zbx_jsonobj_clear(ref->value); + zbx_free(ref->value); + } + } + zbx_vector_jsonobj_ref_clear(refs); } /****************************************************************************** @@ -215,16 +302,116 @@ static void jsonpath_list_free(zbx_jsonpath_list_node_t *list) /****************************************************************************** * * + * Purpose: append array index to list * + * * + ******************************************************************************/ +static zbx_jsonpath_list_node_t *jsonpath_list_append_index(zbx_jsonpath_list_node_t *head, int index, + int check_duplicate) +{ + zbx_jsonpath_list_node_t *node; + + if (0 != check_duplicate) + { + for (node = head; NULL != node; node = node->next) + { + int query_index; + + memcpy(&query_index, node->data, sizeof(query_index)); + if (query_index == index) + return head; + } + } + + node = jsonpath_list_create_node(sizeof(int)); + node->next = head; + memcpy(node->data, &index, sizeof(int)); + + return node; +} + +/****************************************************************************** + * * + * Purpose: append name to list * + * * + ******************************************************************************/ +static zbx_jsonpath_list_node_t *jsonpath_list_append_name(zbx_jsonpath_list_node_t *head, const char *name, size_t len) +{ + zbx_jsonpath_list_node_t *node, *new_node; + + new_node = jsonpath_list_create_node(len + 1); + jsonpath_unquote(new_node->data, name, len + 1); + + for (node = head; NULL != node; node = node->next) + { + if (0 == strcmp((char *)new_node->data, (char *)node->data)) + { + zbx_free(new_node); + return head; + } + } + + new_node->next = head; + + return new_node; +} + +/****************************************************************************** + * * + * Purpose: create jsonpath structure and compile json path * + * * + ******************************************************************************/ +static zbx_jsonpath_t *jsonpath_create_token_jsonpath(const char *text, size_t len) +{ + zbx_jsonpath_t *path; + char *tmp_text; + + tmp_text = jsonpath_strndup(text, len); + + if ('@' == *tmp_text) + *tmp_text = '$'; + + path = (zbx_jsonpath_t *)zbx_malloc(NULL, sizeof(zbx_jsonpath_t)); + + if (FAIL == zbx_jsonpath_compile(tmp_text, path)) + { + zbx_free(path); + goto out; + } + + if (1 != path->definite) + { + zbx_set_json_strerror("only simple path are supported in jsonpath expression: \"%s\"", text); + zbx_jsonpath_clear(path); + zbx_free(path); + goto out; + } + + if (ZBX_JSONPATH_SEGMENT_FUNCTION == path->segments[path->segments_num - 1].type) + { + zbx_set_json_strerror("functions are not supported in jsonpath expression: \"%s\"", text); + zbx_jsonpath_clear(path); + zbx_free(path); + } +out: + zbx_free(tmp_text); + + return path; +} + +/****************************************************************************** + * * * Purpose: create jsonpath expression token * * * * Parameters: type - [IN] the token type * * expression - [IN] the expression * * loc - [IN] the token location in the expression * * * - * Return value: The created token (must be freed by the caller). * + * Return value: The created token (must be freed by the caller) or * + * NULL in the case of error. * * * ******************************************************************************/ -static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *expression, const zbx_strloc_t *loc) +static zbx_jsonpath_token_t *jsonpath_create_token(unsigned char type, const char *expression, + const zbx_strloc_t *loc) { zbx_jsonpath_token_t *token; @@ -234,23 +421,46 @@ static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *express switch (token->type) { case ZBX_JSONPATH_TOKEN_CONST_STR: - token->data = jsonpath_unquote_dyn(expression + loc->l, loc->r - loc->l + 1); + token->text = jsonpath_unquote_dyn(expression + loc->l, loc->r - loc->l + 1); + token->path = NULL; break; case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + if (NULL == (token->path = jsonpath_create_token_jsonpath(expression + loc->l, + loc->r - loc->l + 1))) + { + zbx_free(token); + } + else + token->text = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1); + break; case ZBX_JSONPATH_TOKEN_CONST_NUM: - token->data = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1); + token->text = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1); + token->path = NULL; break; default: - token->data = NULL; + token->text = NULL; + token->path = NULL; } return token; } +/****************************************************************************** + * * + * Purpose: free jsonpath expression token * + * * + ******************************************************************************/ static void jsonpath_token_free(zbx_jsonpath_token_t *token) { - zbx_free(token->data); + zbx_free(token->text); + + if (NULL != token->path) + { + zbx_jsonpath_clear(token->path); + zbx_free(token->path); + } + zbx_free(token); } @@ -274,16 +484,21 @@ static void jsonpath_reserve(zbx_jsonpath_t *jsonpath, int num) jsonpath->segments_alloc *= 2; jsonpath->segments = (zbx_jsonpath_segment_t *)zbx_realloc(jsonpath->segments, - sizeof(zbx_jsonpath_segment_t) * jsonpath->segments_alloc); + sizeof(zbx_jsonpath_segment_t) * (size_t)jsonpath->segments_alloc); /* Initialize the memory allocated for new segments, as parser can set */ /* detached flag for the next segment, so the memory cannot be initialized */ /* when creating a segment. */ memset(jsonpath->segments + old_alloc, 0, - (jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t)); + (size_t)(jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t)); } } +/****************************************************************************** + * * + * Purpose: free resource allocated by jsonpath segment * + * * + ******************************************************************************/ static void jsonpath_segment_clear(zbx_jsonpath_segment_t *segment) { switch (segment->type) @@ -391,7 +606,7 @@ static int jsonpath_next(const char **pnext) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_parse_substring(const char *start, int *len) +static int jsonpath_parse_substring(const char *start, size_t *len) { const char *ptr; char quotes; @@ -400,7 +615,7 @@ static int jsonpath_parse_substring(const char *start, int *len) { if (*ptr == quotes) { - *len = ptr - start + 1; + *len = (size_t)(ptr - start + 1); return SUCCEED; } @@ -429,7 +644,7 @@ static int jsonpath_parse_substring(const char *start, int *len) * jsonpath filter expressions. * * * ******************************************************************************/ -static int jsonpath_parse_path(const char *start, int *len) +static int jsonpath_parse_path(const char *start, size_t *len) { const char *ptr = start + 1; @@ -439,7 +654,7 @@ static int jsonpath_parse_path(const char *start, int *len) return FAIL; } - *len = ptr - start; + *len = (size_t)(ptr - start); return SUCCEED; } @@ -454,7 +669,7 @@ static int jsonpath_parse_path(const char *start, int *len) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_parse_number(const char *start, int *len) +static int jsonpath_parse_number(const char *start, size_t *len) { const char *ptr = start; char *end; @@ -474,7 +689,7 @@ static int jsonpath_parse_number(const char *start, int *len) if (ptr != end || HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno) return FAIL; - *len = (int)(ptr - start); + *len = (size_t)(ptr - start); return SUCCEED; } @@ -494,14 +709,14 @@ static int jsonpath_parse_number(const char *start, int *len) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_expression_next_token(const char *expression, int pos, int prev_group, +static int jsonpath_expression_next_token(const char *expression, size_t pos, int prev_group, zbx_jsonpath_token_type_t *type, zbx_strloc_t *loc) { - int len; + size_t len; const char *ptr = expression + pos; SKIP_WHITESPACE(ptr); - loc->l = ptr - expression; + loc->l = (size_t)(ptr - expression); switch (*ptr) { @@ -633,6 +848,139 @@ out: return zbx_jsonpath_error(ptr); } +/* value types on index stack */ +typedef enum +{ + ZBX_JSONPATH_CONST = 1, /* constant value - string or number */ + ZBX_JSONPATH_VALUE, /* result of an operation after which cannot be used in index */ + ZBX_JSONPATH_PATH, /* relative jsonpath - @.a.b.c */ + ZBX_JSONPATH_PATH_OP /* result of an operation with jsonpath which still can be used in index */ +} +zbx_jsonpath_index_value_type_t; + +typedef struct +{ + zbx_jsonpath_index_value_type_t type; + zbx_jsonpath_token_t *index_token; + zbx_jsonpath_token_t *value_token; +} +zbx_jsonpath_index_value_t; + +ZBX_VECTOR_DECL(jpi_value, zbx_jsonpath_index_value_t) +ZBX_VECTOR_IMPL(jpi_value, zbx_jsonpath_index_value_t) + +/****************************************************************************** + * * + * Purpose: analyze expression and set indexing fields if possible * + * * + * Comments: Expression can be indexed if it contains relative json path * + * comparison with constant that is used in and operations. * + * This is tested by doing a pseudo evaluation by operand types * + * and checking the result type. * + * * + * So expressions like ?(@.a.b == 1), ?(@.a == "A" and @.b == "B") * + * can be indexed (by @.a.b and by @.a) while expressions like * + * ?(@.a == @.b), ?(@.a == "A" or @.b == "B") cannot. * + * * + ******************************************************************************/ +static void jsonpath_expression_prepare_index(zbx_jsonpath_expression_t *exp) +{ + int i; + zbx_vector_jpi_value_t stack; + zbx_jsonpath_index_value_t *left, *right; + + zbx_vector_jpi_value_create(&stack); + + for (i = 0; i < exp->tokens.values_num; i++) + { + zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)exp->tokens.values[i]; + zbx_jsonpath_index_value_t jpi = {0}; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_OP_NOT: + if (1 > stack.values_num) + goto out; + stack.values[stack.values_num - 1].type = ZBX_JSONPATH_VALUE; + stack.values[stack.values_num - 1].index_token = NULL; + stack.values[stack.values_num - 1].value_token = NULL; + continue; + case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + jpi.index_token = token; + jpi.type = ZBX_JSONPATH_PATH; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: + jpi.type = ZBX_JSONPATH_VALUE; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + case ZBX_JSONPATH_TOKEN_CONST_STR: + case ZBX_JSONPATH_TOKEN_CONST_NUM: + jpi.value_token = token; + jpi.type = ZBX_JSONPATH_CONST; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + } + + if (2 > stack.values_num) + goto out; + + left = &stack.values[stack.values_num - 2]; + right = &stack.values[stack.values_num - 1]; + stack.values_num--; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_OP_EQ: + if ((ZBX_JSONPATH_PATH == left->type || ZBX_JSONPATH_PATH == right->type) && + (ZBX_JSONPATH_CONST == left->type || ZBX_JSONPATH_CONST == right->type)) + { + left->type = ZBX_JSONPATH_PATH_OP; + + if (ZBX_JSONPATH_CONST == right->type) + left->value_token = right->value_token; + else + left->index_token = right->index_token; + } + else + left->type = ZBX_JSONPATH_VALUE; + continue; + case ZBX_JSONPATH_TOKEN_OP_AND: + if (ZBX_JSONPATH_PATH == left->type) + left->type = ZBX_JSONPATH_VALUE; + + if (ZBX_JSONPATH_PATH == right->type) + right->type = ZBX_JSONPATH_VALUE; + + if (ZBX_JSONPATH_PATH_OP == left->type && ZBX_JSONPATH_PATH_OP == right->type) + continue; + + if ((ZBX_JSONPATH_PATH_OP == left->type || ZBX_JSONPATH_PATH_OP == right->type) && + (ZBX_JSONPATH_VALUE == left->type || ZBX_JSONPATH_VALUE == right->type)) + { + if (ZBX_JSONPATH_PATH_OP != left->type) + *left = *right; + } + else + left->type = ZBX_JSONPATH_VALUE; + continue; + default: + left->type = ZBX_JSONPATH_VALUE; + left->index_token = NULL; + left->value_token = NULL; + break; + } + } + + if (1 == stack.values_num && ZBX_JSONPATH_PATH_OP == stack.values[0].type) + { + exp->index_token = stack.values[0].index_token; + exp->value_token = stack.values[0].value_token; + } +out: + zbx_vector_jpi_value_destroy(&stack); +} + /****************************************************************************** * * * Purpose: parse jsonpath filter expression in format * @@ -659,7 +1007,7 @@ out: static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jsonpath, const char **next) { int nesting = 1, ret = FAIL; - zbx_jsonpath_token_t *optoken; + zbx_jsonpath_token_t *optoken, *token; zbx_vector_ptr_t output, operators; zbx_strloc_t loc = {0, 0}; zbx_jsonpath_token_type_t token_type; @@ -706,7 +1054,10 @@ static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jso goto out; } - zbx_vector_ptr_append(&output, jsonpath_create_token(token_type, expression, &loc)); + if (NULL == (token = jsonpath_create_token(token_type, expression, &loc))) + goto cleanup; + + zbx_vector_ptr_append(&operators, token); prev_group = jsonpath_token_group(token_type); continue; } @@ -746,14 +1097,20 @@ static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jso zbx_vector_ptr_append(&output, optoken); } - zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc)); + if (NULL == (token = jsonpath_create_token(token_type, expression, &loc))) + goto cleanup; + + zbx_vector_ptr_append(&operators, token); prev_group = jsonpath_token_group(token_type); continue; } if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == token_type) { - zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc)); + if (NULL == (token = jsonpath_create_token(token_type, expression, &loc))) + goto cleanup; + + zbx_vector_ptr_append(&operators, token); prev_group = ZBX_JSONPATH_TOKEN_GROUP_NONE; continue; } @@ -816,6 +1173,10 @@ out: zbx_vector_ptr_create(&segment->data.expression.tokens); zbx_vector_ptr_append_array(&segment->data.expression.tokens, output.values, output.values_num); + /* index only json path that has been definite until this point */ + if (0 != jsonpath->definite) + jsonpath_expression_prepare_index(&segment->data.expression); + jsonpath->definite = 0; } cleanup: @@ -869,18 +1230,13 @@ static int jsonpath_parse_names(const char *list, zbx_jsonpath_t *jsonpath, cons } else if (*start == *end) { - zbx_jsonpath_list_node_t *node; - if (start + 1 == end) { ret = zbx_jsonpath_error(start); goto out; } - node = jsonpath_list_create_node(end - start + 1); - jsonpath_unquote(node->data, start, end - start + 1); - node->next = head; - head = node; + head = jsonpath_list_append_name(head, start, (size_t)(end - start)); parsed_name = 1; start = NULL; } @@ -987,19 +1343,13 @@ static int jsonpath_parse_indexes(const char *list, zbx_jsonpath_t *jsonpath, co if (NULL != start) { - int value; - if ('-' == *start && end == start + 1) { ret = zbx_jsonpath_error(start); goto out; } - node = jsonpath_list_create_node(sizeof(int)); - node->next = head; - head = node; - value = atoi(start); - memcpy(node->data, &value, sizeof(int)); + head = jsonpath_list_append_index(head, atoi(start), type == ZBX_JSONPATH_SEGMENT_MATCH_LIST); start = NULL; parsed_index = 1; } @@ -1163,7 +1513,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat { zbx_jsonpath_segment_t *segment; const char *ptr; - int len; + size_t len; segment = &jsonpath->segments[jsonpath->segments_num]; jsonpath->segments_num++; @@ -1179,6 +1529,8 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat for (ptr = start; 0 != isalnum((unsigned char)*ptr) || '_' == *ptr;) ptr++; + len = (size_t)(ptr - start); + if ('(' == *ptr) { const char *end = ptr + 1; @@ -1186,18 +1538,31 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat SKIP_WHITESPACE(end); if (')' == *end) { - if (ZBX_CONST_STRLEN("min") == ptr - start && 0 == strncmp(start, "min", ptr - start)) + if (ZBX_CONST_STRLEN("min") == len && 0 == strncmp(start, "min", len)) + { segment->data.function.type = ZBX_JSONPATH_FUNCTION_MIN; - else if (ZBX_CONST_STRLEN("max") == ptr - start && 0 == strncmp(start, "max", ptr - start)) + } + else if (ZBX_CONST_STRLEN("max") == len && 0 == strncmp(start, "max", len)) + { segment->data.function.type = ZBX_JSONPATH_FUNCTION_MAX; - else if (ZBX_CONST_STRLEN("avg") == ptr - start && 0 == strncmp(start, "avg", ptr - start)) + } + else if (ZBX_CONST_STRLEN("avg") == len && 0 == strncmp(start, "avg", len)) + { segment->data.function.type = ZBX_JSONPATH_FUNCTION_AVG; - else if (ZBX_CONST_STRLEN("length") == ptr - start && 0 == strncmp(start, "length", ptr - start)) + } + else if (ZBX_CONST_STRLEN("length") == len && 0 == strncmp(start, "length", len)) + { segment->data.function.type = ZBX_JSONPATH_FUNCTION_LENGTH; - else if (ZBX_CONST_STRLEN("first") == ptr - start && 0 == strncmp(start, "first", ptr - start)) + } + else if (ZBX_CONST_STRLEN("first") == len && 0 == strncmp(start, "first", len)) + { segment->data.function.type = ZBX_JSONPATH_FUNCTION_FIRST; - else if (ZBX_CONST_STRLEN("sum") == ptr - start && 0 == strncmp(start, "sum", ptr - start)) + jsonpath->first_match = 1; + } + else if (ZBX_CONST_STRLEN("sum") == len && 0 == strncmp(start, "sum", len)) + { segment->data.function.type = ZBX_JSONPATH_FUNCTION_SUM; + } else return zbx_jsonpath_error(start); @@ -1207,7 +1572,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat } } - if (0 < (len = ptr - start)) + if (0 < len) { segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST; segment->data.list.type = ZBX_JSONPATH_LIST_NAME; @@ -1247,117 +1612,84 @@ static int jsonpath_parse_name_reference(const char *start, zbx_jsonpath_t *json /****************************************************************************** * * - * Purpose: convert a pointer to an object/array/value in json data to * - * json parse structure * - * * - * Parameters: pnext - [IN] a pointer to object/array/value data * - * jp - [OUT] json parse data with start/end set * - * * - * Return value: SUCCEED - pointer was converted successfully * - * FAIL - otherwise * - * * - ******************************************************************************/ -static int jsonpath_pointer_to_jp(const char *pnext, struct zbx_json_parse *jp) -{ - if ('[' == *pnext || '{' == *pnext) - { - return zbx_json_brackets_open(pnext, jp); - } - else - { - jp->start = pnext; - jp->end = pnext + json_parse_value(pnext, NULL) - 1; - return SUCCEED; - } -} - -/****************************************************************************** - * * * Purpose: perform the rest of jsonpath query on json data * * * - * Parameters: jp_root - [IN] the document root * - * pnext - [IN] a pointer to object/array/value in json data * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object * * path_depth - [IN] the jsonpath segment to match * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the data were queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_contents(const struct zbx_json_parse *jp_root, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects) +static int jsonpath_query_contents(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) { - struct zbx_json_parse jp_child; + int ret; - switch (*pnext) + switch (obj->type) { - case '{': - if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) - return FAIL; - - return jsonpath_query_object(jp_root, &jp_child, jsonpath, path_depth, objects); - case '[': - if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) - return FAIL; - - return jsonpath_query_array(jp_root, &jp_child, jsonpath, path_depth, objects); + case ZBX_JSON_TYPE_OBJECT: + ret = jsonpath_query_object(ctx, obj, path_depth); + break; + case ZBX_JSON_TYPE_ARRAY: + ret = jsonpath_query_array(ctx, obj, path_depth); + break; + default: + ret = SUCCEED; } - return SUCCEED; + + return ret; } /****************************************************************************** * * * Purpose: query next segment * * * - * Parameters: jp_root - [IN] the document root * + * Parameters: ctx - [IN] the jsonpath query context * * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to object/array/value in json data * - * jsonpath - [IN] the jsonpath * + * obj - [IN] the current json object * * path_depth - [IN] the jsonpath segment to match * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the segment was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_next_segment(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects) +static int jsonpath_query_next_segment(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj, + int path_depth) { /* check if jsonpath end has been reached, so we have found matching data */ /* (functions are processed afterwards) */ - if (++path_depth == jsonpath->segments_num || - ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath->segments[path_depth].type) + if (++path_depth == ctx->path->segments_num || + ZBX_JSONPATH_SEGMENT_FUNCTION == ctx->path->segments[path_depth].type) { - zbx_vector_json_add_element(objects, name, pnext); + if (1 == ctx->path->first_match) + ctx->found = 1; + + zbx_vector_jsonobj_ref_add_object(&ctx->objects, name, obj); return SUCCEED; } - /* continue by matching found data against the rest of jsonpath segments */ - return jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects); + /* continue by matching found object against the rest of jsonpath segments */ + return jsonpath_query_contents(ctx, obj, path_depth); } /****************************************************************************** * * - * Purpose: match object value name against jsonpath segment name list * + * Purpose: match object contents against jsonpath segment name list * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to object value with the specified * - * name * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] parent json object * * path_depth - [IN] the jsonpath segment to match * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects) +static int jsonpath_match_name(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; const zbx_jsonpath_list_node_t *node; + zbx_jsonobj_el_t el_local, *el; /* object contents can match only name list */ if (ZBX_JSONPATH_LIST_NAME != segment->data.list.type) @@ -1365,11 +1697,11 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char for (node = segment->data.list.values; NULL != node; node = node->next) { - if (0 == strcmp(name, node->data)) + el_local.name = (char *)node->data; + if (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_search(&parent->data.object, &el_local))) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects)) + if (FAIL == jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth)) return FAIL; - break; } } @@ -1380,8 +1712,8 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char * * * Purpose: extract value from json data by the specified path * * * - * Parameters: jp - [IN] the parent object * - * path - [IN] the jsonpath (definite) * + * Parameters: obj - [IN] the parent object * + * path - [IN] the jsonpath * * value - [OUT] the extracted value * * * * Return value: SUCCEED - the value was extracted successfully * @@ -1389,35 +1721,28 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char * extract * * * ******************************************************************************/ -static int jsonpath_extract_value(const struct zbx_json_parse *jp, const char *path, zbx_variant_t *value) +static int jsonpath_extract_value(zbx_jsonobj_t *obj, zbx_jsonpath_t *path, zbx_variant_t *value) { - struct zbx_json_parse jp_child; - char *data = NULL, *tmp_path = NULL; - size_t data_alloc = 0; - int ret = FAIL; + int ret = FAIL; + zbx_jsonpath_context_t ctx; - if ('@' == *path) - { - tmp_path = zbx_strdup(NULL, path); - *tmp_path = '$'; - path = tmp_path; - } + ctx.path = path; + ctx.found = 0; + ctx.root = obj; + zbx_vector_jsonobj_ref_create(&ctx.objects); - if (FAIL == zbx_json_open_path(jp, path, &jp_child)) - goto out; - - if (NULL == zbx_json_decodevalue_dyn(jp_child.start, &data, &data_alloc, NULL)) + if (SUCCEED == jsonpath_query_contents(&ctx, obj, 0) && 0 != ctx.objects.values_num) { - size_t len = jp_child.end - jp_child.start + 2; + char *str = NULL; + size_t str_alloc = 0, str_offset = 0; - data = (char *)zbx_malloc(NULL, len); - zbx_strlcpy(data, jp_child.start, len); + jsonpath_str_copy_value(&str, &str_alloc, &str_offset, ctx.objects.values[0].value); + zbx_variant_set_str(value, str); + ret = SUCCEED; } - zbx_variant_set_str(value, data); - ret = SUCCEED; -out: - zbx_free(tmp_path); + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + zbx_vector_jsonobj_ref_destroy(&ctx.objects); return ret; } @@ -1452,7 +1777,7 @@ static char *jsonpath_expression_to_str(zbx_jsonpath_expression_t *expression) case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: case ZBX_JSONPATH_TOKEN_CONST_STR: case ZBX_JSONPATH_TOKEN_CONST_NUM: - zbx_strcpy_alloc(&str, &str_alloc, &str_offset, token->data); + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, token->text); break; case ZBX_JSONPATH_TOKEN_PAREN_LEFT: zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "("); @@ -1604,35 +1929,123 @@ static int jsonpath_regexp_match(const char *text, const char *pattern, double * /****************************************************************************** * * + * Purpose: add matched object to the index * + * * + * Parameters: index - [IN] the parent object index * + * name - [IN] the name of objec to add to index * + * obj - [IN] the object to add to index * + * value - [IN] the object matched by index path * + * * + ******************************************************************************/ +static void jsonpath_index_append_result(zbx_hashset_t *index, const char *name, zbx_jsonobj_t *obj, + zbx_jsonobj_t *value) +{ + zbx_jsonobj_index_el_t el_local = {.value = NULL}, *el; + size_t value_alloc = 0, value_offset = 0; + zbx_jsonobj_ref_t ref; + + jsonpath_str_copy_value(&el_local.value, &value_alloc, &value_offset, value); + + if (NULL == (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(index, &el_local))) + { + el = (zbx_jsonobj_index_el_t *)zbx_hashset_insert(index, &el_local, sizeof(el_local)); + zbx_vector_jsonobj_ref_create(&el->objects); + } + else + zbx_free(el_local.value); + + ref.name = zbx_strdup(NULL, name); + ref.value = obj; + ref.external = 0; + zbx_vector_jsonobj_ref_append(&el->objects, ref); +} + +/****************************************************************************** + * * + * Purpose: index json object using the expression token index path * + * * + * Parameters: obj - [IN] the object to index * + * index_token - [IN] the expression index token (relative path) * + * * + ******************************************************************************/ +static void jsonpath_create_index(zbx_jsonobj_t *obj, zbx_jsonpath_token_t *index_token) +{ + zbx_jsonpath_context_t ctx; + + jsonobj_init_index(obj, index_token->text); + + ctx.root = obj; + ctx.path = index_token->path; + zbx_vector_jsonobj_ref_create(&ctx.objects); + + if (ZBX_JSON_TYPE_OBJECT == obj->type) + { + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; + + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + ctx.found = 0; + if (SUCCEED == jsonpath_query_contents(&ctx, &el->value, 0) && 1 == ctx.objects.values_num) + { + jsonpath_index_append_result(&obj->index->objects, el->name, &el->value, + ctx.objects.values[0].value); + } + + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + } + } + else + { + int i; + + for (i = 0; i < obj->data.array.values_num; i++) + { + char name[MAX_ID_LEN + 1]; + zbx_jsonobj_t *value = obj->data.array.values[i]; + + zbx_snprintf(name, sizeof(name), "%d", i); + + ctx.found = 0; + if (SUCCEED == jsonpath_query_contents(&ctx, value, 0) && 1 == ctx.objects.values_num) + { + jsonpath_index_append_result(&obj->index->objects, name, value, + ctx.objects.values[0].value); + } + + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + } + } + + zbx_vector_jsonobj_ref_destroy(&ctx.objects); +} + +/****************************************************************************** + * * * Purpose: match json array element/object value against jsonpath expression * * * - * Parameters: jp_root - [IN] the document root * + * Parameters: ctx - [IN] the jsonpath query context * * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to array element/object value * - * jsonpath - [IN] the jsonpath * + * obj - [IN] the jsonobject to match * * path_depth - [IN] the jsonpath segment to match * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects) +static int jsonpath_match_expression(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj, + int path_depth) { - struct zbx_json_parse jp; zbx_vector_var_t stack; int i, ret = SUCCEED; zbx_jsonpath_segment_t *segment; zbx_variant_t value, *right; double res; - if (SUCCEED != jsonpath_pointer_to_jp(pnext, &jp)) - return FAIL; - zbx_vector_var_create(&stack); - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; for (i = 0; i < segment->data.expression.tokens.values_num; i++) { @@ -1779,25 +2192,25 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const switch (token->type) { case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: - if (FAIL == jsonpath_extract_value(jp_root, token->data, &value)) + if (FAIL == jsonpath_extract_value(ctx->root, token->path, &value)) zbx_variant_set_none(&value); zbx_vector_var_append_ptr(&stack, &value); break; case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: /* relative path can be applied only to array or object */ - if ('[' != *jp.start && '{' != *jp.start) + if (ZBX_JSON_TYPE_ARRAY != obj->type && ZBX_JSON_TYPE_OBJECT != obj->type) goto out; - if (FAIL == jsonpath_extract_value(&jp, token->data, &value)) + if (FAIL == jsonpath_extract_value(obj, token->path, &value)) zbx_variant_set_none(&value); zbx_vector_var_append_ptr(&stack, &value); break; case ZBX_JSONPATH_TOKEN_CONST_STR: - zbx_variant_set_str(&value, zbx_strdup(NULL, token->data)); + zbx_variant_set_str(&value, zbx_strdup(NULL, token->text)); zbx_vector_var_append_ptr(&stack, &value); break; case ZBX_JSONPATH_TOKEN_CONST_NUM: - zbx_variant_set_dbl(&value, atof(token->data)); + zbx_variant_set_dbl(&value, atof(token->text)); zbx_vector_var_append_ptr(&stack, &value); break; case ZBX_JSONPATH_TOKEN_OP_NOT: @@ -1824,7 +2237,7 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const jsonpath_variant_to_boolean(&stack.values[0]); if (SUCCEED != zbx_double_compare(stack.values[0].data.dbl, 0.0)) - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects); + ret = jsonpath_query_next_segment(ctx, name, obj, path_depth); out: for (i = 0; i < stack.values_num; i++) zbx_variant_clear(&stack.values[i]); @@ -1835,47 +2248,91 @@ out: /****************************************************************************** * * + * Purpose: query indexed object fields for jsonpath segment match * + * * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object to query * + * path_depth - [IN] the jsonpath segment to match * + * * + * Return value: SUCCEED - the object was queried successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_match_indexed_expression(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) +{ + zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; + zbx_jsonobj_index_el_t index_local, *el; + + index_local.value = segment->data.expression.value_token->text; + + if (NULL != (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(&obj->index->objects, &index_local))) + { + int i; + + for (i = 0; i < el->objects.values_num; i++) + { + jsonpath_match_expression(ctx, el->objects.values[i].name, el->objects.values[i].value, + path_depth); + } + } + + return SUCCEED; +} + +/****************************************************************************** + * * * Purpose: query object fields for jsonpath segment match * * * - * Parameters: jp_root - [IN] the document root * - * jp - [IN] the json object to query * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object to query * * path_depth - [IN] the jsonpath segment to match * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the object was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects) +static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) { - const char *pnext = NULL; - char name[MAX_STRING_LEN]; const zbx_jsonpath_segment_t *segment; int ret = SUCCEED; + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; + + segment = &ctx->path->segments[path_depth]; + + if (ZBX_JSONPATH_SEGMENT_MATCH_LIST == segment->type) + { + ret = jsonpath_match_name(ctx, obj, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + } + else if (ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION == segment->type && NULL != segment->data.expression.index_token) + { + if (NULL == obj->index) + jsonpath_create_index(obj, segment->data.expression.index_token); - segment = &jsonpath->segments[path_depth]; + if (0 == strcmp(obj->index->path, segment->data.expression.index_token->text)) + return jsonpath_match_indexed_expression(ctx, obj, path_depth); + } - while (NULL != (pnext = zbx_json_pair_next(jp, pnext, name, sizeof(name))) && SUCCEED == ret) + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter)) && SUCCEED == ret && + 0 == ctx->found) { switch (segment->type) { case ZBX_JSONPATH_SEGMENT_MATCH_ALL: - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_LIST: - ret = jsonpath_match_name(jp_root, name, pnext, jsonpath, path_depth, objects); + ret = jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth); break; case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: - ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, objects); + ret = jsonpath_match_expression(ctx, el->name, &el->value, path_depth); break; default: break; } if (1 == segment->detached) - ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects); + ret = jsonpath_query_contents(ctx, &el->value, path_depth); } return ret; @@ -1883,25 +2340,19 @@ static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const str /****************************************************************************** * * - * Purpose: match array element against segment index list * + * Purpose: array elements against segment index list * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] the json element name (index) * - * pnext - [IN] a pointer to an array element * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] the json array * * path_depth - [IN] the jsonpath segment to match * - * index - [IN] the array element index * - * elements_num - [IN] the total number of elements in array * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_json_t *objects) +static int jsonpath_match_index(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; const zbx_jsonpath_list_node_t *node; /* array contents can match only index list */ @@ -1914,11 +2365,20 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char memcpy(&query_index, node->data, sizeof(query_index)); - if ((query_index >= 0 && index == query_index) || index == elements_num + query_index) + if (0 > query_index) + query_index += parent->data.array.values_num; + + if (query_index >= 0 && query_index < parent->data.array.values_num) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects)) + char name[MAX_ID_LEN + 1]; + + zbx_snprintf(name, sizeof(name), "%d", query_index); + + if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[query_index], + path_depth)) + { return FAIL; - break; + } } } @@ -1927,38 +2387,37 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char /****************************************************************************** * * - * Purpose: match array element against segment index range * + * Purpose: match array elements against segment index range * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] the json element name (index) * - * pnext - [IN] a pointer to an array element * - * jsonpath - [IN] the jsonpath * - * path_depth - [IN] the jsonpath segment to match * - * index - [IN] the array element index * - * elements_num - [IN] the total number of elements in array * - * objects - [OUT] the matched json elements (name, value) * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] the json array * + * path_depth - [IN] the jsonpath segment to match * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_json_t *objects) +static int jsonpath_match_range(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - int start_index, end_index; - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + int i, start_index, end_index, values_num; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; + values_num = parent->data.array.values_num; start_index = (0 != (segment->data.range.flags & 0x01) ? segment->data.range.start : 0); - end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : elements_num); + end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : values_num); if (0 > start_index) - start_index += elements_num; + start_index += values_num; if (0 > end_index) - end_index += elements_num; + end_index += values_num; - if (start_index <= index && end_index > index) + for (i = start_index; i < end_index; i++) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects)) + char name[MAX_ID_LEN + 1]; + + zbx_snprintf(name, sizeof(name), "%d", i); + + if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[i], path_depth)) return FAIL; } @@ -1967,59 +2426,69 @@ static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char /****************************************************************************** * * - * Purpose: query array elements for jsonpath segment match * + * Purpose: query json array for jsonpath segment match * * * - * Parameters: jp_root - [IN] the document root * - * jp - [IN] the json array to query * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * array - [IN] the json array to query * * path_depth - [IN] the jsonpath segment to match * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the array was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects) +static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth) { - const char *pnext = NULL; - int index = 0, elements_num = 0, ret = SUCCEED; + int ret = SUCCEED, i; zbx_jsonpath_segment_t *segment; - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; - while (NULL != (pnext = zbx_json_next(jp, pnext))) - elements_num++; + switch (segment->type) + { + case ZBX_JSONPATH_SEGMENT_MATCH_LIST: + ret = jsonpath_match_index(ctx, array, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + break; + case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: + ret = jsonpath_match_range(ctx, array, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + break; + case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: + if (NULL != segment->data.expression.index_token) + { + if (NULL == array->index) + jsonpath_create_index(array, segment->data.expression.index_token); - while (NULL != (pnext = zbx_json_next(jp, pnext)) && SUCCEED == ret) + if (0 == strcmp(array->index->path, segment->data.expression.index_token->text)) + return jsonpath_match_indexed_expression(ctx, array, path_depth); + } + break; + default: + break; + } + + for (i = 0; i < array->data.array.values_num && SUCCEED == ret && 0 == ctx->found; i++) { char name[MAX_ID_LEN + 1]; - zbx_snprintf(name, sizeof(name), "%d", index); + zbx_snprintf(name, sizeof(name), "%d", i); + switch (segment->type) { case ZBX_JSONPATH_SEGMENT_MATCH_ALL: - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_LIST: - ret = jsonpath_match_index(jp_root, name, pnext, jsonpath, path_depth, index, - elements_num, objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: - ret = jsonpath_match_range(jp_root, name, pnext, jsonpath, path_depth, index, - elements_num, objects); + ret = jsonpath_query_next_segment(ctx, name, array->data.array.values[i], path_depth); break; case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: - ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, objects); + ret = jsonpath_match_expression(ctx, name, array->data.array.values[i], path_depth); break; default: break; } if (1 == segment->detached) - ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects); - - index++; + ret = jsonpath_query_contents(ctx, array->data.array.values[i], path_depth); } return ret; @@ -2027,123 +2496,99 @@ static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const stru /****************************************************************************** * * - * Purpose: extract JSON element value from data * - * * - * Parameters: ptr - [IN] pointer to the element to extract * - * element - [OUT] the extracted element * + * Purpose: get numeric value from json data * * * - * Return value: SUCCEED - the element was extracted successfully * - * FAIL - the pointer was not pointing to a JSON element * + * Parameters: obj - [IN] json object * + * value - [OUT] the extracted value * * * - * Comments: String value element is unquoted, other elements are copied as * - * is. * + * Return value: SUCCEED - the value was extracted successfully * + * FAIL - the pointer was not pointing at numeric value * * * ******************************************************************************/ -static int jsonpath_extract_element(const char *ptr, char **element) +static int jsonpath_get_numeric_value(const zbx_jsonobj_t *obj, double *value) { - size_t element_size = 0; - - if (NULL == zbx_json_decodevalue_dyn(ptr, element, &element_size, NULL)) + switch (obj->type) { - struct zbx_json_parse jp; - - if (SUCCEED != zbx_json_brackets_open(ptr, &jp)) + case ZBX_JSON_TYPE_NUMBER: + *value = obj->data.number; + return SUCCEED; + case ZBX_JSON_TYPE_STRING: + if (SUCCEED == zbx_is_double(obj->data.string, value)) + return SUCCEED; + zbx_set_json_strerror("array value is not a number or out of range: %s", obj->data.string); + return FAIL; + default: + zbx_set_json_strerror("array value type is not a number or string"); return FAIL; - - *element = jsonpath_strndup(jp.start, jp.end - jp.start + 1); } - - return SUCCEED; } /****************************************************************************** * * - * Purpose: extract numeric value from json data * - * * - * Parameters: ptr - [IN] pointer to the value to extract * - * value - [OUT] the extracted value * + * Purpose: get value from json data * * * * Return value: SUCCEED - the value was extracted successfully * * FAIL - the pointer was not pointing at numeric value * * * ******************************************************************************/ -static int jsonpath_extract_numeric_value(const char *ptr, double *value) +static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj) { - char buffer[MAX_STRING_LEN]; - - if (NULL == zbx_json_decodevalue(ptr, buffer, sizeof(buffer), NULL) || - SUCCEED != zbx_is_double(buffer, value)) + switch (obj->type) { - zbx_set_json_strerror("array value is not a number or out of range starting with: %s", ptr); - return FAIL; + case ZBX_JSON_TYPE_STRING: + zbx_strcpy_alloc(str, str_alloc, str_offset, obj->data.string); + return SUCCEED; + break; + default: + return zbx_jsonobj_to_string(str, str_alloc, str_offset, obj); } - - return SUCCEED; } /****************************************************************************** * * * Purpose: apply jsonpath function to the extracted object list * * * - * Parameters: objects - [IN] the matched json elements (name, value) * + * Parameters: in - [IN] the matched objects * * type - [IN] the function type * - * definite_path - [IN] 1 - if the path is definite (pointing at * - * single object) * - * 0 - otherwise * - * output - [OUT] the output value * + * definite_path - [IN/OUT] 1 - if the path is definite (pointing * + * at single object) * + * 0 - otherwise * + * out - [OUT] the result objects * * * * Return value: SUCCEED - the function was applied successfully * * FAIL - invalid input data for the function or internal * * json error * * * ******************************************************************************/ -static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpath_function_type_t type, - int definite_path, char **output) +static int jsonpath_apply_function(const zbx_vector_jsonobj_ref_t *in, zbx_jsonpath_function_type_t type, + int *definite_path, zbx_vector_jsonobj_ref_t *out) { - int i, ret = FAIL; - zbx_vector_json_t objects_tmp; - double result; + int i, ret = FAIL; + double result; + zbx_vector_jsonobj_ref_t tmp; + char buffer[64]; - zbx_vector_json_create(&objects_tmp); + zbx_vector_jsonobj_ref_create(&tmp); if (ZBX_JSONPATH_FUNCTION_NAME == type) { - if (0 == objects->values_num) + if (0 == in->values_num) { zbx_set_json_strerror("cannot extract name from empty result"); goto out; } - /* For definite paths we have single output value, so return its name. */ - /* Otherwise return array of all output element names. */ - if (0 == definite_path) - { - struct zbx_json j; - - /* reserve some space for output json, 1k being large enough to satisfy most queries */ - zbx_json_initarray(&j, 1024); - for (i = 0; i < objects->values_num; i++) - zbx_json_addstring(&j, NULL, objects->values[i].name, ZBX_JSON_TYPE_STRING); - - zbx_json_close(&j); - *output = zbx_strdup(NULL, j.buffer); - zbx_json_clean(&j); - } - else - *output = zbx_strdup(NULL, objects->values[0].name); + for (i = 0; i < in->values_num; i++) + zbx_vector_jsonobj_ref_add_string(out, "", in->values[i].name); ret = SUCCEED; goto out; } /* convert definite path result to object array if possible */ - if (0 != definite_path) + if (0 != *definite_path) { - const char *pnext; - struct zbx_json_parse jp; - int index = 0; - - if (0 == objects->values_num || '[' != *objects->values[0].value) + if (0 == in->values_num || ZBX_JSON_TYPE_ARRAY != in->values[0].value->type) { /* all functions can be applied only to arrays */ /* attempt to apply a function to non-array will fail */ @@ -2151,51 +2596,51 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat goto out; } - if (FAIL == zbx_json_brackets_open(objects->values[0].value, &jp)) - goto out; - - for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp, pnext));) + for (i = 0; i < in->values[0].value->data.array.values_num; i++) { char name[MAX_ID_LEN + 1]; - zbx_snprintf(name, sizeof(name), "%d", index++); - zbx_vector_json_add_element(&objects_tmp, name, pnext); + zbx_snprintf(name, sizeof(name), "%d", i); + zbx_vector_jsonobj_ref_add_object(&tmp, name, in->values[0].value->data.array.values[i]); } - objects = &objects_tmp; + in = &tmp; + *definite_path = 0; } if (ZBX_JSONPATH_FUNCTION_LENGTH == type) { - *output = zbx_dsprintf(NULL, "%d", objects->values_num); + zbx_snprintf(buffer, sizeof(buffer), "%d", in->values_num); + zbx_vector_jsonobj_ref_add_string(out, "", buffer); + *definite_path = 1; ret = SUCCEED; goto out; } if (ZBX_JSONPATH_FUNCTION_FIRST == type) { - if (0 < objects->values_num) - ret = jsonpath_extract_element(objects->values[0].value, output); - else - ret = SUCCEED; + if (0 < in->values_num) + zbx_vector_jsonobj_ref_add(out, &in->values[0]); + *definite_path = 1; + ret = SUCCEED; goto out; } - if (0 == objects->values_num) + if (0 == in->values_num) { zbx_set_json_strerror("cannot apply aggregation function to empty array"); goto out; } - if (FAIL == jsonpath_extract_numeric_value(objects->values[0].value, &result)) + if (FAIL == jsonpath_get_numeric_value(in->values[0].value, &result)) goto out; - for (i = 1; i < objects->values_num; i++) + for (i = 1; i < in->values_num; i++) { double value; - if (FAIL == jsonpath_extract_numeric_value(objects->values[i].value, &value)) + if (FAIL == jsonpath_get_numeric_value(in->values[i].value, &value)) goto out; switch (type) @@ -2218,19 +2663,22 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat } if (ZBX_JSONPATH_FUNCTION_AVG == type) - result /= objects->values_num; + result /= in->values_num; - *output = zbx_dsprintf(NULL, ZBX_FS_DBL, result); - if (SUCCEED != zbx_is_double(*output, NULL)) + zbx_print_double(buffer, sizeof(buffer), result); + if (SUCCEED != zbx_is_double(buffer, NULL)) { - zbx_set_json_strerror("invalid function result: %s", *output); + zbx_set_json_strerror("invalid function result: %s", buffer); goto out; } - zbx_del_zeros(*output); + + zbx_del_zeros(buffer); + zbx_vector_jsonobj_ref_add_string(out, "", buffer); + *definite_path = 1; ret = SUCCEED; out: - zbx_vector_json_clear_ext(&objects_tmp); - zbx_vector_json_destroy(&objects_tmp); + zbx_vector_jsonobj_ref_clear_ext(&tmp); + zbx_vector_jsonobj_ref_destroy(&tmp); return ret; } @@ -2239,58 +2687,47 @@ out: * * * Purpose: apply jsonpath function to the extracted object list * * * - * Parameters: jp_root - [IN] the document root * - * objects - [IN] the matched json elements (name, value) * - * jsonpath - [IN] the jsonpath * - * path_depth - [IN] the jsonpath segment to match * - * output - [OUT] the output value * + * Parameters: ctx - [IN] the jsonpath query context * + * path_depth - [IN] the jsonpath segment to match * + * definite_path - [IN/OUT] * + * out - [OUT] the result object * * * * Return value: SUCCEED - the function was applied successfully * * FAIL - invalid input data for the function or internal * * json error * * * ******************************************************************************/ -static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const zbx_vector_json_t *objects, - const zbx_jsonpath_t *jsonpath, int path_depth, char **output) +static int jsonpath_apply_functions(zbx_jsonpath_context_t *ctx, int path_depth, int *definite_path, + zbx_vector_jsonobj_ref_t *out) { - int ret, definite_path; - zbx_vector_json_t input; - char *input_json = NULL; + int ret; + zbx_vector_jsonobj_ref_t in; - zbx_vector_json_create(&input); + zbx_vector_jsonobj_ref_create(&in); /* when functions are applied directly to the json document (at the start of the jsonpath ) */ /* it makes all document as input object */ if (0 == path_depth) - zbx_vector_json_add_element(&input, "", jp_root->start); + zbx_vector_jsonobj_ref_add_object(&in, "", ctx->root); else - zbx_vector_json_copy(&input, objects); - - definite_path = jsonpath->definite; + zbx_vector_jsonobj_ref_copy(&in, &ctx->objects); for (;;) { - ret = jsonpath_apply_function(&input, jsonpath->segments[path_depth++].data.function.type, - definite_path, output); + ret = jsonpath_apply_function(&in, ctx->path->segments[path_depth++].data.function.type, + definite_path, out); - zbx_vector_json_clear_ext(&input); - zbx_free(input_json); + zbx_vector_jsonobj_ref_clear_ext(&in); - if (SUCCEED != ret || path_depth == jsonpath->segments_num) + if (SUCCEED != ret || path_depth == ctx->path->segments_num) break; - if (NULL != *output) - { - zbx_vector_json_add_element(&input, "", *output); - input_json = *output; - *output = NULL; - } - - /* functions return single value, so for the next functions path becomes definite */ - definite_path = 1; + zbx_vector_jsonobj_ref_copy(&in, out); + zbx_vector_jsonobj_ref_clear_ext(out); } - zbx_vector_json_destroy(&input); + zbx_vector_jsonobj_ref_clear_ext(&in); + zbx_vector_jsonobj_ref_destroy(&in); return ret; } @@ -2299,49 +2736,37 @@ static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const * * * Purpose: format query result, depending on jsonpath type * * * - * Parameters: objects - [IN] the matched json elements (name, value) * - * jsonpath - [IN] the jsonpath used to acquire result * - * output - [OUT] the output value * + * Parameters: objects - [IN] the matched json refs (name, value) * + * definite_path - [IN] the jsonpath definite flag * + * output - [OUT] the output value * * * * Return value: SUCCEED - the result was formatted successfully * * FAIL - invalid result data (internal json error) * * * ******************************************************************************/ -static int jsonpath_format_query_result(const zbx_vector_json_t *objects, zbx_jsonpath_t *jsonpath, char **output) +static int jsonpath_format_query_result(const zbx_vector_jsonobj_ref_t *objects, int definite_path, char **output) { size_t output_offset = 0, output_alloc; int i; + char delim; if (0 == objects->values_num) return SUCCEED; - if (1 == jsonpath->definite) - { - return jsonpath_extract_element(objects->values[0].value, output); - } + if (1 == definite_path) + return jsonpath_str_copy_value(output, &output_alloc, &output_offset, objects->values[0].value); /* reserve 32 bytes per returned object plus array start/end [] and terminating zero */ - output_alloc = objects->values_num * 32 + 3; + output_alloc = (size_t)objects->values_num * 32 + 3; *output = (char *)zbx_malloc(NULL, output_alloc); - zbx_chrcpy_alloc(output, &output_alloc, &output_offset, '['); + delim = '['; for (i = 0; i < objects->values_num; i++) { - struct zbx_json_parse jp; - - if (FAIL == jsonpath_pointer_to_jp(objects->values[i].value, &jp)) - { - zbx_set_json_strerror("cannot format query result, unrecognized json part starting with: %s", - objects->values[i].value); - zbx_free(*output); - return FAIL; - } - - if (0 != i) - zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ','); - - zbx_strncpy_alloc(output, &output_alloc, &output_offset, jp.start, jp.end - jp.start + 1); + zbx_chrcpy_alloc(output, &output_alloc, &output_offset, delim); + zbx_jsonobj_to_string(output, &output_alloc, &output_offset, objects->values[i].value); + delim = ','; } zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ']'); @@ -2364,7 +2789,7 @@ void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath) * Purpose: compile jsonpath to be used in queries * * * * Parameters: path - [IN] the path to parse * - * jsonpath - [IN/OUT] the compiled jsonpath * + * jsonpath - [IN/OUT] the compiled jsonpath * * * * Return value: SUCCEED - the segment was parsed successfully * * FAIL - otherwise * @@ -2386,6 +2811,7 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath) memset(&jpquery, 0, sizeof(zbx_jsonpath_t)); jsonpath_reserve(&jpquery, 4); jpquery.definite = 1; + jpquery.first_match = 0; for (ptr++; '\0' != *ptr; ptr = next) { @@ -2453,7 +2879,10 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath) ret = zbx_jsonpath_error(ptr); if (SUCCEED == ret) + { + jpquery.first_match |= jpquery.definite; *jsonpath = jpquery; + } else zbx_jsonpath_clear(&jpquery); @@ -2472,37 +2901,89 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath) * being counted as successful query) * * FAIL - otherwise * * * + * Comments: This function is for compatibility purposes. Where possible the * + * zbx_jsonobj_query() function must be used. * + * * ******************************************************************************/ int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output) { + int ret; + zbx_jsonobj_t obj; + + if (SUCCEED != zbx_jsonobj_open(jp->start, &obj)) + return FAIL; + + ret = zbx_jsonobj_query(&obj, path, output); + + zbx_jsonobj_clear(&obj); + + return ret; +} + +/****************************************************************************** + * * + * Purpose: perform jsonpath query on the specified json object * + * * + * Parameters: obj - [IN] the json object * + * path - [IN] the jsonpath * + * output - [OUT] the output value * + * * + * Return value: SUCCEED - the query was performed successfully (empty result * + * being counted as successful query) * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_jsonobj_query(zbx_jsonobj_t *obj, const char *path, char **output) +{ + zbx_jsonpath_context_t ctx; zbx_jsonpath_t jsonpath; - int path_depth = 0, ret = SUCCEED; - zbx_vector_json_t objects; + int ret = SUCCEED; if (FAIL == zbx_jsonpath_compile(path, &jsonpath)) return FAIL; - zbx_vector_json_create(&objects); + ctx.found = 0; + ctx.root = obj; + ctx.path = &jsonpath; + zbx_vector_jsonobj_ref_create(&ctx.objects); - if ('{' == *jp->start) - ret = jsonpath_query_object(jp, jp, &jsonpath, path_depth, &objects); - else if ('[' == *jp->start) - ret = jsonpath_query_array(jp, jp, &jsonpath, path_depth, &objects); + switch (obj->type) + { + case ZBX_JSON_TYPE_OBJECT: + ret = jsonpath_query_object(&ctx, obj, 0); + break; + case ZBX_JSON_TYPE_ARRAY: + ret = jsonpath_query_array(&ctx, obj, 0); + break; + default: + break; + } if (SUCCEED == ret) { + zbx_vector_jsonobj_ref_t out; + int definite_path = jsonpath.definite, path_depth; + + zbx_vector_jsonobj_ref_create(&out); + path_depth = jsonpath.segments_num; while (0 < path_depth && ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath.segments[path_depth - 1].type) path_depth--; if (path_depth < jsonpath.segments_num) - ret = jsonpath_apply_functions(jp, &objects, &jsonpath, path_depth, output); + { + if (SUCCEED == (ret = jsonpath_apply_functions(&ctx, path_depth, &definite_path, &out))) + ret = jsonpath_format_query_result(&out, definite_path, output); + } else - ret = jsonpath_format_query_result(&objects, &jsonpath, output); + ret = jsonpath_format_query_result(&ctx.objects, definite_path, output); + + zbx_vector_jsonobj_ref_clear_ext(&out); + zbx_vector_jsonobj_ref_destroy(&out); } - zbx_vector_json_clear_ext(&objects); - zbx_vector_json_destroy(&objects); + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + zbx_vector_jsonobj_ref_destroy(&ctx.objects); zbx_jsonpath_clear(&jsonpath); return ret; diff --git a/src/libs/zbxjson/jsonpath.h b/src/libs/zbxjson/jsonpath.h index 3a5be7c9413..644aef6aa4a 100644 --- a/src/libs/zbxjson/jsonpath.h +++ b/src/libs/zbxjson/jsonpath.h @@ -20,7 +20,9 @@ #ifndef ZABBIX_JSONPATH_H #define ZABBIX_JSONPATH_H -#include "zbxalgo.h" +#include "zbxjson.h" + +typedef struct zbx_jsonpath_token zbx_jsonpath_token_t; typedef enum { @@ -78,10 +80,19 @@ typedef struct } zbx_jsonpath_range_t; +typedef enum +{ + ZBX_JSONPATH_EXPRESSION_INDEX_TRUE, + ZBX_JSONPATH_EXPRESSION_INDEX_FALSE, +} +zbx_json_path_expression_index_t; + /* expression tokens in postfix notation */ typedef struct { zbx_vector_ptr_t tokens; + zbx_jsonpath_token_t *index_token; /* relative path token that is used to index parent object */ + zbx_jsonpath_token_t *value_token; /* the index value token */ } zbx_jsonpath_expression_t; @@ -150,11 +161,11 @@ typedef enum } zbx_jsonpath_token_type_t; -typedef struct +struct zbx_jsonpath_token { unsigned char type; - char *data; -} -zbx_jsonpath_token_t; + char *text; + zbx_jsonpath_t *path; +}; #endif diff --git a/src/libs/zbxsysinfo/linux/diskio.c b/src/libs/zbxsysinfo/linux/diskio.c index 4464a09c8d2..cc356549508 100644 --- a/src/libs/zbxsysinfo/linux/diskio.c +++ b/src/libs/zbxsysinfo/linux/diskio.c @@ -357,6 +357,8 @@ int vfs_dev_discovery(AGENT_REQUEST *request, AGENT_RESULT *result) zbx_fclose(f); } + else + continue; if (0 == devtype_found) { diff --git a/src/libs/zbxsysinfo/linux/hardware.c b/src/libs/zbxsysinfo/linux/hardware.c index d8eff864d47..8ebd7086a47 100644 --- a/src/libs/zbxsysinfo/linux/hardware.c +++ b/src/libs/zbxsysinfo/linux/hardware.c @@ -496,7 +496,12 @@ int system_hw_cpu(AGENT_REQUEST *request, AGENT_RESULT *result) filter)) { ret = SYSINFO_RET_OK; - sscanf(tmp, ZBX_FS_UI64, &curfreq); + if (1 != sscanf(tmp, ZBX_FS_UI64, &curfreq)) + { + zbx_fclose(f); + SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain CPU frequency.")); + return SYSINFO_RET_FAIL; + } } } diff --git a/src/libs/zbxsysinfo/linux/proc.c b/src/libs/zbxsysinfo/linux/proc.c index f2e80c49d09..01b602af9a2 100644 --- a/src/libs/zbxsysinfo/linux/proc.c +++ b/src/libs/zbxsysinfo/linux/proc.c @@ -1881,11 +1881,12 @@ int proc_get(AGENT_REQUEST *request, AGENT_RESULT *result) if (SUCCEED != get_cmdline(f_cmd, &cmdline, &l)) continue; - read_value_from_proc_file(f_status, 0, "Name", PROC_VAL_TYPE_TEXT, NULL, &prname); + if (SUCCEED != read_value_from_proc_file(f_status, 0, "Name", PROC_VAL_TYPE_TEXT, NULL, &prname)) + continue; if ('\0' != *cmdline) { - char *p, *pend, sep; + char *p, *pend, sep = 0; size_t len; if (NULL != (pend = strpbrk(cmdline, " :"))) diff --git a/src/libs/zbxsysinfo/sysinfo.h b/src/libs/zbxsysinfo/sysinfo.h index ba6474293a9..384fbe09c93 100644 --- a/src/libs/zbxsysinfo/sysinfo.h +++ b/src/libs/zbxsysinfo/sysinfo.h @@ -176,10 +176,10 @@ int perf_counter(AGENT_REQUEST *request, AGENT_RESULT *result); int perf_counter_en(AGENT_REQUEST *request, AGENT_RESULT *result); int perf_instance_discovery(AGENT_REQUEST *request, AGENT_RESULT *result); int perf_instance_discovery_en(AGENT_REQUEST *request, AGENT_RESULT *result); -int service_discovery(AGENT_REQUEST *request, AGENT_RESULT *result); -int service_info(AGENT_REQUEST *request, AGENT_RESULT *result); -int service_state(AGENT_REQUEST *request, AGENT_RESULT *result); -int services(AGENT_REQUEST *request, AGENT_RESULT *result); +int discover_services(AGENT_REQUEST *request, AGENT_RESULT *result); +int get_service_info(AGENT_REQUEST *request, AGENT_RESULT *result); +int get_service_state(AGENT_REQUEST *request, AGENT_RESULT *result); +int get_list_of_services(AGENT_REQUEST *request, AGENT_RESULT *result); int proc_info(AGENT_REQUEST *request, AGENT_RESULT *result); int net_if_list(AGENT_REQUEST *request, AGENT_RESULT *result); int wmi_get(AGENT_REQUEST *request, AGENT_RESULT *result); diff --git a/src/libs/zbxsysinfo/win32/proc.c b/src/libs/zbxsysinfo/win32/proc.c index dedbea03361..ed7babb04d1 100644 --- a/src/libs/zbxsysinfo/win32/proc.c +++ b/src/libs/zbxsysinfo/win32/proc.c @@ -73,7 +73,10 @@ static int zbx_get_process_username(HANDLE hProcess, char *userName, char *sid) int iUse, res = FAIL; /* clean result; */ - *userName = *sid = '\0'; + *userName = '\0'; + + if (NULL != sid) + *sid = '\0'; /* open the processes token */ if (0 == OpenProcessToken(hProcess, TOKEN_QUERY, &tok)) diff --git a/src/libs/zbxsysinfo/win32/services.c b/src/libs/zbxsysinfo/win32/services.c index 05ed85f0ea4..c3a24fd0ec2 100644 --- a/src/libs/zbxsysinfo/win32/services.c +++ b/src/libs/zbxsysinfo/win32/services.c @@ -254,7 +254,7 @@ static zbx_startup_type_t get_service_startup_type(SC_HANDLE h_srv, QUERY_SERVIC } } -int service_discovery(AGENT_REQUEST *request, AGENT_RESULT *result) +int discover_services(AGENT_REQUEST *request, AGENT_RESULT *result) { ENUM_SERVICE_STATUS_PROCESS *ssp = NULL; SC_HANDLE h_mgr; @@ -391,7 +391,7 @@ next: return SYSINFO_RET_OK; } -int service_info(AGENT_REQUEST *request, AGENT_RESULT *result) +int get_service_info(AGENT_REQUEST *request, AGENT_RESULT *result) { #define ZBX_SRV_PARAM_STATE 0x01 #define ZBX_SRV_PARAM_DISPLAYNAME 0x02 @@ -550,7 +550,7 @@ int service_info(AGENT_REQUEST *request, AGENT_RESULT *result) #undef ZBX_NON_EXISTING_SRV } -int service_state(AGENT_REQUEST *request, AGENT_RESULT *result) +int get_service_state(AGENT_REQUEST *request, AGENT_RESULT *result) { SC_HANDLE mgr, service; char *name; @@ -657,7 +657,8 @@ static int check_service_starttype(SC_HANDLE h_srv, int start_type) */ #define ZBX_SRV_STATE_ALL 0x007f /* ZBX_SRV_STATE_STOPPED | ZBX_SRV_STATE_STARTED */ -static int check_service_state(SC_HANDLE h_srv, int service_state) + +static int get_service_state_local(SC_HANDLE h_srv, int service_state) { SERVICE_STATUS status; @@ -699,7 +700,7 @@ static int check_service_state(SC_HANDLE h_srv, int service_state) return FAIL; } -int services(AGENT_REQUEST *request, AGENT_RESULT *result) +int get_list_of_services(AGENT_REQUEST *request, AGENT_RESULT *result) { int start_type, service_state; char *type, *state, *exclude, *buf = NULL, *utf8; @@ -776,7 +777,7 @@ int services(AGENT_REQUEST *request, AGENT_RESULT *result) if (SUCCEED == check_service_starttype(h_srv, start_type)) { - if (SUCCEED == check_service_state(h_srv, service_state)) + if (SUCCEED == get_service_state_local(h_srv, service_state)) { utf8 = zbx_unicode_to_utf8(ssp[i].lpServiceName); diff --git a/src/libs/zbxsysinfo/win32/win32.c b/src/libs/zbxsysinfo/win32/win32.c index 3357f46d566..65ec0854913 100644 --- a/src/libs/zbxsysinfo/win32/win32.c +++ b/src/libs/zbxsysinfo/win32/win32.c @@ -56,10 +56,10 @@ ZBX_METRIC parameters_specific[] = {"system.uname", 0, system_uname, NULL}, - {"service.discovery", 0, service_discovery, NULL}, - {"service.info", CF_HAVEPARAMS, service_info, ZABBIX_SERVICE_NAME}, - {"service_state", CF_HAVEPARAMS, service_state, ZABBIX_SERVICE_NAME}, - {"services", CF_HAVEPARAMS, services, NULL}, + {"service.discovery", 0, discover_services, NULL}, + {"service.info", CF_HAVEPARAMS, get_service_info, ZABBIX_SERVICE_NAME}, + {"service_state", CF_HAVEPARAMS, get_service_state, ZABBIX_SERVICE_NAME}, + {"services", CF_HAVEPARAMS, get_list_of_services, NULL}, {"perf_counter", CF_HAVEPARAMS, perf_counter, "\\System\\Processes"}, {"perf_counter_en", CF_HAVEPARAMS, perf_counter_en, "\\System\\Processes"}, {"perf_instance.discovery", CF_HAVEPARAMS, perf_instance_discovery, "Processor"}, diff --git a/src/zabbix_agent/Makefile.am b/src/zabbix_agent/Makefile.am index 7faa9ad9a17..39ce4441557 100644 --- a/src/zabbix_agent/Makefile.am +++ b/src/zabbix_agent/Makefile.am @@ -54,7 +54,6 @@ zabbix_agentd_LDADD = \ $(top_builddir)/src/libs/zbxsysinfo/alias/libalias.a \ $(top_builddir)/src/libs/zbxlog/libzbxlog.a \ $(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \ - $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_builddir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_builddir)/src/libs/zbxnix/libzbxnix.a \ @@ -62,6 +61,7 @@ zabbix_agentd_LDADD = \ $(top_builddir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ $(top_builddir)/src/libs/zbxconf/libzbxconf.a \ $(top_builddir)/src/libs/zbxjson/libzbxjson.a \ + $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxvariant/libzbxvariant.a \ $(top_builddir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_builddir)/src/libs/zbxgetopt/libzbxgetopt.a \ diff --git a/src/zabbix_server/Makefile.am b/src/zabbix_server/Makefile.am index 4c4870298fe..9ed2104d3a3 100644 --- a/src/zabbix_server/Makefile.am +++ b/src/zabbix_server/Makefile.am @@ -106,7 +106,6 @@ zabbix_server_LDADD = \ $(top_builddir)/src/libs/zbxself/libzbxself.a \ $(top_builddir)/src/libs/zbxself/libzbxself_server.a \ $(top_builddir)/src/libs/zbxipcservice/libzbxipcservice.a \ - $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_builddir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_builddir)/src/libs/zbxconf/libzbxconf.a \ @@ -118,6 +117,7 @@ zabbix_server_LDADD = \ $(top_builddir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ $(top_builddir)/src/libs/zbxxml/libzbxxml.a \ $(top_builddir)/src/libs/zbxjson/libzbxjson.a \ + $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxvault/libzbxvault.a \ $(top_builddir)/src/libs/zbxcyberark/libzbxcyberark.a \ $(top_builddir)/src/libs/zbxhashicorp/libzbxhashicorp.a \ diff --git a/src/zabbix_server/poller/checks_simple_vmware.c b/src/zabbix_server/poller/checks_simple_vmware.c index f2bfbe6e618..14a13ba2872 100644 --- a/src/zabbix_server/poller/checks_simple_vmware.c +++ b/src/zabbix_server/poller/checks_simple_vmware.c @@ -4299,7 +4299,7 @@ int check_vcenter_vm_discovery(AGENT_REQUEST *request, const char *username, con rpool_cmp.id = vm->props[ZBX_VMWARE_VMPROP_RESOURCEPOOL]; if (FAIL != (idx = zbx_vector_vmware_resourcepool_bsearch(&service->data->resourcepools, - &rpool_cmp, vmware_resourcepool_compare_id))) + &rpool_cmp, ZBX_DEFAULT_STR_PTR_COMPARE_FUNC))) { zbx_json_addstring(&json_data, "{#VM.RPOOL.PATH}", ZBX_NULL2EMPTY_STR( service->data->resourcepools.values[idx]->path), @@ -5817,7 +5817,7 @@ static int check_vcenter_rp_common(const char *url, const char *username, const rp_cmp.id = (char *)rpid; if (FAIL == zbx_vector_vmware_resourcepool_bsearch(&service->data->resourcepools, &rp_cmp, - vmware_resourcepool_compare_id)) + ZBX_DEFAULT_STR_PTR_COMPARE_FUNC)) { SET_MSG_RESULT(result, zbx_strdup(NULL, "Unknown resource pool id.")); goto unlock; diff --git a/src/zabbix_server/preprocessor/item_preproc.c b/src/zabbix_server/preprocessor/item_preproc.c index 065eb31ae2e..ae511b94532 100644 --- a/src/zabbix_server/preprocessor/item_preproc.c +++ b/src/zabbix_server/preprocessor/item_preproc.c @@ -934,7 +934,8 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char ** * * * Purpose: execute jsonpath query * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -942,18 +943,59 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char ** * FAIL - otherwise * * * ******************************************************************************/ -static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, char **errmsg) +static int item_preproc_jsonpath_op(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params, + char **errmsg) { - struct zbx_json_parse jp; - char *data = NULL; + char *data = NULL; - if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) - return FAIL; + if (NULL == cache) + { + zbx_jsonobj_t obj; + + if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) + return FAIL; - if (FAIL == zbx_json_open(value->data.str, &jp) || FAIL == zbx_jsonpath_query(&jp, params, &data)) + if (FAIL == zbx_jsonobj_open(value->data.str, &obj)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } + + if (FAIL == zbx_jsonobj_query(&obj, params, &data)) + { + zbx_jsonobj_clear(&obj); + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } + + zbx_jsonobj_clear(&obj); + } + else { - *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); - return FAIL; + zbx_jsonobj_t *obj; + + if (NULL == (obj = (zbx_jsonobj_t *)zbx_preproc_cache_get(cache, ZBX_PREPROC_JSONPATH))) + { + if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) + return FAIL; + + obj = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + + if (SUCCEED != zbx_jsonobj_open(value->data.str, obj)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + zbx_free(obj); + return FAIL; + } + + zbx_preproc_cache_put(cache, ZBX_PREPROC_JSONPATH, obj); + } + + if (FAIL == zbx_jsonobj_query(obj, params, &data)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } } if (NULL == data) @@ -972,7 +1014,8 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch * * * Purpose: execute jsonpath query * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -980,11 +1023,12 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch * FAIL - otherwise * * * ******************************************************************************/ -static int item_preproc_jsonpath(zbx_variant_t *value, const char *params, char **errmsg) +static int item_preproc_jsonpath(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params, + char **errmsg) { char *err = NULL; - if (SUCCEED == item_preproc_jsonpath_op(value, params, &err)) + if (SUCCEED == item_preproc_jsonpath_op(cache, value, params, &err)) return SUCCEED; *errmsg = zbx_dsprintf(*errmsg, "cannot extract value from json by path \"%s\": %s", params, err); @@ -1571,7 +1615,8 @@ fail: * * * Purpose: parse Prometheus format metrics * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -2148,7 +2193,7 @@ int zbx_item_preproc(zbx_preproc_cache_t *cache, unsigned char value_type, zbx_v ret = item_preproc_xpath(value, op->params, error); break; case ZBX_PREPROC_JSONPATH: - ret = item_preproc_jsonpath(value, op->params, error); + ret = item_preproc_jsonpath(cache, value, op->params, error); break; case ZBX_PREPROC_VALIDATE_RANGE: ret = item_preproc_validate_range(value_type, value, op->params, error); diff --git a/src/zabbix_server/preprocessor/preproc_cache.c b/src/zabbix_server/preprocessor/preproc_cache.c index 9b7cff0731e..556f58e53e9 100644 --- a/src/zabbix_server/preprocessor/preproc_cache.c +++ b/src/zabbix_server/preprocessor/preproc_cache.c @@ -95,6 +95,11 @@ void zbx_preproc_cache_clear(zbx_preproc_cache_t *cache) case ZBX_PREPROC_PROMETHEUS_PATTERN: zbx_prometheus_clear((zbx_prometheus_t *)cache->refs.values[i].impl); zbx_free(cache->refs.values[i].impl); + break; + case ZBX_PREPROC_JSONPATH: + zbx_jsonobj_clear((zbx_jsonobj_t *)cache->refs.values[i].impl); + zbx_free(cache->refs.values[i].impl); + break; } } diff --git a/src/zabbix_server/vmware/vmware.c b/src/zabbix_server/vmware/vmware.c index c945040cd45..d710e9e86f8 100644 --- a/src/zabbix_server/vmware/vmware.c +++ b/src/zabbix_server/vmware/vmware.c @@ -239,6 +239,21 @@ static zbx_hashset_t evt_msg_strpool; static zbx_uint64_t evt_req_chunk_size; +/* the vmware resource pool chunk */ +typedef struct +{ + char *id; + char *first_parentid; + char *name; + const char *path; + const char *parentid; + unsigned char parent_is_rp; +} +zbx_vmware_rpool_chunk_t; + +ZBX_PTR_VECTOR_DECL(vmware_rpool_chunk, zbx_vmware_rpool_chunk_t *) +ZBX_PTR_VECTOR_IMPL(vmware_rpool_chunk, zbx_vmware_rpool_chunk_t *) + /* * SOAP support */ @@ -500,17 +515,6 @@ static zbx_vmware_propmap_t vm_propmap[] = { ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_FOLDER, "[text()='" id "']") "/" \ ZBX_XPATH_PROP_NAME_NODE("parent") "[@type='Folder']" -#define ZBX_XPATH_GET_RESOURCEPOOL_NAME(id) \ - ZBX_XPATH_GET_OBJECT_NAME(ZBX_VMWARE_SOAP_RESOURCEPOOL, id) - -#define ZBX_XPATH_GET_RESOURCEPOOL_PARENTID(id) \ - ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_RESOURCEPOOL, "[text()='" id "']") "/" \ - ZBX_XPATH_PROP_NAME_NODE("parent") "[@type='ResourcePool']" - -#define ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID(id) \ - ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_RESOURCEPOOL, "[text()='" id "']") "/" \ - ZBX_XPATH_PROP_NAME_NODE("parent") "[@type!='ResourcePool']" - /* hypervisor hashset support */ static zbx_hash_t vmware_hv_hash(const void *data) { @@ -2375,6 +2379,22 @@ static void vmware_datacenter_free(zbx_vmware_datacenter_t *datacenter) /****************************************************************************** * * + * Purpose: frees resources allocated to store rp_chunk data * + * * + * Parameters: rp_chunk - [IN] the resourcepool chunk * + * * + ******************************************************************************/ +static void vmware_rp_chunk_free(zbx_vmware_rpool_chunk_t *rp_chunk) +{ + zbx_free(rp_chunk->id); + zbx_free(rp_chunk->name); + zbx_free(rp_chunk->first_parentid); + /* path and parent are not cleared */ + zbx_free(rp_chunk); +} + +/****************************************************************************** + * * * Purpose: frees resources allocated to store resourcepool data * * * * Parameters: resourcepool - [IN] the resourcepool * @@ -2864,14 +2884,15 @@ typedef struct zbx_property_collection_iter; static int zbx_property_collection_init(CURL *easyhandle, const char *property_collection_query, - const char *property_collector, zbx_property_collection_iter **iter, xmlDoc **xdoc, char **error) + const char *property_collector, const char *fn_parent, zbx_property_collection_iter **iter, + xmlDoc **xdoc, char **error) { *iter = (zbx_property_collection_iter *)zbx_malloc(*iter, sizeof(zbx_property_collection_iter)); (*iter)->property_collector = property_collector; (*iter)->easyhandle = easyhandle; (*iter)->token = NULL; - if (SUCCEED != zbx_soap_post(__func__, (*iter)->easyhandle, property_collection_query, xdoc, &(*iter)->token, + if (SUCCEED != zbx_soap_post(fn_parent, (*iter)->easyhandle, property_collection_query, xdoc, &(*iter)->token, error)) { return FAIL; @@ -2880,7 +2901,8 @@ static int zbx_property_collection_init(CURL *easyhandle, const char *property_c return SUCCEED; } -static int zbx_property_collection_next(zbx_property_collection_iter *iter, xmlDoc **xdoc, char **error) +static int zbx_property_collection_next(const char *fn_parent, zbx_property_collection_iter *iter, xmlDoc **xdoc, + char **error) { # define ZBX_POST_CONTINUE_RETRIEVE_PROPERTIES \ ZBX_POST_VSPHERE_HEADER \ @@ -2904,7 +2926,7 @@ static int zbx_property_collection_next(zbx_property_collection_iter *iter, xmlD zbx_snprintf(post, sizeof(post), ZBX_POST_CONTINUE_RETRIEVE_PROPERTIES, iter->property_collector, token_esc); zbx_free(token_esc); - if (SUCCEED != zbx_soap_post(__func__, iter->easyhandle, post, xdoc, NULL, error)) + if (SUCCEED != zbx_soap_post(fn_parent, iter->easyhandle, post, xdoc, NULL, error)) return FAIL; zbx_free(iter->token); @@ -3989,83 +4011,6 @@ out: /****************************************************************************** * * - * Purpose: get resource pool parentid and path (chain of resource pool * - * names divided by '/') * - * * - * Parameters: xdoc - [IN] the xml with all vm details * - * r_id - [IN] the resource pool id * - * parentid - [OUT] the resource pool parent id * - * path - [OUT] the resource pool path * - * * - * Return value: SUCCEED - the operation has completed successfully * - * FAIL - the operation has failed * - * * - ******************************************************************************/ -static int vmware_service_get_resourcepool_data(xmlDoc *xdoc, const char *r_id, char **parentid, char **path) -{ - char tmp[MAX_STRING_LEN], *id, *name; - int ret = SUCCEED; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() resource pool id:'%s'", __func__, r_id); - id = zbx_strdup(NULL, r_id); - *path = *parentid = NULL; - - do - { - char *id_esc; - - id_esc = zbx_xml_escape_dyn(id); - zbx_free(id); - zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_RESOURCEPOOL_NAME("%s"), id_esc); - - if (NULL == (name = zbx_xml_doc_read_value(xdoc , tmp))) - { - zbx_free(*parentid); - zbx_free(*path); - zbx_free(id_esc); - ret = FAIL; - break; - } - - zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_RESOURCEPOOL_PARENTID("%s"), id_esc); - id = zbx_xml_doc_read_value(xdoc , tmp); - - if (NULL != id) /* we do not include the last default 'ResourcePool' */ - { - if (NULL == *path) - { - *path = name; - name = NULL; - } - else - *path = zbx_dsprintf(*path, "%s/%s", name, *path); - - } - else - zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID("%s"), id_esc); - - zbx_free(id_esc); - zbx_free(name); - } - while (NULL != id); - - if (SUCCEED == ret) - { - if (NULL != *path && NULL == (*parentid = zbx_xml_doc_read_value(xdoc , tmp))) - zbx_free(*path); - - if (NULL == *path) - ret = FAIL; - } - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s resource pool path: '%s', parentid: '%s'", __func__, - zbx_result_string(ret), ZBX_NULL2EMPTY_STR(*path), ZBX_NULL2EMPTY_STR(*parentid)); - - return ret; -} - -/****************************************************************************** - * * * Purpose: get alarm details * * * * Parameters: service - [IN] the vmware service * @@ -4363,7 +4308,7 @@ static zbx_vmware_vm_t *vmware_service_create_vm(zbx_vmware_service_t *service, rpool_cmp.id = vm->props[ZBX_VMWARE_VMPROP_RESOURCEPOOL]; if (FAIL != (i = zbx_vector_vmware_resourcepool_bsearch(rpools, &rpool_cmp, - vmware_resourcepool_compare_id))) + ZBX_DEFAULT_STR_PTR_COMPARE_FUNC))) { rpools->values[i]->vm_num += 1; } @@ -5279,7 +5224,7 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service, zbx_free(hvid_esc); zbx_free(scsi_req); - if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, &iter, &doc, error))) + if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, __func__, &iter, &doc, error))) goto out; updated += vmware_service_hv_disks_parse_info(doc, dss, disks_info); @@ -5289,7 +5234,7 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service, zbx_xml_free_doc(doc); doc = NULL; - if (SUCCEED != (ret = zbx_property_collection_next(iter, &doc, error))) + if (SUCCEED != (ret = zbx_property_collection_next(__func__, iter, &doc, error))) goto out; updated += vmware_service_hv_disks_parse_info(doc, dss, disks_info); @@ -5306,7 +5251,8 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service, "<ns0:pathSet>config.vsanHostConfig.storageInfo.diskMapping</ns0:pathSet>", hvid_esc); zbx_free(hvid_esc); - if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, &iter, &doc_dinfo, &err))) + if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, __func__, &iter, &doc_dinfo, + &err))) { zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot get vsan disk_info:%s", __func__, err); zbx_str_free(err); @@ -5320,7 +5266,7 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service, zbx_xml_free_doc(doc_dinfo); doc_dinfo = NULL; - if (SUCCEED != (ret = zbx_property_collection_next(iter, &doc_dinfo, error))) + if (SUCCEED != (ret = zbx_property_collection_next(__func__, iter, &doc_dinfo, error))) goto out; updated_vsan += vmware_service_hv_vsan_parse_info(doc_dinfo, vsan_uuid, disks_info); @@ -5862,7 +5808,7 @@ static int vmware_hv_ds_access_update(zbx_vmware_service_t *service, CURL *easyh zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_HV_DS_ACCESS, pcollector, hvid_esc, hvid_esc, hvid_esc, hvid_esc); zbx_free(hvid_esc); - if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, &iter, &doc, error)) + if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, __func__, &iter, &doc, error)) goto out; updated += vmware_hv_ds_access_parse(doc, hv_dss, hv_uuid, hv_id, dss); @@ -5872,7 +5818,7 @@ static int vmware_hv_ds_access_update(zbx_vmware_service_t *service, CURL *easyh zbx_xml_free_doc(doc); doc = NULL; - if (SUCCEED != zbx_property_collection_next(iter, &doc, error)) + if (SUCCEED != zbx_property_collection_next(__func__, iter, &doc, error)) goto out; updated += vmware_hv_ds_access_parse(doc, hv_dss, hv_uuid, hv_id, dss); @@ -6007,19 +5953,6 @@ static const char *vmware_hv_vsan_uuid(zbx_vector_vmware_datastore_t *dss, zbx_v /****************************************************************************** * * - * Purpose: sorting function to sort Resource pool names vector by name * - * * - ******************************************************************************/ -int vmware_resourcepool_compare_id(const void *r1, const void *r2) -{ - const zbx_vmware_resourcepool_t *rp1 = *(const zbx_vmware_resourcepool_t * const *)r1; - const zbx_vmware_resourcepool_t *rp2 = *(const zbx_vmware_resourcepool_t * const *)r2; - - return strcmp(rp1->id, rp2->id); -} - -/****************************************************************************** - * * * Function: vmware_service_init_hv * * * * Purpose: initialize vmware hypervisor object * @@ -6600,7 +6533,7 @@ static int vmware_service_get_hv_ds_dc_dvs_list(const zbx_vmware_service_t *serv zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VCENTER_HV_DS_LIST, pcollector, vmware_service_objects[service->type].root_folder); - if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, &iter, &doc, error)) + if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, __func__, &iter, &doc, error)) goto out; if (ZBX_VMWARE_TYPE_VCENTER == service->type) @@ -6625,7 +6558,7 @@ static int vmware_service_get_hv_ds_dc_dvs_list(const zbx_vmware_service_t *serv zbx_xml_free_doc(doc); doc = NULL; - if (SUCCEED != zbx_property_collection_next(iter, &doc, error)) + if (SUCCEED != zbx_property_collection_next(__func__, iter, &doc, error)) goto out; if (ZBX_VMWARE_TYPE_VCENTER == service->type) @@ -7366,15 +7299,256 @@ out: * * * Purpose: retrieves a list of vmware service clusters and resource pools * * * + * Parameters: service - [IN] the vmware service * + * easyhandle - [IN] the CURL handle * + * cluster_data - [OUT] a pointer to the output variable * + * clusters - [OUT] a pointer to the resulting clusters * + * vector * + * rp_chunks - [OUT] a pointer to the resulting resource pool * + * vector * + * alarms_data - [OUT] the vector with all alarms * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - the operation has completed successfully * + * FAIL - the operation has failed * + * * + ******************************************************************************/ +static int vmware_service_process_cluster_data(zbx_vmware_service_t *service, CURL *easyhandle, + xmlDoc *cluster_data, zbx_vector_ptr_t *clusters, zbx_vector_vmware_rpool_chunk_t *rp_chunks, + zbx_vmware_alarms_data_t *alarms_data, char **error) +{ +#define ZBX_XPATH_GET_RESOURCEPOOL_PARENTID \ + ZBX_XPATH_PROP_NAME_NODE("parent") "[@type='ResourcePool']" +#define ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID \ + ZBX_XPATH_PROP_NAME_NODE("parent") "[@type!='ResourcePool']" + + int ret, i; + char *id_esc, tmp[MAX_STRING_LEN * 2]; + zbx_vmware_cluster_t *cluster; + zbx_vector_str_t rp_ids, ids; + xmlNode *node; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); + + zbx_vector_str_create(&ids); + + zbx_xml_read_values(cluster_data, ZBX_XPATH_OBJS_BY_TYPE(ZBX_VMWARE_SOAP_CLUSTER), &ids); + zbx_vector_ptr_reserve(clusters, (size_t)(clusters->values_alloc + ids.values_num)); + + for (i = 0; i < ids.values_num; i++) + { + char *name; + + id_esc = zbx_xml_escape_dyn(ids.values[i]); + zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_CLUSTER, "[text()='%s']"), + id_esc); + zbx_str_free(id_esc); + + if (NULL == (node = zbx_xml_doc_get(cluster_data, tmp)) || + NULL == (name = zbx_xml_node_read_value(cluster_data, node, + ZBX_XPATH_PROP_NAME_NODE("name")))) + { + continue; + } + + cluster = (zbx_vmware_cluster_t *)zbx_malloc(NULL, sizeof(zbx_vmware_cluster_t)); + cluster->id = zbx_strdup(NULL, ids.values[i]); + cluster->name = name; + cluster->status = NULL; + zbx_vector_str_create(&cluster->dss_uuid); + zbx_vector_str_create(&cluster->alarm_ids); + + if (SUCCEED != vmware_service_get_alarms_data(__func__, service, easyhandle, cluster_data, + zbx_xml_node_get(cluster_data, node, ZBX_XPATH_PROP_NAME_NODE("triggeredAlarmState")), + &cluster->alarm_ids, alarms_data, error)) + { + vmware_cluster_free(cluster); + ret = FAIL; + goto out; + } + + zbx_vector_ptr_append(clusters, cluster); + } + + zbx_vector_str_create(&rp_ids); + zbx_xml_read_values(cluster_data, ZBX_XPATH_OBJS_BY_TYPE(ZBX_VMWARE_SOAP_RESOURCEPOOL), &rp_ids); + zbx_vector_vmware_rpool_chunk_reserve(rp_chunks, (size_t)(rp_chunks->values_num + rp_ids.values_num)); + + for (i = 0; i < rp_ids.values_num; i++) + { + zbx_vmware_rpool_chunk_t *rp_chunk; + + rp_chunk = (zbx_vmware_rpool_chunk_t *)zbx_malloc(NULL, sizeof(zbx_vmware_rpool_chunk_t)); + + id_esc = zbx_xml_escape_dyn(rp_ids.values[i]); + zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_RESOURCEPOOL, "[text()='%s']"), + id_esc); + zbx_str_free(id_esc); + + if (NULL == (node = zbx_xml_doc_get(cluster_data, tmp)) || NULL == ( + rp_chunk->name = zbx_xml_node_read_value(cluster_data, node, + ZBX_XPATH_PROP_NAME_NODE("name")))) + { + zbx_free(rp_chunk); + continue; + } + + if (NULL == (rp_chunk->first_parentid = zbx_xml_node_read_value(cluster_data , node, + ZBX_XPATH_GET_RESOURCEPOOL_PARENTID))) + { + if (NULL == (rp_chunk->first_parentid = zbx_xml_node_read_value(cluster_data , node, + ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID))) + { + zbx_free(rp_chunk->name); + zbx_free(rp_chunk); + continue; + } + + rp_chunk->parent_is_rp = 0; + } + else + rp_chunk->parent_is_rp = 1; + + rp_chunk->id = zbx_strdup(NULL, rp_ids.values[i]); + rp_chunk->path = rp_chunk->parentid = NULL; + zbx_vector_vmware_rpool_chunk_append(rp_chunks, rp_chunk); + } + + zbx_vector_str_clear_ext(&rp_ids, zbx_str_free); + zbx_vector_str_destroy(&rp_ids); + + ret = SUCCEED; +out: + zbx_vector_str_clear_ext(&ids, zbx_str_free); + zbx_vector_str_destroy(&ids); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s cl:%d rp:%d", __func__, zbx_result_string(ret), + clusters->values_num, rp_chunks->values_num); + + return ret; + +#undef ZBX_XPATH_GET_RESOURCEPOOL_PARENTID +#undef ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID +} + +/****************************************************************************** + * * + * Purpose: retrieves status of the specified vmware cluster * + * * * Parameters: easyhandle - [IN] the CURL handle * - * data - [OUT] a pointer to the output variable * + * datastores - [IN] all available Datastores * + * cluster - [IN/OUT] the cluster * + * cq_values - [IN/OUT] the vector with custom query entries * * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the operation has completed successfully * * FAIL - the operation has failed * * * ******************************************************************************/ -static int vmware_service_get_cluster_data(CURL *easyhandle, xmlDoc **data, char **error) +static int vmware_service_get_cluster_state(CURL *easyhandle, const zbx_vector_vmware_datastore_t *datastores, + zbx_vmware_cluster_t *cluster, zbx_vector_cq_value_t *cq_values, char **error) +{ +# define ZBX_POST_VMWARE_CLUSTER_STATUS \ + ZBX_POST_VSPHERE_HEADER \ + "<ns0:RetrievePropertiesEx>" \ + "<ns0:_this type=\"PropertyCollector\">propertyCollector</ns0:_this>" \ + "<ns0:specSet>" \ + "<ns0:propSet>" \ + "<ns0:type>ClusterComputeResource</ns0:type>" \ + "<ns0:all>false</ns0:all>" \ + "<ns0:pathSet>summary.overallStatus</ns0:pathSet>" \ + "<ns0:pathSet>datastore</ns0:pathSet>" \ + "%s" \ + "</ns0:propSet>" \ + "<ns0:objectSet>" \ + "<ns0:obj type=\"ClusterComputeResource\">%s</ns0:obj>" \ + "</ns0:objectSet>" \ + "</ns0:specSet>" \ + "<ns0:options></ns0:options>" \ + "</ns0:RetrievePropertiesEx>" \ + ZBX_POST_VSPHERE_FOOTER + + char *tmp, *clusterid_esc, *cq_prop; + int i, ret; + xmlDoc *doc = NULL; + zbx_vector_cq_value_t cqvs; + zbx_vector_str_t ids; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() clusterid:'%s'", __func__, cluster->id); + + zbx_vector_str_create(&ids); + zbx_vector_cq_value_create(&cqvs); + clusterid_esc = zbx_xml_escape_dyn(cluster->id); + cq_prop = vmware_cq_prop_soap_request(cq_values, ZBX_VMWARE_SOAP_CLUSTER, cluster->id, &cqvs); + + tmp = zbx_dsprintf(NULL, ZBX_POST_VMWARE_CLUSTER_STATUS, cq_prop, clusterid_esc); + + zbx_str_free(cq_prop); + zbx_str_free(clusterid_esc); + ret = zbx_soap_post(__func__, easyhandle, tmp, &doc, NULL, error); + zbx_str_free(tmp); + + if (FAIL == ret) + goto out; + + cluster->status = zbx_xml_doc_read_value(doc, ZBX_XPATH_PROP_NAME("summary.overallStatus")); + + if (0 != cqvs.values_num) + vmware_service_cq_prop_value(__func__, doc, &cqvs); + + zbx_xml_read_values(doc, ZBX_XPATH_PROP_NAME("datastore") "/*", &ids); + + for (i = 0; i < ids.values_num; i++) + { + int j; + zbx_vmware_datastore_t ds_cmp; + + ds_cmp.id = ids.values[i]; + + if (FAIL == (j = zbx_vector_vmware_datastore_bsearch(datastores, &ds_cmp, vmware_ds_id_compare))) + { + zabbix_log(LOG_LEVEL_DEBUG, "%s(): Datastore \"%s\" not found on cluster \"%s\".", __func__, + ds_cmp.id, cluster->id); + continue; + } + + zbx_vector_str_append(&cluster->dss_uuid, zbx_strdup(NULL, datastores->values[j]->uuid)); + } +out: + zbx_vector_cq_value_destroy(&cqvs); + zbx_vector_str_clear_ext(&ids, zbx_str_free); + zbx_vector_str_destroy(&ids); + zbx_xml_free_doc(doc); + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; + +# undef ZBX_POST_VMWARE_CLUSTER_STATUS +} + +/****************************************************************************** + * * + * Purpose: creates lists of vmware cluster and resource pool objects * + * * + * Parameters: service - [IN] the vmware service * + * easyhandle - [IN] the CURL handle * + * datastores - [IN] all available Datastores * + * cq_values - [IN/OUT] the vector with custom query entries * + * clusters - [OUT] a pointer to the resulting clusters * + * vector * + * resourcepools - [OUT] a pointer to the resulting resource pool * + * vector * + * alarms_data - [OUT] the vector with all alarms * + * error - [OUT] the error message in the case of failure * + * * + * Return value: SUCCEED - the operation has completed successfully * + * FAIL - the operation has failed * + * * + ******************************************************************************/ +static int vmware_service_get_clusters_and_resourcepools(zbx_vmware_service_t *service, CURL *easyhandle, + const zbx_vector_vmware_datastore_t *datastores, zbx_vector_cq_value_t *cq_values, + zbx_vector_ptr_t *clusters, zbx_vector_vmware_resourcepool_t *resourcepools, + zbx_vmware_alarms_data_t *alarms_data, char **error) { # define ZBX_POST_VCENTER_CLUSTER \ ZBX_POST_VSPHERE_HEADER \ @@ -7384,15 +7558,9 @@ static int vmware_service_get_cluster_data(CURL *easyhandle, xmlDoc **data, char "<ns0:propSet>" \ "<ns0:type>ClusterComputeResource</ns0:type>" \ "<ns0:pathSet>name</ns0:pathSet>" \ - "<ns0:pathSet>resourcePool</ns0:pathSet>" \ "<ns0:pathSet>triggeredAlarmState</ns0:pathSet>" \ "</ns0:propSet>" \ "<ns0:propSet>" \ - "<ns0:type>ComputeResource</ns0:type>" \ - "<ns0:pathSet>resourcePool</ns0:pathSet>" \ - "<ns0:pathSet>name</ns0:pathSet>" \ - "</ns0:propSet>" \ - "<ns0:propSet>" \ "<ns0:type>ResourcePool</ns0:type>" \ "<ns0:pathSet>resourcePool</ns0:pathSet>" \ "<ns0:pathSet>name</ns0:pathSet>" \ @@ -7521,243 +7689,113 @@ static int vmware_service_get_cluster_data(CURL *easyhandle, xmlDoc **data, char "</ns0:RetrievePropertiesEx>" \ ZBX_POST_VSPHERE_FOOTER - int ret = FAIL; + int i, ret = FAIL; + xmlDoc *cluster_data = NULL; + zbx_property_collection_iter *iter = NULL; + zbx_vector_vmware_rpool_chunk_t rp_chunks; + zbx_vector_ptr_t cl_chunks; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - if (SUCCEED != zbx_soap_post(__func__, easyhandle, ZBX_POST_VCENTER_CLUSTER, data, NULL, error)) - goto out; - - ret = SUCCEED; -out: - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; - -# undef ZBX_POST_VCENTER_CLUSTER -} - -/****************************************************************************** - * * - * Purpose: retrieves status of the specified vmware cluster * - * * - * Parameters: easyhandle - [IN] the CURL handle * - * clusterid - [IN] the cluster id * - * datastores - [IN] all available Datastores * - * cq_values - [IN/OUT] the vector with custom query entries * - * status - [OUT] a pointer to the output variable * - * dss - [OUT] a list of DS available for cluster * - * error - [OUT] the error message in the case of failure * - * * - * Return value: SUCCEED - the operation has completed successfully * - * FAIL - the operation has failed * - * * - ******************************************************************************/ -static int vmware_service_get_cluster_state(CURL *easyhandle, const char *clusterid, - const zbx_vector_vmware_datastore_t *datastores, zbx_vector_cq_value_t *cq_values, char **status, - zbx_vector_str_t *dss, char **error) -{ -# define ZBX_POST_VMWARE_CLUSTER_STATUS \ - ZBX_POST_VSPHERE_HEADER \ - "<ns0:RetrievePropertiesEx>" \ - "<ns0:_this type=\"PropertyCollector\">propertyCollector</ns0:_this>" \ - "<ns0:specSet>" \ - "<ns0:propSet>" \ - "<ns0:type>ClusterComputeResource</ns0:type>" \ - "<ns0:all>false</ns0:all>" \ - "<ns0:pathSet>summary.overallStatus</ns0:pathSet>" \ - "<ns0:pathSet>datastore</ns0:pathSet>" \ - "%s" \ - "</ns0:propSet>" \ - "<ns0:objectSet>" \ - "<ns0:obj type=\"ClusterComputeResource\">%s</ns0:obj>" \ - "</ns0:objectSet>" \ - "</ns0:specSet>" \ - "<ns0:options></ns0:options>" \ - "</ns0:RetrievePropertiesEx>" \ - ZBX_POST_VSPHERE_FOOTER - - char *tmp, *clusterid_esc, *cq_prop; - int i, ret = FAIL; - xmlDoc *doc = NULL; - zbx_vector_cq_value_t cqvs; - zbx_vector_str_t ids; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() clusterid:'%s'", __func__, clusterid); - - zbx_vector_str_create(&ids); - zbx_vector_cq_value_create(&cqvs); - clusterid_esc = zbx_xml_escape_dyn(clusterid); - cq_prop = vmware_cq_prop_soap_request(cq_values, ZBX_VMWARE_SOAP_CLUSTER, clusterid, &cqvs); - - tmp = zbx_dsprintf(NULL, ZBX_POST_VMWARE_CLUSTER_STATUS, cq_prop, clusterid_esc); - - zbx_str_free(cq_prop); - zbx_str_free(clusterid_esc); - ret = zbx_soap_post(__func__, easyhandle, tmp, &doc, NULL, error); - zbx_str_free(tmp); + zbx_vector_vmware_rpool_chunk_create(&rp_chunks); + zbx_vector_ptr_create(&cl_chunks); - if (FAIL == ret) + if (SUCCEED != zbx_property_collection_init(easyhandle, ZBX_POST_VCENTER_CLUSTER, "propertyCollector", + __func__, &iter, &cluster_data, error)) + { goto out; + } - *status = zbx_xml_doc_read_value(doc, ZBX_XPATH_PROP_NAME("summary.overallStatus")); - - if (0 != cqvs.values_num) - vmware_service_cq_prop_value(__func__, doc, &cqvs); - - zbx_xml_read_values(doc, ZBX_XPATH_PROP_NAME("datastore") "/*", &ids); + if (SUCCEED != vmware_service_process_cluster_data(service, easyhandle, cluster_data, &cl_chunks, &rp_chunks, + alarms_data, error)) + { + goto out; + } - for (i = 0; i < ids.values_num; i++) + while (NULL != iter->token) { - int j; - zbx_vmware_datastore_t ds_cmp; + zbx_xml_free_doc(cluster_data); + cluster_data = NULL; - ds_cmp.id = ids.values[i]; + if (SUCCEED != zbx_property_collection_next(__func__, iter, &cluster_data, error)) + goto out; - if (FAIL == (j = zbx_vector_vmware_datastore_bsearch(datastores, &ds_cmp, vmware_ds_id_compare))) + if (SUCCEED != vmware_service_process_cluster_data(service, easyhandle, cluster_data, &cl_chunks, + &rp_chunks, alarms_data, error)) { - zabbix_log(LOG_LEVEL_DEBUG, "%s(): Datastore \"%s\" not found on cluster \"%s\".", __func__, - ds_cmp.id, clusterid); - continue; + goto out; } - - zbx_vector_str_append(dss, zbx_strdup(NULL, datastores->values[j]->uuid)); } -out: - zbx_vector_cq_value_destroy(&cqvs); - zbx_vector_str_clear_ext(&ids, zbx_str_free); - zbx_vector_str_destroy(&ids); - zbx_xml_free_doc(doc); - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; - -# undef ZBX_POST_VMWARE_CLUSTER_STATUS -} -/****************************************************************************** - * * - * Purpose: creates lists of vmware cluster and resource pool objects * - * * - * Parameters: service - [IN] the vmware service * - * easyhandle - [IN] the CURL handle * - * datastores - [IN] all available Datastores * - * cq_values - [IN/OUT] the vector with custom query entries * - * clusters - [OUT] a pointer to the resulting clusters * - * vector * - * resourcepools - [OUT] a pointer to the resulting resource pool * - * vector * - * alarms_data - [OUT] the vector with all alarms * - * error - [OUT] the error message in the case of failure * - * * - * Return value: SUCCEED - the operation has completed successfully * - * FAIL - the operation has failed * - * * - ******************************************************************************/ -static int vmware_service_get_clusters_and_resourcepools(zbx_vmware_service_t *service, CURL *easyhandle, - const zbx_vector_vmware_datastore_t *datastores, zbx_vector_cq_value_t *cq_values, - zbx_vector_ptr_t *clusters, zbx_vector_vmware_resourcepool_t *resourcepools, - zbx_vmware_alarms_data_t *alarms_data, char **error) -{ - char xpath[MAX_STRING_LEN]; - int i, ret = FAIL; - xmlDoc *cluster_data = NULL; - zbx_vector_str_t ids, rpools_all, rpools_uniq, dss; - zbx_vmware_cluster_t *cluster; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); - - zbx_vector_str_create(&ids); - zbx_vector_str_create(&dss); - - if (SUCCEED != vmware_service_get_cluster_data(easyhandle, &cluster_data, error)) - goto out; - - zbx_xml_read_values(cluster_data, ZBX_XPATH_OBJS_BY_TYPE(ZBX_VMWARE_SOAP_CLUSTER), &ids); - zbx_vector_ptr_reserve(clusters, (size_t)(ids.values_num + clusters->values_alloc)); + zbx_property_collection_free(iter); + zbx_vector_vmware_rpool_chunk_sort(&rp_chunks, ZBX_DEFAULT_STR_PTR_COMPARE_FUNC); - for (i = 0; i < ids.values_num; i++) + for (i = 0; i < rp_chunks.values_num; i++) { - char *status, *name; - - zbx_snprintf(xpath, sizeof(xpath), ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_CLUSTER, "[text()='%s']") - "/" ZBX_XPATH_PROP_NAME_NODE("name"), ids.values[i]); + int k; + zbx_vmware_resourcepool_t *rpool; + zbx_vmware_rpool_chunk_t rp_parent, *rp_chunk = rp_chunks.values[i]; - if (NULL == (name = zbx_xml_doc_read_value(cluster_data, xpath))) + if (0 == rp_chunk->parent_is_rp) /* skipped the top (default) resource pool name */ continue; - if (SUCCEED != vmware_service_get_cluster_state(easyhandle, ids.values[i], datastores, cq_values, - &status, &dss, error)) - { - zbx_free(name); - goto out; - } + rpool = (zbx_vmware_resourcepool_t*)zbx_malloc(NULL, sizeof(zbx_vmware_resourcepool_t)); + rpool->id = zbx_strdup(NULL, rp_chunk->id); + rpool->path = zbx_strdup(NULL, rp_chunk->name); + rpool->vm_num = 0; - cluster = (zbx_vmware_cluster_t *)zbx_malloc(NULL, sizeof(zbx_vmware_cluster_t)); - cluster->id = zbx_strdup(NULL, ids.values[i]); - cluster->name = name; - cluster->status = status; - zbx_vector_str_create(&cluster->dss_uuid); - zbx_vector_str_append_array(&cluster->dss_uuid, dss.values, dss.values_num); - zbx_vector_str_clear(&dss); - zbx_vector_str_create(&cluster->alarm_ids); + rp_parent.id = rp_chunk->first_parentid; - if (FAIL == vmware_service_get_alarms_data(__func__, service, easyhandle, cluster_data, NULL, - &cluster->alarm_ids, alarms_data, error)) + while (FAIL != (k = zbx_vector_vmware_rpool_chunk_bsearch(&rp_chunks, &rp_parent, + ZBX_DEFAULT_STR_PTR_COMPARE_FUNC))) { - vmware_cluster_free(cluster); - goto out; - } + zbx_vmware_rpool_chunk_t *rp_next = rp_chunks.values[k]; - zbx_vector_ptr_append(clusters, cluster); - } + if (NULL != rp_next->path) + rpool->path = zbx_dsprintf(rpool->path, "%s/%s", rp_next->path, rpool->path); - /* Add resource pools */ + if (0 == rp_next->parent_is_rp || NULL != rp_next->path) + { + rpool->parentid = zbx_strdup(NULL, 0 == rp_next->parent_is_rp ? + rp_next->first_parentid : rp_next->parentid); + zbx_vector_vmware_resourcepool_append(resourcepools, rpool); + rp_chunk->path = rpool->path; + rp_chunk->parentid = rpool->parentid; + break; + } - zbx_vector_str_create(&rpools_all); - zbx_vector_str_create(&rpools_uniq); - zbx_xml_read_values(cluster_data, "//*[@type='ResourcePool']", &rpools_all); - zbx_vector_str_sort(&rpools_all, ZBX_DEFAULT_STR_COMPARE_FUNC); - zbx_vector_str_append_array(&rpools_uniq, rpools_all.values, rpools_all.values_num); - zbx_vector_str_uniq(&rpools_uniq, ZBX_DEFAULT_STR_COMPARE_FUNC); - zbx_vector_vmware_resourcepool_reserve(resourcepools, (size_t)rpools_all.values_num); + rpool->path = zbx_dsprintf(rpool->path, "%s/%s", rp_next->name, rpool->path); + rp_parent.id = rp_next->first_parentid; + } + } - for (i = 0; i < rpools_uniq.values_num; i++) + zbx_vector_vmware_resourcepool_sort(resourcepools, ZBX_DEFAULT_STR_PTR_COMPARE_FUNC); + zbx_vector_ptr_reserve(clusters, (size_t)(clusters->values_alloc + cl_chunks.values_num)); + + for (i = cl_chunks.values_num - 1; i >= 0 ; i--) { - zbx_vmware_resourcepool_t *rpool; - char *path, *parentid; - const char *id = rpools_uniq.values[i]; + zbx_vmware_cluster_t *cluster = (zbx_vmware_cluster_t*)(cl_chunks.values[i]); - if (SUCCEED != vmware_service_get_resourcepool_data(cluster_data, id, &parentid, &path)) - { - zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot find resource pool name for id:%s", __func__, id); - continue; - } + if (SUCCEED != vmware_service_get_cluster_state(easyhandle, datastores, cluster, cq_values, error)) + goto out; - rpool = (zbx_vmware_resourcepool_t *)zbx_malloc(NULL, sizeof(zbx_vmware_resourcepool_t)); - rpool->id = zbx_strdup(NULL, id); - rpool->path = path; - rpool->parentid = parentid; - rpool->vm_num = 0; - zbx_vector_vmware_resourcepool_append(resourcepools, rpool); + zbx_vector_ptr_append(clusters, cluster); + zbx_vector_ptr_remove_noorder(&cl_chunks, i); } - zbx_vector_vmware_resourcepool_sort(resourcepools, vmware_resourcepool_compare_id); - zbx_vector_str_clear_ext(&rpools_all, zbx_str_free); - zbx_vector_str_destroy(&rpools_all); - zbx_vector_str_destroy(&rpools_uniq); - ret = SUCCEED; out: zbx_xml_free_doc(cluster_data); - zbx_vector_str_clear_ext(&ids, zbx_str_free); - zbx_vector_str_destroy(&ids); - zbx_vector_str_destroy(&dss); + zbx_vector_vmware_rpool_chunk_clear_ext(&rp_chunks, vmware_rp_chunk_free); + zbx_vector_vmware_rpool_chunk_destroy(&rp_chunks); + zbx_vector_ptr_clear_ext(&cl_chunks, (zbx_clean_func_t)vmware_cluster_free); + zbx_vector_ptr_destroy(&cl_chunks); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s found cl:%d rp:%d", __func__, zbx_result_string(ret), clusters->values_num, resourcepools->values_num); return ret; +# undef ZBX_POST_VCENTER_CLUSTER } /****************************************************************************** diff --git a/src/zabbix_server/vmware/vmware.h b/src/zabbix_server/vmware/vmware.h index 15adb060d09..68e95685654 100644 --- a/src/zabbix_server/vmware/vmware.h +++ b/src/zabbix_server/vmware/vmware.h @@ -341,7 +341,6 @@ typedef struct } zbx_vmware_resourcepool_t; -int vmware_resourcepool_compare_id(const void *r1, const void *r2); ZBX_PTR_VECTOR_DECL(vmware_resourcepool, zbx_vmware_resourcepool_t *) /* the vmware eventlog state */ diff --git a/src/zabbix_server/vmware/vmware_rest.c b/src/zabbix_server/vmware/vmware_rest.c index 19665791ae5..78245603fda 100644 --- a/src/zabbix_server/vmware/vmware_rest.c +++ b/src/zabbix_server/vmware/vmware_rest.c @@ -510,8 +510,8 @@ static void vmware_service_rest_logout(CURL *easyhandle, ZBX_HTTPPAGE *page) zbx_snprintf(tmp, sizeof(tmp),"%s/session", page->url); if (CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_URL, tmp)) || - CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_CUSTOMREQUEST, "DELETE")) || - CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_POST, 0L))) + CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_POST, 0L)) || + CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_CUSTOMREQUEST, "DELETE"))) { zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot set cURL option %d: %s.", __func__, (int)opt, curl_easy_strerror(err)); diff --git a/templates/module/process/README.md b/templates/module/process/README.md new file mode 100644 index 00000000000..5e93efb2585 --- /dev/null +++ b/templates/module/process/README.md @@ -0,0 +1,77 @@ + +# OS processes by Zabbix agent + +## Overview + +For Zabbix version: 6.4 and higher. +This template is designed to monitor processes by Zabbix that work without any external scripts. +Most of the metrics are collected in one go, thanks to Zabbix bulk data collection. +For example, by specifying "zabbix" as macro value, you can monitor all zabbix processes. + + + +This template was tested on: + +- CentOS, version CentOS Linux 8; +- Ubuntu, version Ubuntu 22.04.1 LTS. + +## Setup + +> See [Zabbix template operation](https://www.zabbix.com/documentation/6.4/manual/config/templates_out_of_the_box/zabbix_agent) for basic instructions. + +Install and setup [Zabbix agent](https://www.zabbix.com/documentation/6.4/manual/installation/install_from_packages). + +Custom processes set in macros: + +- {$PROC.NAME.MATCHES} +- {$PROC.NAME.NOT_MATCHES} + + +## Zabbix configuration + +No specific Zabbix configuration is required. + +### Macros used + +|Name|Description|Default| +|----|-----------|-------| +|{$PROC.NAME.MATCHES} |<p>This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.</p> |`<CHANGE VALUE>` | +|{$PROC.NAME.NOT_MATCHES} |<p>This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.</p> |`<CHANGE VALUE>` | + +## Template links + +There are no template links in this template. + +## Discovery rules + +|Name|Description|Type|Key and additional info| +|----|-----------|----|----| +|Processes discovery |<p>Discovery of OS summary processes.</p> |DEPENDENT |custom.proc.discovery<p>**Filter**:</p>AND <p>- {#VMEM} NOT_MATCHES_REGEX `-1`</p><p>- {#NAME} MATCHES_REGEX `{$PROC.NAME.MATCHES}`</p><p>- {#NAME} NOT_MATCHES_REGEX `{$PROC.NAME.NOT_MATCHES}`</p> | + +## Items collected + +|Group|Name|Description|Type|Key and additional info| +|-----|----|-----------|----|---------------------| +|OS |Process [{#NAME}]: Get data |<p>Summary metrics collected during the process {#NAME}.</p> |DEPENDENT |custom.proc.get[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.[?(@["name"]=="{#NAME}")].first()`</p><p>⛔️ON_FAIL: `CUSTOM_VALUE -> Failed to retrieve process {#NAME} data`</p> | +|OS |Process [{#NAME}]: Memory usage (rss) |<p>The summary of Resident Set Size (RSS) memory used by the process {#NAME} in bytes.</p> |DEPENDENT |custom.proc.rss[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.rss`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> | +|OS |Process [{#NAME}]: Memory usage (vsize) |<p>The summary of virtual memory used by process {#NAME} in bytes.</p> |DEPENDENT |custom.proc.vmem[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.vsize`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> | +|OS |Process [{#NAME}]: Memory usage, % |<p>The percentage of real memory used by the process {#NAME}.</p> |DEPENDENT |custom.proc.pmem[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.pmem`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> | +|OS |Process [{#NAME}]: Number of running processes |<p>The number of running processes {#NAME}.</p> |DEPENDENT |custom.proc.num[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.processes`</p><p>⛔️ON_FAIL: `CUSTOM_VALUE -> 0`</p><p>- DISCARD_UNCHANGED_HEARTBEAT: `1h`</p> | +|OS |Process [{#NAME}]: Number of threads |<p>The number of threads {#NAME}.</p> |DEPENDENT |custom.proc.thread[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.threads`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> | +|OS |Process [{#NAME}]: Number of page faults |<p>The number of page faults {#NAME}.</p> |DEPENDENT |custom.proc.page[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.page_faults`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> | +|OS |Process [{#NAME}]: Size of locked memory |<p>The size of locked memory {#NAME}.</p> |DEPENDENT |custom.proc.mem.locked[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.lck`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> | +|OS |Process [{#NAME}]: Swap space used |<p>The swap space used by {#NAME}.</p> |DEPENDENT |custom.proc.swap[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.swap`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> | +|Zabbix raw items |OS: Get process summary |<p>The summary of data metrics for all processes.</p> |ZABBIX_PASSIVE |proc.get[,,,summary] | + +## Triggers + +|Name|Description|Expression|Severity|Dependencies and additional info| +|----|-----------|----|----|----| +|Process [{#NAME}]: is not running |<p>-</p> |`last(/OS processes by Zabbix agent/custom.proc.num[{#NAME}])=0` |HIGH |<p>Manual close: YES</p> | + +## Feedback + +Please report any issues with the template at https://support.zabbix.com. + +You can also provide feedback, discuss the template, or ask for help at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback). + diff --git a/templates/module/process/template_module_process.yaml b/templates/module/process/template_module_process.yaml new file mode 100644 index 00000000000..04679ba623f --- /dev/null +++ b/templates/module/process/template_module_process.yaml @@ -0,0 +1,332 @@ +zabbix_export: + version: '6.4' + date: '2022-10-28T08:41:56Z' + template_groups: + - + uuid: 57b7ae836ca64446ba2c296389c009b7 + name: Templates/Modules + templates: + - + uuid: 0f6889282f6048e2b1370e569e578985 + template: 'OS processes by Zabbix agent' + name: 'OS processes by Zabbix agent' + description: | + Get processes metrics using item proc.get by Zabbix agent. + + You can discuss this template or leave feedback on our forum https://www.zabbix.com/forum/zabbix-suggestions-and-feedback + + Template tooling version used: 0.42 + groups: + - + name: Templates/Modules + items: + - + uuid: 803390429cf949d3b8439dd5dd71c706 + name: 'OS: Get process summary' + key: 'proc.get[,,,summary]' + history: '0' + trends: '0' + value_type: TEXT + description: 'The summary of data metrics for all processes.' + tags: + - + tag: component + value: raw + discovery_rules: + - + uuid: 7c0e8b719d0e464f92ee42a3da75b682 + name: 'Processes discovery' + type: DEPENDENT + key: custom.proc.discovery + delay: '0' + filter: + evaltype: AND + conditions: + - + macro: '{#VMEM}' + value: '-1' + operator: NOT_MATCHES_REGEX + formulaid: C + - + macro: '{#NAME}' + value: '{$PROC.NAME.MATCHES}' + formulaid: A + - + macro: '{#NAME}' + value: '{$PROC.NAME.NOT_MATCHES}' + operator: NOT_MATCHES_REGEX + formulaid: B + description: 'Discovery of OS summary processes.' + item_prototypes: + - + uuid: 2e9b31e2b47741f4b35c5d15f33378ea + name: 'Process [{#NAME}]: Get data' + type: DEPENDENT + key: 'custom.proc.get[{#NAME}]' + delay: '0' + history: 0d + trends: '0' + value_type: TEXT + description: 'Summary metrics collected during the process {#NAME}.' + preprocessing: + - + type: JSONPATH + parameters: + - '$.[?(@["name"]=="{#NAME}")].first()' + error_handler: CUSTOM_VALUE + error_handler_params: 'Failed to retrieve process {#NAME} data' + master_item: + key: 'proc.get[,,,summary]' + tags: + - + tag: component + value: raw + - + tag: process + value: '{#NAME}' + - + uuid: 822ffda22eb042b89fc50b212aab133f + name: 'Process [{#NAME}]: Size of locked memory' + type: DEPENDENT + key: 'custom.proc.mem.locked[{#NAME}]' + delay: '0' + history: 7d + value_type: FLOAT + units: B + description: 'The size of locked memory {#NAME}.' + preprocessing: + - + type: JSONPATH + parameters: + - $.lck + error_handler: DISCARD_VALUE + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: memory + - + tag: process + value: '{#NAME}' + - + uuid: 0bb7d924b8814c42a494d8a3baf48a59 + name: 'Process [{#NAME}]: Number of running processes' + type: DEPENDENT + key: 'custom.proc.num[{#NAME}]' + delay: '0' + history: 7d + description: 'The number of running processes {#NAME}.' + preprocessing: + - + type: JSONPATH + parameters: + - $.processes + error_handler: CUSTOM_VALUE + error_handler_params: '0' + - + type: DISCARD_UNCHANGED_HEARTBEAT + parameters: + - 1h + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: system + - + tag: process + value: '{#NAME}' + trigger_prototypes: + - + uuid: 66294f983a134a1e81165878f30d3ebc + expression: 'last(/OS processes by Zabbix agent/custom.proc.num[{#NAME}])=0' + name: 'Process [{#NAME}]: is not running' + priority: HIGH + manual_close: 'YES' + tags: + - + tag: scope + value: availability + - + uuid: abccdac17c7e4b549fcfe70ceeedeb9b + name: 'Process [{#NAME}]: Number of page faults' + type: DEPENDENT + key: 'custom.proc.page[{#NAME}]' + delay: '0' + history: 7d + description: 'The number of page faults {#NAME}.' + preprocessing: + - + type: JSONPATH + parameters: + - $.page_faults + error_handler: DISCARD_VALUE + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: system + - + tag: process + value: '{#NAME}' + - + uuid: 4ffd202fb6044b819f6f28dc866ca8f1 + name: 'Process [{#NAME}]: Memory usage, %' + type: DEPENDENT + key: 'custom.proc.pmem[{#NAME}]' + delay: '0' + history: 7d + value_type: FLOAT + units: '%' + description: 'The percentage of real memory used by the process {#NAME}.' + preprocessing: + - + type: JSONPATH + parameters: + - $.pmem + error_handler: DISCARD_VALUE + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: memory + - + tag: process + value: '{#NAME}' + - + uuid: 7e573e38bce04167bc37712c0a3e2194 + name: 'Process [{#NAME}]: Memory usage (rss)' + type: DEPENDENT + key: 'custom.proc.rss[{#NAME}]' + delay: '0' + history: 7d + value_type: FLOAT + units: B + description: 'The summary of Resident Set Size (RSS) memory used by the process {#NAME} in bytes.' + preprocessing: + - + type: JSONPATH + parameters: + - $.rss + error_handler: DISCARD_VALUE + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: memory + - + tag: process + value: '{#NAME}' + - + uuid: 12370d3b25024d2189cddba8d3b23938 + name: 'Process [{#NAME}]: Swap space used' + type: DEPENDENT + key: 'custom.proc.swap[{#NAME}]' + delay: '0' + history: 7d + value_type: FLOAT + units: B + description: 'The swap space used by {#NAME}.' + preprocessing: + - + type: JSONPATH + parameters: + - $.swap + error_handler: DISCARD_VALUE + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: memory + - + tag: process + value: '{#NAME}' + - + uuid: a7265ca1b1d2463294e26d80fe075639 + name: 'Process [{#NAME}]: Number of threads' + type: DEPENDENT + key: 'custom.proc.thread[{#NAME}]' + delay: '0' + history: 7d + description: 'The number of threads {#NAME}.' + preprocessing: + - + type: JSONPATH + parameters: + - $.threads + error_handler: DISCARD_VALUE + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: system + - + tag: process + value: '{#NAME}' + - + uuid: c1b9b1d8f28947589e46041690899100 + name: 'Process [{#NAME}]: Memory usage (vsize)' + type: DEPENDENT + key: 'custom.proc.vmem[{#NAME}]' + delay: '0' + history: 7d + value_type: FLOAT + units: B + description: 'The summary of virtual memory used by process {#NAME} in bytes.' + preprocessing: + - + type: JSONPATH + parameters: + - $.vsize + error_handler: DISCARD_VALUE + master_item: + key: 'custom.proc.get[{#NAME}]' + tags: + - + tag: component + value: memory + - + tag: process + value: '{#NAME}' + graph_prototypes: + - + uuid: b8f5b539152445fdbadbfba92adad1bf + name: 'Process [{#NAME}]: Memory usage[{#NAME}]' + graph_items: + - + drawtype: BOLD_LINE + color: 1A7C11 + item: + host: 'OS processes by Zabbix agent' + key: 'custom.proc.vmem[{#NAME}]' + - + sortorder: '1' + drawtype: BOLD_LINE + color: 2774A4 + item: + host: 'OS processes by Zabbix agent' + key: 'custom.proc.rss[{#NAME}]' + master_item: + key: 'proc.get[,,,summary]' + lld_macro_paths: + - + lld_macro: '{#NAME}' + path: $.name + - + lld_macro: '{#VMEM}' + path: $.vsize + macros: + - + macro: '{$PROC.NAME.MATCHES}' + value: '<CHANGE VALUE>' + description: 'This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.' + - + macro: '{$PROC.NAME.NOT_MATCHES}' + value: '<CHANGE VALUE>' + description: 'This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.' diff --git a/tests/libs/zbxjson/Makefile.am b/tests/libs/zbxjson/Makefile.am index d5190957c97..5f770afaf0f 100644 --- a/tests/libs/zbxjson/Makefile.am +++ b/tests/libs/zbxjson/Makefile.am @@ -3,7 +3,7 @@ noinst_PROGRAMS = \ zbx_json_decodevalue \ zbx_json_decodevalue_dyn \ zbx_jsonpath_compile \ - zbx_jsonpath_query + zbx_jsonobj_query JSON_LIBS = \ $(top_srcdir)/tests/libzbxmocktest.a \ @@ -104,22 +104,22 @@ endif zbx_jsonpath_compile_CFLAGS = -I@top_srcdir@/tests -# zbx_jsonpath_query +# zbx_jsonobj_query -zbx_jsonpath_query_SOURCES = \ - zbx_jsonpath_query.c \ +zbx_jsonobj_query_SOURCES = \ + zbx_jsonobj_query.c \ ../../zbxmocktest.h -zbx_jsonpath_query_LDADD = $(JSON_LIBS) +zbx_jsonobj_query_LDADD = $(JSON_LIBS) if SERVER -zbx_jsonpath_query_LDADD += @SERVER_LIBS@ -zbx_jsonpath_query_LDFLAGS = @SERVER_LDFLAGS@ +zbx_jsonobj_query_LDADD += @SERVER_LIBS@ +zbx_jsonobj_query_LDFLAGS = @SERVER_LDFLAGS@ else if PROXY -zbx_jsonpath_query_LDADD += @PROXY_LIBS@ -zbx_jsonpath_query_LDFLAGS = @PROXY_LDFLAGS@ +zbx_jsonobj_query_LDADD += @PROXY_LIBS@ +zbx_jsonobj_query_LDFLAGS = @PROXY_LDFLAGS@ endif endif -zbx_jsonpath_query_CFLAGS = -I@top_srcdir@/tests +zbx_jsonobj_query_CFLAGS = -I@top_srcdir@/tests diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.c b/tests/libs/zbxjson/zbx_jsonobj_query.c index 6e560a6a494..25349d6932e 100644 --- a/tests/libs/zbxjson/zbx_jsonpath_query.c +++ b/tests/libs/zbxjson/zbx_jsonobj_query.c @@ -51,26 +51,13 @@ static void check_indefinite_path_result(zbx_mock_handle_t handle, const char *r zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output); } -void zbx_mock_test_entry(void **state) +static void test_query(zbx_jsonobj_t *obj, const char *path, int expected_ret) { - const char *data, *path; - struct zbx_json_parse jp; char *output = NULL; - int expected_ret, returned_ret; + int returned_ret; zbx_mock_handle_t handle; - ZBX_UNUSED(state); - - /* reset json error to check if compilation will set it */ - zbx_set_json_strerror("%s", ""); - - data = zbx_mock_get_parameter_string("in.data"); - if (FAIL == zbx_json_open(data, &jp)) - fail_msg("Invalid json data: %s", zbx_json_strerror()); - - path = zbx_mock_get_parameter_string("in.path"); - returned_ret = zbx_jsonpath_query(&jp, path, &output); - expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return")); + returned_ret = zbx_jsonobj_query(obj, path, &output); if (FAIL == returned_ret) printf("\tzbx_jsonpath_query() failed with: %s\n", zbx_json_strerror()); @@ -97,4 +84,31 @@ void zbx_mock_test_entry(void **state) zbx_mock_assert_str_ne("tzbx_jsonpath_query() error", "", zbx_json_strerror()); zbx_free(output); + +} + +void zbx_mock_test_entry(void **state) +{ + const char *data, *path; + int expected_ret; + zbx_jsonobj_t obj; + + ZBX_UNUSED(state); + + /* reset json error to check if compilation will set it */ + zbx_set_json_strerror("%s", ""); + + data = zbx_mock_get_parameter_string("in.data"); + if (FAIL == zbx_jsonobj_open(data, &obj)) + fail_msg("Invalid json data: %s", zbx_json_strerror()); + + path = zbx_mock_get_parameter_string("in.path"); + expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return")); + + test_query(&obj, path, expected_ret); + + /* query second time to check index reuse */ + test_query(&obj, path, expected_ret); + + zbx_jsonobj_clear(&obj); } diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml b/tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml index 456f49ebe28..456f49ebe28 100644 --- a/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml +++ b/tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.yaml b/tests/libs/zbxjson/zbx_jsonobj_query.yaml index a4b784b90d9..7e9aae0044d 100644 --- a/tests/libs/zbxjson/zbx_jsonpath_query.yaml +++ b/tests/libs/zbxjson/zbx_jsonobj_query.yaml @@ -37,7 +37,7 @@ out: return: SUCCEED --- test case: Query $.filters.price -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.filters.price @@ -46,7 +46,7 @@ out: value: 10 --- test case: Query $.filters.category -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.filters.category @@ -55,7 +55,7 @@ out: value: fiction --- test case: Query $.filters.id -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.filters.id @@ -63,7 +63,7 @@ out: return: SUCCEED --- test case: Query $.books[1].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[1].title @@ -72,7 +72,7 @@ out: value: Sword of Honour --- test case: Query $['closed message'] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $['closed message'] @@ -81,7 +81,7 @@ out: value: Store is closed --- test case: Query $.books[-1].author -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[-1].author @@ -90,7 +90,7 @@ out: value: J. R. R. Tolkien --- test case: Query $.filters -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.filters @@ -104,7 +104,7 @@ out: } --- test case: Query $.books.length() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books.length() @@ -113,7 +113,7 @@ out: value: 4 --- test case: Query $.tags[:] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.tags[:] @@ -122,7 +122,7 @@ out: value: '["a", "b", "c", "d", "e" ]' --- test case: Query $.tags[2:] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.tags[2:] @@ -131,7 +131,7 @@ out: value: '["c", "d", "e" ]' --- test case: Query $.tags[:2] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.tags[:2] @@ -140,7 +140,7 @@ out: value: '["a", "b"]' --- test case: Query $.tags[1:4] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.tags[1:4] @@ -149,7 +149,7 @@ out: value: '["b", "c", "d"]' --- test case: Query $.tags[-2:] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.tags[-2:] @@ -158,7 +158,7 @@ out: value: '["d", "e"]' --- test case: Query $.tags[:-3] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.tags[:-3] @@ -167,7 +167,7 @@ out: value: '["a", "b"]' --- test case: Query $.tags[:-3].length() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.tags[:-3].length() @@ -176,25 +176,25 @@ out: value: 2 --- test case: Query $.books[0, 2].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[0, 2].title out: return: SUCCEED - value: '["Sayings of the Century", "Moby Dick"]' + value: '["Moby Dick", "Sayings of the Century"]' --- test case: Query $.books[1]['author', "title"] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[1]['author', "title"] out: return: SUCCEED - value: '["Evelyn Waugh", "Sword of Honour"]' + value: '["Sword of Honour", "Evelyn Waugh"]' --- test case: Query $..id -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $..id @@ -203,16 +203,16 @@ out: value: '[1, 2, 3, 4]' --- test case: Query $.services..price -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.services..price out: return: SUCCEED - value: '[5, 154.99, 46, 24.5, 99.49]' + value: '[154.99, 5, 46, 24.5, 99.49]' --- test case: Query $.books[?(@.id == 1 + 1)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 1 + 1)].title @@ -221,7 +221,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == 4 / 2)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 4 / 2)].title @@ -230,7 +230,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == 7 - 5)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 7 - 5)].title @@ -239,7 +239,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == 0.4 * 5)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 0.4 * 5)].title @@ -248,7 +248,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == 4 - 0.4 * 5)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 4 - 0.4 * 5)].title @@ -257,7 +257,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == -0.4 * 5 + 4)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == -0.4 * 5 + 4)].title @@ -266,7 +266,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == 0.4 * (-5) + 4)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 0.4 * (-5) + 4)].title @@ -275,7 +275,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == 2 || @.id == 4)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 2 || @.id == 4)].title @@ -284,7 +284,7 @@ out: value: '["Sword of Honour", "The Lord of the Rings"]' --- test case: Query $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title @@ -293,7 +293,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.id == 2 == 1)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 2 == 1)].title @@ -302,7 +302,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(!(@.id == 2))].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(!(@.id == 2))].title @@ -311,7 +311,7 @@ out: value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' --- test case: Query $.books[?(@.id != 2)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id != 2)].title @@ -320,7 +320,7 @@ out: value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' --- test case: Query $.books[?(@.title =~ " of ")].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.title =~ " of ")].title @@ -329,7 +329,7 @@ out: value: '["Sayings of the Century", "Sword of Honour", "The Lord of the Rings"]' --- test case: Query $.books[?(@.price > 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.price > 12.99)].title @@ -338,7 +338,7 @@ out: value: '["The Lord of the Rings"]' --- test case: Query $.books[?(@.price >= 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.price >= 12.99)].title @@ -347,7 +347,7 @@ out: value: '["Sword of Honour", "The Lord of the Rings"]' --- test case: Query $.books[?(@.price < 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.price < 12.99)].title @@ -356,7 +356,7 @@ out: value: '["Sayings of the Century", "Moby Dick"]' --- test case: Query $.books[?(@.price <= 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.price <= 12.99)].title @@ -365,7 +365,7 @@ out: value: '["Sayings of the Century", "Sword of Honour", "Moby Dick"]' --- test case: Query $.books[?(@.author > "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.author > "Herman Melville")].title @@ -374,7 +374,7 @@ out: value: '["Sayings of the Century", "The Lord of the Rings"]' --- test case: Query $.books[?(@.author >= "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.author >= "Herman Melville")].title @@ -383,7 +383,7 @@ out: value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' --- test case: Query $.books[?(@.author < "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.author < "Herman Melville")].title @@ -392,7 +392,7 @@ out: value: '["Sword of Honour"]' --- test case: Query $.books[?(@.author <= "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.author <= "Herman Melville")].title @@ -401,7 +401,7 @@ out: value: '["Sword of Honour", "Moby Dick"]' --- test case: Query $.books[?(@.price > $.filters.price)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.price > $.filters.price)].title @@ -410,7 +410,7 @@ out: value: '["Sword of Honour", "The Lord of the Rings"]' --- test case: Query $.books[?(@.category == $.filters.category)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.category == $.filters.category)].title @@ -419,7 +419,7 @@ out: value: '["Sword of Honour","Moby Dick","The Lord of the Rings"]' --- test case: Query $.books[?(@.category != $.filters.category)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.category != $.filters.category)].title @@ -428,7 +428,7 @@ out: value: '["Sayings of the Century"]' --- test case: Query $..[?(@.id)] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $..[?(@.id)] @@ -469,7 +469,7 @@ out: ] --- test case: Query $.services..[?(@.price > 50)].description -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.services..[?(@.price > 50)].description @@ -478,7 +478,7 @@ out: value: '["Printing and assembling book in A5 format", "Rebinding torn book"]' --- test case: Query $..id.length() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $..id.length() @@ -487,7 +487,7 @@ out: value: 4 --- test case: Query $.books[?(@.price >= 12.99)].length() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.price >= 12.99)].length() @@ -496,7 +496,7 @@ out: value: 2 --- test case: Query $.books[?(@.id == 2)].title.first() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.id == 2)].title.first() @@ -505,7 +505,7 @@ out: value: Sword of Honour --- test case: Query $..tags.first().length() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $..tags.first().length() @@ -514,7 +514,7 @@ out: value: 5 --- test case: Query $.bad.path.first().length() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.bad.path.first().length() @@ -522,7 +522,7 @@ out: return: FAIL --- test case: Query $.[?(@.ElementName == "test")].values.first().length() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.[?(@.ElementName == "test")].values.first().length() @@ -562,7 +562,7 @@ out: value: a --- test case: Query $.books[*].price.min() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[*].price.min() @@ -571,7 +571,7 @@ out: value: 8.95 --- test case: Query $..price.max() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $..price.max() @@ -580,7 +580,7 @@ out: value: 154.99 --- test case: Query $.books[?(@.category == "fiction")].price.avg() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.category == "fiction")].price.avg() @@ -589,7 +589,7 @@ out: value: 14.99 --- test case: Query $.books[?(@.category == $.filters.xyz)].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.category == $.filters.xyz)].title @@ -597,7 +597,7 @@ out: return: SUCCEED --- test case: Query $.filters['no filters'] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.filters['no filters'] @@ -606,16 +606,16 @@ out: value: no "filters" --- test case: Query $.services[?(@.active=="true")].servicegroup -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.services[?(@.active=="true")].servicegroup out: return: SUCCEED - value: '[1000,1001]' + value: '[1001,1000]' --- test case: Query $.services[?(@.active=="false")].servicegroup -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.services[?(@.active=="false")].servicegroup @@ -624,7 +624,7 @@ out: value: '[1002]' --- test case: Query $.books[?(@.title =~ "[a-z")].title -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.title =~ "[a-z")].title @@ -632,7 +632,7 @@ out: return: FAIL --- test case: $..books[?(!@.isbn)] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $..books[?(!@.isbn)] @@ -657,7 +657,7 @@ out: ] --- test case: $..books[?(@.isbn)] -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $..books[?(@.isbn)] @@ -684,7 +684,7 @@ out: ] --- test case: Query $.books[*].price.sum() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[*].price.sum() @@ -725,25 +725,25 @@ out: values: '[2]' --- test case: Query $.*~ -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.*~ out: return: SUCCEED - value: '["books","services","filters","closed message","tags"]' + value: '["filters", "services", "tags", "books", "closed message"]' --- test case: Query $.*~.first() -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.*~.first() out: return: SUCCEED - value: 'books' + value: 'filters' --- test case: Query $.services[?(@.servicegroup=="1002")]~ -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.services[?(@.servicegroup=="1002")]~ @@ -752,11 +752,49 @@ out: value: '["restoration"]' --- test case: Query $.books[?(@.category=="fiction")]~ -include: &include zbx_jsonpath_query.inc.yaml +include: &include zbx_jsonobj_query.inc.yaml in: data: *include path: $.books[?(@.category=="fiction")]~ out: return: SUCCEED value: '["1","2","3"]' +--- +test case: Query $.books[?(@.category=="reference")].price +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.category=="reference")].price +out: + return: SUCCEED + value: '[8.95]' +--- +test case: Query $.books[1,1].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1,1].title +out: + return: SUCCEED + value: Sword of Honour +--- +test case: Query $.books[1:2].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1:2].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[1]["title","title"] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1].["title","title"] +out: + return: SUCCEED + value: Sword of Honour ... + + diff --git a/tests/libs/zbxjson/zbx_jsonpath_compile.c b/tests/libs/zbxjson/zbx_jsonpath_compile.c index e98a2c7827c..0caab92de81 100644 --- a/tests/libs/zbxjson/zbx_jsonpath_compile.c +++ b/tests/libs/zbxjson/zbx_jsonpath_compile.c @@ -54,7 +54,7 @@ static void jsonpath_token_print(char **data, size_t *data_alloc, size_t *data_o case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: case ZBX_JSONPATH_TOKEN_CONST_STR: case ZBX_JSONPATH_TOKEN_CONST_NUM: - zbx_strcpy_alloc(data, data_alloc, data_offset, token->data); + zbx_strcpy_alloc(data, data_alloc, data_offset, token->text); break; case ZBX_JSONPATH_TOKEN_PAREN_LEFT: zbx_strcpy_alloc(data, data_alloc, data_offset, "("); diff --git a/tests/libs/zbxsysinfo/Makefile.am b/tests/libs/zbxsysinfo/Makefile.am index 720ce4f31ff..670be6d8d55 100644 --- a/tests/libs/zbxsysinfo/Makefile.am +++ b/tests/libs/zbxsysinfo/Makefile.am @@ -119,11 +119,11 @@ check_service_test_LDADD = \ $(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_srcdir)/src/libs/zbxhash/libzbxhash.a \ $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ - $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxexec/libzbxexec.a \ $(top_srcdir)/src/libs/zbxvariant/libzbxvariant.a \ $(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a \ @@ -238,6 +238,7 @@ check_key_access_rules_SOURCES = \ check_key_access_rules_LDADD = $(COMMON_LIB_FILES) \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo_http.a \ $(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a diff --git a/tests/zabbix_server/poller/Makefile.am b/tests/zabbix_server/poller/Makefile.am index 5531959151d..31f478fa1bb 100644 --- a/tests/zabbix_server/poller/Makefile.am +++ b/tests/zabbix_server/poller/Makefile.am @@ -10,7 +10,6 @@ POLLER_LIBS = \ $(top_srcdir)/tests/libzbxmocktest.a \ $(top_srcdir)/tests/libzbxmockdata.a \ $(top_srcdir)/src/libs/zbxsysinfo/libzbxserversysinfo.a \ - $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ $(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo.a \ @@ -24,6 +23,7 @@ POLLER_LIBS = \ $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_srcdir)/src/libs/zbxexec/libzbxexec.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxhash/libzbxhash.a \ $(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a \ $(top_srcdir)/src/libs/zbxvariant/libzbxvariant.a \ diff --git a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml index ef12178fa0a..640fcbef4b7 100644 --- a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml +++ b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml @@ -965,7 +965,7 @@ in: out: return: SUCCEED value: |- - {"b":[1, 2, 3]} + {"b":[1,2,3]} --- test case: jsonpath5 in: @@ -980,7 +980,7 @@ in: out: return: SUCCEED value: |- - [1, 2, 3] + [1,2,3] --- test case: jsonpath6 in: @@ -1008,7 +1008,7 @@ in: params: $.a['b c'] out: return: SUCCEED - value: '["one", "two", "three"]' + value: '["one","two","three"]' --- test case: jsonpath8 in: diff --git a/ui/app/controllers/CControllerLatestView.php b/ui/app/controllers/CControllerLatestView.php index d31f904b9a4..8c98c1b93df 100644 --- a/ui/app/controllers/CControllerLatestView.php +++ b/ui/app/controllers/CControllerLatestView.php @@ -200,6 +200,7 @@ class CControllerLatestView extends CControllerLatest { 'sort_order' => $sort_order, 'view_curl' => $view_url, 'paging' => $paging, + 'uncheck' => $this->hasInput('filter_reset'), 'config' => [ 'hk_trends' => CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS), 'hk_trends_global' => CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL), diff --git a/ui/app/views/js/monitoring.latest.view.js.php b/ui/app/views/js/monitoring.latest.view.js.php index e727894273d..0171c31ec34 100644 --- a/ui/app/views/js/monitoring.latest.view.js.php +++ b/ui/app/views/js/monitoring.latest.view.js.php @@ -71,11 +71,11 @@ this.filter.on(TABFILTER_EVENT_URLSET, () => { this.reloadPartialAndTabCounters(); + chkbxRange.clearSelectedOnFilterChange(); if (this.active_filter !== this.filter._active_item) { this.active_filter = this.filter._active_item; chkbxRange.checkObjectAll(chkbxRange.pageGoName, false); - chkbxRange.clearSelectedOnFilterChange(); } }); diff --git a/ui/app/views/js/monitoring.problem.view.js.php b/ui/app/views/js/monitoring.problem.view.js.php index 931f317fb28..4f854d9976a 100644 --- a/ui/app/views/js/monitoring.problem.view.js.php +++ b/ui/app/views/js/monitoring.problem.view.js.php @@ -95,11 +95,11 @@ this.refreshResults(); this.refreshCounters(); + chkbxRange.clearSelectedOnFilterChange(); if (this.active_filter !== this.filter._active_item) { this.active_filter = this.filter._active_item; chkbxRange.checkObjectAll(chkbxRange.pageGoName, false); - chkbxRange.clearSelectedOnFilterChange(); } }); diff --git a/ui/app/views/monitoring.latest.view.php b/ui/app/views/monitoring.latest.view.php index 337ff26bf06..d95fdaf18a8 100644 --- a/ui/app/views/monitoring.latest.view.php +++ b/ui/app/views/monitoring.latest.view.php @@ -35,6 +35,10 @@ $this->includeJsFile('monitoring.latest.view.js.php'); $this->enableLayoutModes(); $web_layout_mode = $this->getLayoutMode(); +if ($data['uncheck']) { + uncheckTableRows('latest'); +} + $widget = (new CWidget()) ->setTitle(_('Latest data')) ->setWebLayoutMode($web_layout_mode) diff --git a/ui/js/widgets/class.widget.js b/ui/js/widgets/class.widget.js index e366479402d..2b5456d2f41 100644 --- a/ui/js/widgets/class.widget.js +++ b/ui/js/widgets/class.widget.js @@ -122,6 +122,8 @@ class CWidget extends CBaseComponent { this._update_retry_sec = 3; this._show_preloader_asap = true; this._resizable_handles = []; + + this._hide_preloader_animation_frame = null; } // Logical state control methods. @@ -816,15 +818,38 @@ class CWidget extends CBaseComponent { } _showPreloader() { + // Fixed Safari 16 bug: removing preloader classes on animation frame to ensure removal of icons. + + if (this._hide_preloader_animation_frame !== null) { + cancelAnimationFrame(this._hide_preloader_animation_frame); + this._hide_preloader_animation_frame = null; + } + this._content_body.classList.add('is-loading'); this._content_body.classList.remove('is-loading-fadein', 'delayed-15s'); } _hidePreloader() { - this._content_body.classList.remove('is-loading', 'is-loading-fadein', 'delayed-15s'); + // Fixed Safari 16 bug: removing preloader classes on animation frame to ensure removal of icons. + + if (this._hide_preloader_animation_frame !== null) { + return; + } + + this._hide_preloader_animation_frame = requestAnimationFrame(() => { + this._content_body.classList.remove('is-loading', 'is-loading-fadein', 'delayed-15s'); + this._hide_preloader_animation_frame = null; + }); } _schedulePreloader() { + // Fixed Safari 16 bug: removing preloader classes on animation frame to ensure removal of icons. + + if (this._hide_preloader_animation_frame !== null) { + cancelAnimationFrame(this._hide_preloader_animation_frame); + this._hide_preloader_animation_frame = null; + } + this._content_body.classList.add('is-loading', 'is-loading-fadein', 'delayed-15s'); } diff --git a/ui/tests/include/web/CPage.php b/ui/tests/include/web/CPage.php index cc2abc0f9fa..eab84952c98 100644 --- a/ui/tests/include/web/CPage.php +++ b/ui/tests/include/web/CPage.php @@ -219,7 +219,6 @@ class CPage { self::$cookie = [ 'name' => 'zbx_session', 'value' => base64_encode(json_encode($data)), - 'domain' => parse_url(PHPUNIT_URL, PHP_URL_HOST), 'path' => rtrim(substr($path, 0, strrpos($path, '/')), '/') ]; diff --git a/ui/tests/selenium/dashboard/testDashboardPages.php b/ui/tests/selenium/dashboard/testDashboardPages.php index cc0165bec87..4ccbca0c1cb 100644 --- a/ui/tests/selenium/dashboard/testDashboardPages.php +++ b/ui/tests/selenium/dashboard/testDashboardPages.php @@ -456,6 +456,7 @@ class testDashboardPages extends CWebTest { $dashboard->addPage(); $page_dialog = COverlayDialogElement::find()->waitUntilReady()->one(); $page_dialog->query('name:dashboard_page_properties_form')->asForm()->one()->fill($data['fields'])->submit(); + $page_dialog->ensureNotPresent(); $dashboard->waitUntilReady(); $title = $data['fields']['Name']; diff --git a/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php b/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php index 6b424d030c1..658d2921f36 100644 --- a/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php +++ b/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php @@ -415,21 +415,6 @@ class testDashboardTriggerOverviewWidget extends CWebTest { [ [ 'fields' => [ - 'Name' => 'Filter triggers by tag with default operator' - ], - 'tags' => [ - ['name' => 'server', 'operator' => 'Contains', 'value' => 'sel'] - ], - 'expected' => [ - 'Host for triggers filtering' => [ - 'Inheritance trigger with tags' - ] - ] - ] - ], - [ - [ - 'fields' => [ 'Name' => 'Filter triggers by 2 tags with Or operator', 'Tags' => 'Or' ], @@ -795,7 +780,6 @@ class testDashboardTriggerOverviewWidget extends CWebTest { $widget_name = (array_key_exists('fields', $data)) ? $data['fields']['Name'] : 'Trigger overview'; $widget = $dashboard->getWidget($widget_name); - $widget->query('xpath://div[contains(@class, "is-loading")]')->waitUntilNotPresent(); $dashboard->save(); $this->assertMessage(TEST_GOOD, 'Dashboard updated'); diff --git a/ui/tests/selenium/testInheritanceHostPrototype.php b/ui/tests/selenium/testInheritanceHostPrototype.php index 73a40bb3c99..57de3f86508 100644 --- a/ui/tests/selenium/testInheritanceHostPrototype.php +++ b/ui/tests/selenium/testInheritanceHostPrototype.php @@ -456,7 +456,7 @@ class testInheritanceHostPrototype extends CLegacyWebTest { if (array_key_exists('template', $data)) { $this->zbxTestClickButtonMultiselect('add_templates_'); $this->zbxTestLaunchOverlayDialog('Templates'); - COverlayDialogElement::find()->one()->setDataContext('Templates'); + COverlayDialogElement::find()->waitUntilReady()->one()->setDataContext('Templates'); $this->zbxTestClickLinkTextWait($data['template']); } |