diff options
-rw-r--r-- | lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/dynamic_cfg.c | 199 |
2 files changed, 191 insertions, 9 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d6d45fc91..0c05477b8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -30,6 +30,7 @@ SET(LIBRSPAMDSERVERSRC ../src/cfg_xml.c ../src/dkim.c ../src/dns.c + ../src/dynamic_cfg.c ../src/events.c ../src/html.c ../src/kvstorage.c diff --git a/src/dynamic_cfg.c b/src/dynamic_cfg.c index 6f868271d..4f4167887 100644 --- a/src/dynamic_cfg.c +++ b/src/dynamic_cfg.c @@ -23,10 +23,25 @@ #include "config.h" #include "main.h" +#include "map.h" +#include "filter.h" #include "dynamic_cfg.h" +#include "json/jansson.h" -struct dynamic_cfg { +struct dynamic_cfg_symbol { + gchar *name; + gdouble value; +}; + +struct dynamic_cfg_action { + enum rspamd_metric_action action; + gdouble value; +}; +struct dynamic_cfg_metric { + GList *symbols; + GList *actions; + gchar *name; }; struct config_json_buf { @@ -34,18 +49,111 @@ struct config_json_buf { gchar *pos; size_t buflen; struct config_file *cfg; + GList *config_metrics; }; +/** + * Free dynamic configuration + * @param conf_metrics + */ +static void +dynamic_cfg_free (GList *conf_metrics) +{ + GList *cur, *cur_elt; + struct dynamic_cfg_metric *metric; + struct dynamic_cfg_symbol *sym; + struct dynamic_cfg_action *act; + + if (conf_metrics) { + cur = conf_metrics; + while (cur) { + metric = cur->data; + if (metric->symbols) { + cur_elt = metric->symbols; + while (cur_elt) { + sym = cur_elt->data; + g_free (sym->name); + g_slice_free1 (sizeof (struct dynamic_cfg_symbol), sym); + cur_elt = g_list_next (cur_elt); + } + g_list_free (metric->symbols); + } + + if (metric->actions) { + cur_elt = metric->actions; + while (cur_elt) { + act = cur_elt->data; + g_slice_free1 (sizeof (struct dynamic_cfg_symbol), act); + cur_elt = g_list_next (cur_elt); + } + g_list_free (metric->actions); + } + g_slice_free1 (sizeof (struct dynamic_cfg_metric), metric); + cur = g_list_next (cur); + } + g_list_free (conf_metrics); + } +} +/** + * Apply configuration to the specified configuration + * @param conf_metrics + * @param cfg + */ +static void +apply_dynamic_conf (GList *conf_metrics, struct config_file *cfg) +{ + GList *cur, *cur_elt, *tmp; + struct dynamic_cfg_metric *metric; + struct dynamic_cfg_symbol *sym; + struct dynamic_cfg_action *act; + struct metric *real_metric; + struct metric_action *real_act; + gdouble *w; + + cur = conf_metrics; + while (cur) { + metric = cur->data; + if ((real_metric = g_hash_table_lookup (cfg->metrics, metric->name)) != NULL) { + cur_elt = metric->symbols; + while (cur_elt) { + sym = cur_elt->data; + if ((w = g_hash_table_lookup (real_metric->symbols, sym->name)) != NULL) { + *w = sym->value; + } + else { + msg_info ("symbol %s is not found in the main configuration", sym->name); + } + cur_elt = g_list_next (cur_elt); + } + + cur_elt = metric->actions; + while (cur_elt) { + act = cur_elt->data; + tmp = real_metric->actions; + while (tmp) { + real_act = tmp->data; + if (real_act->action == act->action) { + real_act->score = act->value; + } + tmp = g_list_next (tmp); + } + cur_elt = g_list_next (cur_elt); + } + } + cur = g_list_next (cur); + } +} + /* Callbacks for reading json dynamic rules */ gchar * json_config_read_cb (memory_pool_t * pool, gchar * chunk, gint len, struct map_cb_data *data) { - struct regexp_json_buf *jb; + struct config_json_buf *jb; gint free, off; if (data->cur_data == NULL) { - jb = g_malloc (sizeof (struct regexp_json_buf)); - jb->cfg = ((struct regexp_json_buf *)data->prev_data)->cfg; + jb = g_malloc (sizeof (struct config_json_buf)); + jb->cfg = ((struct config_json_buf *)data->prev_data)->cfg; jb->buf = NULL; jb->pos = NULL; data->cur_data = jb; @@ -81,9 +189,13 @@ void json_config_fin_cb (memory_pool_t * pool, struct map_cb_data *data) { struct config_json_buf *jb; - guint nelts, i, j; + guint nelts, i, j, selts; + gint test_act; json_t *js, *cur_elt, *cur_nm, *it_val; json_error_t je; + struct dynamic_cfg_metric *cur_metric; + struct dynamic_cfg_symbol *cur_symbol; + struct dynamic_cfg_action *cur_action; if (data->prev_data) { jb = data->prev_data; @@ -120,6 +232,76 @@ json_config_fin_cb (memory_pool_t * pool, struct map_cb_data *data) msg_err ("loaded json is not an array"); return; } + + dynamic_cfg_free (jb->config_metrics); + jb->config_metrics = NULL; + + /* Parse configuration */ + nelts = json_array_size (js); + for (i = 0; i < nelts; i++) { + cur_elt = json_array_get (js, i); + if (!cur_elt || !json_is_object (cur_elt)) { + msg_err ("loaded json array element is not an object"); + continue; + } + + cur_nm = json_object_get (cur_elt, "name"); + if (!cur_nm || !json_is_string (cur_nm)) { + msg_err ("loaded json array element has no 'name' attribute"); + continue; + } + cur_metric = g_slice_alloc0 (sizeof (struct dynamic_cfg_metric)); + cur_metric->name = g_strdup (json_string_value (cur_nm)); + cur_nm = json_object_get (cur_elt, "symbols"); + /* Parse symbols */ + if (cur_nm && json_is_array (cur_nm)) { + selts = json_array_size (cur_nm); + for (j = 0; j < selts; j ++) { + it_val = json_array_get (cur_nm, j); + if (it_val && json_is_object (it_val)) { + if (json_object_get (it_val, "name") && json_object_get (it_val, "value")) { + cur_symbol = g_slice_alloc0 (sizeof (struct dynamic_cfg_symbol)); + cur_symbol->name = g_strdup (json_string_value (json_object_get (it_val, "name"))); + cur_symbol->value = json_number_value (json_object_get (it_val, "value")); + /* Insert symbol */ + cur_metric->symbols = g_list_prepend (cur_metric->symbols, cur_symbol); + } + } + } + } + cur_nm = json_object_get (cur_elt, "actions"); + /* Parse actions */ + if (cur_nm && json_is_array (cur_nm)) { + selts = json_array_size (cur_nm); + for (j = 0; j < selts; j ++) { + it_val = json_array_get (cur_nm, j); + if (it_val && json_is_object (it_val)) { + if (json_object_get (it_val, "name") && json_object_get (it_val, "value")) { + + cur_action = g_slice_alloc0 (sizeof (struct dynamic_cfg_action)); + if (!check_action_str (json_string_value (json_object_get (it_val, "name")), &test_act)) { + msg_err ("unknown action: %s", json_string_value (json_object_get (it_val, "name"))); + g_slice_free1 (sizeof (struct dynamic_cfg_action), cur_action); + continue; + } + cur_action->action = test_act; + cur_action->value = json_number_value (json_object_get (it_val, "value")); + /* Insert action */ + cur_metric->actions = g_list_prepend (cur_metric->actions, cur_action); + } + } + } + } + jb->config_metrics = g_list_prepend (jb->config_metrics, cur_metric); + } + /* + * Note about thread safety: we are updating values that are gdoubles so it is not atomic in general case + * but on the other hand all that data is used only in the main thread, so why it is *likely* safe + * to do this task in this way without explicit lock. + */ + apply_dynamic_conf (jb->config_metrics, jb->cfg); + + json_decref (js); } /** @@ -129,7 +311,6 @@ json_config_fin_cb (memory_pool_t * pool, struct map_cb_data *data) void init_dynamic_config (struct config_file *cfg) { - gint fd; struct stat st; struct config_json_buf *jb, **pjb; @@ -148,12 +329,12 @@ init_dynamic_config (struct config_file *cfg) } /* Now try to add map with json data */ - jb = g_malloc (sizeof (struct regexp_json_buf)); - pjb = g_malloc (sizeof (struct regexp_json_buf *)); + jb = g_malloc (sizeof (struct config_json_buf)); + pjb = g_malloc (sizeof (struct config_json_buf *)); jb->buf = NULL; jb->cfg = cfg; *pjb = jb; - if (!add_map (cfg->dynamic_conf, json_config_read_cb, json_config_fin_cb, (void **)pjb)) { + if (!add_map (cfg, cfg->dynamic_conf, json_config_read_cb, json_config_fin_cb, (void **)pjb)) { msg_err ("cannot add map for configuration %s", cfg->dynamic_conf); } } |