From: Vsevolod Stakhov Date: Thu, 5 Nov 2015 13:49:25 +0000 (+0300) Subject: Add parsing logic for log_format X-Git-Tag: 1.1.0~637 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ac7156007316c0b9f3ed061dcd9e3f3c51bb69c1;p=rspamd.git Add parsing logic for log_format --- diff --git a/src/libserver/cfg_utils.c b/src/libserver/cfg_utils.c index bad00db8a..fe7237080 100644 --- a/src/libserver/cfg_utils.c +++ b/src/libserver/cfg_utils.c @@ -280,42 +280,263 @@ rspamd_config_parse_flag (const gchar *str, guint len) return -1; } -gboolean -rspamd_config_calculate_checksum (struct rspamd_config *cfg) +static gboolean +rspamd_config_process_var (struct rspamd_config *cfg, const rspamd_ftok_t *var, + const rspamd_ftok_t *content) { - gint fd; - void *map; - struct stat st; - - /* Compute checksum for config file that should be used by xml dumper */ - if ((fd = open (cfg->cfg_name, O_RDONLY)) == -1) { - msg_err_config ( - "config file %s is no longer available, cannot calculate checksum"); - return FALSE; + guint flags = RSPAMD_LOG_FLAG_DEFAULT; + struct rspamd_log_format *lf; + enum rspamd_log_format_type type; + rspamd_ftok_t tok; + gint id; + + g_assert (var != NULL); + + if (var->len > 3 && rspamd_lc_cmp (var->begin, "if_", 3) == 0) { + flags |= RSPAMD_LOG_FLAG_CONDITION; + tok.begin = var->begin + 3; + tok.len = var->len - 3; + } + else { + tok.begin = var->begin; + tok.len = var->len; + } + + /* Now compare variable and check what we have */ + /* + RSPAMD_LOG_MID, + RSPAMD_LOG_QID, + RSPAMD_LOG_USER, + RSPAMD_LOG_ISSPAM, + RSPAMD_LOG_ACTION, + RSPAMD_LOG_SCORES, + RSPAMD_LOG_SYMBOLS, + RSPAMD_LOG_IP, + RSPAMD_LOG_DNS_REQ, + RSPAMD_LOG_SMTP_FROM, + RSPAMD_LOG_MIME_FROM, + RSPAMD_LOG_TIME_REAL, + RSPAMD_LOG_TIME_VIRTUAL, + RSPAMD_LOG_LUA + */ + if (rspamd_ftok_cstr_equal (&tok, "mid", TRUE)) { + type = RSPAMD_LOG_MID; + } + else if (rspamd_ftok_cstr_equal (&tok, "qid", TRUE)) { + type = RSPAMD_LOG_QID; + } + else if (rspamd_ftok_cstr_equal (&tok, "user", TRUE)) { + type = RSPAMD_LOG_USER; + } + else if (rspamd_ftok_cstr_equal (&tok, "isspam", TRUE)) { + type = RSPAMD_LOG_ISSPAM; + } + else if (rspamd_ftok_cstr_equal (&tok, "action", TRUE)) { + type = RSPAMD_LOG_ACTION; } - if (stat (cfg->cfg_name, &st) == -1) { - msg_err_config ("cannot stat %s: %s", cfg->cfg_name, strerror (errno)); - close (fd); + else if (rspamd_ftok_cstr_equal (&tok, "scores", TRUE)) { + type = RSPAMD_LOG_SCORES; + } + else if (rspamd_ftok_cstr_equal (&tok, "symbols", TRUE)) { + type = RSPAMD_LOG_SYMBOLS; + } + else if (rspamd_ftok_cstr_equal (&tok, "ip", TRUE)) { + type = RSPAMD_LOG_IP; + } + else if (rspamd_ftok_cstr_equal (&tok, "dns_req", TRUE)) { + type = RSPAMD_LOG_DNS_REQ; + } + else if (rspamd_ftok_cstr_equal (&tok, "smtp_from", TRUE)) { + type = RSPAMD_LOG_SMTP_FROM; + } + else if (rspamd_ftok_cstr_equal (&tok, "mime_from", TRUE)) { + type = RSPAMD_LOG_MIME_FROM; + } + else if (rspamd_ftok_cstr_equal (&tok, "time_real", TRUE)) { + type = RSPAMD_LOG_TIME_REAL; + } + else if (rspamd_ftok_cstr_equal (&tok, "time_virtual", TRUE)) { + type = RSPAMD_LOG_TIME_VIRTUAL; + } + else if (rspamd_ftok_cstr_equal (&tok, "lua", TRUE)) { + type = RSPAMD_LOG_LUA; + } + else { + msg_err_config ("unknown log variable: %T", &tok); return FALSE; } - /* Now mmap this file to simplify reading process */ - if ((map = - mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { - msg_err_config ("cannot mmap %s: %s", cfg->cfg_name, strerror (errno)); - close (fd); + lf = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*lf)); + lf->type = type; + lf->flags = flags; + + if (type != RSPAMD_LOG_LUA) { + if (content && content->len > 0) { + lf->data = rspamd_mempool_alloc0 (cfg->cfg_pool, + sizeof (rspamd_ftok_t)); + memcpy (lf->data, &tok, sizeof (tok)); + } + } + else { + /* Load lua code and ensure that we have function ref returned */ + if (!content || content->len == 0) { + msg_err_config ("lua variable needs content: %T", &tok); + return FALSE; + } + + if (luaL_loadbuffer (cfg->lua_state, content->begin, content->len, + "lua log variable") != 0) { + msg_err_config ("error loading lua code: '%T': %s", content, + lua_tostring (cfg->lua_state, -1)); + return FALSE; + } + if (lua_pcall (cfg->lua_state, 0, 1, 0) != 0) { + msg_err_config ("error executing lua code: '%T': %s", content, + lua_tostring (cfg->lua_state, -1)); + return FALSE; + } + + if (lua_type (cfg->lua_state, -1) != LUA_TFUNCTION) { + msg_err_config ("lua variable should return function: %T", content); + lua_pop (cfg->lua_state, 1); + return FALSE; + } + + id = luaL_ref (cfg->lua_state, LUA_REGISTRYINDEX); + lf->data = GINT_TO_POINTER (id); + } + + DL_APPEND (cfg->log_format, lf); + + return TRUE; +} + +static gboolean +rspamd_config_parse_log_format (struct rspamd_config *cfg) +{ + const gchar *p, *c, *end; + struct rspamd_log_format *lf = NULL; + rspamd_ftok_t var, var_content; + enum { + parse_str, + parse_dollar, + parse_var_name, + parse_var_content, + } state = parse_str; + + g_assert (cfg != NULL); + c = cfg->log_format_str; + + if (c == NULL) { return FALSE; } - close (fd); - /* Get checksum for a file */ - cfg->checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, - map, - st.st_size); - munmap (map, st.st_size); + p = c; + end = p + strlen (p); + + while (p < end) { + switch (state) { + case parse_str: + if (*p == '$') { + state = parse_dollar; + } + else { + p ++; + } + break; + case parse_dollar: + if (p > c) { + /* We have string element that we need to store */ + lf = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*lf)); + lf->type = RSPAMD_LOG_STRING; + lf->data = rspamd_mempool_alloc (cfg->cfg_pool, p - c + 1); + rspamd_strlcpy (lf->data, c, p - c + 1); + DL_APPEND (cfg->log_format, lf); + lf = NULL; + } + p++; + c = p; + state = parse_var_name; + break; + case parse_var_name: + if (*p == '{') { + var.begin = c; + var.len = p - c; + p ++; + c = p; + state = parse_var_content; + } + else if (g_ascii_isspace (*p)) { + /* Variable with no content */ + var.begin = c; + var.len = p - c; + p++; + c = p; + + if (!rspamd_config_process_var (cfg, &var, NULL)) { + return FALSE; + } + + state = parse_str; + } + else { + p++; + } + break; + case parse_var_content: + if (*p == '}') { + var_content.begin = c; + var_content.len = p - c; + p ++; + c = p; + + if (!rspamd_config_process_var (cfg, &var, &var_content)) { + return FALSE; + } + + state = parse_str; + } + else { + p++; + } + break; + } + } + + /* Last state */ + switch (state) { + case parse_str: + if (p > c) { + /* We have string element that we need to store */ + lf = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*lf)); + lf->type = RSPAMD_LOG_STRING; + lf->data = rspamd_mempool_alloc (cfg->cfg_pool, p - c + 1); + rspamd_strlcpy (lf->data, c, p - c + 1); + DL_APPEND (cfg->log_format, lf); + lf = NULL; + } + break; + + case parse_var_name: + var.begin = c; + var.len = p - c; + + if (!rspamd_config_process_var (cfg, &var, NULL)) { + return FALSE; + } + break; + case parse_dollar: + case parse_var_content: + msg_err_config ("cannot parse log format %s: incomplete string", + cfg->log_format_str); + return FALSE; + break; + } return TRUE; } + + /* * Perform post load actions */ @@ -385,6 +606,11 @@ rspamd_config_post_load (struct rspamd_config *cfg) /* Insert classifiers symbols */ (void)rspamd_config_insert_classify_symbols (cfg); + + /* Parse format string that we have */ + if (!rspamd_config_parse_log_format (cfg)) { + msg_err_config ("cannot parse log format, task logging will not be available"); + } } #if 0