/* ** 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 "proxyconfig_read.h" #include "zbxcommon.h" #include "zbxdbwrap.h" #include "zbxdbhigh.h" #include "zbxkvs.h" #include "zbxvault.h" #include "log.h" #include "zbxcommshigh.h" #include "zbxcompress.h" #include "zbxcrypto.h" #include "zbx_item_constants.h" extern int CONFIG_TRAPPER_TIMEOUT; typedef struct { char *path; zbx_hashset_t keys; } zbx_keys_path_t; static int keys_path_compare(const void *d1, const void *d2) { const zbx_keys_path_t *ptr1 = *((const zbx_keys_path_t * const *)d1); const zbx_keys_path_t *ptr2 = *((const zbx_keys_path_t * const *)d2); return strcmp(ptr1->path, ptr2->path); } static zbx_hash_t keys_hash(const void *data) { return ZBX_DEFAULT_STRING_HASH_ALGO(*(const char * const *)data, strlen(*(const char * const *)data), ZBX_DEFAULT_HASH_SEED); } static int keys_compare(const void *d1, const void *d2) { return strcmp(*(const char * const *)d1, *(const char * const *)d2); } static void key_path_free(void *data) { zbx_hashset_iter_t iter; char **ptr; zbx_keys_path_t *keys_path = (zbx_keys_path_t *)data; zbx_hashset_iter_reset(&keys_path->keys, &iter); while (NULL != (ptr = (char **)zbx_hashset_iter_next(&iter))) zbx_free(*ptr); zbx_hashset_destroy(&keys_path->keys); zbx_free(keys_path->path); zbx_free(keys_path); } static void get_macro_secrets(const zbx_vector_ptr_t *keys_paths, struct zbx_json *j, const zbx_config_vault_t *config_vault) { int i; zbx_kvs_t kvs; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_kvs_create(&kvs, 100); zbx_json_addobject(j, ZBX_PROTO_TAG_MACRO_SECRETS); for (i = 0; i < keys_paths->values_num; i++) { zbx_keys_path_t *keys_path; char *error = NULL, **ptr; zbx_hashset_iter_t iter; keys_path = (zbx_keys_path_t *)keys_paths->values[i]; if (FAIL == zbx_vault_kvs_get(keys_path->path, &kvs, config_vault, &error)) { zabbix_log(LOG_LEVEL_WARNING, "cannot get secrets for path \"%s\": %s", keys_path->path, error); zbx_free(error); continue; } zbx_json_addobject(j, keys_path->path); zbx_hashset_iter_reset(&keys_path->keys, &iter); while (NULL != (ptr = (char **)zbx_hashset_iter_next(&iter))) { zbx_kv_t *kv, kv_local; kv_local.key = *ptr; if (NULL != (kv = zbx_kvs_search(&kvs, &kv_local))) zbx_json_addstring(j, kv->key, kv->value, ZBX_JSON_TYPE_STRING); } zbx_json_close(j); zbx_kvs_clear(&kvs); } zbx_json_close(j); zbx_kvs_destroy(&kvs); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } /****************************************************************************** * * * Purpose: add database row to the proxy config json data * * * * Parameters: j - [OUT] the output json * * row - [IN] the database row to add * * table - [IN] the table configuration * * recids - [OUT] the record identifiers (optional) * * * ******************************************************************************/ static void proxyconfig_add_row(struct zbx_json *j, const DB_ROW row, const ZBX_TABLE *table, zbx_vector_uint64_t *recids) { int fld = 0, i; zbx_json_addstring(j, NULL, row[fld++], ZBX_JSON_TYPE_INT); if (NULL != recids) { zbx_uint64_t recid; ZBX_STR2UINT64(recid, row[0]); zbx_vector_uint64_append(recids, recid); } for (i = 0; 0 != table->fields[i].name; i++) { if (0 == (table->fields[i].flags & ZBX_PROXY)) continue; switch (table->fields[i].type) { case ZBX_TYPE_INT: case ZBX_TYPE_UINT: case ZBX_TYPE_ID: if (SUCCEED != zbx_db_is_null(row[fld])) zbx_json_addstring(j, NULL, row[fld], ZBX_JSON_TYPE_INT); else zbx_json_addstring(j, NULL, NULL, ZBX_JSON_TYPE_NULL); break; default: zbx_json_addstring(j, NULL, row[fld], ZBX_JSON_TYPE_STRING); break; } fld++; } } /****************************************************************************** * * * Purpose: get table fields, add them to output json and sql select * * * * Parameters: sql - [IN/OUT] the sql select string * * sql_alloc - [IN/OUT] * * sql_offset - [IN/OUT] * * table - [IN] the table * * alias - [IN] the table alias * * j - [OUT] the output json * * * ******************************************************************************/ static void proxyconfig_get_fields(char **sql, size_t *sql_alloc, size_t *sql_offset, const ZBX_TABLE *table, const char *alias, struct zbx_json *j) { int i; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "select %s%s", alias, table->recid); zbx_json_addarray(j, "fields"); zbx_json_addstring(j, NULL, table->recid, ZBX_JSON_TYPE_STRING); for (i = 0; 0 != table->fields[i].name; i++) { if (0 == (table->fields[i].flags & ZBX_PROXY)) continue; zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ','); zbx_strcpy_alloc(sql, sql_alloc, sql_offset, alias); zbx_strcpy_alloc(sql, sql_alloc, sql_offset, table->fields[i].name); zbx_json_addstring(j, NULL, table->fields[i].name, ZBX_JSON_TYPE_STRING); } zbx_json_close(j); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } /************************************************************************************** * * * Purpose: get global/host macro data of the specified hosts from database * * * * Parameters: table_name - [IN] table name - globalmacro/hostmacro * * hostids - [IN] the target hostids for hostmacro table and * * NULL for globalmacro table * * config_vault_db_path - [IN] * * keys_paths - [OUT] the vault macro path/key * * j - [OUT] the output json * * error - [OUT] the error message * * * * Return value: SUCCEED - the data was read successfully * * FAIL - otherwise * * * **************************************************************************************/ static int proxyconfig_get_macro_updates(const char *table_name, const zbx_vector_uint64_t *hostids, const char *config_vault_db_path, zbx_vector_ptr_t *keys_paths, struct zbx_json *j, char **error) { DB_RESULT result; DB_ROW row; const ZBX_TABLE *table; char *sql; size_t sql_alloc = 4 * ZBX_KIBIBYTE, sql_offset = 0; int i, ret = FAIL, offset; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); table = zbx_db_get_table(table_name); zbx_json_addobject(j, table->table); sql = (char *)zbx_malloc(NULL, sql_alloc); proxyconfig_get_fields(&sql, &sql_alloc, &sql_offset, table, "", j); zbx_json_addarray(j, "data"); zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " from %s", table->table); if (NULL != hostids) { /* no hosts to get macros from, send empty data */ if (0 == hostids->values_num) goto end; zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " where"); zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "hostid", hostids->values, hostids->values_num); offset = 1; } else offset = 0; zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " order by "); zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, table->recid); if (NULL == (result = zbx_db_select("%s", sql))) { *error = zbx_dsprintf(*error, "failed to get data from table \"%s\"", table->table); goto out; } zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " from %s", table->table); while (NULL != (row = zbx_db_fetch(result))) { zbx_keys_path_t *keys_path, keys_path_local; unsigned char type; char *path, *key; zbx_json_addarray(j, NULL); proxyconfig_add_row(j, row, table, NULL); zbx_json_close(j); ZBX_STR2UCHAR(type, row[3 + offset]); if (ZBX_MACRO_VALUE_VAULT != type) continue; zbx_strsplit_last(row[2 + offset], ':', &path, &key); if (NULL == key) { zabbix_log(LOG_LEVEL_WARNING, "cannot parse macro \"%s\" value \"%s\"", row[1 + offset], row[2 + offset]); goto next; } if (NULL != config_vault_db_path && 0 == strcasecmp(config_vault_db_path, path) && (0 == strcasecmp(key, ZBX_PROTO_TAG_PASSWORD) || 0 == strcasecmp(key, ZBX_PROTO_TAG_USERNAME))) { zabbix_log(LOG_LEVEL_WARNING, "cannot parse macro \"%s\" value \"%s\":" " database credentials should not be used with Vault macros", row[1 + offset], row[2 + offset]); goto next; } keys_path_local.path = path; if (FAIL == (i = zbx_vector_ptr_search(keys_paths, &keys_path_local, keys_path_compare))) { keys_path = zbx_malloc(NULL, sizeof(zbx_keys_path_t)); keys_path->path = path; zbx_hashset_create(&keys_path->keys, 0, keys_hash, keys_compare); zbx_hashset_insert(&keys_path->keys, &key, sizeof(char *)); zbx_vector_ptr_append(keys_paths, keys_path); path = key = NULL; } else { keys_path = (zbx_keys_path_t *)keys_paths->values[i]; if (NULL == zbx_hashset_search(&keys_path->keys, &key)) { zbx_hashset_insert(&keys_path->keys, &key, sizeof(char **)); key = NULL; } } next: zbx_free(key); zbx_free(path); } zbx_db_free_result(result); end: zbx_json_close(j); zbx_json_close(j); ret = SUCCEED; out: zbx_free(sql); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return ret; } /****************************************************************************** * * * Purpose: get table data from database * * * * Parameters: table_name - [IN] table name - * * key_name - [IN] key field name used to select rows * * (all rows selected when NULL) * * key_ids - [IN] key values used to select rows (optional) * * condition - [IN] custom condition to apply when selecting rows* * (optional) * * join - [IN] custom join to apply when selecting rows * * (optional) * * ids_filter - [IN] key values used to filter rows * * (optional) * * filter_name- [IN] filter field name used to filter rows * * (optional) * * recids - [OUT] selected record identifiers, sorted * * j - [OUT] output json * * error - [OUT] error message * * * * Return value: SUCCEED - the data was read successfully * * FAIL - otherwise * * * ******************************************************************************/ static int proxyconfig_get_table_data_ext(const char *table_name, const char *key_name, const zbx_vector_uint64_t *key_ids, const char *condition, const char *join, const zbx_hashset_t *ids_filter, const char *filter_name, zbx_vector_uint64_t *recids, struct zbx_json *j, char **error) { DB_RESULT result; DB_ROW row; const ZBX_TABLE *table; char *sql = NULL; size_t sql_alloc = 4 * ZBX_KIBIBYTE, sql_offset = 0; int ret = FAIL, i, fld_ids_filter = -1; const char *alias = "t.", *alias_from = " t"; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); table = zbx_db_get_table(table_name); zbx_json_addobject(j, table->table); if (NULL != ids_filter) { for (i = 0; 0 != table->fields[i].name; i++) { if (0 == strcmp(filter_name, table->fields[i].name)) { fld_ids_filter = i; break; } } if (-1 == fld_ids_filter) THIS_SHOULD_NEVER_HAPPEN; } sql = (char *)zbx_malloc(NULL, sql_alloc); proxyconfig_get_fields(&sql, &sql_alloc, &sql_offset, table, alias, j); zbx_json_addarray(j, ZBX_PROTO_TAG_DATA); if ((NULL == key_ids || 0 != key_ids->values_num) && (NULL == ids_filter || 0 != ids_filter->num_data)) { zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " from %s%s", table->table, alias_from); if (NULL != join) zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, join); if (NULL != key_ids || NULL != condition) { const char *keyword = " where"; if (NULL != key_ids) { char *key_name_aliased = zbx_dsprintf(NULL, "%s%s", alias, key_name); zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, keyword); zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, key_name_aliased, key_ids->values, key_ids->values_num); keyword = " and"; zbx_free(key_name_aliased); } if (NULL != condition) { zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, keyword); zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, condition); } } zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " order by "); zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, alias); zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, table->recid); if (NULL == (result = zbx_db_select("%s", sql))) { *error = zbx_dsprintf(*error, "failed to get data from table \"%s\"", table->table); goto out; } while (NULL != (row = zbx_db_fetch(result))) { if (-1 != fld_ids_filter) { zbx_uint64_t recid; ZBX_STR2UINT64(recid, row[fld_ids_filter]); if (NULL == zbx_hashset_search(ids_filter, &recid)) continue; } zbx_json_addarray(j, NULL); proxyconfig_add_row(j, row, table, recids); zbx_json_close(j); } zbx_db_free_result(result); } zbx_json_close(j); zbx_json_close(j); if (NULL != recids) zbx_vector_uint64_sort(recids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); ret = SUCCEED; out: zbx_free(sql); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return ret; } static int proxyconfig_get_table_data(const char *table_name, const char *key_name, const zbx_vector_uint64_t *key_ids, const char *condition, zbx_vector_uint64_t *recids, struct zbx_json *j, char **error) { return proxyconfig_get_table_data_ext(table_name, key_name, key_ids, condition, NULL, NULL, NULL, recids, j, error); } typedef struct { zbx_uint64_t itemid; zbx_uint64_t master_itemid; DB_ROW row; int cols_num; } zbx_proxyconfig_dep_item_t; ZBX_PTR_VECTOR_DECL(proxyconfig_dep_item_ptr, zbx_proxyconfig_dep_item_t *) ZBX_PTR_VECTOR_IMPL(proxyconfig_dep_item_ptr, zbx_proxyconfig_dep_item_t *) static void proxyconfig_dep_item_free(zbx_proxyconfig_dep_item_t *item) { int i; for (i = 0; i < item->cols_num; i++) zbx_free(item->row[i]); zbx_free(item->row); zbx_free(item); } static zbx_proxyconfig_dep_item_t *proxyconfig_dep_item_create(zbx_uint64_t itemid, zbx_uint64_t master_itemid, const DB_ROW row, int cols_num) { zbx_proxyconfig_dep_item_t *item; int i; item = (zbx_proxyconfig_dep_item_t *)zbx_malloc(NULL, sizeof(zbx_proxyconfig_dep_item_t)); item->itemid = itemid; item->master_itemid = master_itemid; item->cols_num = cols_num; item->row = (DB_ROW)zbx_malloc(NULL, sizeof(char *) * (size_t)cols_num); for (i = 0; i < cols_num; i++) { if (NULL == row[i]) item->row[i] = NULL; else item->row[i] = zbx_strdup(NULL, row[i]); } return item; } /****************************************************************************** * * * Purpose: get item data from items table * * * * Parameters: hostids - [IN] the target host identifiers * * items - [IN] the selected item identifiers * * j - [OUT] the output json * * error - [OUT] the error message * * * * Return value: SUCCEED - the data was read successfully * * FAIL - otherwise * * * ******************************************************************************/ static int proxyconfig_get_item_data(const zbx_vector_uint64_t *hostids, zbx_hashset_t *items, struct zbx_json *j, char **error) { DB_RESULT result; DB_ROW row; const ZBX_TABLE *table; char *sql; size_t sql_alloc = 4 * ZBX_KIBIBYTE, sql_offset = 0; int ret = FAIL, fld_key = -1, fld_type = -1, fld_master_itemid = -1, i, fld, dep_items_num; zbx_uint64_t itemid, master_itemid; zbx_vector_proxyconfig_dep_item_ptr_t dep_items; zbx_proxyconfig_dep_item_t *dep_item; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_proxyconfig_dep_item_ptr_create(&dep_items); table = zbx_db_get_table("items"); /* get type, key_ field indexes used to check if item is processed by server */ for (i = 0, fld = 1; 0 != table->fields[i].name; i++) { if (0 == (table->fields[i].flags & ZBX_PROXY)) continue; if (0 == strcmp(table->fields[i].name, "type")) fld_type = fld; else if (0 == strcmp(table->fields[i].name, "key_")) fld_key = fld; else if (0 == strcmp(table->fields[i].name, "master_itemid")) fld_master_itemid = fld; fld++; } if (-1 == fld_type || -1 == fld_key || -1 == fld_master_itemid) { THIS_SHOULD_NEVER_HAPPEN; exit(EXIT_FAILURE); } zbx_json_addobject(j, table->table); sql = (char *)zbx_malloc(NULL, sql_alloc); proxyconfig_get_fields(&sql, &sql_alloc, &sql_offset, table, "", j); zbx_json_addarray(j, ZBX_PROTO_TAG_DATA); if (0 != hostids->values_num) { zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " from %s where", table->table); zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "hostid", hostids->values, hostids->values_num); zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " and flags<>%d and type<>%d", ZBX_FLAG_DISCOVERY_PROTOTYPE, ITEM_TYPE_CALCULATED); if (NULL == (result = zbx_db_select("%s", sql))) { *error = zbx_dsprintf(*error, "failed to get data from table \"%s\"", table->table); goto out; } zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " from %s", table->table); while (NULL != (row = zbx_db_fetch(result))) { unsigned char type; ZBX_STR2UCHAR(type, row[fld_type]); if (SUCCEED == is_item_processed_by_server(type, row[fld_key])) continue; ZBX_DBROW2UINT64(itemid, row[0]); if (ITEM_TYPE_DEPENDENT != atoi(row[fld_type])) { zbx_json_addarray(j, NULL); proxyconfig_add_row(j, row, table, NULL); zbx_json_close(j); zbx_hashset_insert(items, &itemid, sizeof(itemid)); } else { ZBX_DBROW2UINT64(master_itemid, row[fld_master_itemid]); dep_item = proxyconfig_dep_item_create(itemid, master_itemid, row, fld); zbx_vector_proxyconfig_dep_item_ptr_append(&dep_items, dep_item); } } zbx_db_free_result(result); /* add dependent items processed by proxy */ if (0 != dep_items.values_num) { do { dep_items_num = dep_items.values_num; for (i = 0; i < dep_items.values_num; ) { dep_item = dep_items.values[i]; if (NULL != zbx_hashset_search(items, &dep_item->master_itemid)) { zbx_json_addarray(j, NULL); proxyconfig_add_row(j, dep_item->row, table, NULL); zbx_json_close(j); zbx_hashset_insert(items, &dep_item->itemid, sizeof(zbx_uint64_t)); proxyconfig_dep_item_free(dep_item); zbx_vector_proxyconfig_dep_item_ptr_remove_noorder(&dep_items, i); } else i++; } } while (dep_items_num != dep_items.values_num); } } zbx_json_close(j); zbx_json_close(j); ret = SUCCEED; out: zbx_free(sql); zbx_vector_proxyconfig_dep_item_ptr_clear_ext(&dep_items, proxyconfig_dep_item_free); zbx_vector_proxyconfig_dep_item_ptr_destroy(&dep_items); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return ret; } /****************************************************************************** * * * Purpose: get host and related table data from database * * * * Parameters: hostids - [IN] the target host identifiers * * j - [OUT] the output json * * error - [OUT] the error message * * * * Return value: SUCCEED - the data was read successfully * * FAIL - otherwise * * * ******************************************************************************/ static int proxyconfig_get_host_data(const zbx_vector_uint64_t *hostids, struct zbx_json *j, char **error) { zbx_vector_uint64_t interfaceids; int ret = FAIL; char *sql = NULL; size_t sql_alloc = 0, sql_offset = 0; zbx_hashset_t items; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_uint64_create(&interfaceids); zbx_hashset_create(&items, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC); if (SUCCEED != proxyconfig_get_table_data("hosts", "hostid", hostids, NULL, NULL, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("interface", "hostid", hostids, NULL, &interfaceids, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("interface_snmp", "interfaceid", &interfaceids, NULL, NULL, j, error)) { goto out; } if (SUCCEED != proxyconfig_get_table_data("host_inventory", "hostid", hostids, NULL, NULL, j, error)) goto out; if (SUCCEED != proxyconfig_get_item_data(hostids, &items, j, error)) goto out; if (0 != items.num_data) { zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " join items i on i.itemid=t.itemid and"); zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.hostid", hostids->values, hostids->values_num); } if (SUCCEED != proxyconfig_get_table_data_ext("item_rtdata", NULL, NULL, NULL, sql, &items, "itemid", NULL, j, error)) { goto out; } if (SUCCEED != proxyconfig_get_table_data_ext("item_preproc", NULL, NULL, NULL, sql, &items, "itemid", NULL, j, error)) { goto out; } if (SUCCEED != proxyconfig_get_table_data_ext("item_parameter", NULL, NULL, NULL, sql, &items, "itemid", NULL, j, error)) { goto out; } ret = SUCCEED; out: zbx_vector_uint64_destroy(&interfaceids); zbx_hashset_destroy(&items); zbx_free(sql); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return ret; } /****************************************************************************** * * * Purpose: get discovery rule and checks data from database * * * * Parameters: proxy - [IN] the target proxy * * j - [OUT] the output json * * error - [OUT] the error message * * * * Return value: SUCCEED - the data was read successfully * * FAIL - otherwise * * * ******************************************************************************/ static int proxyconfig_get_drules_data(const DC_PROXY *proxy, struct zbx_json *j, char **error) { zbx_vector_uint64_t druleids; zbx_vector_uint64_t proxy_hostids; int ret = FAIL; char *filter = NULL; size_t filter_alloc = 0, filter_offset = 0; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_uint64_create(&druleids); zbx_vector_uint64_create(&proxy_hostids); zbx_vector_uint64_append(&proxy_hostids, proxy->hostid); zbx_snprintf_alloc(&filter, &filter_alloc, &filter_offset, " status=%d", DRULE_STATUS_MONITORED); if (SUCCEED != proxyconfig_get_table_data("drules", "proxy_hostid", &proxy_hostids, filter, &druleids, j, error)) { goto out; } if (SUCCEED != proxyconfig_get_table_data("dchecks", "druleid", &druleids, NULL, NULL, j, error)) goto out; ret = SUCCEED; out: zbx_free(filter); zbx_vector_uint64_destroy(&proxy_hostids); zbx_vector_uint64_destroy(&druleids); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return ret; } /****************************************************************************** * * * Purpose: get global regular expression (regexps/expressions) data from * * database * * * * Parameters: j - [OUT] the output json * * error - [OUT] the error message * * * * Return value: SUCCEED - the data was read successfully * * FAIL - otherwise * * * ******************************************************************************/ static int proxyconfig_get_expression_data(struct zbx_json *j, char **error) { zbx_vector_uint64_t regexpids; int ret = FAIL; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_uint64_create(®expids); if (SUCCEED != proxyconfig_get_table_data("regexps", NULL, NULL, NULL, ®expids, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("expressions", "regexpid", ®expids, NULL, NULL, j, error)) goto out; ret = SUCCEED; out: zbx_vector_uint64_destroy(®expids); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return ret; } /****************************************************************************** * * * Purpose: get httptest and related data from database * * * * Parameters: httptestids - [IN] the httptest identifiers * * j - [OUT] the output json * * error - [OUT] the error message * * * * Return value: SUCCEED - the data was read successfully * * FAIL - otherwise * * * ******************************************************************************/ static int proxyconfig_get_httptest_data(const zbx_vector_uint64_t *httptestids, struct zbx_json *j, char **error) { zbx_vector_uint64_t httpstepids; int ret = FAIL; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_uint64_create(&httpstepids); if (SUCCEED != proxyconfig_get_table_data("httptest", "httptestid", httptestids, NULL, NULL, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("httptestitem", "httptestid", httptestids, NULL, NULL, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("httptest_field", "httptestid", httptestids, NULL, NULL, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("httpstep", "httptestid", httptestids, NULL, &httpstepids, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("httpstepitem", "httpstepid", &httpstepids, NULL, NULL, j, error)) goto out; if (SUCCEED != proxyconfig_get_table_data("httpstep_field", "httpstepid", &httpstepids, NULL, NULL, j, error)) goto out; ret = SUCCEED; out: zbx_vector_uint64_destroy(&httpstepids); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return ret; } static int proxyconfig_get_tables(const DC_PROXY *proxy, zbx_uint64_t proxy_config_revision, const zbx_dc_revision_t *dc_revision, struct zbx_json *j, zbx_proxyconfig_status_t *status, const zbx_config_vault_t *config_vault, char **error) { #define ZBX_PROXYCONFIG_SYNC_HOSTS 0x0001 #define ZBX_PROXYCONFIG_SYNC_GMACROS 0x0002 #define ZBX_PROXYCONFIG_SYNC_HMACROS 0x0004 #define ZBX_PROXYCONFIG_SYNC_DRULES 0x0008 #define ZBX_PROXYCONFIG_SYNC_EXPRESSIONS 0x0010 #define ZBX_PROXYCONFIG_SYNC_CONFIG 0x0020 #define ZBX_PROXYCONFIG_SYNC_HTTPTESTS 0x0040 #define ZBX_PROXYCONFIG_SYNC_AUTOREG 0x0080 #define ZBX_PROXYCONFIG_SYNC_ALL (ZBX_PROXYCONFIG_SYNC_HOSTS | ZBX_PROXYCONFIG_SYNC_GMACROS | \ ZBX_PROXYCONFIG_SYNC_HMACROS |ZBX_PROXYCONFIG_SYNC_DRULES | \ ZBX_PROXYCONFIG_SYNC_EXPRESSIONS | ZBX_PROXYCONFIG_SYNC_CONFIG | \ ZBX_PROXYCONFIG_SYNC_HTTPTESTS | ZBX_PROXYCONFIG_SYNC_AUTOREG) zbx_vector_uint64_t hostids, httptestids, updated_hostids, removed_hostids, del_macro_hostids, macro_hostids; zbx_vector_ptr_t keys_paths; int global_macros = FAIL, ret = FAIL, i; zbx_uint64_t flags = 0; zbx_vector_uint64_create(&hostids); zbx_vector_uint64_create(&updated_hostids); zbx_vector_uint64_create(&removed_hostids); zbx_vector_uint64_create(&httptestids); zbx_vector_uint64_create(¯o_hostids); zbx_vector_uint64_create(&del_macro_hostids); zbx_vector_ptr_create(&keys_paths); if (proxy_config_revision < proxy->revision || proxy_config_revision < proxy->macro_revision) { zbx_vector_uint64_reserve(&hostids, 1000); zbx_vector_uint64_reserve(&updated_hostids, 1000); zbx_vector_uint64_reserve(&removed_hostids, 100); zbx_vector_uint64_reserve(&httptestids, 100); zbx_vector_uint64_reserve(¯o_hostids, 1000); zbx_vector_uint64_reserve(&del_macro_hostids, 100); zbx_dc_get_proxy_config_updates(proxy->hostid, proxy_config_revision, &hostids, &updated_hostids, &removed_hostids, &httptestids); zbx_dc_get_macro_updates(&hostids, &updated_hostids, proxy_config_revision, ¯o_hostids, &global_macros, &del_macro_hostids); } if (0 != proxy_config_revision) { if (0 != updated_hostids.values_num) flags |= ZBX_PROXYCONFIG_SYNC_HOSTS; if (SUCCEED == global_macros) flags |= ZBX_PROXYCONFIG_SYNC_GMACROS; if(0 != macro_hostids.values_num) flags |= ZBX_PROXYCONFIG_SYNC_HMACROS; if (proxy_config_revision < proxy->revision) flags |= ZBX_PROXYCONFIG_SYNC_DRULES; if (proxy_config_revision < dc_revision->expression) flags |= ZBX_PROXYCONFIG_SYNC_EXPRESSIONS; if (proxy_config_revision < dc_revision->config_table) flags |= ZBX_PROXYCONFIG_SYNC_CONFIG; if (0 != httptestids.values_num) flags |= ZBX_PROXYCONFIG_SYNC_HTTPTESTS; if (proxy_config_revision < dc_revision->autoreg_tls) flags |= ZBX_PROXYCONFIG_SYNC_AUTOREG; } else flags = ZBX_PROXYCONFIG_SYNC_ALL; zbx_json_addobject(j, ZBX_PROTO_TAG_DATA); if (0 != flags) { zbx_db_begin(); if (0 != (flags & ZBX_PROXYCONFIG_SYNC_HOSTS) && SUCCEED != proxyconfig_get_host_data(&updated_hostids, j, error)) { goto out; } if (0 != (flags & ZBX_PROXYCONFIG_SYNC_GMACROS) && SUCCEED != proxyconfig_get_macro_updates("globalmacro", NULL, config_vault->db_path, &keys_paths, j, error)) { goto out; } if (0 != (flags & ZBX_PROXYCONFIG_SYNC_HMACROS)) { if (SUCCEED != proxyconfig_get_table_data("hosts_templates", "hostid", ¯o_hostids, NULL, NULL, j, error)) { goto out; } if (SUCCEED != proxyconfig_get_macro_updates("hostmacro", ¯o_hostids, config_vault->db_path, &keys_paths, j, error)) { goto out; } } if (0 != (flags & ZBX_PROXYCONFIG_SYNC_DRULES) && SUCCEED != proxyconfig_get_drules_data(proxy, j, error)) { goto out; } if (0 != (flags & ZBX_PROXYCONFIG_SYNC_EXPRESSIONS) && SUCCEED != proxyconfig_get_expression_data(j, error)) { goto out; } if (0 != (flags & ZBX_PROXYCONFIG_SYNC_CONFIG) && SUCCEED != proxyconfig_get_table_data("config", NULL, NULL, NULL, NULL, j, error)) { goto out; } if (0 != (flags & ZBX_PROXYCONFIG_SYNC_HTTPTESTS) && SUCCEED != proxyconfig_get_httptest_data(&httptestids, j, error)) { goto out; } if (0 != (flags & ZBX_PROXYCONFIG_SYNC_AUTOREG) && SUCCEED != proxyconfig_get_table_data("config_autoreg_tls", NULL, NULL, NULL, NULL, j, error)) { goto out; } } zbx_json_close(j); if (0 != removed_hostids.values_num) { zbx_json_addarray(j, ZBX_PROTO_TAG_REMOVED_HOSTIDS); for (i = 0; i < removed_hostids.values_num; i++) zbx_json_adduint64(j, NULL, removed_hostids.values[i]); zbx_json_close(j); } if (0 != del_macro_hostids.values_num) { zbx_json_addarray(j, ZBX_PROTO_TAG_REMOVED_MACRO_HOSTIDS); for (i = 0; i < del_macro_hostids.values_num; i++) zbx_json_adduint64(j, NULL, del_macro_hostids.values[i]); zbx_json_close(j); } if (0 != keys_paths.values_num) get_macro_secrets(&keys_paths, j, config_vault); if (0 == flags && 0 == removed_hostids.values_num && 0 == del_macro_hostids.values_num) *status = ZBX_PROXYCONFIG_STATUS_EMPTY; else *status = ZBX_PROXYCONFIG_STATUS_DATA; ret = SUCCEED; out: if (0 != flags) zbx_db_commit(); zbx_vector_ptr_clear_ext(&keys_paths, key_path_free); zbx_vector_ptr_destroy(&keys_paths); zbx_vector_uint64_destroy(&httptestids); zbx_vector_uint64_destroy(¯o_hostids); zbx_vector_uint64_destroy(&del_macro_hostids); zbx_vector_uint64_destroy(&removed_hostids); zbx_vector_uint64_destroy(&updated_hostids); zbx_vector_uint64_destroy(&hostids); return ret; #undef ZBX_PROXYCONFIG_SYNC_HOSTS #undef ZBX_PROXYCONFIG_SYNC_GMACROS #undef ZBX_PROXYCONFIG_SYNC_HMACROS #undef ZBX_PROXYCONFIG_SYNC_DRULES #undef ZBX_PROXYCONFIG_SYNC_EXPRESSIONS #undef ZBX_PROXYCONFIG_SYNC_CONFIG #undef ZBX_PROXYCONFIG_SYNC_HTTPTESTS #undef ZBX_PROXYCONFIG_SYNC_AUTOREG } /****************************************************************************** * * * Purpose: prepare proxy configuration data * * * ******************************************************************************/ int zbx_proxyconfig_get_data(DC_PROXY *proxy, const struct zbx_json_parse *jp_request, struct zbx_json *j, zbx_proxyconfig_status_t *status, const zbx_config_vault_t *config_vault, char **error) { int ret = FAIL; char token[ZBX_SESSION_TOKEN_SIZE + 1], tmp[ZBX_MAX_UINT64_LEN + 1]; zbx_uint64_t proxy_config_revision; zbx_dc_revision_t dc_revision; zabbix_log(LOG_LEVEL_DEBUG, "In %s() proxy_hostid:" ZBX_FS_UI64, __func__, proxy->hostid); if (SUCCEED != zbx_json_value_by_name(jp_request, ZBX_PROTO_TAG_SESSION, token, sizeof(token), NULL)) { *error = zbx_strdup(NULL, "cannot get session from proxy configuration request"); goto out; } if (SUCCEED != zbx_json_value_by_name(jp_request, ZBX_PROTO_TAG_CONFIG_REVISION, tmp, sizeof(tmp), NULL)) { *error = zbx_strdup(NULL, "cannot get revision from proxy configuration request"); goto out; } if (SUCCEED != zbx_is_uint64(tmp, &proxy_config_revision)) { *error = zbx_dsprintf(NULL, "invalid proxy configuration revision: %s", tmp); goto out; } if (0 != zbx_dc_register_config_session(proxy->hostid, token, proxy_config_revision, &dc_revision) || 0 == proxy_config_revision) { zabbix_log(LOG_LEVEL_DEBUG, "%s() forcing full proxy configuration sync", __func__); proxy_config_revision = 0; zbx_json_addint64(j, ZBX_PROTO_TAG_FULL_SYNC, 1); } else { zabbix_log(LOG_LEVEL_DEBUG, "%s() updating proxy configuration " ZBX_FS_UI64 "->" ZBX_FS_UI64, __func__, proxy_config_revision, dc_revision.config); } if (proxy_config_revision != dc_revision.config) { if (SUCCEED != (ret = proxyconfig_get_tables(proxy, proxy_config_revision, &dc_revision, j, status, config_vault, error))) { goto out; } zbx_json_adduint64(j, ZBX_PROTO_TAG_CONFIG_REVISION, dc_revision.config); zabbix_log(LOG_LEVEL_TRACE, "%s() configuration: %s", __func__, j->buffer); } else { *status = ZBX_PROXYCONFIG_STATUS_EMPTY; ret = SUCCEED; } out: zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); return ret; } /****************************************************************************** * * * Purpose: send configuration tables to the proxy from server * * (for active proxies) * * * ******************************************************************************/ void zbx_send_proxyconfig(zbx_socket_t *sock, const struct zbx_json_parse *jp, const zbx_config_vault_t *config_vault, int config_timeout) { char *error = NULL, *buffer = NULL, *version_str = NULL; struct zbx_json j; DC_PROXY proxy; int ret, flags = ZBX_TCP_PROTOCOL, loglevel, version_int; size_t buffer_size, reserved = 0; zbx_proxyconfig_status_t status; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); if (SUCCEED != zbx_get_active_proxy_from_request(jp, &proxy, &error)) { zabbix_log(LOG_LEVEL_WARNING, "cannot parse proxy configuration data request from active proxy at" " \"%s\": %s", sock->peer, error); goto out; } if (SUCCEED != zbx_proxy_check_permissions(&proxy, sock, &error)) { zabbix_log(LOG_LEVEL_WARNING, "cannot accept connection from proxy \"%s\" at \"%s\", allowed address:" " \"%s\": %s", proxy.host, sock->peer, proxy.proxy_address, error); goto out; } version_str = zbx_get_proxy_protocol_version_str(jp); version_int = zbx_get_proxy_protocol_version_int(version_str); zbx_update_proxy_data(&proxy, version_str, version_int, (int)time(NULL), (0 != (sock->protocol & ZBX_TCP_COMPRESS) ? 1 : 0), ZBX_FLAGS_PROXY_DIFF_UPDATE_CONFIG); if (0 != proxy.auto_compress) flags |= ZBX_TCP_COMPRESS; if (ZBX_PROXY_VERSION_CURRENT != proxy.compatibility) { error = zbx_strdup(error, "proxy and server major versions do not match"); (void)zbx_send_response_ext(sock, NOTSUPPORTED, error, ZABBIX_VERSION, flags, config_timeout); zabbix_log(LOG_LEVEL_WARNING, "configuration update is disabled for this version of proxy \"%s\" at" " \"%s\": %s", proxy.host, sock->peer, error); goto out; } zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN); if (SUCCEED != zbx_proxyconfig_get_data(&proxy, jp, &j, &status, config_vault, &error)) { (void)zbx_send_response_ext(sock, FAIL, error, NULL, flags, config_timeout); zabbix_log(LOG_LEVEL_WARNING, "cannot collect configuration data for proxy \"%s\" at \"%s\": %s", proxy.host, sock->peer, error); goto clean; } loglevel = (ZBX_PROXYCONFIG_STATUS_DATA == status ? LOG_LEVEL_WARNING : LOG_LEVEL_DEBUG); if (0 != proxy.auto_compress) { if (SUCCEED != zbx_compress(j.buffer, j.buffer_size, &buffer, &buffer_size)) { zabbix_log(LOG_LEVEL_ERR,"cannot compress data: %s", zbx_compress_strerror()); goto clean; } reserved = j.buffer_size; zbx_json_free(&j); /* json buffer can be large, free as fast as possible */ zabbix_log(loglevel, "sending configuration data to proxy \"%s\" at \"%s\", datalen " ZBX_FS_SIZE_T ", bytes " ZBX_FS_SIZE_T " with compression ratio %.1f", proxy.host, sock->peer, (zbx_fs_size_t)reserved, (zbx_fs_size_t)buffer_size, (double)reserved / (double)buffer_size); ret = zbx_tcp_send_ext(sock, buffer, buffer_size, reserved, (unsigned char)flags, CONFIG_TRAPPER_TIMEOUT); } else { zabbix_log(loglevel, "sending configuration data to proxy \"%s\" at \"%s\", datalen " ZBX_FS_SIZE_T, proxy.host, sock->peer, (zbx_fs_size_t)j.buffer_size); ret = zbx_tcp_send_ext(sock, j.buffer, strlen(j.buffer), 0, (unsigned char)flags, CONFIG_TRAPPER_TIMEOUT); } if (SUCCEED != ret) { zabbix_log(LOG_LEVEL_WARNING, "cannot send configuration data to proxy \"%s\" at \"%s\": %s", proxy.host, sock->peer, zbx_socket_strerror()); } clean: zbx_json_free(&j); out: zbx_free(error); zbx_free(buffer); zbx_free(version_str); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); }