/*
** Copyright (C) 2001-2024 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

#include "vmware_event.h"

#include "zbxcommon.h"

#if defined(HAVE_LIBXML2) && defined(HAVE_LIBCURL)

#include "vmware_internal.h"
#include "vmware_shmem.h"

#include "zbxalgo.h"
#include "zbxnix.h"
#include "zbxstr.h"
#include "zbxtime.h"
#include "zbxshmem.h"
#include "zbxxml.h"

#ifdef HAVE_LIBXML2
#	include <libxml/xpath.h>
#endif

typedef struct
{
	zbx_uint64_t	id;
	xmlNode		*xml_node;
	time_t		created_time;
}
zbx_id_xmlnode_t;

ZBX_VECTOR_DECL(id_xmlnode, zbx_id_xmlnode_t)
ZBX_VECTOR_IMPL(id_xmlnode, zbx_id_xmlnode_t)

/* VMware events host information */
typedef struct
{
	const char	*node_name;
	int		flag;
	char		*name;
}
event_hostinfo_node_t;

/******************************************************************************
 *                                                                            *
 * Purpose: frees resources allocated to store vmware event                   *
 *                                                                            *
 * Parameters: event - [IN] vmware event                                      *
 *                                                                            *
 ******************************************************************************/
void	vmware_event_free(zbx_vmware_event_t *event)
{
	evt_msg_strpool_strfree(event->message);
	zbx_free(event);
}

/******************************************************************************
 *                                                                            *
 * Purpose: gets map of severity and event type                               *
 *                                                                            *
 * Parameters: service        - [IN] vmware service                           *
 *             easyhandle     - [IN] CURL handle                              *
 *             evt_severities - [IN/OUT] key-value vector with EventID and    *
 *                                       severity as value                    *
 *             error          - [OUT] error message in case of failure        *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
int	vmware_service_get_evt_severity(zbx_vmware_service_t *service, CURL *easyhandle,
		zbx_vector_vmware_key_value_t *evt_severities, char **error)
{
#	define ZBX_POST_VMWARE_GET_EVT_SEVERITY							\
		ZBX_POST_VSPHERE_HEADER								\
		"<ns0:RetrievePropertiesEx>"							\
			"<ns0:_this type=\"PropertyCollector\">%s</ns0:_this>"			\
			"<ns0:specSet>"								\
				"<ns0:propSet>"							\
					"<ns0:type>EventManager</ns0:type>"			\
					"<ns0:pathSet>description</ns0:pathSet>"		\
				"</ns0:propSet>"						\
				"<ns0:objectSet>"						\
					"<ns0:obj type=\"EventManager\">%s</ns0:obj>"		\
				"</ns0:objectSet>"						\
			"</ns0:specSet>"							\
			"<ns0:options/>"							\
		"</ns0:RetrievePropertiesEx>"							\
		ZBX_POST_VSPHERE_FOOTER

	char		tmp[MAX_STRING_LEN];
	xmlDoc		*doc = NULL;
	xmlXPathContext	*xpathCtx;
	xmlXPathObject	*xpathObj;
	xmlNodeSetPtr	nodeset;
	int		i, ret = FAIL;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VMWARE_GET_EVT_SEVERITY,
			get_vmware_service_objects()[service->type].property_collector,
			get_vmware_service_objects()[service->type].event_manager);

	if (SUCCEED != zbx_soap_post(__func__, easyhandle, tmp, &doc, NULL, error))
		goto out;

	xpathCtx = xmlXPathNewContext(doc);

	if (NULL == (xpathObj = xmlXPathEvalExpression((const xmlChar *)ZBX_XPATH_PROP_NAME("description")
			ZBX_XPATH_LN("eventInfo"), xpathCtx)))
	{
		*error = zbx_strdup(*error, "Cannot make events description list parsing query.");
		goto clean;
	}

	if (0 != xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
	{
		*error = zbx_strdup(*error, "Cannot find items in events description list.");
		goto clean;
	}

	nodeset = xpathObj->nodesetval;
	zbx_vector_vmware_key_value_reserve(evt_severities, (size_t)nodeset->nodeNr);

	for (i = 0; i < nodeset->nodeNr; i++)
	{
		zbx_vmware_key_value_t	evt_sev;
		char			*delimetr, *full_format;

		if (NULL == (full_format = zbx_xml_node_read_value(doc, nodeset->nodeTab[i], ZBX_XNN("fullFormat"))))
			continue;

		if (NULL != (delimetr = strchr(full_format, '|')))
		{
			*delimetr = '\0';
			evt_sev.key = zbx_strdup(NULL, full_format);
		}
		else
			evt_sev.key = zbx_xml_node_read_value(doc, nodeset->nodeTab[i], ZBX_XNN("key"));

		zbx_str_free(full_format);
		evt_sev.value = zbx_xml_node_read_value(doc, nodeset->nodeTab[i], ZBX_XNN("category"));

		if (NULL == evt_sev.key || NULL == evt_sev.value)
		{
			zbx_vmware_key_value_free(evt_sev);
			continue;
		}

		zbx_vector_vmware_key_value_append(evt_severities, evt_sev);
	}

	ret = SUCCEED;
clean:
	xmlXPathFreeObject(xpathObj);
	xmlXPathFreeContext(xpathCtx);
out:
	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() evt_severities:%d", __func__, evt_severities->values_num);

	return ret;
#	undef ZBX_POST_VMWARE_GET_EVT_SEVERITY
}

/******************************************************************************
 *                                                                            *
 * Purpose: retrieves event session name                                      *
 *                                                                            *
 * Parameters: service       - [IN] vmware service                            *
 *             easyhandle    - [IN] CURL handle                               *
 *             event_session - [OUT] pointer to output variable               *
 *             error         - [OUT] error message in case of failure         *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_get_event_session(const zbx_vmware_service_t *service, CURL *easyhandle,
		char **event_session, char **error)
{
#	define ZBX_POST_VMWARE_CREATE_EVENT_COLLECTOR				\
		ZBX_POST_VSPHERE_HEADER						\
		"<ns0:CreateCollectorForEvents>"				\
			"<ns0:_this type=\"EventManager\">%s</ns0:_this>"	\
			"<ns0:filter>%s</ns0:filter>"				\
		"</ns0:CreateCollectorForEvents>"				\
		ZBX_POST_VSPHERE_FOOTER

#	define ZBX_POST_VMWARE_EVENT_FILTER_SPEC_CATEGORY			\
		"<ns0:category>%s</ns0:category>"

	static const char	*levels[] = {ZBX_VMWARE_EVTLOG_SEVERITIES};
	char			tmp[MAX_STRING_LEN], *filter = NULL;
	size_t			alloc_len = 0, offset = 0;
	int			ret = FAIL;
	xmlDoc			*doc = NULL;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	for (size_t i = 0; i < ARRSIZE(levels); i++)
	{
		if (0 != ((1 << i) & service->eventlog.severity))
		{
			zbx_snprintf_alloc(&filter, &alloc_len, &offset, ZBX_POST_VMWARE_EVENT_FILTER_SPEC_CATEGORY,
					levels[i]);
		}
	}

	zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VMWARE_CREATE_EVENT_COLLECTOR,
			get_vmware_service_objects()[service->type].event_manager, ZBX_NULL2EMPTY_STR(filter));

	zbx_free(filter);

	if (SUCCEED != zbx_soap_post(__func__, easyhandle, tmp, &doc, NULL, error))
		goto out;

	if (NULL == (*event_session = zbx_xml_doc_read_value(doc, "/*/*/*/*[@type='EventHistoryCollector']")))
	{
		*error = zbx_strdup(*error, "Cannot get EventHistoryCollector session.");
		goto out;
	}

	ret = SUCCEED;
out:
	zbx_xml_free_doc(doc);
	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s event_session:'%s'", __func__, zbx_result_string(ret),
			ZBX_NULL2EMPTY_STR(*event_session));

	return ret;

#	undef ZBX_POST_VMWARE_CREATE_EVENT_COLLECTOR
#	undef ZBX_POST_VMWARE_EVENT_FILTER_SPEC_CATEGORY
}

/******************************************************************************
 *                                                                            *
 * Purpose: resets "scrollable view" to latest events                         *
 *                                                                            *
 * Parameters: easyhandle    - [IN] CURL handle                               *
 *             event_session - [IN] event session (EventHistoryCollector)     *
 *                                  identifier                                *
 *             error         - [OUT] error message in case of failure         *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_reset_event_history_collector(CURL *easyhandle, const char *event_session, char **error)
{
#	define ZBX_POST_VMWARE_RESET_EVENT_COLLECTOR					\
		ZBX_POST_VSPHERE_HEADER							\
		"<ns0:ResetCollector>"							\
			"<ns0:_this type=\"EventHistoryCollector\">%s</ns0:_this>"	\
		"</ns0:ResetCollector>"							\
		ZBX_POST_VSPHERE_FOOTER

	int		ret = FAIL;
	char		tmp[MAX_STRING_LEN], *event_session_esc;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	event_session_esc = zbx_xml_escape_dyn(event_session);

	zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VMWARE_RESET_EVENT_COLLECTOR, event_session_esc);

	zbx_free(event_session_esc);

	if (SUCCEED != zbx_soap_post(__func__, easyhandle, tmp, NULL, 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_VMWARE_DESTROY_EVENT_COLLECTOR
}

/******************************************************************************
 *                                                                            *
 * Purpose: reads events from "scrollable view" and moves it back in time     *
 *                                                                            *
 * Parameters: easyhandle    - [IN] CURL handle                               *
 *             event_session - [IN] event session (EventHistoryCollector)     *
 *                                  identifier                                *
 *             soap_count    - [IN] max count of events in response           *
 *             xdoc          - [OUT] result as xml document                   *
 *             error         - [OUT] error message in case of failure         *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_read_previous_events(CURL *easyhandle, const char *event_session, int soap_count,
		xmlDoc **xdoc, char **error)
{
#	define ZBX_POST_VMWARE_READ_PREVIOUS_EVENTS					\
		ZBX_POST_VSPHERE_HEADER							\
		"<ns0:ReadPreviousEvents>"						\
			"<ns0:_this type=\"EventHistoryCollector\">%s</ns0:_this>"	\
			"<ns0:maxCount>%d</ns0:maxCount>"				\
		"</ns0:ReadPreviousEvents>"						\
		ZBX_POST_VSPHERE_FOOTER

	int	ret = FAIL;
	char	tmp[MAX_STRING_LEN], *event_session_esc;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s() soap_count: %d", __func__, soap_count);

	event_session_esc = zbx_xml_escape_dyn(event_session);

	zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VMWARE_READ_PREVIOUS_EVENTS, event_session_esc, soap_count);

	zbx_free(event_session_esc);

	if (SUCCEED != zbx_soap_post(__func__, easyhandle, tmp, xdoc, 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_VMWARE_READ_PREVIOUS_EVENTS
}

/******************************************************************************
 *                                                                            *
 * Purpose: reads events from "latest page" and moves it back in time         *
 *                                                                            *
 * Parameters: service       - [IN] vmware service                            *
 *             easyhandle    - [IN] CURL handle                               *
 *             event_session - [IN] event session (EventHistoryCollector)     *
 *                                  identifier                                *
 *             xdoc          - [OUT] result as xml document                   *
 *             error         - [OUT] error message in case of failure         *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_get_event_latestpage(const zbx_vmware_service_t *service, CURL *easyhandle,
		const char *event_session, xmlDoc **xdoc, char **error)
{
#	define ZBX_POST_VMWARE_READ_EVENT_LATEST_PAGE						\
		ZBX_POST_VSPHERE_HEADER								\
		"<ns0:RetrievePropertiesEx>"							\
			"<ns0:_this type=\"PropertyCollector\">%s</ns0:_this>"			\
			"<ns0:specSet>"								\
				"<ns0:propSet>"							\
					"<ns0:type>EventHistoryCollector</ns0:type>"		\
					"<ns0:pathSet>latestPage</ns0:pathSet>"			\
				"</ns0:propSet>"						\
				"<ns0:objectSet>"						\
					"<ns0:obj type=\"EventHistoryCollector\">%s</ns0:obj>"	\
				"</ns0:objectSet>"						\
			"</ns0:specSet>"							\
			"<ns0:options/>"							\
		"</ns0:RetrievePropertiesEx>"							\
		ZBX_POST_VSPHERE_FOOTER

	int	ret = FAIL;
	char	tmp[MAX_STRING_LEN], *event_session_esc;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	event_session_esc = zbx_xml_escape_dyn(event_session);

	zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VMWARE_READ_EVENT_LATEST_PAGE,
			get_vmware_service_objects()[service->type].property_collector, event_session_esc);

	zbx_free(event_session_esc);

	if (SUCCEED != zbx_soap_post(__func__, easyhandle, tmp, xdoc, 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_VMWARE_READ_EVENT_LATEST_PAGE
}

/******************************************************************************
 *                                                                            *
 * Parameters: easyhandle    - [IN] CURL handle                               *
 *             event_session - [IN] event session (EventHistoryCollector)     *
 *                                  identifier                                *
 *             error         - [OUT] error message in case of failure         *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_destroy_event_session(CURL *easyhandle, const char *event_session, char **error)
{
#	define ZBX_POST_VMWARE_DESTROY_EVENT_COLLECTOR					\
		ZBX_POST_VSPHERE_HEADER							\
		"<ns0:DestroyCollector>"						\
			"<ns0:_this type=\"EventHistoryCollector\">%s</ns0:_this>"	\
		"</ns0:DestroyCollector>"						\
		ZBX_POST_VSPHERE_FOOTER

	int	ret = FAIL;
	char	tmp[MAX_STRING_LEN], *event_session_esc;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	event_session_esc = zbx_xml_escape_dyn(event_session);

	zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VMWARE_DESTROY_EVENT_COLLECTOR, event_session_esc);

	zbx_free(event_session_esc);

	if (SUCCEED != zbx_soap_post(__func__, easyhandle, tmp, NULL, 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_VMWARE_DESTROY_EVENT_COLLECTOR
}

/******************************************************************************
 *                                                                            *
 * Purpose: reads event data by id from xml and puts it to array of events    *
 *                                                                            *
 * Parameters: events         - [IN/OUT] array of parsed events               *
 *             xml_event      - [IN] xml node and id of parsed event          *
 *             xdoc           - [IN] xml document with eventlog records       *
 *             evt_severities - [IN] dictionary of severity for event types   *
 *             alloc_sz       - [OUT] allocated memory size for events        *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_put_event_data(zbx_vector_vmware_event_ptr_t *events, zbx_id_xmlnode_t xml_event,
		xmlDoc *xdoc, const zbx_hashset_t *evt_severities, zbx_uint64_t *alloc_sz)
{
#define ZBX_HOSTINFO_NODES_DATACENTER		0x01
#define ZBX_HOSTINFO_NODES_COMPRES		0x02
#define ZBX_HOSTINFO_NODES_HOST			0x04
#define ZBX_HOSTINFO_NODES_VM			0x08
#define ZBX_HOSTINFO_NODES_DS			0x10
#define ZBX_HOSTINFO_NODES_NET			0x20
#define ZBX_HOSTINFO_NODES_DVS			0x40
#define ZBX_HOSTINFO_NODES_MASK_ALL									\
		(ZBX_HOSTINFO_NODES_DATACENTER | ZBX_HOSTINFO_NODES_COMPRES | ZBX_HOSTINFO_NODES_HOST | \
		ZBX_HOSTINFO_NODES_VM | ZBX_HOSTINFO_NODES_DS | ZBX_HOSTINFO_NODES_NET | ZBX_HOSTINFO_NODES_DVS)

#	define	ZBX_XPATH_EVT_INFO(param)				\
		"*[local-name()='" param "']/*[local-name()='name']"
#	define	ZBX_XPATH_EVT_ARGUMENT(key)									\
		"*[local-name()='arguments'][*[local-name()='key'][text()='" key "']]/*[local-name()='value']"

	zbx_vmware_event_t		*event = NULL;
	char				*message, *ip, *type, *username, *info;
	int				nodes_det = 0;
	unsigned int			i;
	zbx_uint64_t			sz;
	static event_hostinfo_node_t	host_nodes[] =
	{
		{ ZBX_XPATH_EVT_INFO("datacenter"),		ZBX_HOSTINFO_NODES_DATACENTER,	NULL },
		{ ZBX_XPATH_EVT_INFO("computeResource"),	ZBX_HOSTINFO_NODES_COMPRES,	NULL },
		{ ZBX_XPATH_EVT_INFO("host"),			ZBX_HOSTINFO_NODES_HOST,	NULL },
		{ ZBX_XPATH_EVT_ARGUMENT("_sourcehost_"),	ZBX_HOSTINFO_NODES_HOST,	NULL },
		{ ZBX_XPATH_EVT_ARGUMENT("entityName"),		ZBX_HOSTINFO_NODES_HOST,	NULL },
		{ ZBX_XPATH_EVT_INFO("vm"),			ZBX_HOSTINFO_NODES_VM,		NULL },
		{ ZBX_XPATH_EVT_INFO("ds"),			ZBX_HOSTINFO_NODES_DS,		NULL },
		{ ZBX_XPATH_EVT_INFO("net"),			ZBX_HOSTINFO_NODES_NET,		NULL },
		{ ZBX_XPATH_EVT_INFO("dvs"),			ZBX_HOSTINFO_NODES_DVS,		NULL }
	};

	if (NULL == (message = zbx_xml_node_read_value(xdoc, xml_event.xml_node, ZBX_XPATH_NN("fullFormattedMessage"))))
	{
		zabbix_log(LOG_LEVEL_TRACE, "skipping event key '" ZBX_FS_UI64 "', fullFormattedMessage"
				" is missing", xml_event.id);
		return FAIL;
	}

	if (NULL == (type = zbx_xml_node_read_value(xdoc, xml_event.xml_node, ZBX_XPATH_NN("eventTypeId"))))
		type = zbx_xml_node_read_prop(xml_event.xml_node, "type");

	info = zbx_strdup(NULL, "");

	if (NULL != type)
	{
		zbx_vmware_key_value_t	*severity, evt_cmp = {.key=type};
		char			*value;

		if (NULL != (value = zbx_xml_node_read_value(xdoc, xml_event.xml_node, ZBX_XPATH_NN("severity"))))
		{
			info = zbx_dsprintf(info, "\ntype: %s/%s", value, type);
			zbx_str_free(value);
		}
		else if (NULL != (severity = (zbx_vmware_key_value_t *)zbx_hashset_search(evt_severities, &evt_cmp)))
		{
			info = zbx_dsprintf(info, "\ntype: %s/%s", severity->value, type);
		}
		else
			info = zbx_dsprintf(info, "\ntype: %s", type);

		zbx_free(type);
	}

	for (i = 0; i < ARRSIZE(host_nodes); i++)
	{
		if (0 == (nodes_det & host_nodes[i].flag) && NULL != (host_nodes[i].name =
				zbx_xml_node_read_value(xdoc, xml_event.xml_node, host_nodes[i].node_name)))
		{
			switch(host_nodes[i].flag)
			{
				case ZBX_HOSTINFO_NODES_DS:
					host_nodes[i].name = zbx_dsprintf(host_nodes[i].name, " ds:%s",
							host_nodes[i].name);
					break;
				case ZBX_HOSTINFO_NODES_NET:
					host_nodes[i].name = zbx_dsprintf(host_nodes[i].name," net:%s",
							host_nodes[i].name);
					break;
				case ZBX_HOSTINFO_NODES_DVS:
					host_nodes[i].name = zbx_dsprintf(host_nodes[i].name, " dvs:%s",
							host_nodes[i].name);
					break;
				default:
					host_nodes[i].name = zbx_dsprintf(host_nodes[i].name, "%s%s",
							0 != nodes_det ? "/" : ": ", host_nodes[i].name);
			}

			nodes_det |= host_nodes[i].flag;
		}
	}

	if (0 != (nodes_det & ZBX_HOSTINFO_NODES_MASK_ALL))
	{
		info = zbx_dsprintf(info, "%s\nsource", info);

		for (i = 0; i < ARRSIZE(host_nodes); i++)
		{
			if (NULL == host_nodes[i].name)
				continue;

			info = zbx_dsprintf(info, "%s%s", info, host_nodes[i].name);
			zbx_free(host_nodes[i].name);
		}
	}
	else
	{
		for (i = 0; i < ARRSIZE(host_nodes); i++)
			zbx_free(host_nodes[i].name);

		if (NULL != (ip = zbx_xml_node_read_value(xdoc, xml_event.xml_node, ZBX_XPATH_NN("ipAddress"))))
		{
			info = zbx_dsprintf(info, "%s\nsource: %s", info, ip);
			zbx_free(ip);
		}
	}

	if (NULL != (username = zbx_xml_node_read_value(xdoc, xml_event.xml_node, ZBX_XPATH_NN("userName"))))
	{
		info = zbx_dsprintf(info, "%s\nuser: %s", info, username);
		zbx_free(username);
	}

	if ('\0' != *info)
		message = zbx_dsprintf(message, "%s\n%s", message, info);

	zbx_free(info);
	zbx_replace_invalid_utf8(message);

	event = (zbx_vmware_event_t *)zbx_malloc(event, sizeof(zbx_vmware_event_t));
	event->key = xml_event.id;
	event->timestamp = xml_event.created_time;
	event->message = evt_msg_strpool_strdup(message, &sz);
	zbx_free(message);
	zbx_vector_vmware_event_ptr_append(events, event);

	if (0 < sz)
		*alloc_sz += zbx_shmem_required_chunk_size(sz);

	return SUCCEED;
#undef ZBX_HOSTINFO_NODES_DATACENTER
#undef ZBX_HOSTINFO_NODES_COMPRES
#undef ZBX_HOSTINFO_NODES_HOST
#undef ZBX_HOSTINFO_NODES_VM
#undef ZBX_HOSTINFO_NODES_DS
#undef ZBX_HOSTINFO_NODES_NET
#undef ZBX_HOSTINFO_NODES_DVS
#undef ZBX_HOSTINFO_NODES_MASK_ALL

#	undef	ZBX_XPATH_EVT_INFO
#	undef	ZBX_XPATH_EVT_ARGUMENT
}

/******************************************************************************
 *                                                                            *
 * Purpose: reads event's createdTime from xml                                *
 *                                                                            *
 * Parameters: doc       - [IN] xml document with eventlog records            *
 *             node      - [IN] the xml node with given event                 *
 *             eventid   - [IN]                                               *
 *                                                                            *
 * Return value: createdTime converted to timestamp - if operation has        *
 *                       completed successfully,                              *
 *               0 - otherwise                                                *
 *                                                                            *
 ******************************************************************************/
static time_t	vmware_service_parse_event_ts(xmlDoc *doc, xmlNode *node, zbx_uint64_t eventid)
{
	char	*ts;
	time_t	created_time = 0;

	if (NULL == (ts = zbx_xml_node_read_value(doc, node, ZBX_XPATH_NN("createdTime"))))
	{
		zabbix_log(LOG_LEVEL_TRACE, "eventlog record without createdTime, event key '" ZBX_FS_UI64 "'",
				eventid);
		return 0;
	}

	if (FAIL == zbx_iso8601_utc(ts, &created_time))	/* 2013-06-04T14:19:23.406298Z */
	{
		zabbix_log(LOG_LEVEL_TRACE, "unexpected format of createdTime '%s' for event key '" ZBX_FS_UI64 "'",
				ts, eventid);
	}

	zbx_free(ts);

	return created_time;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses multiple events data                                       *
 *                                                                            *
 * Parameters: events     - [IN/OUT] array of parsed events                   *
 *             last_key   - [IN] key of last parsed event                     *
 *             last_ts    - [IN] the timestamp of last parsed event           *
 *             is_prop    - [IN] read events from RetrieveProperties xml      *
 *             xdoc       - [IN] xml document with eventlog records           *
 *             eventlog   - [IN] VMware event log state                       *
 *             alloc_sz   - [OUT] allocated memory size for events            *
 *             node_count - [OUT] count of xml event nodes                    *
 *             skip_old   - [OUT] detected event key reset                    *
 *                                                                            *
 * Return value: count of events successfully parsed                          *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_parse_event_data(zbx_vector_vmware_event_ptr_t *events, zbx_uint64_t last_key,
		time_t last_ts, const int is_prop, xmlDoc *xdoc, const zbx_vmware_eventlog_state_t *eventlog,
		zbx_uint64_t *alloc_sz, int *node_count, unsigned char *skip_old)
{
#	define LAST_KEY(evs)	(evs->values[evs->values_num - 1]->key)

	zbx_vector_id_xmlnode_t	ids;
	int			parsed_num = 0;
	char			*value;
	xmlXPathContext		*xpathCtx;
	xmlXPathObject		*xpathObj;
	xmlNodeSetPtr		nodeset;
	static int		is_clear = 0;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s() last_key:" ZBX_FS_UI64, __func__, last_key);

	xpathCtx = xmlXPathNewContext(xdoc);
	zbx_vector_id_xmlnode_create(&ids);

	if (NULL == (xpathObj = xmlXPathEvalExpression((const xmlChar *)(0 == is_prop ? "/*/*/*"
			ZBX_XPATH_LN("returnval") : "/*/*/*" ZBX_XPATH_LN("returnval") "/*/*/*"ZBX_XPATH_LN("Event")),
			xpathCtx)))
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Cannot make evenlog list parsing query.");
		goto clean;
	}

	if (0 != xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
	{
		if (NULL != node_count)
			*node_count = 0;

		zabbix_log(LOG_LEVEL_DEBUG, "Cannot find items in evenlog list.");
		goto clean;
	}

	nodeset = xpathObj->nodesetval;
	zbx_vector_id_xmlnode_reserve(&ids, (size_t)nodeset->nodeNr);

	if (NULL != node_count)
		*node_count = nodeset->nodeNr;

	for (int i = 0; i < nodeset->nodeNr; i++)
	{
		zbx_id_xmlnode_t	xml_event;
		zbx_uint64_t		key;

		if (NULL == (value = zbx_xml_node_read_value(xdoc, nodeset->nodeTab[i], ZBX_XPATH_NN("key"))))
		{
			zabbix_log(LOG_LEVEL_TRACE, "skipping eventlog record without key, xml number '%d'", i);
			continue;
		}

		key = (unsigned int) atoi(value);

		if (0 == key && 0 == isdigit(value[('-' == *value || '+' == *value) ? 1 : 0 ]))
		{
			zabbix_log(LOG_LEVEL_TRACE, "skipping eventlog key '%s', not a number", value);
			zbx_free(value);
			continue;
		}

		zbx_free(value);

		xml_event.created_time = vmware_service_parse_event_ts(xdoc, nodeset->nodeTab[i], key);

		if (key <= last_key)
		{
			if (xml_event.created_time <= last_ts)
			{
				zabbix_log(LOG_LEVEL_TRACE, "skipping event key '" ZBX_FS_UI64 "', has been processed",
						key);
				continue;
			}

			zabbix_log(LOG_LEVEL_TRACE, "event key reset, key: '" ZBX_FS_UI64 "', last_key: '"
					ZBX_FS_UI64 "', createdTime: '" ZBX_FS_TIME_T "', last_ts: '" ZBX_FS_TIME_T "'",
					key, last_key, xml_event.created_time, last_ts);
			*skip_old = 1;
			goto clean;
		}

		xml_event.id = key;
		xml_event.xml_node = nodeset->nodeTab[i];
		zbx_vector_id_xmlnode_append(&ids, xml_event);
	}

	if (0 != ids.values_num)
	{
		zbx_vector_id_xmlnode_sort(&ids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
		zbx_vector_vmware_event_ptr_reserve(events, (size_t)(ids.values_num + events->values_alloc));

		/* validate that last event from "latestPage" is connected with first event from ReadPreviousEvents */
		if (0 != events->values_num && LAST_KEY(events) != ids.values[ids.values_num -1].id + 1)
		{
			zabbix_log(LOG_LEVEL_DEBUG, "%s() events:%d is_clear:%d id gap:%d severity:%d", __func__,
					events->values_num, is_clear,
					(int)(LAST_KEY(events) - (ids.values[ids.values_num -1].id + 1)),
					(int)eventlog->severity);

			/* if sequence of events is not continuous, ignore events from "latestPage" property */
			/* except when events are filtered by severity */
			if (0 != is_clear && 0 == eventlog->severity)
				zbx_vector_vmware_event_ptr_clear_ext(events, vmware_event_free);
		}

		/* we are reading "scrollable views" in reverse chronological order, */
		/* so inside a "scrollable view" latest events should come first too */
		for (int i = ids.values_num - 1; i >= 0; i--)
		{
			if (SUCCEED == vmware_service_put_event_data(events, ids.values[i], xdoc,
					&eventlog->evt_severities, alloc_sz))
			{
				parsed_num++;
			}
		}
	}
	else if (0 != last_key && 0 != events->values_num && LAST_KEY(events) != last_key + 1)
	{
		zabbix_log(LOG_LEVEL_DEBUG, "%s() events:%d is_clear:%d last_key id gap:%d severity:%d", __func__,
				events->values_num, is_clear, (int)(LAST_KEY(events) - (last_key + 1)),
				(int)eventlog->severity);

		/* if sequence of events is not continuous, ignore events from "latestPage" property */
		/* except when events are filtered by severity */
		if (0 != is_clear && 0 == eventlog->severity)
			zbx_vector_vmware_event_ptr_clear_ext(events, vmware_event_free);
	}
clean:
	zbx_vector_id_xmlnode_destroy(&ids);
	xmlXPathFreeObject(xpathObj);
	xmlXPathFreeContext(xpathCtx);
	is_clear = is_prop;

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() parsed:%d", __func__, parsed_num);

	return parsed_num;

#	undef LAST_KEY
}

/******************************************************************************
 *                                                                            *
 * Parameters: service    - [IN] vmware service                               *
 *             easyhandle - [IN] CURL handle                                  *
 *             last_key   - [IN] ID of last processed event                   *
 *             last_ts    - [IN] the create time of last processed event      *
 *             skip_old   - [IN/OUT] reset last_key of event                  *
 *             events     - [OUT] pointer to output variable                  *
 *             alloc_sz   - [OUT] allocated memory size for events            *
 *             error      - [OUT] error message in case of failure            *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_get_event_data(const zbx_vmware_service_t *service, CURL *easyhandle,
		zbx_uint64_t last_key, time_t last_ts, unsigned char *skip_old, zbx_vector_vmware_event_ptr_t *events,
		zbx_uint64_t *alloc_sz, char **error)
{
#	define ATTEMPTS_NUM	4
#	define EVENT_TAG	1
#	define RETURNVAL_TAG	0
#	define LAST_KEY(evs)	(evs->values[evs->values_num - 1]->key)

	char		*event_session = NULL, *err = NULL;
	int		ret = FAIL, node_count = 1, soap_retry = ATTEMPTS_NUM,
			soap_count = 5; /* 10 - initial value of eventlog records number in one response */
	xmlDoc		*doc = NULL;
	zbx_uint64_t	eventlog_last_key;
	time_t		eventlog_last_ts;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	if (SUCCEED != vmware_service_get_event_session(service, easyhandle, &event_session, error))
		goto out;

	if (SUCCEED != vmware_service_reset_event_history_collector(easyhandle, event_session, error))
		goto end_session;

	if (NULL != service->eventlog.data && 0 != service->eventlog.data->events.values_num &&
			service->eventlog.data->events.values[0]->key > last_key)
	{
		eventlog_last_key = service->eventlog.data->events.values[0]->key;
		eventlog_last_ts = service->eventlog.data->events.values[0]->timestamp;
	}
	else
	{
		eventlog_last_key = last_key;
		eventlog_last_ts = last_ts;
	}

	if (SUCCEED != vmware_service_get_event_latestpage(service, easyhandle, event_session, &doc, error))
		goto end_session;

	if (0 < vmware_service_parse_event_data(events, eventlog_last_key, eventlog_last_ts, EVENT_TAG, doc,
			&service->eventlog, alloc_sz, NULL, skip_old) &&
			(0 != *skip_old || LAST_KEY(events) == eventlog_last_key + 1))
	{
		zabbix_log(LOG_LEVEL_TRACE, "%s() latestPage events:%d", __func__, events->values_num);

		ret = SUCCEED;
		goto end_session;
	}

	do
	{
		zbx_xml_free_doc(doc);
		doc = NULL;

		if ((ZBX_MAXQUERYMETRICS_UNLIMITED / 2) >= soap_count)
			soap_count = soap_count * 2;
		else if (ZBX_MAXQUERYMETRICS_UNLIMITED != soap_count)
			soap_count = ZBX_MAXQUERYMETRICS_UNLIMITED;

		if (0 != events->values_num && (LAST_KEY(events) - eventlog_last_key -1) < (unsigned int)soap_count)
		{
			soap_count = (int)(LAST_KEY(events) - eventlog_last_key - 1);
		}

		if (!ZBX_IS_RUNNING() || (0 < soap_count && SUCCEED != vmware_service_read_previous_events(easyhandle,
				event_session, soap_count, &doc, error)))
		{
			goto end_session;
		}

		if (0 != node_count)
			soap_retry = ATTEMPTS_NUM;
	}
	while ((0 < vmware_service_parse_event_data(events, eventlog_last_key, eventlog_last_ts, RETURNVAL_TAG, doc,
			&service->eventlog, alloc_sz, &node_count, skip_old) ||
			(0 == node_count && 0 < soap_retry--)) && 0 == *skip_old);

	if (0 != eventlog_last_key && 0 != events->values_num && LAST_KEY(events) != eventlog_last_key + 1)
	{
		zabbix_log(LOG_LEVEL_DEBUG, "%s() events:%d id gap:%d", __func__, events->values_num,
				(int)(LAST_KEY(events) - (eventlog_last_key + 1)));
	}

	ret = SUCCEED;
end_session:
	if (SUCCEED != vmware_service_destroy_event_session(easyhandle, event_session, &err))
	{
		*error = zbx_strdcatf(*error, "%s%s", NULL != *error ? "; " : "", err);
		zbx_free(err);
		ret = FAIL;
	}
out:
	zbx_free(event_session);
	zbx_xml_free_doc(doc);

	if (SUCCEED == ret && 10 == soap_count && 0 == events->values_num && 0 == *skip_old)
		zabbix_log(LOG_LEVEL_WARNING, "vmware events collector returned empty result");

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s events:%d", __func__, zbx_result_string(ret), events->values_num);

	return ret;

#	undef ATTEMPTS_NUM
#	undef EVENT_TAG
#	undef RETURNVAL_TAG
#	undef LAST_KEY
}

/******************************************************************************
 *                                                                            *
 * Parameters: service    - [IN] vmware service                               *
 *             easyhandle - [IN] CURL handle                                  *
 *             events     - [OUT] pointer to output variable                  *
 *             alloc_sz   - [OUT] allocated memory size for events            *
 *             error      - [OUT] error message in case of failure            *
 *                                                                            *
 * Return value: SUCCEED - operation has completed successfully               *
 *               FAIL    - operation has failed                               *
 *                                                                            *
 ******************************************************************************/
static int	vmware_service_get_last_event_data(const zbx_vmware_service_t *service, CURL *easyhandle,
		zbx_vector_vmware_event_ptr_t *events, zbx_uint64_t *alloc_sz, char **error)
{
#	define ZBX_POST_VMWARE_LASTEVENT 								\
		ZBX_POST_VSPHERE_HEADER									\
		"<ns0:RetrievePropertiesEx>"								\
			"<ns0:_this type=\"PropertyCollector\">%s</ns0:_this>"				\
			"<ns0:specSet>"									\
				"<ns0:propSet>"								\
					"<ns0:type>EventManager</ns0:type>"				\
					"<ns0:all>false</ns0:all>"					\
					"<ns0:pathSet>latestEvent</ns0:pathSet>"			\
				"</ns0:propSet>"							\
				"<ns0:objectSet>"							\
					"<ns0:obj type=\"EventManager\">%s</ns0:obj>"			\
				"</ns0:objectSet>"							\
			"</ns0:specSet>"								\
			"<ns0:options/>"								\
		"</ns0:RetrievePropertiesEx>"								\
		ZBX_POST_VSPHERE_FOOTER

	char			tmp[MAX_STRING_LEN], *value;
	int			ret = FAIL;
	xmlDoc			*doc = NULL;
	zbx_id_xmlnode_t	xml_event;
	xmlXPathContext		*xpathCtx;
	xmlXPathObject		*xpathObj;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VMWARE_LASTEVENT,
			get_vmware_service_objects()[service->type].property_collector,
			get_vmware_service_objects()[service->type].event_manager);

	if (SUCCEED != zbx_soap_post(__func__, easyhandle, tmp, &doc, NULL, error))
		goto out;

	xpathCtx = xmlXPathNewContext(doc);

	if (NULL == (xpathObj = xmlXPathEvalExpression((const xmlChar *)ZBX_XPATH_PROP_NAME("latestEvent"), xpathCtx)))
	{
		*error = zbx_strdup(*error, "Cannot make lastevenlog list parsing query.");
		goto clean;
	}

	if (0 != xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
	{
		*error = zbx_strdup(*error, "Cannot find items in lastevenlog list.");
		goto clean;
	}

	xml_event.xml_node = xpathObj->nodesetval->nodeTab[0];

	if (NULL == (value = zbx_xml_node_read_value(doc, xml_event.xml_node, ZBX_XPATH_NN("key"))))
	{
		*error = zbx_strdup(*error, "Cannot find last event key");
		goto clean;
	}

	xml_event.id = (unsigned int) atoi(value);

	if (0 == xml_event.id && 0 == isdigit(value[('-' == *value || '+' == *value) ? 1 : 0 ]))
	{
		*error = zbx_dsprintf(*error, "Cannot convert eventlog key from %s", value);
		zbx_free(value);
		goto clean;
	}

	zbx_free(value);

	xml_event.created_time = vmware_service_parse_event_ts(doc, xpathObj->nodesetval->nodeTab[0], xml_event.id);

	if (SUCCEED != vmware_service_put_event_data(events, xml_event, doc, &service->eventlog.evt_severities,
			alloc_sz))
	{
		*error = zbx_dsprintf(*error, "Cannot retrieve last eventlog data for key "ZBX_FS_UI64, xml_event.id);
		goto clean;
	}

	ret = SUCCEED;
clean:
	xmlXPathFreeObject(xpathObj);
	xmlXPathFreeContext(xpathCtx);
out:
	zbx_xml_free_doc(doc);
	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s last_key:" ZBX_FS_UI64, __func__, zbx_result_string(ret),
			(SUCCEED == ret ? xml_event.id : 0));
	return ret;

#	undef ZBX_POST_VMWARE_LASTEVENT
}

/******************************************************************************
 *                                                                            *
 * Purpose: frees resources allocated to store vmware service event log data  *
 *                                                                            *
 * Parameters: evt_data - [IN] vmware service event log data                  *
 *                                                                            *
 ******************************************************************************/
static void	vmware_eventlog_data_free(zbx_vmware_eventlog_data_t *evt_data)
{
	zbx_vector_vmware_event_ptr_clear_ext(&evt_data->events, vmware_event_free);
	zbx_vector_vmware_event_ptr_destroy(&evt_data->events);

	zbx_free(evt_data->error);
	zbx_free(evt_data);
}

/******************************************************************************
 *                                                                            *
 * Purpose: updates vmware event log                                          *
 *                                                                            *
 * Parameters: service               - [IN] vmware service                    *
 *             config_source_ip      - [IN]                                   *
 *             config_vmware_timeout - [IN]                                   *
 *                                                                            *
 ******************************************************************************/
int	zbx_vmware_service_eventlog_update(zbx_vmware_service_t *service, const char *config_source_ip,
		int config_vmware_timeout)
{
#define ZBX_INIT_UPD_XML_SIZE		(100 * ZBX_KIBIBYTE)
	CURL				*easyhandle = NULL;
	struct curl_slist		*headers = NULL;
	zbx_vmware_eventlog_data_t	*evt_data;
	zbx_vector_vmware_event_ptr_t	events;
	int				ret = FAIL;
	ZBX_HTTPPAGE			page;	/* 347K/87K */
	unsigned char			evt_pause = 0, evt_skip_old;
	zbx_uint64_t			evt_last_key, events_sz = 0;
	time_t				evt_last_ts;
	char				msg[MAX_STRING_LEN / 8];

	zabbix_log(LOG_LEVEL_DEBUG, "In %s() '%s'@'%s'", __func__, service->username, service->url);

	evt_data = (zbx_vmware_eventlog_data_t *)zbx_malloc(NULL, sizeof(zbx_vmware_eventlog_data_t));
	evt_data->error = NULL;
	zbx_vector_vmware_event_ptr_create(&evt_data->events);
	page.alloc = 0;

	zbx_vmware_lock();
	evt_last_key = service->eventlog.last_key;
	evt_skip_old = service->eventlog.skip_old;
	evt_last_ts = service->eventlog.last_ts;
	zbx_vmware_unlock();

	if (NULL == (easyhandle = curl_easy_init()))
	{
		zabbix_log(LOG_LEVEL_WARNING, "Cannot initialize cURL library");
		goto out;
	}

	page.alloc = ZBX_INIT_UPD_XML_SIZE;
	page.data = (char *)zbx_malloc(NULL, page.alloc);

	if (SUCCEED != vmware_curl_set_header(easyhandle, service->major_version, &headers, &evt_data->error))
		goto clean;

	if (SUCCEED != vmware_service_authenticate(service, easyhandle, &page, config_source_ip, config_vmware_timeout,
			&evt_data->error))
	{
		goto clean;
	}

	if (NULL != service->eventlog.data && 0 != service->eventlog.data->events.values_num && 0 == evt_skip_old &&
			service->eventlog.data->events.values[0]->key > evt_last_key)
	{
		evt_pause = 1;
	}

	if (0 == service->eventlog.req_sz && 0 == evt_pause)
	{
		/* skip collection of event data if we don't know where	*/
		/* we stopped last time or item can't accept values 	*/
		if (ZBX_VMWARE_EVENT_KEY_UNINITIALIZED != evt_last_key && 0 == evt_skip_old &&
				SUCCEED != vmware_service_get_event_data(service, easyhandle, evt_last_key, evt_last_ts,
				&evt_skip_old, &evt_data->events, &events_sz, &evt_data->error))
		{
			goto clean;
		}

		if (0 != evt_skip_old)
		{
			char	*error = NULL;

			/* May not be present */
			if (SUCCEED != vmware_service_get_last_event_data(service, easyhandle, &evt_data->events,
					&events_sz, &error))
			{
				zabbix_log(LOG_LEVEL_DEBUG, "Unable retrieve lastevent value: %s.", error);
				zbx_free(error);
			}
			else
				evt_skip_old = 0;
		}
	}
	else if (0 != service->eventlog.req_sz)
	{
		zabbix_log(LOG_LEVEL_WARNING, "Postponed VMware events requires up to " ZBX_FS_UI64
				" bytes of free VMwareCache memory. Reading events skipped", service->eventlog.req_sz);
	}
	else
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Previous events have not been read. Reading new events skipped");
	}

	if (SUCCEED != vmware_service_logout(service, easyhandle, &evt_data->error))
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Cannot close vmware connection: %s.", evt_data->error);
		zbx_free(evt_data->error);
	}

	ret = SUCCEED;
clean:
	curl_slist_free_all(headers);
	curl_easy_cleanup(easyhandle);
	zbx_free(page.data);
out:
	zbx_vector_vmware_event_ptr_create(&events);
	zbx_vmware_lock();

	if (0 < evt_data->events.values_num)
	{
		if (0 != service->eventlog.oom)
			service->eventlog.oom = 0;

		events_sz += zbx_vmware_get_evt_req_chunk_sz() * evt_data->events.values_num +
				zbx_shmem_required_chunk_size(evt_data->events.values_alloc *
				sizeof(zbx_vmware_event_t*));

		if (0 == service->eventlog.last_key || vmware_shmem_get_vmware_mem()->free_size < events_sz ||
				SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG))
		{
			for (int i = 0; i < evt_data->events.values_num; i++)
			{
				zbx_vmware_event_t	*event = evt_data->events.values[i];

				if (SUCCEED == vmware_shared_strsearch(event->message))
				{
					events_sz -= zbx_shmem_required_chunk_size(strlen(event->message) +
							REFCOUNT_FIELD_SIZE + 1 + ZBX_HASHSET_ENTRY_OFFSET);
				}
			}

			if (vmware_shmem_get_vmware_mem()->free_size < events_sz)
			{
				service->eventlog.req_sz = events_sz;
				service->eventlog.oom = 1;
				zbx_vector_vmware_event_ptr_clear_ext(&evt_data->events, vmware_event_free);

				zabbix_log(LOG_LEVEL_WARNING, "Postponed VMware events requires up to " ZBX_FS_UI64
						" bytes of free VMwareCache memory, while currently only " ZBX_FS_UI64
						" bytes are free. VMwareCache memory usage (free/strpool/total): "
						ZBX_FS_UI64 " / " ZBX_FS_UI64 " / " ZBX_FS_UI64, events_sz,
						vmware_shmem_get_vmware_mem()->free_size,
						vmware_shmem_get_vmware_mem()->free_size,
						zbx_vmware_get_vmware()->strpool_sz,
						vmware_shmem_get_vmware_mem()->total_size);
			}
			else if (0 == evt_pause)
			{
				int	level;

				level = 0 == service->eventlog.last_key ? LOG_LEVEL_WARNING : LOG_LEVEL_DEBUG;

				zabbix_log(level, "Processed VMware events requires up to " ZBX_FS_UI64
						" bytes of free VMwareCache memory. VMwareCache memory usage"
						" (free/strpool/total): " ZBX_FS_UI64 " / " ZBX_FS_UI64 " / "
						ZBX_FS_UI64, events_sz, vmware_shmem_get_vmware_mem()->free_size,
						zbx_vmware_get_vmware()->strpool_sz,
						vmware_shmem_get_vmware_mem()->total_size);
			}
		}
	}
	else if (0 < service->eventlog.req_sz && service->eventlog.req_sz <= vmware_shmem_get_vmware_mem()->free_size)
	{
		service->eventlog.req_sz = 0;
	}

	if (0 != evt_pause)
	{
		zbx_vector_vmware_event_ptr_append_array(&events, service->eventlog.data->events.values,
				service->eventlog.data->events.values_num);
		zbx_vector_vmware_event_ptr_reserve(&evt_data->events,
				(size_t)(evt_data->events.values_num + service->eventlog.data->events.values_num));
		zbx_vector_vmware_event_ptr_clear(&service->eventlog.data->events);
	}

	vmware_eventlog_data_shared_free(service->eventlog.data);
	service->eventlog.data = vmware_shmem_eventlog_data_dup(evt_data);
	service->eventlog.skip_old = evt_skip_old;

	if (0 != events.values_num)
	{
		zbx_vector_vmware_event_ptr_append_array(&service->eventlog.data->events, events.values,
				events.values_num);
	}

	if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG))
		zbx_shmem_dump_stats(LOG_LEVEL_DEBUG, vmware_shmem_get_vmware_mem());

	zbx_snprintf(msg, sizeof(msg), "Events:%d VMwareCache memory usage (free/strpool/total): " ZBX_FS_UI64 " / "
			ZBX_FS_UI64 " / " ZBX_FS_UI64,
			NULL != service->eventlog.data ? service->eventlog.data->events.values_num : 0,
			vmware_shmem_get_vmware_mem()->free_size, zbx_vmware_get_vmware()->strpool_sz,
			vmware_shmem_get_vmware_mem()->total_size);

	zbx_vmware_unlock();

	vmware_eventlog_data_free(evt_data);
	zbx_vector_vmware_event_ptr_destroy(&events);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s \tprocessed:" ZBX_FS_SIZE_T " bytes of data. %s", __func__,
			zbx_result_string(ret), (zbx_fs_size_t)page.alloc, msg);

	return ret;
#undef ZBX_INIT_UPD_XML_SIZE
}

#endif /* defined(HAVE_LIBXML2) && defined(HAVE_LIBCURL) */