From 58daab21cd043e1dc37024a7f99b396788372918 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 9 Mar 2024 14:19:48 +0100 Subject: Merging upstream version 1.44.3. Signed-off-by: Daniel Baumann --- fluent-bit/src/flb_config_map.c | 817 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 817 insertions(+) create mode 100644 fluent-bit/src/flb_config_map.c (limited to 'fluent-bit/src/flb_config_map.c') diff --git a/fluent-bit/src/flb_config_map.c b/fluent-bit/src/flb_config_map.c new file mode 100644 index 000000000..9a3dd7ac4 --- /dev/null +++ b/fluent-bit/src/flb_config_map.c @@ -0,0 +1,817 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2022 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int check_list_size(struct mk_list *list, int type) +{ + int len; + + len = mk_list_size(list); + if (type == FLB_CONFIG_MAP_SLIST_1 || type == FLB_CONFIG_MAP_CLIST_1) { + if (len < 1) { + return -1; + } + } + else if (type == FLB_CONFIG_MAP_SLIST_2 || type == FLB_CONFIG_MAP_CLIST_2) { + if (len < 2) { + return -1; + } + } + else if (type == FLB_CONFIG_MAP_SLIST_3 || type == FLB_CONFIG_MAP_CLIST_3) { + if (len < 3) { + return -1; + } + } + else if (type == FLB_CONFIG_MAP_SLIST_4 || type == FLB_CONFIG_MAP_CLIST_4) { + if (len < 4) { + return -1; + } + } + + return 0; +} + +/* + * Given a string, split the content using it proper separator generating a linked + * list of 'slist' + */ +static struct mk_list *parse_string_map_to_list(struct flb_config_map *map, char *str) +{ + int ret = -1; + int type; + int max_split = -1; + struct mk_list *list; + + type = map->type; + + /* Allocate list head */ + list = flb_malloc(sizeof(struct mk_list)); + if (!list) { + flb_errno(); + return NULL; + } + mk_list_init(list); + + /* Determinate the max split value based on it type */ + if (map->type > FLB_CONFIG_MAP_CLIST && map->type < FLB_CONFIG_MAP_SLIST) { + type = FLB_CONFIG_MAP_CLIST; + max_split = (map->type - FLB_CONFIG_MAP_CLIST); + } + else if (map->type > FLB_CONFIG_MAP_SLIST) { + type = FLB_CONFIG_MAP_SLIST; + max_split = (map->type - FLB_CONFIG_MAP_SLIST); + } + + if (type == FLB_CONFIG_MAP_CLIST) { + ret = flb_slist_split_string(list, str, ',', max_split); + } + else if (type == FLB_CONFIG_MAP_SLIST) { + ret = flb_slist_split_tokens(list, str, max_split); + } + + if (ret == -1) { + flb_error("[config map] error reading list of options"); + flb_free(list); + return NULL; + } + + return list; +} + +static int translate_default_value(struct flb_config_map *map, char *val) +{ + int ret; + struct flb_config_map_val *entry = NULL; + struct mk_list *list = NULL; + + /* Prepare contexts if the map allows multiple entries */ + if (map->flags & FLB_CONFIG_MAP_MULT) { + entry = flb_calloc(1, sizeof(struct flb_config_map_val)); + if (!entry) { + flb_errno(); + /* + * do not worry about 'list' allocation, it will be destroyed by the caller + * when it catches this error + */ + return -1; + } + } + else { + entry = &map->value; + } + + /* Based on specific data types, populate 'value' */ + if (map->type == FLB_CONFIG_MAP_STR) { + /* Duplicate string as a flb_sds_t */ + entry->val.str = flb_sds_create(val); + + /* Validate new memory allocation */ + if (!entry->val.str) { + goto error; + } + } + else if (map->type == FLB_CONFIG_MAP_STR_PREFIX) { + /* + * For prefixed string types we don't process them, just validate + * that no default value has been set. + */ + if (val) { + flb_error("[config map] invalid default value for prefixed string '%s'", + map->name); + goto error; + } + } + else if (map->type == FLB_CONFIG_MAP_BOOL) { + ret = flb_utils_bool(val); + if (ret == -1) { + flb_error("[config map] invalid default value for boolean '%s=%s'", + map->name, val); + goto error; + } + entry->val.boolean = flb_utils_bool(val); + } + else if (map->type == FLB_CONFIG_MAP_INT) { + entry->val.i_num = atoi(val); + } + else if (map->type == FLB_CONFIG_MAP_DOUBLE) { + entry->val.d_num = atof(val); + } + else if (map->type == FLB_CONFIG_MAP_SIZE) { + entry->val.s_num = flb_utils_size_to_bytes(val); + } + else if (map->type == FLB_CONFIG_MAP_TIME) { + entry->val.i_num = flb_utils_time_to_seconds(val); + } + else if (map->type >= FLB_CONFIG_MAP_CLIST && + map->type <= FLB_CONFIG_MAP_SLIST_4) { + + list = parse_string_map_to_list(map, val); + if (!list) { + flb_error("[config map] cannot parse list of values '%s'", val); + goto error; + } + + entry->val.list = list; + list = NULL; + } + + if (map->flags & FLB_CONFIG_MAP_MULT) { + mk_list_add(&entry->_head, map->value.mult); + } + + return 0; + + error: + if (map->flags & FLB_CONFIG_MAP_MULT) { + flb_free(entry); + } + return -1; +} + +static flb_sds_t helper_map_options(struct mk_list *map) +{ + flb_sds_t buf; + flb_sds_t tmp; + struct mk_list *head; + struct flb_config_map *m; + + buf = flb_sds_create_size(256); + if (!buf) { + flb_errno(); + return NULL; + } + + tmp = flb_sds_printf(&buf, "The following properties are allowed: "); + if (!tmp) { + flb_errno(); + flb_sds_destroy(buf); + return NULL; + } + buf = tmp; + + mk_list_foreach(head, map) { + m = mk_list_entry(head, struct flb_config_map, _head); + if (head->next != map) { + tmp = flb_sds_printf(&buf, "%s, ", m->name); + } + else { + if (mk_list_size(map) == 1) { + tmp = flb_sds_printf(&buf, "%s.", m->name); + } + else { + tmp = flb_sds_printf(&buf, "and %s.", m->name); + } + } + + if (!tmp) { + flb_errno(); + flb_sds_destroy(buf); + return NULL; + } + buf = tmp; + } + + return buf; +} + +/* + * Given a static plugin configuration map, create a linked list representation. We use a + * linked list using heap memory instead of the stack since a plugin can be loaded multiple + * times. + * + * In addition, for default values, we process them and populate the 'value' field with + * proper data types. + */ +struct mk_list *flb_config_map_create(struct flb_config *config, + struct flb_config_map *map) +{ + int ret; + flb_sds_t env; + struct mk_list *tmp; + struct mk_list *list; + struct flb_config_map *new = NULL; + struct flb_config_map *m; + + list = flb_malloc(sizeof(struct mk_list)); + if (!list) { + flb_errno(); + return NULL; + } + mk_list_init(list); + + /* + * Read every property defined in the config map and create a new dynamic list + * with the same content. + * + * As an additional step, it populate the 'value' field using the given default + * value if any. Note that default values are strings so they are processed + * to fit into the proper data type of 'value'. + */ + m = map; + while (m && m->name) { + /* Allocate map node */ + new = flb_calloc(1, sizeof(struct flb_config_map)); + if (!new) { + flb_errno(); + flb_config_map_destroy(list); + return NULL; + } + + new->type = m->type; + new->name = flb_sds_create(m->name); + if (new->name == NULL) { + flb_free(new); + flb_config_map_destroy(list); + return NULL; + } + + /* Translate default value */ + if (m->def_value) { + /* + * Before to translate any value, make sure to disable the warning + * about unused variables. This might happen if a default value is an + * environment variable and the user is not using it (which is ok for + * that specific use case). + */ + flb_env_warn_unused(config->env, FLB_FALSE); + + /* Translate the value */ + env = flb_env_var_translate(config->env, m->def_value); + if (env == NULL) { + flb_errno(); + flb_sds_destroy(new->name); + flb_free(new); + flb_config_map_destroy(list); + return NULL; + } + new->def_value = env; + flb_env_warn_unused(config->env, FLB_TRUE); + } + + new->flags = m->flags; + new->set_property = m->set_property; + new->offset = m->offset; + new->value.mult = NULL; + new->desc = m->desc; + mk_list_add(&new->_head, list); + + if (new->set_property == FLB_FALSE) { + m++; + continue; + } + + /* If this is a multiple type of entries, initialize the main list */ + if (new->flags & FLB_CONFIG_MAP_MULT) { + tmp = flb_malloc(sizeof(struct mk_list)); + if (!tmp) { + flb_errno(); + flb_config_map_destroy(list); + return NULL; + } + mk_list_init(tmp); + new->value.mult = tmp; + } + + /* + * If there is no default value or the entry will not be set, just + * continue with the next map entry + */ + if (!m->def_value) { + m++; + continue; + } + + /* Assign value based on data type and multiple mode if set */ + ret = translate_default_value(new, new->def_value); + if (ret == -1) { + flb_config_map_destroy(list); + return NULL; + } + m++; + } + + return list; +} + +static void destroy_map_val(int type, struct flb_config_map_val *value) +{ + if (type == FLB_CONFIG_MAP_STR && value->val.str) { + flb_sds_destroy(value->val.str); + } + else if ((type >= FLB_CONFIG_MAP_CLIST && + type <= FLB_CONFIG_MAP_SLIST_4) && + value->val.list) { + flb_slist_destroy(value->val.list); + flb_free(value->val.list); + } +} + +/* Destroy a config map context */ +void flb_config_map_destroy(struct mk_list *list) +{ + struct mk_list *tmp; + struct mk_list *head; + struct mk_list *v_head; + struct mk_list *v_tmp; + struct flb_config_map *map; + struct flb_config_map_val *entry; + + mk_list_foreach_safe(head, tmp, list) { + map = mk_list_entry(head, struct flb_config_map, _head); + mk_list_del(&map->_head); + + if (map->flags & FLB_CONFIG_MAP_MULT && map->value.mult) { + mk_list_foreach_safe(v_head, v_tmp, map->value.mult) { + entry = mk_list_entry(v_head, struct flb_config_map_val, _head); + mk_list_del(&entry->_head); + destroy_map_val(map->type, entry); + flb_free(entry); + } + flb_free(map->value.mult); + } + else { + destroy_map_val(map->type, &map->value); + } + if (map->def_value) { + flb_sds_destroy(map->def_value); + } + flb_sds_destroy(map->name); + flb_free(map); + } + flb_free(list); +} + +/* Count the number of times a property key exists */ +int property_count(char *key, int len, struct mk_list *properties) +{ + int count = 0; + struct mk_list *head; + struct flb_kv *kv; + + mk_list_foreach(head, properties) { + kv = mk_list_entry(head, struct flb_kv, _head); + if (flb_sds_len(kv->key) != len) { + continue; + } + + if (strncmp(kv->key, key, len) == 0) { + count++; + } + } + return count; +} + +/* + * If the property starts with '_debug.', it's an internal property for + * some component of Fluent Bit, not the plugin it self. + */ +static int is_internal_debug_property(char *prop_name) +{ +#ifdef FLB_HAVE_HTTP_CLIENT_DEBUG + if (strncmp(prop_name, "_debug.http.", 12) == 0) { + return FLB_TRUE; + } +#endif + + return FLB_FALSE; +} + + +/* Validate that the incoming properties set by the caller are allowed by the plugin */ +int flb_config_map_properties_check(char *context_name, + struct mk_list *in_properties, + struct mk_list *map) +{ + int len; + int found; + int count = 0; + int ret; + flb_sds_t helper; + struct flb_kv *kv; + struct mk_list *head; + struct mk_list *m_head; + struct flb_config_map *m; + + /* Iterate all incoming property list */ + mk_list_foreach(head, in_properties) { + kv = mk_list_entry(head, struct flb_kv, _head); + found = FLB_FALSE; + + + ret = is_internal_debug_property(kv->key); + if (ret == FLB_TRUE) { + /* Skip the config map */ + continue; + } + + if (strcasecmp(kv->key, "active") == 0) { + /* Accept 'active' property ... */ + continue; + } + + /* Lookup the key into the provided map */ + mk_list_foreach(m_head, map) { + m = mk_list_entry(m_head, struct flb_config_map, _head); + + len = flb_sds_len(m->name); + if (m->type != FLB_CONFIG_MAP_STR_PREFIX) { + if (len != flb_sds_len(kv->key)) { + continue; + } + } + + if (strncasecmp(kv->key, m->name, len) == 0) { + if (m->type == FLB_CONFIG_MAP_STR_PREFIX) { + if (flb_sds_len(kv->key) <= len) { + flb_error("[config] incomplete prefixed key '%s'", kv->key); + found = FLB_FALSE; + break; + } + } + else if(m->type == FLB_CONFIG_MAP_DEPRECATED) { + flb_warn("[config] %s: '%s' is deprecated", + context_name, kv->key); + } + found = FLB_TRUE; + break; + } + } + + if (found == FLB_FALSE) { + helper = helper_map_options(map); + if (!helper) { + flb_error("[config] %s: unknown configuration property '%s'", + context_name, kv->key); + } + else { + flb_error("[config] %s: unknown configuration property '%s'. %s", + context_name, kv->key, helper); + flb_sds_destroy(helper); + } + + return -1; + } + + /* Validate number of times the property is set */ + count = property_count(kv->key, flb_sds_len(kv->key), in_properties); + if ((m->flags & FLB_CONFIG_MAP_MULT) == 0) { + if (count > 1) { + flb_error("[config] %s: configuration property '%s' is set %i times", + context_name, kv->key, count); + return -1; + } + } + } + + return 0; +} + +/* + * Returns FLB_TRUE or FLB_FALSE if a property aims to override the default value + * assigned to the map key valled 'name'. + */ +static int properties_override_default(struct mk_list *properties, char *name) +{ + int len; + struct mk_list *head; + struct flb_kv *kv; + + len = strlen(name); + + mk_list_foreach(head, properties) { + kv = mk_list_entry(head, struct flb_kv, _head); + if (flb_sds_len(kv->key) != len) { + continue; + } + + if (strcasecmp(kv->key, name) == 0) { + return FLB_TRUE; + } + } + + return FLB_FALSE; +} + +/* + * Return the number of expected values if the property type is from CLIST + * or SLIST family. + */ +int flb_config_map_expected_values(int type) +{ + if (type > FLB_CONFIG_MAP_CLIST && type < FLB_CONFIG_MAP_SLIST) { + return type - FLB_CONFIG_MAP_CLIST; + } + if (type > FLB_CONFIG_MAP_SLIST && type <= FLB_CONFIG_MAP_SLIST_4) { + return type - FLB_CONFIG_MAP_SLIST; + } + return -1; +} + + +/* + * Function used by plugins that needs to populate their context structure with the + * configuration properties already mapped. + */ +int flb_config_map_set(struct mk_list *properties, struct mk_list *map, void *context) +{ + int ret; + int len; + char *base; + char *m_bool; + int *m_i_num; + double *m_d_num; + size_t *m_s_num; + flb_sds_t *m_str; + struct flb_kv *kv; + struct mk_list *head; + struct mk_list *m_head; + struct mk_list **m_list; + struct mk_list *list; + struct flb_config_map *m = NULL; + struct flb_config_map_val *entry = NULL; + + base = context; + + /* Link 'already processed default values' into the caller context */ + mk_list_foreach(m_head, map) { + m = mk_list_entry(m_head, struct flb_config_map, _head); + + /* + * If the map type allows multiple entries, the user context is a pointer + * for a linked list. We just point their structure to our pre-processed + * list of entries. + */ + if (m->flags & FLB_CONFIG_MAP_MULT && m->set_property == FLB_TRUE) { + m_list = (struct mk_list **) (base + m->offset); + *m_list = m->value.mult; + continue; + } + + /* + * If no default value exists or the map will not write to the user + * context.. skip it. + */ + if (!m->def_value || m->set_property == FLB_FALSE) { + continue; + } + + /* + * If a property set by the user will override the default value, just + * do not put the default value into the context since it will be replaced + * later. + */ + ret = properties_override_default(properties, m->name); + if (ret == FLB_TRUE) { + continue; + } + + /* All the following steps are direct writes to the user context */ + if (m->type == FLB_CONFIG_MAP_STR) { + m_str = (char **) (base + m->offset); + *m_str = m->value.val.str; + } + else if (m->type == FLB_CONFIG_MAP_INT) { + m_i_num = (int *) (base + m->offset); + *m_i_num = m->value.val.i_num; + } + else if (m->type == FLB_CONFIG_MAP_DOUBLE) { + m_d_num = (double *) (base + m->offset); + *m_d_num = m->value.val.d_num; + } + else if (m->type == FLB_CONFIG_MAP_SIZE) { + m_s_num = (size_t *) (base + m->offset); + *m_s_num = m->value.val.s_num; + } + else if (m->type == FLB_CONFIG_MAP_TIME) { + m_i_num = (int *) (base + m->offset); + *m_i_num = m->value.val.s_num; + } + else if (m->type == FLB_CONFIG_MAP_BOOL) { + m_bool = (char *) (base + m->offset); + *m_bool = m->value.val.boolean; + } + else if (m->type >= FLB_CONFIG_MAP_CLIST || + m->type <= FLB_CONFIG_MAP_SLIST_4) { + m_list = (struct mk_list **) (base + m->offset); + *m_list = m->value.val.list; + } + } + + /* + * Iterate all properties coming from the configuration reader. If a property overrides + * a default value already set in the previous step, just link to the new value. + */ + mk_list_foreach(head, properties) { + kv = mk_list_entry(head, struct flb_kv, _head); + if (kv->val == NULL) { + continue; + } + + mk_list_foreach(m_head, map) { + m = mk_list_entry(m_head, struct flb_config_map, _head); + if (flb_sds_len(kv->key) != flb_sds_len(m->name)) { + m = NULL; + continue; + } + + if (strncasecmp(kv->key, m->name, flb_sds_len(m->name)) == 0) { + break; + } + m = NULL; + continue; + + } + + if (!m || m->set_property == FLB_FALSE) { + continue; + } + + /* Check if the map allows multiple entries */ + if (m->flags & FLB_CONFIG_MAP_MULT) { + /* Create node */ + entry = flb_calloc(1, sizeof(struct flb_config_map_val)); + if (!entry) { + flb_errno(); + return -1; + } + + /* Populate value */ + if (m->type == FLB_CONFIG_MAP_STR) { + entry->val.str = flb_sds_create(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_INT) { + entry->val.i_num = atoi(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_DOUBLE) { + entry->val.d_num = atof(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_SIZE) { + entry->val.s_num = flb_utils_size_to_bytes(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_TIME) { + entry->val.i_num = flb_utils_time_to_seconds(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_BOOL) { + ret = flb_utils_bool(kv->val); + if (ret == -1) { + flb_free(entry); + flb_error("[config map] invalid value for boolean property '%s=%s'", + m->name, kv->val); + return -1; + } + entry->val.boolean = ret; + } + else if (m->type >= FLB_CONFIG_MAP_CLIST || + m->type <= FLB_CONFIG_MAP_SLIST_4) { + + list = parse_string_map_to_list(m, kv->val); + if (!list) { + flb_error("[config map] cannot parse list of values '%s'", kv->val); + flb_free(entry); + return -1; + } + entry->val.list = list; + + /* Validate the number of entries are the minimum expected */ + len = mk_list_size(list); + ret = check_list_size(list, m->type); + if (ret == -1) { + flb_error("[config map] property '%s' expects %i values " + "(only %i were found)", + kv->key, + flb_config_map_expected_values(m->type), len); + /* + * Register the entry anyways, so on exit the resources will + * be released + */ + mk_list_add(&entry->_head, m->value.mult); + return -1; + } + } + + /* Add entry to the map 'mult' list tail */ + mk_list_add(&entry->_head, m->value.mult); + + /* Override user context */ + m_list = (struct mk_list **) (base + m->offset); + *m_list = m->value.mult; + } + else if (map != NULL) { + /* Direct write to user context */ + if (m->type == FLB_CONFIG_MAP_STR) { + m_str = (char **) (base + m->offset); + *m_str = kv->val; + } + else if (m->type == FLB_CONFIG_MAP_INT) { + m_i_num = (int *) (base + m->offset); + *m_i_num = atoi(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_DOUBLE) { + m_d_num = (double *) (base + m->offset); + *m_d_num = atof(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_BOOL) { + m_bool = (char *) (base + m->offset); + ret = flb_utils_bool(kv->val); + if (ret == -1) { + flb_error("[config map] invalid value for boolean property '%s=%s'", + m->name, kv->val); + return -1; + } + *m_bool = ret; + } + else if (m->type == FLB_CONFIG_MAP_SIZE) { + m_s_num = (size_t *) (base + m->offset); + *m_s_num = flb_utils_size_to_bytes(kv->val); + } + else if (m->type == FLB_CONFIG_MAP_TIME) { + m_i_num = (int *) (base + m->offset); + *m_i_num = flb_utils_time_to_seconds(kv->val); + } + else if (m->type >= FLB_CONFIG_MAP_CLIST || + m->type <= FLB_CONFIG_MAP_SLIST_4) { + list = parse_string_map_to_list(m, kv->val); + if (!list) { + flb_error("[config map] cannot parse list of values '%s'", kv->val); + flb_free(entry); + return -1; + } + + if (m->value.val.list) { + destroy_map_val(m->type, &m->value); + } + + m->value.val.list = list; + m_list = (struct mk_list **) (base + m->offset); + *m_list = m->value.val.list; + } + } + } + + return 0; +} -- cgit v1.2.3