123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182 |
- /*
- * Copyright (c) 2009-2012, Vsevolod Stakhov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
- #include "config.h"
-
- #include "cfg_file.h"
- #include "main.h"
- #include "filter.h"
- #include "settings.h"
- #include "classifiers/classifiers.h"
- #include "cfg_xml.h"
- #include "lua/lua_common.h"
- #include "kvstorage_config.h"
- #include "map.h"
- #include "dynamic_cfg.h"
-
- #define DEFAULT_SCORE 10.0
-
- #define DEFAULT_RLIMIT_NOFILE 2048
- #define DEFAULT_RLIMIT_MAXCORE 0
- #define DEFAULT_MAP_TIMEOUT 10
-
-
- gboolean
- parse_host_port_priority (memory_pool_t *pool, const gchar *str, gchar **addr, guint16 *port, guint *priority)
- {
- gchar **tokens, *err_str, *cur_tok;
- struct addrinfo hints, *res;
- guint port_parsed, priority_parsed, saved_errno = errno;
- gint r;
- union {
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- } addr_holder;
-
- tokens = g_strsplit_set (str, ":", 0);
- if (!tokens || !tokens[0]) {
- return FALSE;
- }
-
- /* Now try to parse host and write address to ina */
- memset (&hints, 0, sizeof (hints));
- hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
- hints.ai_socktype = SOCK_STREAM; /* Type of the socket */
- hints.ai_flags = 0;
- hints.ai_protocol = 0; /* Any protocol */
- hints.ai_canonname = NULL;
- hints.ai_addr = NULL;
- hints.ai_next = NULL;
-
- if (strcmp (tokens[0], "*") == 0) {
- /* XXX: actually we still cannot listen on multiply protocols */
- if (pool != NULL) {
- *addr = memory_pool_alloc (pool, INET_ADDRSTRLEN + 1);
- }
- rspamd_strlcpy (*addr, "0.0.0.0", INET_ADDRSTRLEN + 1);
- goto port_parse;
- }
- else {
- cur_tok = tokens[0];
- }
-
- if ((r = getaddrinfo (cur_tok, NULL, &hints, &res)) == 0) {
- memcpy (&addr_holder, res->ai_addr, MIN (sizeof (addr_holder), res->ai_addrlen));
- if (res->ai_family == AF_INET) {
- if (pool != NULL) {
- *addr = memory_pool_alloc (pool, INET_ADDRSTRLEN + 1);
- }
- inet_ntop (res->ai_family, &addr_holder.v4.sin_addr, *addr, INET_ADDRSTRLEN + 1);
- }
- else {
- if (pool != NULL) {
- *addr = memory_pool_alloc (pool, INET6_ADDRSTRLEN + 1);
- }
- inet_ntop (res->ai_family, &addr_holder.v6.sin6_addr, *addr, INET6_ADDRSTRLEN + 1);
- }
- freeaddrinfo (res);
- }
- else {
- msg_err ("address resolution for %s failed: %s", tokens[0], gai_strerror (r));
- goto err;
- }
-
- port_parse:
- if (tokens[1] != NULL) {
- /* Port part */
- if (port != NULL) {
- errno = 0;
- port_parsed = strtoul (tokens[1], &err_str, 10);
- if (*err_str != '\0' || errno != 0) {
- msg_warn ("cannot parse port: %s, at symbol %c, error: %s", tokens[1], *err_str, strerror (errno));
- goto err;
- }
- if (port_parsed > G_MAXUINT16) {
- errno = ERANGE;
- msg_warn ("cannot parse port: %s, error: %s", tokens[1], *err_str, strerror (errno));
- goto err;
- }
- *port = port_parsed;
- }
- if (priority != NULL) {
- if (port != NULL) {
- cur_tok = tokens[2];
- }
- else {
- cur_tok = tokens[1];
- }
- if (cur_tok != NULL) {
- /* Priority part */
- errno = 0;
- priority_parsed = strtoul (cur_tok, &err_str, 10);
- if (*err_str != '\0' || errno != 0) {
- msg_warn ("cannot parse priority: %s, at symbol %c, error: %s", tokens[1], *err_str, strerror (errno));
- goto err;
- }
- *priority = priority_parsed;
- }
- }
- }
-
- /* Restore errno */
- errno = saved_errno;
- g_strfreev (tokens);
- return TRUE;
-
- err:
- errno = saved_errno;
- g_strfreev (tokens);
- return FALSE;
- }
-
- gboolean
- parse_host_port (memory_pool_t *pool, const gchar *str, gchar **addr, guint16 *port)
- {
- return parse_host_port_priority (pool, str, addr, port, NULL);
- }
-
- gboolean
- parse_host_priority (memory_pool_t *pool, const gchar *str, gchar **addr, guint *priority)
- {
- return parse_host_port_priority (pool, str, addr, NULL, priority);
- }
-
- gint
- parse_bind_line (struct config_file *cfg, struct worker_conf *cf, gchar *str)
- {
- gchar **host;
- guint16 *family, *port;
- gchar **addr;
-
- if (str == NULL)
- return 0;
-
- host = &cf->bind_host;
- port = &cf->bind_port;
- *port = DEFAULT_BIND_PORT;
- family = &cf->bind_family;
- addr = &cf->bind_addr;
-
- if (str[0] == '/' || str[0] == '.') {
- #ifdef HAVE_DIRNAME
- /* Try to check path of bind credit */
- struct stat st;
- gint fd;
- gchar *copy = memory_pool_strdup (cfg->cfg_pool, str);
- if (stat (copy, &st) == -1) {
- if (errno == ENOENT) {
- if ((fd = open (str, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) {
- msg_err ("cannot open path %s for making socket, %s", str, strerror (errno));
- return 0;
- }
- else {
- close (fd);
- unlink (str);
- }
- }
- else {
- msg_err ("cannot stat path %s for making socket, %s", str, strerror (errno));
- return 0;
- }
- }
- else {
- if (unlink (str) == -1) {
- msg_err ("cannot remove path %s for making socket, %s", str, strerror (errno));
- return 0;
- }
- }
- #endif
- *host = memory_pool_strdup (cfg->cfg_pool, str);
- *family = AF_UNIX;
- return 1;
- }
- else {
- if (parse_host_port (cfg->cfg_pool, str, addr, port)) {
- *host = memory_pool_strdup (cfg->cfg_pool, str);
- *family = AF_INET;
-
- return 1;
- }
- }
-
- return 0;
- }
-
- void
- init_defaults (struct config_file *cfg)
- {
-
- cfg->memcached_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
- cfg->memcached_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
- cfg->memcached_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
- cfg->memcached_protocol = TCP_TEXT;
-
- cfg->dns_timeout = 1000;
- cfg->dns_retransmits = 5;
- /* After 20 errors do throttling for 10 seconds */
- cfg->dns_throttling_errors = 20;
- cfg->dns_throttling_time = 10000;
-
- cfg->statfile_sync_interval = 60000;
- cfg->statfile_sync_timeout = 20000;
-
- /* 20 Kb */
- cfg->max_diff = 20480;
-
- cfg->max_statfile_size = DEFAULT_STATFILE_SIZE;
- cfg->modules_opts = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->modules_metas = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->variables = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->metrics = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->c_modules = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->composite_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->classifiers_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->cfg_params = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- cfg->metrics_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
-
- cfg->map_timeout = DEFAULT_MAP_TIMEOUT;
-
- cfg->log_level = G_LOG_LEVEL_WARNING;
- cfg->log_extended = TRUE;
-
- init_settings (cfg);
-
- }
-
- void
- free_config (struct config_file *cfg)
- {
- GList *cur;
- struct symbols_group *gr;
-
- remove_all_maps (cfg);
- g_hash_table_remove_all (cfg->modules_opts);
- g_hash_table_unref (cfg->modules_opts);
- g_hash_table_unref (cfg->modules_metas);
- g_hash_table_remove_all (cfg->variables);
- g_hash_table_unref (cfg->variables);
- g_hash_table_remove_all (cfg->metrics);
- g_hash_table_unref (cfg->metrics);
- g_hash_table_remove_all (cfg->c_modules);
- g_hash_table_unref (cfg->c_modules);
- g_hash_table_remove_all (cfg->composite_symbols);
- g_hash_table_unref (cfg->composite_symbols);
- g_hash_table_remove_all (cfg->cfg_params);
- g_hash_table_unref (cfg->cfg_params);
- g_hash_table_destroy (cfg->metrics_symbols);
- g_hash_table_destroy (cfg->classifiers_symbols);
- /* Free symbols groups */
- cur = cfg->symbols_groups;
- while (cur) {
- gr = cur->data;
- if (gr->symbols) {
- g_list_free (gr->symbols);
- }
- cur = g_list_next (cur);
- }
- if (cfg->symbols_groups) {
- g_list_free (cfg->symbols_groups);
- }
-
- if (cfg->checksum) {
- g_free (cfg->checksum);
- }
- g_list_free (cfg->classifiers);
- g_list_free (cfg->metrics_list);
- memory_pool_delete (cfg->cfg_pool);
- }
-
- gchar *
- get_module_opt (struct config_file *cfg, gchar *module_name, gchar *opt_name)
- {
- GList *cur_opt;
- struct module_opt *cur;
- static gchar numbuf[64];
-
- cur_opt = g_hash_table_lookup (cfg->modules_opts, module_name);
- if (cur_opt == NULL) {
- return NULL;
- }
-
- while (cur_opt) {
- cur = cur_opt->data;
- if (strcmp (cur->param, opt_name) == 0) {
- /* Check if it is lua variable */
- if (! cur->is_lua) {
- /* Plain variable */
- return cur->value;
- }
- else {
- /* Check type */
- switch (cur->lua_type) {
- case LUA_VAR_NUM:
- /* numbuf is static, so it is safe to return it "as is" */
- snprintf (numbuf, sizeof (numbuf), "%f", *(double *)cur->actual_data);
- return numbuf;
- case LUA_VAR_BOOLEAN:
- snprintf (numbuf, sizeof (numbuf), "%s", *(gboolean *)cur->actual_data ? "yes" : "no");
- return numbuf;
- case LUA_VAR_STRING:
- return (gchar *)cur->actual_data;
- case LUA_VAR_FUNCTION:
- msg_info ("option %s is dynamic, so it cannot be aqquired statically", opt_name);
- return NULL;
- case LUA_VAR_UNKNOWN:
- msg_info ("option %s has unknown type, maybe there is error in LUA code", opt_name);
- return NULL;
- }
- }
- }
- cur_opt = g_list_next (cur_opt);
- }
-
- return NULL;
- }
-
- guint64
- parse_limit (const gchar *limit, guint len)
- {
- guint64 result = 0;
- const gchar *err_str;
-
- if (!limit || *limit == '\0' || len == 0) {
- return 0;
- }
-
- errno = 0;
- result = strtoull (limit, (gchar **)&err_str, 10);
-
- if (*err_str != '\0') {
- /* Megabytes */
- if (*err_str == 'm' || *err_str == 'M') {
- result *= 1048576L;
- }
- /* Kilobytes */
- else if (*err_str == 'k' || *err_str == 'K') {
- result *= 1024;
- }
- /* Gigabytes */
- else if (*err_str == 'g' || *err_str == 'G') {
- result *= 1073741824L;
- }
- else if (len > 0 && err_str - limit != (gint)len) {
- msg_warn ("invalid limit value '%s' at position '%s'", limit, err_str);
- result = 0;
- }
- }
-
- return result;
- }
-
- gdouble
- cfg_parse_time (const gchar *t, enum time_type default_type)
- {
- union {
- guint i;
- double d;
- } result;
- gboolean use_double = FALSE;
- gchar *err_str;
-
- if (!t || *t == '\0')
- return 0;
-
- errno = 0;
- result.i = strtoul (t, &err_str, 10);
-
- if (*err_str != '\0') {
- if (*err_str == '.') {
- /* Try to handle decimal point */
- errno = 0;
- result.d = strtod (t, &err_str);
- use_double = TRUE;
- }
- /* Seconds */
- if (*err_str == 's' || *err_str == 'S') {
- if (use_double) {
- result.d *= 1000.;
- }
- else {
- result.i *= 1000;
- }
- }
- /* Minutes */
- else if (*err_str == 'm' || *err_str == 'M') {
- /* Handle ms correctly */
- if (*(err_str + 1) != 's' && *(err_str + 1) != 'S') {
- if (use_double) {
- result.d *= 60. * 1000.;
- }
- else {
- result.i *= 60 * 1000;
- }
- }
- }
- /* Hours */
- else if (*err_str == 'h' || *err_str == 'H') {
- if (use_double) {
- result.d *= 60. * 60. * 1000.;
- }
- else {
- result.i *= 60 * 60 * 1000;
- }
- }
- /* Days */
- else if (*err_str == 'd' || *err_str == 'D') {
- if (use_double) {
- result.d *= 24. * 60. * 60. * 1000.;
- }
- else {
- result.i *= 24 * 60 * 60 * 1000;
- }
- }
- else {
- msg_warn ("invalid time value '%s' at position '%s'", t, err_str);
- if (use_double) {
- result.d = 0.;
- }
- else {
- result.i = 0;
- }
- }
- }
- else {
- /* Switch to default time multiplier */
- switch (default_type) {
- case TIME_HOURS:
- if (use_double) {
- result.d *= 60. * 60. * 1000.;
- }
- else {
- result.i *= 60 * 60 * 1000;
- }
- break;
- case TIME_MINUTES:
- if (use_double) {
- result.d *= 60. * 1000.;
- }
- else {
- result.i *= 60 * 1000;
- }
- break;
- case TIME_SECONDS:
- if (use_double) {
- result.d *= 1000.;
- }
- else {
- result.i *= 1000;
- }
- break;
- case TIME_MILLISECONDS:
- break;
- }
- }
- if (use_double) {
- return result.d;
- }
- else {
- return (gdouble)result.i;
- }
- }
-
- gchar
- parse_flag (const gchar *str)
- {
- guint len;
- gchar c;
-
- if (!str || !*str) {
- return -1;
- }
-
- len = strlen (str);
-
- switch (len) {
- case 1:
- c = g_ascii_tolower (*str);
- if (c == 'y' || c == '1') {
- return 1;
- }
- else if (c == 'n' || c == '0') {
- return 0;
- }
- break;
- case 2:
- if (g_ascii_strncasecmp (str, "no", len) == 0) {
- return 0;
- }
- else if (g_ascii_strncasecmp (str, "on", len) == 0) {
- return 1;
- }
- break;
- case 3:
- if (g_ascii_strncasecmp (str, "yes", len) == 0) {
- return 1;
- }
- else if (g_ascii_strncasecmp (str, "off", len) == 0) {
- return 0;
- }
- break;
- case 4:
- if (g_ascii_strncasecmp (str, "true", len) == 0) {
- return 1;
- }
- break;
- case 5:
- if (g_ascii_strncasecmp (str, "false", len) == 0) {
- return 0;
- }
- break;
- }
-
- return -1;
- }
-
- /*
- * Try to substitute all variables in given string
- * Return: newly allocated string with substituted variables (original string may be freed if variables are found)
- */
- gchar *
- substitute_variable (struct config_file *cfg, gchar *name, gchar *str, guchar recursive)
- {
- gchar *var, *new, *v_begin, *v_end, *p, t;
- gsize len;
- gboolean changed = FALSE;
-
- if (str == NULL) {
- msg_warn ("trying to substitute variable in NULL string");
- return NULL;
- }
-
- p = str;
- while ((v_begin = strstr (p, "${")) != NULL) {
- len = strlen (str);
- *v_begin = '\0';
- v_begin += 2;
- if ((v_end = strstr (v_begin, "}")) == NULL) {
- /* Not a variable, skip */
- p = v_begin;
- continue;
- }
- t = *v_end;
- *v_end = '\0';
- var = g_hash_table_lookup (cfg->variables, v_begin);
- if (var == NULL) {
- msg_warn ("variable %s is not defined", v_begin);
- *v_end = t;
- p = v_end + 1;
- continue;
- }
- else if (recursive) {
- var = substitute_variable (cfg, v_begin, var, recursive);
- }
- /* Allocate new string */
- new = memory_pool_alloc (cfg->cfg_pool, len - strlen (v_begin) + strlen (var) + 3);
-
- snprintf (new, len - strlen (v_begin) + strlen (var) + 3, "%s(%s)%s", str, var, v_end + 1);
- p = new;
- str = new;
- changed = TRUE;
- }
-
- if (changed && name != NULL) {
- g_hash_table_insert (cfg->variables, name, str);
- }
-
- return str;
- }
-
- static void
- substitute_module_variables (gpointer key, gpointer value, gpointer data)
- {
- struct config_file *cfg = (struct config_file *)data;
- GList *cur_opt = (GList *) value;
- struct module_opt *cur;
-
- while (cur_opt) {
- cur = cur_opt->data;
- if (cur->value) {
- cur->value = substitute_variable (cfg, NULL, cur->value, 1);
- }
- cur_opt = g_list_next (cur_opt);
- }
- }
-
- static void
- substitute_all_variables (gpointer key, gpointer value, gpointer data)
- {
- struct config_file *cfg = (struct config_file *)data;
-
- /* Do recursive substitution */
- (void)substitute_variable (cfg, (gchar *)key, (gchar *)value, 1);
- }
-
- /*
- * Place pointers to cfg_file structure to hash cfg_params
- */
- static void
- fill_cfg_params (struct config_file *cfg)
- {
- struct config_scalar *scalars;
-
- scalars = memory_pool_alloc (cfg->cfg_pool, 10 * sizeof (struct config_scalar));
-
- scalars[0].type = SCALAR_TYPE_STR;
- scalars[0].pointer = &cfg->cfg_name;
- g_hash_table_insert (cfg->cfg_params, "cfg_name", &scalars[0]);
- scalars[1].type = SCALAR_TYPE_STR;
- scalars[1].pointer = &cfg->pid_file;
- g_hash_table_insert (cfg->cfg_params, "pid_file", &scalars[1]);
- scalars[2].type = SCALAR_TYPE_STR;
- scalars[2].pointer = &cfg->temp_dir;
- g_hash_table_insert (cfg->cfg_params, "temp_dir", &scalars[2]);
- scalars[3].type = SCALAR_TYPE_SIZE;
- scalars[3].pointer = &cfg->max_statfile_size;
- g_hash_table_insert (cfg->cfg_params, "max_statfile_size", &scalars[3]);
-
- }
-
- gboolean
- get_config_checksum (struct config_file *cfg)
- {
- 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 file %s is no longer available, cannot calculate checksum");
- return FALSE;
- }
- if (stat (cfg->cfg_name, &st) == -1) {
- msg_err ("cannot stat %s: %s", cfg->cfg_name, strerror (errno));
- 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 ("cannot mmap %s: %s", cfg->cfg_name, strerror (errno));
- close (fd);
- 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);
-
- return TRUE;
- }
- /*
- * Perform post load actions
- */
- void
- post_load_config (struct config_file *cfg)
- {
- #ifdef HAVE_CLOCK_GETTIME
- struct timespec ts;
- #endif
- struct metric *def_metric;
-
- g_hash_table_foreach (cfg->variables, substitute_all_variables, cfg);
- g_hash_table_foreach (cfg->modules_opts, substitute_module_variables, cfg);
- fill_cfg_params (cfg);
-
- #ifdef HAVE_CLOCK_GETTIME
- #ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID
- clock_getres (CLOCK_PROCESS_CPUTIME_ID, &ts);
- # elif defined(HAVE_CLOCK_VIRTUAL)
- clock_getres (CLOCK_VIRTUAL, &ts);
- # else
- clock_getres (CLOCK_REALTIME, &ts);
- # endif
-
- cfg->clock_res = (gint)log10 (1000000 / ts.tv_nsec);
- if (cfg->clock_res < 0) {
- cfg->clock_res = 0;
- }
- if (cfg->clock_res > 3) {
- cfg->clock_res = 3;
- }
- #else
- /* For gettimeofday */
- cfg->clock_res = 1;
- #endif
-
- if ((def_metric = g_hash_table_lookup (cfg->metrics, DEFAULT_METRIC)) == NULL) {
- def_metric = check_metric_conf (cfg, NULL);
- def_metric->name = DEFAULT_METRIC;
- def_metric->required_score = DEFAULT_SCORE;
- def_metric->reject_score = DEFAULT_REJECT_SCORE;
- cfg->metrics_list = g_list_prepend (cfg->metrics_list, def_metric);
- g_hash_table_insert (cfg->metrics, DEFAULT_METRIC, def_metric);
- }
-
- cfg->default_metric = def_metric;
-
- /* Lua options */
- (void)lua_post_load_config (cfg);
- init_dynamic_config (cfg);
- }
-
- #if 0
- void
- parse_err (const gchar *fmt, ...)
- {
- va_list aq;
- gchar logbuf[BUFSIZ], readbuf[32];
- gint r;
-
- va_start (aq, fmt);
- rspamd_strlcpy (readbuf, yytext, sizeof (readbuf));
-
- r = snprintf (logbuf, sizeof (logbuf), "config file parse error! line: %d, text: %s, reason: ", yylineno, readbuf);
- r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
-
- va_end (aq);
- g_critical ("%s", logbuf);
- }
-
- void
- parse_warn (const gchar *fmt, ...)
- {
- va_list aq;
- gchar logbuf[BUFSIZ], readbuf[32];
- gint r;
-
- va_start (aq, fmt);
- rspamd_strlcpy (readbuf, yytext, sizeof (readbuf));
-
- r = snprintf (logbuf, sizeof (logbuf), "config file parse warning! line: %d, text: %s, reason: ", yylineno, readbuf);
- r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
-
- va_end (aq);
- g_warning ("%s", logbuf);
- }
- #endif
-
- void
- unescape_quotes (gchar *line)
- {
- gchar *c = line, *t;
-
- while (*c) {
- if (*c == '\\' && *(c + 1) == '"') {
- t = c;
- while (*t) {
- *t = *(t + 1);
- t++;
- }
- }
- c++;
- }
- }
-
- GList *
- parse_comma_list (memory_pool_t * pool, gchar *line)
- {
- GList *res = NULL;
- gchar *c, *p, *str;
-
- c = line;
- p = c;
-
- while (*p) {
- if (*p == ',' && *c != *p) {
- str = memory_pool_alloc (pool, p - c + 1);
- rspamd_strlcpy (str, c, p - c + 1);
- res = g_list_prepend (res, str);
- /* Skip spaces */
- while (g_ascii_isspace (*(++p)));
- c = p;
- continue;
- }
- p++;
- }
- if (res != NULL) {
- memory_pool_add_destructor (pool, (pool_destruct_func) g_list_free, res);
- }
-
- return res;
- }
-
- struct classifier_config *
- check_classifier_conf (struct config_file *cfg, struct classifier_config *c)
- {
- if (c == NULL) {
- c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct classifier_config));
- }
- if (c->opts == NULL) {
- c->opts = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->opts);
- }
- if (c->labels == NULL) {
- c->labels = g_hash_table_new_full (rspamd_str_hash, rspamd_str_equal, NULL, (GDestroyNotify)g_list_free);
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->labels);
- }
-
- return c;
- }
-
- struct statfile*
- check_statfile_conf (struct config_file *cfg, struct statfile *c)
- {
- if (c == NULL) {
- c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct statfile));
- }
- if (c->opts == NULL) {
- c->opts = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->opts);
- }
-
- return c;
- }
-
- struct metric *
- check_metric_conf (struct config_file *cfg, struct metric *c)
- {
- if (c == NULL) {
- c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
- c->action = METRIC_ACTION_REJECT;
- c->grow_factor = 1.0;
- c->symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- c->descriptions = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->symbols);
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->descriptions);
- }
-
- return c;
- }
-
- struct worker_conf *
- check_worker_conf (struct config_file *cfg, struct worker_conf *c)
- {
- if (c == NULL) {
- c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct worker_conf));
- c->params = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
- c->active_workers = g_queue_new ();
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_hash_table_destroy, c->params);
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_queue_free, c->active_workers);
- #ifdef HAVE_SC_NPROCESSORS_ONLN
- c->count = sysconf (_SC_NPROCESSORS_ONLN);
- #else
- c->count = DEFAULT_WORKERS_NUM;
- #endif
- c->rlimit_nofile = DEFAULT_RLIMIT_NOFILE;
- c->rlimit_maxcore = DEFAULT_RLIMIT_MAXCORE;
- }
-
- return c;
- }
-
- static double
- internal_normalizer_func (struct config_file *cfg, long double score, void *data)
- {
- long double max = *(double *)data;
-
- if (score < 0) {
- return score;
- }
- #ifdef HAVE_TANHL
- return max * tanhl (score / max);
- #else
- /*
- * As some implementations of libm does not support tanhl, try to use
- * tanh
- */
- return max * tanh ((double) (score / max));
- #endif
- }
-
- static gboolean
- parse_internal_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
- {
- double *max;
- gchar *err;
-
- /* Line contains maximum value for internal normalizer */
- max = memory_pool_alloc (cfg->cfg_pool, sizeof (double));
-
- errno = 0;
- *max = strtod (line, &err);
-
- if (errno != 0 || *err != '\0') {
- msg_err ("cannot parse max number for internal normalizer");
- return FALSE;
- }
-
- st->normalizer = internal_normalizer_func;
- st->normalizer_data = (void *)max;
- return TRUE;
- }
-
- #ifdef WITH_LUA
- static gboolean
- parse_lua_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
- {
- gchar *code_begin;
- GList *params = NULL;
- gint len;
-
- code_begin = strchr (line, ':');
-
- if (code_begin == NULL) {
- /* Just function name without code */
- params = g_list_prepend (g_list_prepend (NULL, NULL), memory_pool_strdup (cfg->cfg_pool, line));
- }
- else {
- /* Postpone actual code load as lua libraries are not loaded */
- /* Put code to list */
- params = g_list_prepend (NULL, code_begin + 1);
- /* Put function name */
- len = code_begin - line;
- code_begin = memory_pool_alloc (cfg->cfg_pool, len + 1);
- rspamd_strlcpy (code_begin, line, len + 1);
- params = g_list_prepend (params, code_begin);
- }
- memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_list_free, params);
- st->normalizer = lua_normalizer_func;
- st->normalizer_data = params;
- return TRUE;
- }
- #endif
-
-
- gboolean
- parse_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
- {
- gchar *params_begin;
-
- params_begin = strchr (line, ':');
- if (params_begin == NULL) {
- msg_err ("no parameters are specified for normalizer %s", line);
- return FALSE;
- }
-
- /* Try to guess normalizer */
- if (g_ascii_strncasecmp (line, "internal", sizeof ("points")) == 0) {
- return parse_internal_normalizer (cfg, st, params_begin + 1);
- }
- #ifdef WITH_LUA
- else if (g_ascii_strncasecmp (line, "points", sizeof ("points")) == 0) {
- return parse_lua_normalizer (cfg, st, params_begin + 1);
- }
- #endif
-
- msg_err ("unknown normalizer %s", line);
- return FALSE;
- }
-
- static GMarkupParser xml_parser = {
- .start_element = rspamd_xml_start_element,
- .end_element = rspamd_xml_end_element,
- .passthrough = NULL,
- .text = rspamd_xml_text,
- .error = rspamd_xml_error,
- };
-
- gboolean
- read_xml_config (struct config_file *cfg, const gchar *filename)
- {
- struct stat st;
- gint fd;
- gchar *data;
- gboolean res;
- GMarkupParseContext *ctx;
- GError *err = NULL;
-
- struct rspamd_xml_userdata ud;
-
- if (stat (filename, &st) == -1) {
- msg_err ("cannot stat %s: %s", filename, strerror (errno));
- return FALSE;
- }
- if ((fd = open (filename, O_RDONLY)) == -1) {
- msg_err ("cannot open %s: %s", filename, strerror (errno));
- return FALSE;
-
- }
- /* Now mmap this file to simplify reading process */
- if ((data = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
- msg_err ("cannot mmap %s: %s", filename, strerror (errno));
- close (fd);
- return FALSE;
- }
- close (fd);
-
- /* Prepare xml parser */
- ud.cfg = cfg;
- ud.state = XML_READ_START;
- ud.if_stack = g_queue_new ();
-
- ctx = g_markup_parse_context_new (&xml_parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &ud, NULL);
- init_kvstorage_config ();
- res = g_markup_parse_context_parse (ctx, data, st.st_size, &err);
-
- if (g_queue_get_length (ud.if_stack) != 0) {
- msg_err ("unexpected nesting for if arguments");
- res = FALSE;
- }
-
- munmap (data, st.st_size);
-
- return res;
- }
-
- static void
- modules_config_callback (gpointer key, gpointer value, gpointer ud)
- {
- extern GHashTable *module_options;
- GHashTable *cur_module;
- GList *cur;
- struct module_opt *opt;
- const gchar *mname = key;
- gboolean *res = ud;
-
- if ((cur_module = g_hash_table_lookup (module_options, mname)) == NULL) {
- msg_warn ("module %s has not registered any options but is presented in configuration", mname);
- *res = FALSE;
- return;
- }
-
- cur = value;
- while (cur) {
- opt = cur->data;
-
- if (!opt->is_lua && !check_module_option (mname, opt->param, opt->value)) {
- *res = FALSE;
- return;
- }
-
- cur = g_list_next (cur);
- }
- }
-
- gboolean
- check_modules_config (struct config_file *cfg)
- {
- gboolean res = TRUE;
-
- g_hash_table_foreach (cfg->modules_opts, modules_config_callback, &res);
- return res;
- }
-
- static void
- symbols_classifiers_callback (gpointer key, gpointer value, gpointer ud)
- {
- struct config_file *cfg = ud;
-
- register_virtual_symbol (&cfg->cache, key, 1.0);
- }
-
- void
- insert_classifier_symbols (struct config_file *cfg)
- {
- g_hash_table_foreach (cfg->classifiers_symbols, symbols_classifiers_callback, cfg);
- }
-
- struct classifier_config*
- find_classifier_conf (struct config_file *cfg, const gchar *name)
- {
- GList *cur;
- struct classifier_config *cf;
-
- if (name == NULL) {
- return NULL;
- }
-
- cur = cfg->classifiers;
- while (cur) {
- cf = cur->data;
-
- if (g_ascii_strcasecmp (cf->classifier->name, name) == 0) {
- return cf;
- }
-
- cur = g_list_next (cur);
- }
-
- return NULL;
- }
-
- gboolean
- check_classifier_statfiles (struct classifier_config *cf)
- {
- struct statfile *st;
- gboolean has_other = FALSE, res = FALSE, cur_class;
- GList *cur;
-
- /* First check classes directly */
- cur = cf->statfiles;
- while (cur) {
- st = cur->data;
- if (!has_other) {
- cur_class = st->is_spam;
- has_other = TRUE;
- }
- else {
- if (cur_class != st->is_spam) {
- return TRUE;
- }
- }
-
- cur = g_list_next (cur);
- }
-
- if (!has_other) {
- /* We have only one statfile */
- return FALSE;
- }
- /* We have not detected any statfile that has different class, so turn on euristic based on symbol's name */
- has_other = FALSE;
- cur = cf->statfiles;
- while (cur) {
- st = cur->data;
- if (rspamd_strncasestr (st->symbol, "spam", -1) != NULL) {
- st->is_spam = TRUE;
- }
- else if (rspamd_strncasestr (st->symbol, "ham", -1) != NULL) {
- st->is_spam = FALSE;
- }
-
- if (!has_other) {
- cur_class = st->is_spam;
- has_other = TRUE;
- }
- else {
- if (cur_class != st->is_spam) {
- res = TRUE;
- }
- }
-
- cur = g_list_next (cur);
- }
-
- return res;
- }
-
- /*
- * vi:ts=4
- */
|