/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "active.h" #include "zbxserver.h" #include "log.h" #include "zbxregexp.h" #include "zbxcompress.h" #include "zbxcrypto.h" #include "zbxnum.h" #include "zbxcomms.h" #include "zbxip.h" #include "zbxsysinfo.h" #include "zbxversion.h" #include "zbx_host_constants.h" #include "zbx_item_constants.h" extern unsigned char program_type; /********************************************************************************** * * * Purpose: perform active agent auto registration * * * * Parameters: host - [IN] name of the host to be added or updated * * ip - [IN] IP address of the host * * port - [IN] port of the host * * connection_type - [IN] ZBX_TCP_SEC_UNENCRYPTED, * * ZBX_TCP_SEC_TLS_PSK or ZBX_TCP_SEC_TLS_CERT * * host_metadata - [IN] host metadata * * flag - [IN] flag describing interface type * * interface - [IN] interface value if flag is not default * * config_timeout - [IN] * * * * Comments: helper function for get_hostid_by_host * * * **********************************************************************************/ static void db_register_host(const char *host, const char *ip, unsigned short port, unsigned int connection_type, const char *host_metadata, zbx_conn_flags_t flag, const char *interface, int config_timeout) { char dns[ZBX_INTERFACE_DNS_LEN_MAX]; char ip_addr[ZBX_INTERFACE_IP_LEN_MAX]; const char *p; const char *p_ip, *p_dns; int now; p_ip = ip; p_dns = dns; if (ZBX_CONN_DEFAULT == flag) p = ip; else if (ZBX_CONN_IP == flag) p_ip = p = interface; zbx_alarm_on(config_timeout); if (ZBX_CONN_DEFAULT == flag || ZBX_CONN_IP == flag) { if (0 == strncmp("::ffff:", p, 7) && SUCCEED == zbx_is_ip4(p + 7)) p += 7; zbx_gethost_by_ip(p, dns, sizeof(dns)); } else if (ZBX_CONN_DNS == flag) { zbx_getip_by_host(interface, ip_addr, sizeof(ip_addr)); p_ip = ip_addr; p_dns = interface; } zbx_alarm_off(); now = time(NULL); /* update before changing database in case Zabbix proxy also changed database and then deleted from cache */ DCconfig_update_autoreg_host(host, p_ip, p_dns, port, host_metadata, flag, now); do { zbx_db_begin(); if (0 != (program_type & ZBX_PROGRAM_TYPE_SERVER)) { zbx_db_register_host(0, host, p_ip, p_dns, port, connection_type, host_metadata, (unsigned short)flag, now); } else { zbx_db_proxy_register_host(host, p_ip, p_dns, port, connection_type, host_metadata, (unsigned short)flag, now); } } while (ZBX_DB_DOWN == zbx_db_commit()); } static int zbx_autoreg_host_check_permissions(const char *host, const char *ip, unsigned short port, const zbx_socket_t *sock) { zbx_config_t cfg; int ret = FAIL; zbx_config_get(&cfg, ZBX_CONFIG_FLAGS_AUTOREG_TLS_ACCEPT); if (0 == (cfg.autoreg_tls_accept & sock->connection_type)) { zabbix_log(LOG_LEVEL_WARNING, "autoregistration from \"%s\" denied (host:\"%s\" ip:\"%s\"" " port:%hu): connection type \"%s\" is not allowed for autoregistration", sock->peer, host, ip, port, zbx_tcp_connection_type_name(sock->connection_type)); goto out; } #if defined(HAVE_GNUTLS) || (defined(HAVE_OPENSSL) && defined(HAVE_OPENSSL_WITH_PSK)) if (ZBX_TCP_SEC_TLS_PSK == sock->connection_type) { if (0 == (ZBX_PSK_FOR_AUTOREG & zbx_tls_get_psk_usage())) { zabbix_log(LOG_LEVEL_WARNING, "autoregistration from \"%s\" denied (host:\"%s\" ip:\"%s\"" " port:%hu): connection used PSK which is not configured for autoregistration", sock->peer, host, ip, port); goto out; } ret = SUCCEED; } else if (ZBX_TCP_SEC_UNENCRYPTED == sock->connection_type) { ret = SUCCEED; } else THIS_SHOULD_NEVER_HAPPEN; #else ret = SUCCEED; #endif out: zbx_config_clean(&cfg); return ret; } /****************************************************************************** * * * Purpose: check for host name and return hostid * * * * Parameters: sock - [IN] open socket of server-agent connection * * host - [IN] host name * * ip - [IN] IP address of the host * * port - [IN] port of the host * * host_metadata - [IN] host metadata * * flag - [IN] flag describing interface type * * interface - [IN] interface value if flag is not default * * config_timeout - [IN] * * hostid - [OUT] host ID * * revision - [OUT] host configuration revision * * error - [OUT] error message * * * * Return value: SUCCEED - host is found * * FAIL - an error occurred or host not found * * * * Comments: NB! adds host to the database if it does not exist or if it * * exists but metadata, interface, interface type or port has * * changed * * * ******************************************************************************/ static int get_hostid_by_host(const zbx_socket_t *sock, const char *host, const char *ip, unsigned short port, const char *host_metadata, zbx_conn_flags_t flag, const char *interface, int config_timeout, zbx_uint64_t *hostid, zbx_uint64_t *revision, char *error) { #define AUTO_REGISTRATION_HEARTBEAT 120 char *ch_error; int ret = FAIL, heartbeat; zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'%s' metadata:'%s'", __func__, host, host_metadata); if (FAIL == zbx_check_hostname(host, &ch_error)) { zbx_snprintf(error, MAX_STRING_LEN, "invalid host name [%s]: %s", host, ch_error); zbx_free(ch_error); goto out; } /* if host exists then check host connection permissions */ if (FAIL == DCcheck_host_permissions(host, sock, hostid, revision, &ch_error)) { zbx_snprintf(error, MAX_STRING_LEN, "%s", ch_error); zbx_free(ch_error); goto out; } /* if host does not exist then check autoregistration connection permissions */ if (0 == *hostid) { zbx_snprintf(error, MAX_STRING_LEN, "host [%s] not found", host); if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER) || 0 != DCget_auto_registration_action_count()) { if (SUCCEED == zbx_autoreg_host_check_permissions(host, ip, port, sock)) { if (SUCCEED == DCis_autoreg_host_changed(host, port, host_metadata, flag, interface, (int)time(NULL), AUTO_REGISTRATION_HEARTBEAT)) { db_register_host(host, ip, port, sock->connection_type, host_metadata, flag, interface, config_timeout); } } } goto out; } if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) heartbeat = AUTO_REGISTRATION_HEARTBEAT; else heartbeat = 0; if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER) || 0 != DCget_auto_registration_action_count()) { if (SUCCEED == DCis_autoreg_host_changed(host, port, host_metadata, flag, interface, (int)time(NULL), heartbeat)) { db_register_host(host, ip, port, sock->connection_type, host_metadata, flag, interface, config_timeout); } } ret = SUCCEED; out: zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); #undef PROXY_AUTO_REGISTRATION_HEARTBEAT return ret; } /****************************************************************************** * * * Purpose: send list of active checks to the host (older version agent) * * * * Parameters: sock - open socket of server-agent connection * * request - request buffer * * config_timeout - [IN] * * * * Return value: SUCCEED - list of active checks sent successfully * * FAIL - an error occurred * * * * Comments: format of the request: ZBX_GET_ACTIVE_CHECKS\n<host name>\n * * format of the list: key:delay:last_log_size * * * ******************************************************************************/ int send_list_of_active_checks(zbx_socket_t *sock, char *request, int config_timeout) { char *host = NULL, *p, *buffer = NULL, error[MAX_STRING_LEN]; size_t buffer_alloc = 8 * ZBX_KIBIBYTE, buffer_offset = 0; int ret = FAIL, i, num = 0; zbx_uint64_t hostid, revision; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); if (NULL != (host = strchr(request, '\n'))) { host++; if (NULL != (p = strchr(host, '\n'))) *p = '\0'; } else { zbx_snprintf(error, sizeof(error), "host is null"); goto out; } /* no host metadata in older versions of agent */ if (FAIL == get_hostid_by_host(sock, host, sock->peer, ZBX_DEFAULT_AGENT_PORT, "", 0, "", config_timeout, &hostid, &revision, error)) { goto out; } num = DCconfig_get_active_items_count_by_hostid(hostid); buffer = (char *)zbx_malloc(buffer, buffer_alloc); if (0 != num) { DC_ITEM *dc_items; int *errcodes; zbx_dc_um_handle_t *um_handle; um_handle = zbx_dc_open_user_macros(); dc_items = (DC_ITEM *)zbx_malloc(NULL, sizeof(DC_ITEM) * num); errcodes = (int *)zbx_malloc(NULL, sizeof(int) * num); DCconfig_get_active_items_by_hostid(dc_items, hostid, errcodes, num); for (i = 0; i < num; i++) { int delay; if (SUCCEED != errcodes[i]) { zabbix_log(LOG_LEVEL_DEBUG, "%s() Item for host [" ZBX_FS_UI64 "] was not found in the" " server cache.", __func__, hostid); continue; } if (ITEM_STATUS_ACTIVE != dc_items[i].status) continue; if (HOST_STATUS_MONITORED != dc_items[i].host.status) continue; zbx_substitute_simple_macros(NULL, NULL, NULL, NULL, &dc_items[i].host.hostid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dc_items[i].delay, MACRO_TYPE_COMMON, NULL, 0); if (SUCCEED != zbx_interval_preproc(dc_items[i].delay, &delay, NULL, NULL)) continue; zbx_snprintf_alloc(&buffer, &buffer_alloc, &buffer_offset, "%s:%d:" ZBX_FS_UI64 "\n", dc_items[i].key_orig, delay, dc_items[i].lastlogsize); } DCconfig_clean_items(dc_items, errcodes, num); zbx_free(errcodes); zbx_free(dc_items); zbx_dc_close_user_macros(um_handle); } zbx_strcpy_alloc(&buffer, &buffer_alloc, &buffer_offset, "ZBX_EOF\n"); zabbix_log(LOG_LEVEL_DEBUG, "%s() sending [%s]", __func__, buffer); zbx_alarm_on(config_timeout); if (SUCCEED != zbx_tcp_send_raw(sock, buffer)) zbx_strlcpy(error, zbx_socket_strerror(), MAX_STRING_LEN); else ret = SUCCEED; zbx_alarm_off(); zbx_free(buffer); out: if (FAIL == ret) zabbix_log(LOG_LEVEL_WARNING, "cannot send list of active checks to \"%s\": %s", sock->peer, error); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); return ret; } /****************************************************************************** * * * Purpose: append non duplicate string to the string vector * * * * Parameters: vector - [IN/OUT] the string vector * * str - [IN] the string to append * * * ******************************************************************************/ static void zbx_vector_str_append_uniq(zbx_vector_str_t *vector, const char *str) { if (FAIL == zbx_vector_str_search(vector, str, ZBX_DEFAULT_STR_COMPARE_FUNC)) zbx_vector_str_append(vector, zbx_strdup(NULL, str)); } /****************************************************************************** * * * Purpose: extract global regular expression names from item key * * * * Parameters: key - [IN] the item key to parse * * regexps - [OUT] the extracted regular expression names * * * ******************************************************************************/ static void zbx_itemkey_extract_global_regexps(const char *key, zbx_vector_str_t *regexps) { #define ZBX_KEY_LOG 1 #define ZBX_KEY_EVENTLOG 2 AGENT_REQUEST request; int item_key; const char *param; if (0 == strncmp(key, "log[", 4) || 0 == strncmp(key, "logrt[", 6) || 0 == strncmp(key, "log.count[", 10) || 0 == strncmp(key, "logrt.count[", 12)) item_key = ZBX_KEY_LOG; else if (0 == strncmp(key, "eventlog[", 9)) item_key = ZBX_KEY_EVENTLOG; else return; zbx_init_agent_request(&request); if(SUCCEED != zbx_parse_item_key(key, &request)) goto out; /* "params" parameter */ if (NULL != (param = get_rparam(&request, 1)) && '@' == *param) zbx_vector_str_append_uniq(regexps, param + 1); if (ZBX_KEY_EVENTLOG == item_key) { /* "severity" parameter */ if (NULL != (param = get_rparam(&request, 2)) && '@' == *param) zbx_vector_str_append_uniq(regexps, param + 1); /* "source" parameter */ if (NULL != (param = get_rparam(&request, 3)) && '@' == *param) zbx_vector_str_append_uniq(regexps, param + 1); /* "logeventid" parameter */ if (NULL != (param = get_rparam(&request, 4)) && '@' == *param) zbx_vector_str_append_uniq(regexps, param + 1); } out: zbx_free_agent_request(&request); } /****************************************************************************** * * * Purpose: send list of active checks to the host * * * * Parameters: sock - open socket of server-agent connection * * jp - request buffer * * config_timeout - [IN] * * * * Return value: SUCCEED - list of active checks sent successfully * * FAIL - an error occurred * * * ******************************************************************************/ int send_list_of_active_checks_json(zbx_socket_t *sock, struct zbx_json_parse *jp, int config_timeout) { char host[ZBX_HOSTNAME_BUF_LEN], tmp[MAX_STRING_LEN], ip[ZBX_INTERFACE_IP_LEN_MAX], error[MAX_STRING_LEN], *host_metadata = NULL, *interface = NULL, *buffer = NULL; struct zbx_json json; int ret = FAIL, i, version, num = 0; zbx_uint64_t hostid, revision, agent_config_revision; size_t host_metadata_alloc = 1; /* for at least NUL-terminated string */ size_t interface_alloc = 1; /* for at least NUL-terminated string */ size_t buffer_size, reserved = 0; unsigned short port; zbx_conn_flags_t flag = ZBX_CONN_DEFAULT; zbx_session_t *session = NULL; zbx_vector_expression_t regexps; zbx_vector_str_t names; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_expression_create(®exps); zbx_vector_str_create(&names); if (FAIL == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_HOST, host, sizeof(host), NULL)) { zbx_snprintf(error, MAX_STRING_LEN, "%s", zbx_json_strerror()); goto error; } host_metadata = (char *)zbx_malloc(host_metadata, host_metadata_alloc); if (FAIL == zbx_json_value_by_name_dyn(jp, ZBX_PROTO_TAG_HOST_METADATA, &host_metadata, &host_metadata_alloc, NULL)) { *host_metadata = '\0'; } interface = (char *)zbx_malloc(interface, interface_alloc); if (FAIL == zbx_json_value_by_name_dyn(jp, ZBX_PROTO_TAG_INTERFACE, &interface, &interface_alloc, NULL)) { *interface = '\0'; } else if (SUCCEED == zbx_is_ip(interface)) { flag = ZBX_CONN_IP; } else if (SUCCEED == zbx_validate_hostname(interface)) { flag = ZBX_CONN_DNS; } else { zbx_snprintf(error, MAX_STRING_LEN, "\"%s\" is not a valid IP or DNS", interface); goto error; } if (FAIL == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_IP, ip, sizeof(ip), NULL)) zbx_strscpy(ip, sock->peer); /* check even if 'ip' came from zbx_socket_peer_ip_save() - it can return not a valid IP */ if (FAIL == zbx_is_ip(ip)) { zbx_snprintf(error, MAX_STRING_LEN, "\"%s\" is not a valid IP address", ip); goto error; } if (FAIL == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_PORT, tmp, sizeof(tmp), NULL)) { port = ZBX_DEFAULT_AGENT_PORT; } else if (FAIL == zbx_is_ushort(tmp, &port)) { zbx_snprintf(error, MAX_STRING_LEN, "\"%s\" is not a valid port", tmp); goto error; } if (FAIL == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_CONFIG_REVISION, tmp, sizeof(tmp), NULL)) { agent_config_revision = 0; } else if (FAIL == zbx_is_uint64(tmp, &agent_config_revision)) { zbx_snprintf(error, MAX_STRING_LEN, "\"%s\" is not a valid revision", tmp); goto error; } if (FAIL == get_hostid_by_host(sock, host, ip, port, host_metadata, flag, interface, config_timeout, &hostid, &revision, error)) { goto error; } if (SUCCEED != zbx_json_value_by_name(jp, ZBX_PROTO_TAG_VERSION, tmp, sizeof(tmp), NULL) || FAIL == (version = zbx_get_component_version_without_patch(tmp))) { version = ZBX_COMPONENT_VERSION(4, 2, 0); } if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_SESSION, tmp, sizeof(tmp), NULL)) { size_t token_len; if (ZBX_SESSION_TOKEN_SIZE != (token_len = strlen(tmp))) { zbx_snprintf(error, MAX_STRING_LEN, "invalid session token length %d", (int)token_len); goto error; } session = zbx_dc_get_or_create_session(hostid, tmp, ZBX_SESSION_TYPE_CONFIG); } zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN); zbx_json_addstring(&json, ZBX_PROTO_TAG_RESPONSE, ZBX_PROTO_VALUE_SUCCESS, ZBX_JSON_TYPE_STRING); if (NULL == session || 0 == session->last_id || agent_config_revision != revision) { zbx_json_adduint64(&json, ZBX_PROTO_TAG_CONFIG_REVISION, (zbx_uint64_t)revision); zbx_json_addarray(&json, ZBX_PROTO_TAG_DATA); /* determine items count to ensure allocation is done outside of a lock */ num = DCconfig_get_active_items_count_by_hostid(hostid); } if (0 != num) { DC_ITEM *dc_items; int *errcodes, delay; zbx_dc_um_handle_t *um_handle; dc_items = (DC_ITEM *)zbx_malloc(NULL, sizeof(DC_ITEM) * num); errcodes = (int *)zbx_malloc(NULL, sizeof(int) * num); DCconfig_get_active_items_by_hostid(dc_items, hostid, errcodes, num); um_handle = zbx_dc_open_user_macros(); for (i = 0; i < num; i++) { if (SUCCEED != errcodes[i]) { /* items or host removed between checking item count and retrieving items */ zabbix_log(LOG_LEVEL_DEBUG, "%s() Item for host [" ZBX_FS_UI64 "] was not found in the" " server cache.", __func__, hostid); continue; } if (ITEM_STATUS_ACTIVE != dc_items[i].status) continue; if (HOST_STATUS_MONITORED != dc_items[i].host.status) continue; zbx_substitute_simple_macros(NULL, NULL, NULL, NULL, &dc_items[i].host.hostid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dc_items[i].delay, MACRO_TYPE_COMMON, NULL, 0); if (SUCCEED != zbx_interval_preproc(dc_items[i].delay, &delay, NULL, NULL)) continue; dc_items[i].key = zbx_strdup(dc_items[i].key, dc_items[i].key_orig); zbx_substitute_key_macros_unmasked(&dc_items[i].key, NULL, &dc_items[i], NULL, NULL, MACRO_TYPE_ITEM_KEY, NULL, 0); zbx_json_addobject(&json, NULL); zbx_json_addstring(&json, ZBX_PROTO_TAG_KEY, dc_items[i].key, ZBX_JSON_TYPE_STRING); if (ZBX_COMPONENT_VERSION(4, 4, 0) > version) { if (0 != strcmp(dc_items[i].key, dc_items[i].key_orig)) { zbx_json_addstring(&json, ZBX_PROTO_TAG_KEY_ORIG, dc_items[i].key_orig, ZBX_JSON_TYPE_STRING); } /* in the case scheduled/flexible interval set delay to 0 causing */ /* 'Incorrect update interval' error in agent */ if (NULL != strchr(dc_items[i].delay, ';')) delay = 0; zbx_json_adduint64(&json, ZBX_PROTO_TAG_DELAY, delay); } else { zbx_json_adduint64(&json, ZBX_PROTO_TAG_ITEMID, dc_items[i].itemid); zbx_json_addstring(&json, ZBX_PROTO_TAG_DELAY, dc_items[i].delay, ZBX_JSON_TYPE_STRING); } /* The agent expects ALWAYS to have lastlogsize and mtime tags. */ /* Removing those would cause older agents to fail. */ zbx_json_adduint64(&json, ZBX_PROTO_TAG_LASTLOGSIZE, dc_items[i].lastlogsize); zbx_json_adduint64(&json, ZBX_PROTO_TAG_MTIME, dc_items[i].mtime); zbx_json_close(&json); zbx_itemkey_extract_global_regexps(dc_items[i].key, &names); zbx_free(dc_items[i].key); } DCconfig_clean_items(dc_items, errcodes, num); zbx_free(errcodes); zbx_free(dc_items); zbx_dc_close_user_macros(um_handle); } zbx_json_close(&json); if (ZBX_COMPONENT_VERSION(4, 4, 0) == version || ZBX_COMPONENT_VERSION(5, 0, 0) == version) zbx_json_adduint64(&json, ZBX_PROTO_TAG_REFRESH_UNSUPPORTED, 600); DCget_expressions_by_names(®exps, (const char * const *)names.values, names.values_num); if (0 < regexps.values_num) { char str[32]; zbx_json_addarray(&json, ZBX_PROTO_TAG_REGEXP); for (i = 0; i < regexps.values_num; i++) { zbx_expression_t *regexp = regexps.values[i]; zbx_json_addobject(&json, NULL); zbx_json_addstring(&json, "name", regexp->name, ZBX_JSON_TYPE_STRING); zbx_json_addstring(&json, "expression", regexp->expression, ZBX_JSON_TYPE_STRING); zbx_snprintf(str, sizeof(str), "%d", regexp->expression_type); zbx_json_addstring(&json, "expression_type", str, ZBX_JSON_TYPE_INT); zbx_snprintf(str, sizeof(str), "%c", regexp->exp_delimiter); zbx_json_addstring(&json, "exp_delimiter", str, ZBX_JSON_TYPE_STRING); zbx_snprintf(str, sizeof(str), "%d", regexp->case_sensitive); zbx_json_addstring(&json, "case_sensitive", str, ZBX_JSON_TYPE_INT); zbx_json_close(&json); } zbx_json_close(&json); } zabbix_log(LOG_LEVEL_DEBUG, "%s() sending [%s]", __func__, json.buffer); if (0 != (ZBX_TCP_COMPRESS & sock->protocol)) { if (SUCCEED != zbx_compress(json.buffer, json.buffer_size, &buffer, &buffer_size)) { zbx_snprintf(error, MAX_STRING_LEN, "cannot compress data: %s", zbx_compress_strerror()); goto error; } reserved = json.buffer_size; zbx_json_free(&json); /* json buffer can be large, free as fast as possible */ if (SUCCEED != (ret = zbx_tcp_send_ext(sock, buffer, buffer_size, reserved, sock->protocol, config_timeout))) { zbx_strscpy(error, zbx_socket_strerror()); } } else { if (SUCCEED != (ret = zbx_tcp_send_ext(sock, json.buffer, json.buffer_size, 0, sock->protocol, config_timeout))) { zbx_strscpy(error, zbx_socket_strerror()); } } zbx_json_free(&json); if (SUCCEED == ret) { /* remember if configuration was successfully sent for new session */ if (NULL != session) session->last_id = (zbx_uint64_t)revision; } goto out; error: zabbix_log(LOG_LEVEL_WARNING, "cannot send list of active checks to \"%s\": %s", sock->peer, error); zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN); zbx_json_addstring(&json, ZBX_PROTO_TAG_RESPONSE, ZBX_PROTO_VALUE_FAILED, ZBX_JSON_TYPE_STRING); zbx_json_addstring(&json, ZBX_PROTO_TAG_INFO, error, ZBX_JSON_TYPE_STRING); zabbix_log(LOG_LEVEL_DEBUG, "%s() sending [%s]", __func__, json.buffer); ret = zbx_tcp_send(sock, json.buffer); zbx_json_free(&json); out: for (i = 0; i < names.values_num; i++) zbx_free(names.values[i]); zbx_vector_str_destroy(&names); zbx_regexp_clean_expressions(®exps); zbx_vector_expression_destroy(®exps); zbx_free(host_metadata); zbx_free(interface); zbx_free(buffer); zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); return ret; }